From cb85ecf124bdbd6054d7d3dc3f8495b1b07d63ac Mon Sep 17 00:00:00 2001 From: marcrasi Date: Thu, 3 Oct 2019 10:37:38 -0700 Subject: [PATCH 001/478] SymbolicValue is_pod -> is_trivial is_pod is getting deprecated. All we really care about is that the type stay trivial. (And that's just a performance thing, I don't think we rely on that anywhere). --- include/swift/SIL/SILConstants.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swift/SIL/SILConstants.h b/include/swift/SIL/SILConstants.h index a3b7909a4e00e..966a4252092fb 100644 --- a/include/swift/SIL/SILConstants.h +++ b/include/swift/SIL/SILConstants.h @@ -558,8 +558,8 @@ class SymbolicValue { static_assert(sizeof(SymbolicValue) == 2 * sizeof(void *), "SymbolicValue should stay small"); -static_assert(std::is_pod::value, - "SymbolicValue should stay POD"); +static_assert(std::is_trivial::value, + "SymbolicValue should stay trivial"); inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, SymbolicValue val) { val.print(os); From f5677cbe42b1a703ff5aabb6607a8dd57b1b0ce5 Mon Sep 17 00:00:00 2001 From: Marc Rasi Date: Thu, 14 Nov 2019 16:40:45 -0800 Subject: [PATCH 002/478] add @nondiff to AnyFunctionType params --- include/swift/AST/Attr.def | 1 + include/swift/AST/DiagnosticsSema.def | 5 ++ include/swift/AST/Types.h | 35 +++++++++----- lib/AST/ASTContext.cpp | 7 +-- lib/AST/ASTPrinter.cpp | 2 + lib/AST/Decl.cpp | 9 ++-- lib/AST/TypeRepr.cpp | 2 + lib/Sema/TypeCheckType.cpp | 48 +++++++++++++++---- lib/Serialization/Deserialization.cpp | 8 ++-- lib/Serialization/ModuleFormat.h | 15 +++--- lib/Serialization/Serialization.cpp | 4 +- .../ModuleInterface/differentiation.swift | 3 ++ .../Parse/differentiable_func_type.swift | 13 +++++ .../differentiable_features_disabled.swift | 10 ++++ .../Sema/differentiable_func_type.swift | 21 ++++++++ .../Serialization/differentiation.swift | 3 ++ 16 files changed, 147 insertions(+), 39 deletions(-) diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 301028b5bdc01..9d56b8e611ec3 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -52,6 +52,7 @@ TYPE_ATTR(convention) TYPE_ATTR(noescape) TYPE_ATTR(escaping) TYPE_ATTR(differentiable) +TYPE_ATTR(nondiff) // SIL-specific attributes TYPE_ATTR(block_storage) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index df60139eea9e4..4e2e5bb177652 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3866,6 +3866,11 @@ ERROR(opaque_type_in_protocol_requirement,none, "'some' type cannot be the return type of a protocol requirement; did you mean to add an associated type?", ()) +// Function differentiability +ERROR(attr_only_on_parameters_of_differentiable,none, + "'%0' may only be used on parameters of '@differentiable' function " + "types", (StringRef)) + // SIL ERROR(opened_non_protocol,none, "@opened cannot be applied to non-protocol type %0", (Type)) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index c816e5ab3cbcb..6187278226dc2 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -1778,8 +1778,8 @@ class ParameterTypeFlags { NonEphemeral = 1 << 2, OwnershipShift = 3, Ownership = 7 << OwnershipShift, - - NumBits = 6 + NonDifferentiable = 1 << 7, + NumBits = 7 }; OptionSet value; static_assert(NumBits < 8*sizeof(OptionSet), "overflowed"); @@ -1793,15 +1793,17 @@ class ParameterTypeFlags { } ParameterTypeFlags(bool variadic, bool autoclosure, bool nonEphemeral, - ValueOwnership ownership) + ValueOwnership ownership, bool nonDifferentiable) : value((variadic ? Variadic : 0) | (autoclosure ? AutoClosure : 0) | (nonEphemeral ? NonEphemeral : 0) | - uint8_t(ownership) << OwnershipShift) {} + uint8_t(ownership) << OwnershipShift | + (nonDifferentiable ? NonDifferentiable : 0)) {} /// Create one from what's present in the parameter type inline static ParameterTypeFlags fromParameterType(Type paramTy, bool isVariadic, bool isAutoClosure, - bool isNonEphemeral, ValueOwnership ownership); + bool isNonEphemeral, ValueOwnership ownership, + bool isNonDifferentiable); bool isNone() const { return !value; } bool isVariadic() const { return value.contains(Variadic); } @@ -1810,6 +1812,7 @@ class ParameterTypeFlags { bool isInOut() const { return getValueOwnership() == ValueOwnership::InOut; } bool isShared() const { return getValueOwnership() == ValueOwnership::Shared;} bool isOwned() const { return getValueOwnership() == ValueOwnership::Owned; } + bool isNonDifferentiable() const { return value.contains(NonDifferentiable); } ValueOwnership getValueOwnership() const { return ValueOwnership((value.toRaw() & Ownership) >> OwnershipShift); @@ -1852,6 +1855,12 @@ class ParameterTypeFlags { : value - ParameterTypeFlags::NonEphemeral); } + ParameterTypeFlags withNonDifferentiable(bool nonDifferentiable) const { + return ParameterTypeFlags( + nonDifferentiable ? value | ParameterTypeFlags::NonDifferentiable + : value - ParameterTypeFlags::NonDifferentiable); + } + bool operator ==(const ParameterTypeFlags &other) const { return value.toRaw() == other.value.toRaw(); } @@ -1919,7 +1928,8 @@ class YieldTypeFlags { return ParameterTypeFlags(/*variadic*/ false, /*autoclosure*/ false, /*nonEphemeral*/ false, - getValueOwnership()); + getValueOwnership(), + /*nonDifferentiable*/ false); } bool operator ==(const YieldTypeFlags &other) const { @@ -2791,6 +2801,9 @@ class AnyFunctionType : public TypeBase { /// Whether the parameter is marked '@_nonEphemeral' bool isNonEphemeral() const { return Flags.isNonEphemeral(); } + /// Whether the parameter is marked '@nondiff'. + bool isNonDifferentiable() const { return Flags.isNonDifferentiable(); } + ValueOwnership getValueOwnership() const { return Flags.getValueOwnership(); } @@ -5684,10 +5697,9 @@ inline TupleTypeElt TupleTypeElt::getWithType(Type T) const { } /// Create one from what's present in the parameter decl and type -inline ParameterTypeFlags -ParameterTypeFlags::fromParameterType(Type paramTy, bool isVariadic, - bool isAutoClosure, bool isNonEphemeral, - ValueOwnership ownership) { +inline ParameterTypeFlags ParameterTypeFlags::fromParameterType( + Type paramTy, bool isVariadic, bool isAutoClosure, bool isNonEphemeral, + ValueOwnership ownership, bool isNonDifferentiable) { // FIXME(Remove InOut): The last caller that needs this is argument // decomposition. Start by enabling the assertion there and fixing up those // callers, then remove this, then remove @@ -5697,7 +5709,8 @@ ParameterTypeFlags::fromParameterType(Type paramTy, bool isVariadic, ownership == ValueOwnership::InOut); ownership = ValueOwnership::InOut; } - return {isVariadic, isAutoClosure, isNonEphemeral, ownership}; + return {isVariadic, isAutoClosure, isNonEphemeral, ownership, + isNonDifferentiable}; } inline const Type *BoundGenericType::getTrailingObjectsPointer() const { diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index ad7bf0d7308ea..b16a4ecd1b658 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2885,9 +2885,10 @@ void AnyFunctionType::decomposeInput( } default: - result.emplace_back(type->getInOutObjectType(), Identifier(), - ParameterTypeFlags::fromParameterType( - type, false, false, false, ValueOwnership::Default)); + result.emplace_back( + type->getInOutObjectType(), Identifier(), + ParameterTypeFlags::fromParameterType(type, false, false, false, + ValueOwnership::Default, false)); return; } } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index f48b5a60f7f24..e9077d094354f 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2476,6 +2476,8 @@ static void printParameterFlags(ASTPrinter &printer, PrintOptions options, ParameterTypeFlags flags, bool escaping) { if (!options.excludeAttrKind(TAK_autoclosure) && flags.isAutoClosure()) printer << "@autoclosure "; + if (!options.excludeAttrKind(TAK_nondiff) && flags.isNonDifferentiable()) + printer << "@nondiff "; switch (flags.getValueOwnership()) { case ValueOwnership::Default: diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 1e1d93ec4f036..afe07b7695c67 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6035,11 +6035,10 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const { type = ParamDecl::getVarargBaseTy(type); auto label = getArgumentName(); - auto flags = ParameterTypeFlags::fromParameterType(type, - isVariadic(), - isAutoClosure(), - isNonEphemeral(), - getValueOwnership()); + auto flags = ParameterTypeFlags::fromParameterType( + type, isVariadic(), isAutoClosure(), isNonEphemeral(), + getValueOwnership(), + /*isNonDifferentiable*/ false); return AnyFunctionType::Param(type, label, flags); } diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index c6168a918ac0a..a643a7f18b852 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -298,6 +298,8 @@ void AttributedTypeRepr::printAttrs(ASTPrinter &Printer, Printer.printSimpleAttr("@autoclosure") << " "; if (hasAttr(TAK_escaping)) Printer.printSimpleAttr("@escaping") << " "; + if (hasAttr(TAK_nondiff)) + Printer.printSimpleAttr("@nondiff") << " "; if (hasAttr(TAK_differentiable)) { if (Attrs.isLinear()) { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 6b8dd9d5e8633..5b4a65c222002 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1736,7 +1736,7 @@ namespace { bool resolveASTFunctionTypeParams(TupleTypeRepr *inputRepr, TypeResolutionOptions options, - bool requiresMappingOut, + bool requiresMappingOut, bool isDifferentiable, SmallVectorImpl &ps); Type resolveSILFunctionType(FunctionTypeRepr *repr, @@ -1970,6 +1970,11 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Remember whether this is a function parameter. bool isParam = options.is(TypeResolverContext::FunctionInput); + // Remember whether this is a variadic function parameter. + bool isVariadicFunctionParam = + options.is(TypeResolverContext::VariadicFunctionInput) && + !options.hasBase(TypeResolverContext::EnumElementDecl); + // The type we're working with, in case we want to build it differently // based on the attributes we see. Type ty; @@ -2198,10 +2203,6 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // @autoclosure is only valid on parameters. if (!isParam && attrs.has(TAK_autoclosure)) { - bool isVariadicFunctionParam = - options.is(TypeResolverContext::VariadicFunctionInput) && - !options.hasBase(TypeResolverContext::EnumElementDecl); - diagnose(attrs.getLoc(TAK_autoclosure), isVariadicFunctionParam ? diag::attr_not_on_variadic_parameters : diag::attr_only_on_parameters, @@ -2308,6 +2309,21 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, attrs.convention = None; } + if (attrs.has(TAK_nondiff)) { + if (!Context.LangOpts.EnableExperimentalDifferentiableProgramming) { + diagnose(attrs.getLoc(TAK_nondiff), + diag::experimental_differentiable_programming_disabled); + } else if (!isParam) { + // @nondiff is only valid on parameters. + diagnose(attrs.getLoc(TAK_nondiff), + (isVariadicFunctionParam + ? diag::attr_not_on_variadic_parameters + : diag::attr_only_on_parameters_of_differentiable), + "@nondiff"); + } + attrs.clearAttribute(TAK_nondiff); + } + // In SIL, handle @opened (n), which creates an existential archetype. if (attrs.has(TAK_opened)) { if (!ty->isExistentialType()) { @@ -2360,7 +2376,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, bool TypeResolver::resolveASTFunctionTypeParams( TupleTypeRepr *inputRepr, TypeResolutionOptions options, - bool requiresMappingOut, + bool requiresMappingOut, bool isDifferentiable, SmallVectorImpl &elements) { elements.reserve(inputRepr->getNumElements()); @@ -2424,8 +2440,23 @@ bool TypeResolver::resolveASTFunctionTypeParams( ownership = ValueOwnership::Default; break; } + + bool nondiff = false; + if (auto *attrTypeRepr = dyn_cast(eltTypeRepr)) { + if (attrTypeRepr->getAttrs().has(TAK_nondiff)) { + if (!isDifferentiable && + Context.LangOpts.EnableExperimentalDifferentiableProgramming) + diagnose(eltTypeRepr->getLoc(), + diag::attr_only_on_parameters_of_differentiable, "@nondiff") + .highlight(eltTypeRepr->getSourceRange()); + else + nondiff = true; + } + } + auto paramFlags = ParameterTypeFlags::fromParameterType( - ty, variadic, autoclosure, /*isNonEphemeral*/ false, ownership); + ty, variadic, autoclosure, /*isNonEphemeral*/ false, ownership, + nondiff); elements.emplace_back(ty, Identifier(), paramFlags); } @@ -2477,7 +2508,8 @@ Type TypeResolver::resolveASTFunctionType(FunctionTypeRepr *repr, SmallVector params; if (resolveASTFunctionTypeParams(repr->getArgsTypeRepr(), options, - repr->getGenericEnvironment() != nullptr, params)) { + repr->getGenericEnvironment() != nullptr, + extInfo.isDifferentiable(), params)) { return Type(); } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 56e23d273fc8f..a3b8ff9b52ef7 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4673,12 +4673,13 @@ class swift::TypeDeserializer { IdentifierID labelID; TypeID typeID; - bool isVariadic, isAutoClosure, isNonEphemeral; + bool isVariadic, isAutoClosure, isNonEphemeral, isNonDifferentiable; unsigned rawOwnership; decls_block::FunctionParamLayout::readRecord(scratch, labelID, typeID, isVariadic, isAutoClosure, isNonEphemeral, - rawOwnership); + rawOwnership, + isNonDifferentiable); auto ownership = getActualValueOwnership((serialization::ValueOwnership)rawOwnership); @@ -4692,7 +4693,8 @@ class swift::TypeDeserializer { params.emplace_back(paramTy.get(), MF.getIdentifier(labelID), ParameterTypeFlags(isVariadic, isAutoClosure, - isNonEphemeral, *ownership)); + isNonEphemeral, *ownership, + isNonDifferentiable)); } if (!isGeneric) { diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 18907813aa127..13eaf473f3d9b 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 524; // function type differentiability +const uint16_t SWIFTMODULE_VERSION_MINOR = 525; // function parameter nondiff /// A standard hash seed used for all string hashes in a serialized module. /// @@ -893,12 +893,13 @@ namespace decls_block { using FunctionParamLayout = BCRecordLayout< FUNCTION_PARAM, - IdentifierIDField, // name - TypeIDField, // type - BCFixed<1>, // vararg? - BCFixed<1>, // autoclosure? - BCFixed<1>, // non-ephemeral? - ValueOwnershipField // inout, shared or owned? + IdentifierIDField, // name + TypeIDField, // type + BCFixed<1>, // vararg? + BCFixed<1>, // autoclosure? + BCFixed<1>, // non-ephemeral? + ValueOwnershipField, // inout, shared or owned? + BCFixed<1> // nondifferentiable? >; using MetatypeTypeLayout = BCRecordLayout< diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index a2b76ccc90989..bf9920d7598c5 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3931,8 +3931,8 @@ class Serializer::TypeSerializer : public TypeVisitor { S.Out, S.ScratchRecord, abbrCode, S.addDeclBaseNameRef(param.getLabel()), S.addTypeRef(param.getPlainType()), paramFlags.isVariadic(), - paramFlags.isAutoClosure(), paramFlags.isNonEphemeral(), - rawOwnership); + paramFlags.isAutoClosure(), paramFlags.isNonEphemeral(), rawOwnership, + paramFlags.isNonDifferentiable()); } } diff --git a/test/AutoDiff/ModuleInterface/differentiation.swift b/test/AutoDiff/ModuleInterface/differentiation.swift index 6b3a791fb9ca3..2e6ef3534bf99 100644 --- a/test/AutoDiff/ModuleInterface/differentiation.swift +++ b/test/AutoDiff/ModuleInterface/differentiation.swift @@ -6,3 +6,6 @@ public func a(f: @differentiable (Float) -> Float) {} public func b(f: @differentiable(linear) (Float) -> Float) {} // CHECK: public func b(f: @differentiable(linear) (Swift.Float) -> Swift.Float) + +public func c(f: @differentiable (Float, @nondiff Float) -> Float) {} +// CHECK: public func c(f: @differentiable (Swift.Float, @nondiff Swift.Float) -> Swift.Float) diff --git a/test/AutoDiff/Parse/differentiable_func_type.swift b/test/AutoDiff/Parse/differentiable_func_type.swift index e6ceb0937821a..2bff11d10ae16 100644 --- a/test/AutoDiff/Parse/differentiable_func_type.swift +++ b/test/AutoDiff/Parse/differentiable_func_type.swift @@ -8,6 +8,19 @@ let b: @differentiable(linear) (Float) -> Float // okay // CHECK: (pattern_named 'b' // CHECK-NEXT: (type_attributed attrs=@differentiable(linear) +let c: @differentiable (Float, @nondiff Float) -> Float // okay +// CHECK: (pattern_named 'c' +// CHECK-NEXT: (type_attributed attrs=@differentiable +// CHECK-NEXT: (type_function +// CHECK-NEXT: (type_tuple +// CHECK-NEXT: (type_ident +// CHECK-NEXT: (component id='Float' bind=none)) +// CHECK-NEXT: (type_attributed attrs=@nondiff +// CHECK-NEXT: (type_ident +// CHECK-NEXT: (component id='Float' bind=none))) +// CHECK-NEXT: (type_ident +// CHECK-NEXT: (component id='Float' bind=none))))) + // Generic type test. struct A { func foo() { diff --git a/test/AutoDiff/Sema/differentiable_features_disabled.swift b/test/AutoDiff/Sema/differentiable_features_disabled.swift index a4afe4463389b..8ad0e0315b666 100644 --- a/test/AutoDiff/Sema/differentiable_features_disabled.swift +++ b/test/AutoDiff/Sema/differentiable_features_disabled.swift @@ -2,3 +2,13 @@ // expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} let _: @differentiable (Float) -> Float + +// expected-error @+2 {{differentiable programming is an experimental feature that is currently disabled}} +// expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} +let _: @differentiable (Float, @nondiff Float) -> Float + +// expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} +let _: (Float, @nondiff Float) -> Float + +// expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} +let _: @nondiff Float diff --git a/test/AutoDiff/Sema/differentiable_func_type.swift b/test/AutoDiff/Sema/differentiable_func_type.swift index 0ca2f8aa50296..a304102fa6e92 100644 --- a/test/AutoDiff/Sema/differentiable_func_type.swift +++ b/test/AutoDiff/Sema/differentiable_func_type.swift @@ -4,3 +4,24 @@ let _: @differentiable Float let _: @differentiable (Float) -> Float + +// expected-error @+1 {{'@nondiff' may only be used on parameters of '@differentiable' function types}} +let _: @nondiff Float + +// expected-error @+1 {{'@nondiff' may only be used on parameters of '@differentiable' function types}} +let _: (Float) -> @nondiff Float + +// expected-error @+1 {{'@nondiff' may only be used on parameters of '@differentiable' function types}} +let _: @differentiable (Float) -> @nondiff Float + +// expected-error @+1 {{'@nondiff' may only be used on parameters of '@differentiable' function types}} +let _: (@nondiff Float) -> Float + +// expected-error @+2 {{'@nondiff' may only be used on parameters of '@differentiable' function types}} +// expected-error @+1 {{'@nondiff' must not be used on variadic parameters}} +let _: (Float, @nondiff Float...) -> Float + +let _: @differentiable (@nondiff Float, Float) -> Float + +// expected-error @+1 {{'@nondiff' must not be used on variadic parameters}} +let _: @differentiable (Float, @nondiff Float...) -> Float diff --git a/test/AutoDiff/Serialization/differentiation.swift b/test/AutoDiff/Serialization/differentiation.swift index d5f1276c3f494..23541379a069b 100644 --- a/test/AutoDiff/Serialization/differentiation.swift +++ b/test/AutoDiff/Serialization/differentiation.swift @@ -10,3 +10,6 @@ func a(_ f: @differentiable (Float) -> Float) {} func b(_ f: @differentiable(linear) (Float) -> Float) {} // CHECK: func b(_ f: @differentiable(linear) (Float) -> Float) + +func c(_ f: @differentiable (Float, @nondiff Float) -> Float) {} +// CHECK: func c(_ f: @differentiable (Float, @nondiff Float) -> Float) From 6e559ab8fadc267cb01484e55636bcaa7b69f819 Mon Sep 17 00:00:00 2001 From: Marc Rasi Date: Fri, 15 Nov 2019 16:36:26 -0800 Subject: [PATCH 003/478] rename nondiff => noDerivative --- include/swift/AST/Attr.def | 2 +- include/swift/AST/Types.h | 26 ++++++++--------- lib/AST/ASTPrinter.cpp | 4 +-- lib/AST/Decl.cpp | 2 +- lib/AST/TypeRepr.cpp | 4 +-- lib/Sema/TypeCheckType.cpp | 22 +++++++-------- lib/Serialization/Deserialization.cpp | 6 ++-- lib/Serialization/ModuleFormat.h | 4 +-- lib/Serialization/Serialization.cpp | 2 +- .../ModuleInterface/differentiation.swift | 4 +-- .../Parse/differentiable_func_type.swift | 4 +-- .../differentiable_features_disabled.swift | 6 ++-- .../Sema/differentiable_func_type.swift | 28 +++++++++---------- .../Serialization/differentiation.swift | 4 +-- 14 files changed, 59 insertions(+), 59 deletions(-) diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 9d56b8e611ec3..67267699bf095 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -52,7 +52,7 @@ TYPE_ATTR(convention) TYPE_ATTR(noescape) TYPE_ATTR(escaping) TYPE_ATTR(differentiable) -TYPE_ATTR(nondiff) +TYPE_ATTR(noDerivative) // SIL-specific attributes TYPE_ATTR(block_storage) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 6187278226dc2..9c80a6b6b19fb 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -1778,7 +1778,7 @@ class ParameterTypeFlags { NonEphemeral = 1 << 2, OwnershipShift = 3, Ownership = 7 << OwnershipShift, - NonDifferentiable = 1 << 7, + NoDerivative = 1 << 7, NumBits = 7 }; OptionSet value; @@ -1793,17 +1793,17 @@ class ParameterTypeFlags { } ParameterTypeFlags(bool variadic, bool autoclosure, bool nonEphemeral, - ValueOwnership ownership, bool nonDifferentiable) + ValueOwnership ownership, bool noDerivative) : value((variadic ? Variadic : 0) | (autoclosure ? AutoClosure : 0) | (nonEphemeral ? NonEphemeral : 0) | uint8_t(ownership) << OwnershipShift | - (nonDifferentiable ? NonDifferentiable : 0)) {} + (noDerivative ? NoDerivative : 0)) {} /// Create one from what's present in the parameter type inline static ParameterTypeFlags fromParameterType(Type paramTy, bool isVariadic, bool isAutoClosure, bool isNonEphemeral, ValueOwnership ownership, - bool isNonDifferentiable); + bool isNoDerivative); bool isNone() const { return !value; } bool isVariadic() const { return value.contains(Variadic); } @@ -1812,7 +1812,7 @@ class ParameterTypeFlags { bool isInOut() const { return getValueOwnership() == ValueOwnership::InOut; } bool isShared() const { return getValueOwnership() == ValueOwnership::Shared;} bool isOwned() const { return getValueOwnership() == ValueOwnership::Owned; } - bool isNonDifferentiable() const { return value.contains(NonDifferentiable); } + bool isNoDerivative() const { return value.contains(NoDerivative); } ValueOwnership getValueOwnership() const { return ValueOwnership((value.toRaw() & Ownership) >> OwnershipShift); @@ -1855,10 +1855,10 @@ class ParameterTypeFlags { : value - ParameterTypeFlags::NonEphemeral); } - ParameterTypeFlags withNonDifferentiable(bool nonDifferentiable) const { + ParameterTypeFlags withNoDerivative(bool noDerivative) const { return ParameterTypeFlags( - nonDifferentiable ? value | ParameterTypeFlags::NonDifferentiable - : value - ParameterTypeFlags::NonDifferentiable); + noDerivative ? value | ParameterTypeFlags::NoDerivative + : value - ParameterTypeFlags::NoDerivative); } bool operator ==(const ParameterTypeFlags &other) const { @@ -1929,7 +1929,7 @@ class YieldTypeFlags { /*autoclosure*/ false, /*nonEphemeral*/ false, getValueOwnership(), - /*nonDifferentiable*/ false); + /*noDerivative*/ false); } bool operator ==(const YieldTypeFlags &other) const { @@ -2801,8 +2801,8 @@ class AnyFunctionType : public TypeBase { /// Whether the parameter is marked '@_nonEphemeral' bool isNonEphemeral() const { return Flags.isNonEphemeral(); } - /// Whether the parameter is marked '@nondiff'. - bool isNonDifferentiable() const { return Flags.isNonDifferentiable(); } + /// Whether the parameter is marked '@noDerivative'. + bool isNoDerivative() const { return Flags.isNoDerivative(); } ValueOwnership getValueOwnership() const { return Flags.getValueOwnership(); @@ -5699,7 +5699,7 @@ inline TupleTypeElt TupleTypeElt::getWithType(Type T) const { /// Create one from what's present in the parameter decl and type inline ParameterTypeFlags ParameterTypeFlags::fromParameterType( Type paramTy, bool isVariadic, bool isAutoClosure, bool isNonEphemeral, - ValueOwnership ownership, bool isNonDifferentiable) { + ValueOwnership ownership, bool isNoDerivative) { // FIXME(Remove InOut): The last caller that needs this is argument // decomposition. Start by enabling the assertion there and fixing up those // callers, then remove this, then remove @@ -5710,7 +5710,7 @@ inline ParameterTypeFlags ParameterTypeFlags::fromParameterType( ownership = ValueOwnership::InOut; } return {isVariadic, isAutoClosure, isNonEphemeral, ownership, - isNonDifferentiable}; + isNoDerivative}; } inline const Type *BoundGenericType::getTrailingObjectsPointer() const { diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index e9077d094354f..f2ca9e3904f3e 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2476,8 +2476,8 @@ static void printParameterFlags(ASTPrinter &printer, PrintOptions options, ParameterTypeFlags flags, bool escaping) { if (!options.excludeAttrKind(TAK_autoclosure) && flags.isAutoClosure()) printer << "@autoclosure "; - if (!options.excludeAttrKind(TAK_nondiff) && flags.isNonDifferentiable()) - printer << "@nondiff "; + if (!options.excludeAttrKind(TAK_noDerivative) && flags.isNoDerivative()) + printer << "@noDerivative "; switch (flags.getValueOwnership()) { case ValueOwnership::Default: diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index afe07b7695c67..53d39c776948d 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6038,7 +6038,7 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const { auto flags = ParameterTypeFlags::fromParameterType( type, isVariadic(), isAutoClosure(), isNonEphemeral(), getValueOwnership(), - /*isNonDifferentiable*/ false); + /*isNoDerivative*/ false); return AnyFunctionType::Param(type, label, flags); } diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index a643a7f18b852..5d3b0a2c27110 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -298,8 +298,8 @@ void AttributedTypeRepr::printAttrs(ASTPrinter &Printer, Printer.printSimpleAttr("@autoclosure") << " "; if (hasAttr(TAK_escaping)) Printer.printSimpleAttr("@escaping") << " "; - if (hasAttr(TAK_nondiff)) - Printer.printSimpleAttr("@nondiff") << " "; + if (hasAttr(TAK_noDerivative)) + Printer.printSimpleAttr("@noDerivative") << " "; if (hasAttr(TAK_differentiable)) { if (Attrs.isLinear()) { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 5b4a65c222002..b9d477e56b012 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2309,19 +2309,19 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, attrs.convention = None; } - if (attrs.has(TAK_nondiff)) { + if (attrs.has(TAK_noDerivative)) { if (!Context.LangOpts.EnableExperimentalDifferentiableProgramming) { - diagnose(attrs.getLoc(TAK_nondiff), + diagnose(attrs.getLoc(TAK_noDerivative), diag::experimental_differentiable_programming_disabled); } else if (!isParam) { - // @nondiff is only valid on parameters. - diagnose(attrs.getLoc(TAK_nondiff), + // @noDerivative is only valid on parameters. + diagnose(attrs.getLoc(TAK_noDerivative), (isVariadicFunctionParam ? diag::attr_not_on_variadic_parameters : diag::attr_only_on_parameters_of_differentiable), - "@nondiff"); + "@noDerivative"); } - attrs.clearAttribute(TAK_nondiff); + attrs.clearAttribute(TAK_noDerivative); } // In SIL, handle @opened (n), which creates an existential archetype. @@ -2441,22 +2441,22 @@ bool TypeResolver::resolveASTFunctionTypeParams( break; } - bool nondiff = false; + bool noDerivative = false; if (auto *attrTypeRepr = dyn_cast(eltTypeRepr)) { - if (attrTypeRepr->getAttrs().has(TAK_nondiff)) { + if (attrTypeRepr->getAttrs().has(TAK_noDerivative)) { if (!isDifferentiable && Context.LangOpts.EnableExperimentalDifferentiableProgramming) diagnose(eltTypeRepr->getLoc(), - diag::attr_only_on_parameters_of_differentiable, "@nondiff") + diag::attr_only_on_parameters_of_differentiable, "@noDerivative") .highlight(eltTypeRepr->getSourceRange()); else - nondiff = true; + noDerivative = true; } } auto paramFlags = ParameterTypeFlags::fromParameterType( ty, variadic, autoclosure, /*isNonEphemeral*/ false, ownership, - nondiff); + noDerivative); elements.emplace_back(ty, Identifier(), paramFlags); } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index a3b8ff9b52ef7..0b92fc82cde53 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4673,13 +4673,13 @@ class swift::TypeDeserializer { IdentifierID labelID; TypeID typeID; - bool isVariadic, isAutoClosure, isNonEphemeral, isNonDifferentiable; + bool isVariadic, isAutoClosure, isNonEphemeral, isNoDerivative; unsigned rawOwnership; decls_block::FunctionParamLayout::readRecord(scratch, labelID, typeID, isVariadic, isAutoClosure, isNonEphemeral, rawOwnership, - isNonDifferentiable); + isNoDerivative); auto ownership = getActualValueOwnership((serialization::ValueOwnership)rawOwnership); @@ -4694,7 +4694,7 @@ class swift::TypeDeserializer { MF.getIdentifier(labelID), ParameterTypeFlags(isVariadic, isAutoClosure, isNonEphemeral, *ownership, - isNonDifferentiable)); + isNoDerivative)); } if (!isGeneric) { diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 13eaf473f3d9b..c9c56f15d5189 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 525; // function parameter nondiff +const uint16_t SWIFTMODULE_VERSION_MINOR = 525; // function parameter noDerivative /// A standard hash seed used for all string hashes in a serialized module. /// @@ -899,7 +899,7 @@ namespace decls_block { BCFixed<1>, // autoclosure? BCFixed<1>, // non-ephemeral? ValueOwnershipField, // inout, shared or owned? - BCFixed<1> // nondifferentiable? + BCFixed<1> // noDerivative? >; using MetatypeTypeLayout = BCRecordLayout< diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index bf9920d7598c5..667705b367ee9 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3932,7 +3932,7 @@ class Serializer::TypeSerializer : public TypeVisitor { S.addDeclBaseNameRef(param.getLabel()), S.addTypeRef(param.getPlainType()), paramFlags.isVariadic(), paramFlags.isAutoClosure(), paramFlags.isNonEphemeral(), rawOwnership, - paramFlags.isNonDifferentiable()); + paramFlags.isNoDerivative()); } } diff --git a/test/AutoDiff/ModuleInterface/differentiation.swift b/test/AutoDiff/ModuleInterface/differentiation.swift index 2e6ef3534bf99..35ee0f9067891 100644 --- a/test/AutoDiff/ModuleInterface/differentiation.swift +++ b/test/AutoDiff/ModuleInterface/differentiation.swift @@ -7,5 +7,5 @@ public func a(f: @differentiable (Float) -> Float) {} public func b(f: @differentiable(linear) (Float) -> Float) {} // CHECK: public func b(f: @differentiable(linear) (Swift.Float) -> Swift.Float) -public func c(f: @differentiable (Float, @nondiff Float) -> Float) {} -// CHECK: public func c(f: @differentiable (Swift.Float, @nondiff Swift.Float) -> Swift.Float) +public func c(f: @differentiable (Float, @noDerivative Float) -> Float) {} +// CHECK: public func c(f: @differentiable (Swift.Float, @noDerivative Swift.Float) -> Swift.Float) diff --git a/test/AutoDiff/Parse/differentiable_func_type.swift b/test/AutoDiff/Parse/differentiable_func_type.swift index 2bff11d10ae16..74aa7d44b95bd 100644 --- a/test/AutoDiff/Parse/differentiable_func_type.swift +++ b/test/AutoDiff/Parse/differentiable_func_type.swift @@ -8,14 +8,14 @@ let b: @differentiable(linear) (Float) -> Float // okay // CHECK: (pattern_named 'b' // CHECK-NEXT: (type_attributed attrs=@differentiable(linear) -let c: @differentiable (Float, @nondiff Float) -> Float // okay +let c: @differentiable (Float, @noDerivative Float) -> Float // okay // CHECK: (pattern_named 'c' // CHECK-NEXT: (type_attributed attrs=@differentiable // CHECK-NEXT: (type_function // CHECK-NEXT: (type_tuple // CHECK-NEXT: (type_ident // CHECK-NEXT: (component id='Float' bind=none)) -// CHECK-NEXT: (type_attributed attrs=@nondiff +// CHECK-NEXT: (type_attributed attrs=@noDerivative // CHECK-NEXT: (type_ident // CHECK-NEXT: (component id='Float' bind=none))) // CHECK-NEXT: (type_ident diff --git a/test/AutoDiff/Sema/differentiable_features_disabled.swift b/test/AutoDiff/Sema/differentiable_features_disabled.swift index 8ad0e0315b666..c26c816a3f54c 100644 --- a/test/AutoDiff/Sema/differentiable_features_disabled.swift +++ b/test/AutoDiff/Sema/differentiable_features_disabled.swift @@ -5,10 +5,10 @@ let _: @differentiable (Float) -> Float // expected-error @+2 {{differentiable programming is an experimental feature that is currently disabled}} // expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} -let _: @differentiable (Float, @nondiff Float) -> Float +let _: @differentiable (Float, @noDerivative Float) -> Float // expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} -let _: (Float, @nondiff Float) -> Float +let _: (Float, @noDerivative Float) -> Float // expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} -let _: @nondiff Float +let _: @noDerivative Float diff --git a/test/AutoDiff/Sema/differentiable_func_type.swift b/test/AutoDiff/Sema/differentiable_func_type.swift index a304102fa6e92..0515dcc95d027 100644 --- a/test/AutoDiff/Sema/differentiable_func_type.swift +++ b/test/AutoDiff/Sema/differentiable_func_type.swift @@ -5,23 +5,23 @@ let _: @differentiable Float let _: @differentiable (Float) -> Float -// expected-error @+1 {{'@nondiff' may only be used on parameters of '@differentiable' function types}} -let _: @nondiff Float +// expected-error @+1 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +let _: @noDerivative Float -// expected-error @+1 {{'@nondiff' may only be used on parameters of '@differentiable' function types}} -let _: (Float) -> @nondiff Float +// expected-error @+1 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +let _: (Float) -> @noDerivative Float -// expected-error @+1 {{'@nondiff' may only be used on parameters of '@differentiable' function types}} -let _: @differentiable (Float) -> @nondiff Float +// expected-error @+1 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +let _: @differentiable (Float) -> @noDerivative Float -// expected-error @+1 {{'@nondiff' may only be used on parameters of '@differentiable' function types}} -let _: (@nondiff Float) -> Float +// expected-error @+1 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +let _: (@noDerivative Float) -> Float -// expected-error @+2 {{'@nondiff' may only be used on parameters of '@differentiable' function types}} -// expected-error @+1 {{'@nondiff' must not be used on variadic parameters}} -let _: (Float, @nondiff Float...) -> Float +// expected-error @+2 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +// expected-error @+1 {{'@noDerivative' must not be used on variadic parameters}} +let _: (Float, @noDerivative Float...) -> Float -let _: @differentiable (@nondiff Float, Float) -> Float +let _: @differentiable (@noDerivative Float, Float) -> Float -// expected-error @+1 {{'@nondiff' must not be used on variadic parameters}} -let _: @differentiable (Float, @nondiff Float...) -> Float +// expected-error @+1 {{'@noDerivative' must not be used on variadic parameters}} +let _: @differentiable (Float, @noDerivative Float...) -> Float diff --git a/test/AutoDiff/Serialization/differentiation.swift b/test/AutoDiff/Serialization/differentiation.swift index 23541379a069b..3a17b5440b8a9 100644 --- a/test/AutoDiff/Serialization/differentiation.swift +++ b/test/AutoDiff/Serialization/differentiation.swift @@ -11,5 +11,5 @@ func a(_ f: @differentiable (Float) -> Float) {} func b(_ f: @differentiable(linear) (Float) -> Float) {} // CHECK: func b(_ f: @differentiable(linear) (Float) -> Float) -func c(_ f: @differentiable (Float, @nondiff Float) -> Float) {} -// CHECK: func c(_ f: @differentiable (Float, @nondiff Float) -> Float) +func c(_ f: @differentiable (Float, @noDerivative Float) -> Float) {} +// CHECK: func c(_ f: @differentiable (Float, @noDerivative Float) -> Float) From 1058a38abd2a7b3b068f2df28c04db30939cf0c6 Mon Sep 17 00:00:00 2001 From: Marc Rasi Date: Fri, 15 Nov 2019 16:37:29 -0800 Subject: [PATCH 004/478] clang-format --- include/swift/AST/Types.h | 12 +++++------- lib/Sema/TypeCheckType.cpp | 3 ++- lib/Serialization/Deserialization.cpp | 11 ++++------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 9c80a6b6b19fb..38518ed5677c8 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -1856,9 +1856,9 @@ class ParameterTypeFlags { } ParameterTypeFlags withNoDerivative(bool noDerivative) const { - return ParameterTypeFlags( - noDerivative ? value | ParameterTypeFlags::NoDerivative - : value - ParameterTypeFlags::NoDerivative); + return ParameterTypeFlags(noDerivative + ? value | ParameterTypeFlags::NoDerivative + : value - ParameterTypeFlags::NoDerivative); } bool operator ==(const ParameterTypeFlags &other) const { @@ -1927,8 +1927,7 @@ class YieldTypeFlags { ParameterTypeFlags asParamFlags() const { return ParameterTypeFlags(/*variadic*/ false, /*autoclosure*/ false, - /*nonEphemeral*/ false, - getValueOwnership(), + /*nonEphemeral*/ false, getValueOwnership(), /*noDerivative*/ false); } @@ -5709,8 +5708,7 @@ inline ParameterTypeFlags ParameterTypeFlags::fromParameterType( ownership == ValueOwnership::InOut); ownership = ValueOwnership::InOut; } - return {isVariadic, isAutoClosure, isNonEphemeral, ownership, - isNoDerivative}; + return {isVariadic, isAutoClosure, isNonEphemeral, ownership, isNoDerivative}; } inline const Type *BoundGenericType::getTrailingObjectsPointer() const { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index b9d477e56b012..448aaabd2d372 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2447,7 +2447,8 @@ bool TypeResolver::resolveASTFunctionTypeParams( if (!isDifferentiable && Context.LangOpts.EnableExperimentalDifferentiableProgramming) diagnose(eltTypeRepr->getLoc(), - diag::attr_only_on_parameters_of_differentiable, "@noDerivative") + diag::attr_only_on_parameters_of_differentiable, + "@noDerivative") .highlight(eltTypeRepr->getSourceRange()); else noDerivative = true; diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 0b92fc82cde53..aadd6e8604e45 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4675,11 +4675,9 @@ class swift::TypeDeserializer { TypeID typeID; bool isVariadic, isAutoClosure, isNonEphemeral, isNoDerivative; unsigned rawOwnership; - decls_block::FunctionParamLayout::readRecord(scratch, labelID, typeID, - isVariadic, isAutoClosure, - isNonEphemeral, - rawOwnership, - isNoDerivative); + decls_block::FunctionParamLayout::readRecord( + scratch, labelID, typeID, isVariadic, isAutoClosure, isNonEphemeral, + rawOwnership, isNoDerivative); auto ownership = getActualValueOwnership((serialization::ValueOwnership)rawOwnership); @@ -4690,8 +4688,7 @@ class swift::TypeDeserializer { if (!paramTy) return paramTy.takeError(); - params.emplace_back(paramTy.get(), - MF.getIdentifier(labelID), + params.emplace_back(paramTy.get(), MF.getIdentifier(labelID), ParameterTypeFlags(isVariadic, isAutoClosure, isNonEphemeral, *ownership, isNoDerivative)); From b52a0929c42a025ac61d59ecfa02e1d540fc28c3 Mon Sep 17 00:00:00 2001 From: Vlasov Anton Date: Sun, 12 May 2019 17:53:57 +0300 Subject: [PATCH 005/478] SR-5741 Refactoring action to convert from field initialization to computed property --- include/swift/IDE/RefactoringKinds.def | 2 + lib/IDE/Refactoring.cpp | 91 +++++++++++++++++++ .../Outputs/basic/L10-3.swift.expected | 28 ++++++ .../Outputs/basic/L11-3.swift.expected | 28 ++++++ .../Outputs/basic/L12-3.swift.expected | 28 ++++++ .../Outputs/basic/L13-3.swift.expected | 28 ++++++ .../Outputs/basic/L14-3.swift.expected | 28 ++++++ .../Outputs/basic/L2-3.swift.expected | 28 ++++++ .../Outputs/basic/L3-3.swift.expected | 28 ++++++ .../Outputs/basic/L4-3.swift.expected | 28 ++++++ .../Outputs/basic/L5-3.swift.expected | 28 ++++++ .../Outputs/basic/L6-3.swift.expected | 28 ++++++ .../ConvertToComputedProperty/basic.swift | 47 ++++++++++ test/refactoring/RefactoringKind/basic.swift | 36 +++++++- tools/swift-refactor/swift-refactor.cpp | 4 +- 15 files changed, 456 insertions(+), 4 deletions(-) create mode 100644 test/refactoring/ConvertToComputedProperty/Outputs/basic/L10-3.swift.expected create mode 100644 test/refactoring/ConvertToComputedProperty/Outputs/basic/L11-3.swift.expected create mode 100644 test/refactoring/ConvertToComputedProperty/Outputs/basic/L12-3.swift.expected create mode 100644 test/refactoring/ConvertToComputedProperty/Outputs/basic/L13-3.swift.expected create mode 100644 test/refactoring/ConvertToComputedProperty/Outputs/basic/L14-3.swift.expected create mode 100644 test/refactoring/ConvertToComputedProperty/Outputs/basic/L2-3.swift.expected create mode 100644 test/refactoring/ConvertToComputedProperty/Outputs/basic/L3-3.swift.expected create mode 100644 test/refactoring/ConvertToComputedProperty/Outputs/basic/L4-3.swift.expected create mode 100644 test/refactoring/ConvertToComputedProperty/Outputs/basic/L5-3.swift.expected create mode 100644 test/refactoring/ConvertToComputedProperty/Outputs/basic/L6-3.swift.expected create mode 100644 test/refactoring/ConvertToComputedProperty/basic.swift diff --git a/include/swift/IDE/RefactoringKinds.def b/include/swift/IDE/RefactoringKinds.def index 84daad8bc9b86..b6565439a2016 100644 --- a/include/swift/IDE/RefactoringKinds.def +++ b/include/swift/IDE/RefactoringKinds.def @@ -70,6 +70,8 @@ RANGE_REFACTORING(ConvertIfLetExprToGuardExpr, "Convert To Guard Expression", co RANGE_REFACTORING(ConvertGuardExprToIfLetExpr, "Convert To IfLet Expression", convert.to.iflet.expr) +RANGE_REFACTORING(ConvertToComputedProperty, "Convert To Computed Property", convert.to.computed.property) + // These internal refactorings are designed to be helpful for working on // the compiler/standard library, etc., but are likely to be just confusing and // noise for general development. diff --git a/lib/IDE/Refactoring.cpp b/lib/IDE/Refactoring.cpp index b148785f6d070..9ce2bc7739024 100644 --- a/lib/IDE/Refactoring.cpp +++ b/lib/IDE/Refactoring.cpp @@ -3166,6 +3166,97 @@ static bool rangeStartMayNeedRename(ResolvedRangeInfo Info) { } llvm_unreachable("unhandled kind"); } + +bool RefactoringActionConvertToComputedProperty:: +isApplicable(ResolvedRangeInfo Info, DiagnosticEngine &Diag) { + if (Info.Kind != RangeKind::SingleDecl) { + return false; + } + + if (Info.ContainedNodes.size() != 1) { + return false; + } + + auto D = Info.ContainedNodes[0].dyn_cast(); + if (!D) { + return false; + } + + auto Binding = dyn_cast(D); + if (!Binding) { + return false; + } + + auto SV = Binding->getSingleVar(); + if (!SV) { + return false; + } + + // willSet, didSet cannot be provided together with a getter + for (auto AD : SV->getAllAccessors()) { + if (AD->isObservingAccessor()) { + return false; + } + } + + // 'lazy' must not be used on a computed property + // NSCopying and IBOutlet attribute requires property to be mutable + auto Attributies = SV->getAttrs(); + if (Attributies.hasAttribute() || + Attributies.hasAttribute() || + Attributies.hasAttribute()) { + return false; + } + + // Property wrapper cannot be applied to a computed property + if (SV->hasAttachedPropertyWrapper()) { + return false; + } + + // has an initializer + return Binding->hasInitStringRepresentation(0); +} + +bool RefactoringActionConvertToComputedProperty::performChange() { + // Get an initialization + auto D = RangeInfo.ContainedNodes[0].dyn_cast(); + auto Binding = dyn_cast(D); + SmallString<128> scratch; + auto Init = Binding->getInitStringRepresentation(0, scratch); + + // Get type + auto SV = Binding->getSingleVar(); + auto SVType = SV->getType(); + auto TR = SV->getTypeReprOrParentPatternTypeRepr(); + + llvm::SmallString<64> DeclBuffer; + llvm::raw_svector_ostream OS(DeclBuffer); + llvm::StringRef Space = " "; + llvm::StringRef NewLine = "\n"; + + OS << tok::kw_var << Space; + // Add var name + OS << SV->getNameStr().str() << ":" << Space; + // For computed property must write a type of var + if (TR) { + OS << Lexer::getCharSourceRangeFromSourceRange(SM, TR->getSourceRange()).str(); + } else { + SVType.print(OS); + } + + OS << Space << tok::l_brace << NewLine; + // Add an initialization + OS << tok::kw_return << Space << Init.str() << NewLine; + OS << tok::r_brace; + + // Replace initializer to computed property + auto ReplaceStartLoc = Binding->getLoc(); + auto ReplaceEndLoc = Binding->getSourceRange().End; + auto ReplaceRange = SourceRange(ReplaceStartLoc, ReplaceEndLoc); + auto ReplaceCharSourceRange = Lexer::getCharSourceRangeFromSourceRange(SM, ReplaceRange); + EditConsumer.accept(SM, ReplaceCharSourceRange, DeclBuffer.str()); + return false; // success +} }// end of anonymous namespace StringRef swift::ide:: diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L10-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L10-3.swift.expected new file mode 100644 index 0000000000000..e8eda6da2ee82 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L10-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1: S { +return S() +} + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L11-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L11-3.swift.expected new file mode 100644 index 0000000000000..6c0e0427aa9f3 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L11-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2: Int { +return 2 +} + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L12-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L12-3.swift.expected new file mode 100644 index 0000000000000..8e04908653313 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L12-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3: Int { +return 5 +} + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L13-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L13-3.swift.expected new file mode 100644 index 0000000000000..b85ee87f74713 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L13-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4: Int { +return 4 +} + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L14-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L14-3.swift.expected new file mode 100644 index 0000000000000..d909eca08cfc0 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L14-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + var field5: Int { +return 5 +} +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L2-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L2-3.swift.expected new file mode 100644 index 0000000000000..87f15cfc7bbfe --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L2-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1: Int { +return 2 +} + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L3-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L3-3.swift.expected new file mode 100644 index 0000000000000..09b7fb68a5ebe --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L3-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2: String { +return "2" +} + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L4-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L4-3.swift.expected new file mode 100644 index 0000000000000..2702ab0a321d1 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L4-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3: String { +return String() +} + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L5-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L5-3.swift.expected new file mode 100644 index 0000000000000..2d53bc984c398 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L5-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4: Int { +return 4 +} + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L6-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L6-3.swift.expected new file mode 100644 index 0000000000000..f31ba4658f4ce --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L6-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! { +return 45 +} +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/basic.swift b/test/refactoring/ConvertToComputedProperty/basic.swift new file mode 100644 index 0000000000000..1a9f119290286 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/basic.swift @@ -0,0 +1,47 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + +// RUN: %empty-directory(%t.result) + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=2:3 -end-pos=2:17 > %t.result/L2-3.swift +// RUN: diff -u %S/Outputs/basic/L2-3.swift.expected %t.result/L2-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=3:3 -end-pos=3:19 > %t.result/L3-3.swift +// RUN: diff -u %S/Outputs/basic/L3-3.swift.expected %t.result/L3-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=4:3 -end-pos=4:24 > %t.result/L4-3.swift +// RUN: diff -u %S/Outputs/basic/L4-3.swift.expected %t.result/L4-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=5:3 -end-pos=5:24 > %t.result/L5-3.swift +// RUN: diff -u %S/Outputs/basic/L5-3.swift.expected %t.result/L5-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=6:3 -end-pos=6:19 > %t.result/L6-3.swift +// RUN: diff -u %S/Outputs/basic/L6-3.swift.expected %t.result/L6-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=10:3 -end-pos=10:26 > %t.result/L10-3.swift +// RUN: diff -u %S/Outputs/basic/L10-3.swift.expected %t.result/L10-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=11:3 -end-pos=11:24 > %t.result/L11-3.swift +// RUN: diff -u %S/Outputs/basic/L11-3.swift.expected %t.result/L11-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=12:3 -end-pos=12:33 > %t.result/L12-3.swift +// RUN: diff -u %S/Outputs/basic/L12-3.swift.expected %t.result/L12-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=13:3 -end-pos=13:67 > %t.result/L13-3.swift +// RUN: diff -u %S/Outputs/basic/L13-3.swift.expected %t.result/L13-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=14:3 -end-pos=14:17 > %t.result/L14-3.swift +// RUN: diff -u %S/Outputs/basic/L14-3.swift.expected %t.result/L14-3.swift diff --git a/test/refactoring/RefactoringKind/basic.swift b/test/refactoring/RefactoringKind/basic.swift index 7d0775c976909..2ec7e032ef8e0 100644 --- a/test/refactoring/RefactoringKind/basic.swift +++ b/test/refactoring/RefactoringKind/basic.swift @@ -275,13 +275,33 @@ func testConvertToIfLetExpr(idxOpt: Int?) { print(idx) } +@propertyWrapper +struct TwelveOrLess { + private var number = 0 + var wrappedValue: Int { + get { return number } + set { number = min(newValue, 12) } + } +} + +struct S { + var field = 2 + let (x, y) = (2, 4) + @TwelveOrLess var height: Int + lazy var z = 42 + var totalSteps: Int = 0 { + willSet(newTotalSteps) { + print("About to set totalSteps to \(newTotalSteps)") + } + } +} // RUN: %refactor -source-filename %s -pos=2:1 -end-pos=5:13 | %FileCheck %s -check-prefix=CHECK1 // RUN: %refactor -source-filename %s -pos=3:1 -end-pos=5:13 | %FileCheck %s -check-prefix=CHECK1 // RUN: %refactor -source-filename %s -pos=4:1 -end-pos=5:13 | %FileCheck %s -check-prefix=CHECK1 // RUN: %refactor -source-filename %s -pos=5:1 -end-pos=5:13 | %FileCheck %s -check-prefix=CHECK1 -// RUN: %refactor -source-filename %s -pos=2:1 -end-pos=2:18 | %FileCheck %s -check-prefix=CHECK2 +// RUN: %refactor -source-filename %s -pos=2:1 -end-pos=2:18 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY // RUN: %refactor -source-filename %s -pos=2:1 -end-pos=3:16 | %FileCheck %s -check-prefix=CHECK2 // RUN: %refactor -source-filename %s -pos=2:1 -end-pos=4:26 | %FileCheck %s -check-prefix=CHECK2 @@ -367,12 +387,16 @@ func testConvertToIfLetExpr(idxOpt: Int?) { // RUN: %refactor -source-filename %s -pos=251:3 -end-pos=251:24 | %FileCheck %s -check-prefix=CHECK-EXPAND-TERNARY-EXPRESSEXPRESSION -// RUN: %refactor -source-filename %s -pos=257:3 -end-pos=262:4 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-TERNARY-EXPRESSEXPRESSION - // RUN: %refactor -source-filename %s -pos=266:3 -end-pos=268:4 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-GUARD-EXPRESSION // RUN: %refactor -source-filename %s -pos=272:3 -end-pos=275:13 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-IFLET-EXPRESSION +// RUN: %refactor -source-filename %s -pos=288:3 -end-pos=288:16 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY +// RUN: %refactor -source-filename %s -pos=289:3 -end-pos=289:22 | %FileCheck %s -check-prefix=CHECK-NONE +// RUN: %refactor -source-filename %s -pos=290:3 -end-pos=290:32 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY2 +// RUN: %refactor -source-filename %s -pos=291:3 -end-pos=291:18 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY2 +// RUN: %refactor -source-filename %s -pos=292:3 -end-pos=296:4 | %FileCheck %s -check-prefix=CHECK-NONE + // CHECK1: Action begins // CHECK1-NEXT: Extract Method // CHECK1-NEXT: Action ends @@ -423,3 +447,9 @@ func testConvertToIfLetExpr(idxOpt: Int?) { // CHECK-CONVERT-TO-GUARD-EXPRESSION: Convert To Guard Expression // CHECK-CONVERT-TO-IFLET-EXPRESSION: Convert To IfLet Expression + +// CHECK-CONVERT-TO-COMPUTED-PROPERTY: Convert To Computed Property + +// CHECK-CONVERT-TO-COMPUTED-PROPERTY2: Action begins +// CHECK-CONVERT-TO-COMPUTED-PROPERTY2-NEXT: Move To Extension +// CHECK-CONVERT-TO-COMPUTED-PROPERTY2-NEXT: Action ends \ No newline at end of file diff --git a/tools/swift-refactor/swift-refactor.cpp b/tools/swift-refactor/swift-refactor.cpp index 83417f6cf97fd..22c885b97c2cb 100644 --- a/tools/swift-refactor/swift-refactor.cpp +++ b/tools/swift-refactor/swift-refactor.cpp @@ -71,7 +71,9 @@ Action(llvm::cl::desc("kind:"), llvm::cl::init(RefactoringKind::None), "trailingclosure", "Perform trailing closure refactoring"), clEnumValN(RefactoringKind::ReplaceBodiesWithFatalError, "replace-bodies-with-fatalError", "Perform trailing closure refactoring"), - clEnumValN(RefactoringKind::MemberwiseInitLocalRefactoring, "memberwise-init", "Generate member wise initializer"))); + clEnumValN(RefactoringKind::MemberwiseInitLocalRefactoring, "memberwise-init", "Generate member wise initializer"), + clEnumValN(RefactoringKind::ConvertToComputedProperty, + "convert-to-computed-property", "Convert from field initialization to computed property"))); static llvm::cl::opt From d1259cec504f7e9d78288c84671134e446378a27 Mon Sep 17 00:00:00 2001 From: Mao ZiJun Date: Mon, 9 Dec 2019 17:36:51 +0900 Subject: [PATCH 006/478] eliminated "dangling pointer" warnings --- benchmark/utils/DriverUtils.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/benchmark/utils/DriverUtils.swift b/benchmark/utils/DriverUtils.swift index 351d599e0fb20..13557d0158875 100644 --- a/benchmark/utils/DriverUtils.swift +++ b/benchmark/utils/DriverUtils.swift @@ -412,9 +412,10 @@ final class TestRunner { private static func getExecutedInstructions() -> UInt64 { if #available(OSX 10.9, iOS 7.0, *) { var u = rusage_info_v4() - let p = UnsafeMutablePointer(&u) - p.withMemoryRebound(to: Optional.self, capacity: 1) { up in - let _ = proc_pid_rusage(getpid(), RUSAGE_INFO_V4, up) + withUnsafeMutablePointer(to: &u) { p in + p.withMemoryRebound(to: Optional.self, capacity: 1) { up in + let _ = proc_pid_rusage(getpid(), RUSAGE_INFO_V4, up) + } } return u.ri_instructions } else { From 1da182555a28552ebf855e395671a88c273d056b Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Wed, 11 Dec 2019 17:00:35 -0500 Subject: [PATCH 007/478] [Runtime] When the ObjC runtime supports lazy class names, lazily create the ObjC names for generic classes. rdar://problem/57674583 --- stdlib/public/runtime/Metadata.cpp | 43 +++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 66f1dc053d182..7bfe15dd5207f 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -2197,7 +2197,7 @@ static inline ClassROData *getROData(ClassMetadata *theClass) { return (ClassROData*)(theClass->Data & ~uintptr_t(SWIFT_CLASS_IS_SWIFT_MASK)); } -static void initGenericClassObjCName(ClassMetadata *theClass) { +static char *copyGenericClassObjCName(ClassMetadata *theClass) { // Use the remangler to generate a mangled name from the type metadata. Demangle::StackAllocatedDemangler<4096> Dem; @@ -2230,11 +2230,46 @@ static void initGenericClassObjCName(ClassMetadata *theClass) { } else { fullNameBuf[string.size()] = '\0'; } + return fullNameBuf; +} +static void initGenericClassObjCName(ClassMetadata *theClass) { auto theMetaclass = (ClassMetadata *)object_getClass((id)theClass); - getROData(theClass)->Name = fullNameBuf; - getROData(theMetaclass)->Name = fullNameBuf; + char *name = copyGenericClassObjCName(theClass); + getROData(theClass)->Name = name; + getROData(theMetaclass)->Name = name; +} + +static bool installLazyClassNameHandler() { + auto _objc_setLazyClassNamer = + (void (*)(char * (*)(Class))) + dlsym(RTLD_NEXT, "_objc_setLazyClassNamer"); + if (_objc_setLazyClassNamer == nullptr) + return false; + + _objc_setLazyClassNamer([](Class theClass) { + ClassMetadata *metadata; + if (class_isMetaClass(theClass)) { + metadata = (ClassMetadata *)class_getIvarLayout(theClass); + } else { + metadata = (ClassMetadata *)theClass; + } + return copyGenericClassObjCName(metadata); + }); + return true; +} + +static void setUpGenericClassObjCName(ClassMetadata *theClass) { + bool supportsLazyNames = SWIFT_LAZY_CONSTANT(installLazyClassNameHandler()); + if (supportsLazyNames) { + getROData(theClass)->Name = nullptr; + auto theMetaclass = (ClassMetadata *)object_getClass((id)theClass); + getROData(theMetaclass)->Name = nullptr; + getROData(theMetaclass)->IvarLayout = (const uint8_t *)theClass; + } else { + initGenericClassObjCName(theClass); + } } #endif @@ -2488,7 +2523,7 @@ initGenericObjCClass(ClassMetadata *self, size_t numFields, const TypeLayout * const *fieldTypes, size_t *fieldOffsets) { // If the class is generic, we need to give it a name for Objective-C. - initGenericClassObjCName(self); + setUpGenericClassObjCName(self); ClassROData *rodata = getROData(self); From 6c183406753c5a42bf5efd875e83e587b1265636 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Thu, 12 Dec 2019 15:25:04 -0500 Subject: [PATCH 008/478] [Runtime] Remove demangleObjCTypeName and use the old demangler instead. Archiving expects to be able to instantiate generic classes by name. This previously worked if you had instantiated the specialization in question, because the ObjC runtime would be able to look it up. Now, if the name hasn't been created, Swift has to look it up. demangleObjCTypeName doesn't do generics, so this failed. rdar://problem/57674583 --- include/swift/Demangling/Demangler.h | 1 - lib/Demangling/Demangler.cpp | 44 +--------------------------- 2 files changed, 1 insertion(+), 44 deletions(-) diff --git a/include/swift/Demangling/Demangler.h b/include/swift/Demangling/Demangler.h index b4ee935eb71db..c17f157976aa8 100644 --- a/include/swift/Demangling/Demangler.h +++ b/include/swift/Demangling/Demangler.h @@ -563,7 +563,6 @@ class Demangler : public NodeFactory { NodePointer demangleGenericType(); NodePointer demangleValueWitness(); - NodePointer demangleObjCTypeName(); NodePointer demangleTypeMangling(); NodePointer demangleSymbolicReference(unsigned char rawKind, const void *at); diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 9cd4e486f6496..6549ab21b6fa9 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -529,7 +529,7 @@ NodePointer Demangler::demangleSymbol(StringRef MangledName, // Demangle old-style class and protocol names, which are still used in the // ObjC metadata. if (nextIf("_Tt")) - return demangleObjCTypeName(); + return demangleOldSymbolAsNode(Text, *this); unsigned PrefixLength = getManglingPrefixLength(MangledName); if (PrefixLength == 0) @@ -3193,45 +3193,3 @@ NodePointer Demangler::demangleValueWitness() { addChild(VW, createNode(Node::Kind::Index, unsigned(Kind))); return addChild(VW, popNode(Node::Kind::Type)); } - -NodePointer Demangler::demangleObjCTypeName() { - NodePointer Ty = createNode(Node::Kind::Type); - NodePointer Global = addChild(createNode(Node::Kind::Global), - addChild(createNode(Node::Kind::TypeMangling), Ty)); - NodePointer Nominal = nullptr; - bool isProto = false; - if (nextIf('C')) { - Nominal = createNode(Node::Kind::Class); - addChild(Ty, Nominal); - } else if (nextIf('P')) { - isProto = true; - Nominal = createNode(Node::Kind::Protocol); - addChild(Ty, addChild(createNode(Node::Kind::ProtocolList), - addChild(createNode(Node::Kind::TypeList), - addChild(createNode(Node::Kind::Type), Nominal)))); - } else { - return nullptr; - } - - if (nextIf('s')) { - Nominal->addChild(createNode(Node::Kind::Module, "Swift"), *this); - } else { - NodePointer Module = demangleIdentifier(); - if (!Module) - return nullptr; - Nominal->addChild(changeKind(Module, Node::Kind::Module), *this); - } - - NodePointer Ident = demangleIdentifier(); - if (!Ident) - return nullptr; - Nominal->addChild(Ident, *this); - - if (isProto && !nextIf('_')) - return nullptr; - - if (Pos < Text.size()) - return nullptr; - - return Global; -} From c484894668482c94aea655f5793b0f27a270a0a8 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Tue, 10 Dec 2019 16:44:29 -0800 Subject: [PATCH 009/478] TBDGen: generate $ld$hide$os symbols for decls marked with @_originallyDefinedIn Combined with @available attribute, we could infer the OS versions that a particular symbol doesn't exist in the current framework (LowLevel framework). For these OS versions, we need to emit $ld$hide directives to avoid the LowLevel framework to be linked against. Need this for rdar://55268186 --- lib/TBDGen/TBDGen.cpp | 66 ++++++++++++++++++++++++++++++++------ lib/TBDGen/TBDGenVisitor.h | 3 ++ 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index 1f3fea838c059..37de480e9d986 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -56,19 +56,64 @@ static bool isGlobalOrStaticVar(VarDecl *VD) { return VD->isStatic() || VD->getDeclContext()->isModuleScopeContext(); } +void TBDGenVisitor::addSymbolInternal(StringRef name, + llvm::MachO::SymbolKind kind) { + Symbols.addSymbol(kind, name, Targets); + if (StringSymbols && kind == SymbolKind::GlobalSymbol) { + auto isNewValue = StringSymbols->insert(name).second; + (void)isNewValue; + assert(isNewValue && "symbol appears twice"); + } +} + +void TBDGenVisitor::addLinkerDirectiveSymbols(StringRef name, + llvm::MachO::SymbolKind kind) { + if (kind != llvm::MachO::SymbolKind::GlobalSymbol) + return; + if (!TopLevelDecl) + return; + auto ODA = TopLevelDecl->getAttrs().getAttribute(); + if (!ODA) + return; + unsigned Major[2]; + unsigned Minor[2]; + Major[1] = ODA->MovedVersion.getMajor(); + Minor[1] = ODA->MovedVersion.getMinor().hasValue() ? + *ODA->MovedVersion.getMinor(): 0; + auto AvailRange = AvailabilityInference::availableRange(TopLevelDecl, + TopLevelDecl->getASTContext()).getOSVersion(); + assert(AvailRange.hasLowerEndpoint() && + "cannot find the start point of availability"); + if (!AvailRange.hasLowerEndpoint()) + return; + assert(AvailRange.getLowerEndpoint() < ODA->MovedVersion); + if (AvailRange.getLowerEndpoint() >= ODA->MovedVersion) + return; + Major[0] = AvailRange.getLowerEndpoint().getMajor(); + Minor[0] = AvailRange.getLowerEndpoint().getMinor().hasValue() ? + AvailRange.getLowerEndpoint().getMinor().getValue() : 0; + for (auto CurMaj = Major[0]; CurMaj <= Major[1]; ++ CurMaj) { + unsigned MinRange[2] = {0, 31}; + if (CurMaj == Major[0]) + MinRange[0] = Minor[0]; + if (CurMaj == Major[1]) + MinRange[1] = Minor[1]; + for (auto CurMin = MinRange[0]; CurMin != MinRange[1]; ++ CurMin) { + llvm::SmallString<64> Buffer; + llvm::raw_svector_ostream OS(Buffer); + OS << "$ld$hide$os" << CurMaj << "." << CurMin << "$" << name; + addSymbolInternal(OS.str(), llvm::MachO::SymbolKind::GlobalSymbol); + } + } +} + void TBDGenVisitor::addSymbol(StringRef name, SymbolKind kind) { // The linker expects to see mangled symbol names in TBD files, so make sure // to mangle before inserting the symbol. SmallString<32> mangled; llvm::Mangler::getNameWithPrefix(mangled, name, DataLayout); - - Symbols.addSymbol(kind, mangled, Targets); - - if (StringSymbols && kind == SymbolKind::GlobalSymbol) { - auto isNewValue = StringSymbols->insert(mangled).second; - (void)isNewValue; - assert(isNewValue && "symbol appears twice"); - } + addSymbolInternal(mangled, kind); + addLinkerDirectiveSymbols(mangled, kind); } void TBDGenVisitor::addSymbol(SILDeclRef declRef) { @@ -688,8 +733,11 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, visitor.addMainIfNecessary(file); - for (auto d : decls) + for (auto d : decls) { + visitor.TopLevelDecl = d; + SWIFT_DEFER { visitor.TopLevelDecl = nullptr; }; visitor.visit(d); + } }; if (singleFile) { diff --git a/lib/TBDGen/TBDGenVisitor.h b/lib/TBDGen/TBDGenVisitor.h index a76da073ef8cf..523ff6262d4b1 100644 --- a/lib/TBDGen/TBDGenVisitor.h +++ b/lib/TBDGen/TBDGenVisitor.h @@ -54,8 +54,11 @@ class TBDGenVisitor : public ASTVisitor { const UniversalLinkageInfo &UniversalLinkInfo; ModuleDecl *SwiftModule; const TBDGenOptions &Opts; + Decl* TopLevelDecl = nullptr; private: + void addSymbolInternal(StringRef name, llvm::MachO::SymbolKind kind); + void addLinkerDirectiveSymbols(StringRef name, llvm::MachO::SymbolKind kind); void addSymbol(StringRef name, llvm::MachO::SymbolKind kind = llvm::MachO::SymbolKind::GlobalSymbol); From fdcd50628ca22c974ef03273893cbfd27b65555e Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Wed, 11 Dec 2019 16:35:02 -0800 Subject: [PATCH 010/478] IRGen: add a function suitable for emitting linker directive as global variable --- lib/IRGen/GenDecl.cpp | 43 ++++++++++++++++++++++ lib/IRGen/GenDecl.h | 3 ++ test/attr/Inputs/SymbolMove/LowLevel.swift | 7 ++++ 3 files changed, 53 insertions(+) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 6980e75fb508b..562acba3de1b9 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1929,6 +1929,49 @@ llvm::GlobalVariable *swift::irgen::createVariable( return var; } +llvm::GlobalVariable * +swift::irgen::createLinkerDirectiveVariable(IRGenModule &IGM, StringRef name) { + + // A prefix of \1 can avoid further mangling of the symbol (prefixing _). + llvm::SmallString<32> NameWithFlag; + NameWithFlag.push_back('\1'); + NameWithFlag.append(name); + name = NameWithFlag.str(); + static const uint8_t Size = 8; + static const uint8_t Alignment = 8; + + // Use a char type as the type for this linker directive. + auto ProperlySizedIntTy = SILType::getBuiltinIntegerType( + Size, IGM.getSwiftModule()->getASTContext()); + auto storageType = IGM.getStorageType(ProperlySizedIntTy); + + llvm::GlobalValue *existingValue = IGM.Module.getNamedGlobal(name); + if (existingValue) { + auto existingVar = dyn_cast(existingValue); + if (existingVar && isPointerTo(existingVar->getType(), storageType)) + return existingVar; + + IGM.error(SourceLoc(), + "program too clever: variable collides with existing symbol " + + name); + + // Note that this will implicitly unique if the .unique name is also taken. + existingValue->setName(name + ".unique"); + } + + llvm::GlobalValue::LinkageTypes Linkage = + llvm::GlobalValue::LinkageTypes::ExternalLinkage; + auto var = new llvm::GlobalVariable(IGM.Module, storageType, /*constant*/true, + Linkage, /*Init to zero*/llvm::Constant::getNullValue(storageType), name); + ApplyIRLinkage({Linkage, + llvm::GlobalValue::VisibilityTypes::DefaultVisibility, + llvm::GlobalValue::DLLStorageClassTypes::DefaultStorageClass}).to(var); + var->setAlignment(Alignment); + disableAddressSanitizer(IGM, var); + IGM.addUsedGlobal(var); + return var; +} + void swift::irgen::disableAddressSanitizer(IRGenModule &IGM, llvm::GlobalVariable *var) { // Add an operand to llvm.asan.globals blacklisting this global variable. llvm::Metadata *metadata[] = { diff --git a/lib/IRGen/GenDecl.h b/lib/IRGen/GenDecl.h index aabd2448096fd..93656316c6a0c 100644 --- a/lib/IRGen/GenDecl.h +++ b/lib/IRGen/GenDecl.h @@ -52,6 +52,9 @@ namespace irgen { Optional DebugLoc = None, StringRef DebugName = StringRef(), bool heapAllocated = false); + llvm::GlobalVariable * + createLinkerDirectiveVariable(IRGenModule &IGM, StringRef Name); + void disableAddressSanitizer(IRGenModule &IGM, llvm::GlobalVariable *var); } } diff --git a/test/attr/Inputs/SymbolMove/LowLevel.swift b/test/attr/Inputs/SymbolMove/LowLevel.swift index 5a42275802f46..46450b44f3d71 100644 --- a/test/attr/Inputs/SymbolMove/LowLevel.swift +++ b/test/attr/Inputs/SymbolMove/LowLevel.swift @@ -1,8 +1,10 @@ +@available(OSX 10.8, *) @_originallyDefinedIn(module: "HighLevel", OSX 10.10) public func printMessageMoved() { print("Hello from LowLevel") } +@available(OSX 10.8, *) @_originallyDefinedIn(module: "HighLevel", OSX 10.10) public struct Entity { public let value = "LowLevel" @@ -11,6 +13,7 @@ public struct Entity { } // =================== Move protocol =================================// +@available(OSX 10.8, *) @_originallyDefinedIn(module: "HighLevel", OSX 10.10) public protocol Box { associatedtype Item @@ -19,11 +22,13 @@ public protocol Box { func shape() -> String } +@available(OSX 10.8, *) @_originallyDefinedIn(module: "HighLevel", OSX 10.10) extension Box { public func shape() -> String { return "round"} } +@available(OSX 10.8, *) @_originallyDefinedIn(module: "HighLevel", OSX 10.10) public struct Candy { public var kind = "candy" @@ -31,6 +36,7 @@ public struct Candy { } // =================== Move enum ============================ // +@available(OSX 10.8, *) @_originallyDefinedIn(module: "HighLevel", OSX 10.10) public enum LanguageKind: Int { case Cpp = 1 @@ -39,6 +45,7 @@ public enum LanguageKind: Int { } // =================== Move class ============================ // +@available(OSX 10.8, *) @_originallyDefinedIn(module: "HighLevel", OSX 10.10) open class Vehicle { public init() {} From 93a83d3f1436bd6042134ec8628e8fc5e1257e63 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Wed, 11 Dec 2019 18:32:07 -0800 Subject: [PATCH 011/478] IRGen: reuse linker directives collected from tbd gen to emit these symbols to IR --- include/swift/Subsystems.h | 7 ++++-- include/swift/TBDGen/TBDGen.h | 3 +++ lib/FrontendTool/FrontendTool.cpp | 23 ++++++++++++++---- lib/IRGen/GenDecl.cpp | 8 +++++-- lib/IRGen/IRGen.cpp | 25 ++++++++++++------- lib/IRGen/IRGenModule.h | 2 +- lib/TBDGen/TBDGen.cpp | 16 +++++++++++-- lib/TBDGen/TBDGenVisitor.h | 3 ++- test/attr/Inputs/SymbolMove/LowLevel.swift | 28 +++++++++++----------- 9 files changed, 80 insertions(+), 35 deletions(-) diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 6175cc7615328..df36560a29203 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -25,6 +25,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/Mutex.h" #include @@ -276,7 +277,8 @@ namespace swift { StringRef ModuleName, const PrimarySpecificPaths &PSPs, llvm::LLVMContext &LLVMContext, ArrayRef parallelOutputFilenames, - llvm::GlobalVariable **outModuleHash = nullptr); + llvm::GlobalVariable **outModuleHash = nullptr, + llvm::StringSet<> *LinkerDirectives = nullptr); /// Turn the given Swift module into either LLVM IR or native code /// and return the generated LLVM IR module. @@ -286,7 +288,8 @@ namespace swift { std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, llvm::LLVMContext &LLVMContext, - llvm::GlobalVariable **outModuleHash = nullptr); + llvm::GlobalVariable **outModuleHash = nullptr, + llvm::StringSet<> *LinkerDirectives = nullptr); /// Given an already created LLVM module, construct a pass pipeline and run /// the Swift LLVM Pipeline upon it. This does not cause the module to be diff --git a/include/swift/TBDGen/TBDGen.h b/include/swift/TBDGen/TBDGen.h index c78803ff58e38..d4cfad2175080 100644 --- a/include/swift/TBDGen/TBDGen.h +++ b/include/swift/TBDGen/TBDGen.h @@ -33,6 +33,9 @@ struct TBDGenOptions { /// Whether this compilation is producing a TBD for InstallAPI. bool IsInstallAPI; + /// Only collect linker directive symbols. + bool LinkerDirectivesOnly = false; + /// The install_name to use in the TBD file. std::string InstallName; diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 1427e7197a524..35acaca6f5d70 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1347,18 +1347,20 @@ static void generateIR(IRGenOptions &IRGenOpts, std::unique_ptr SM, StringRef OutputFilename, ModuleOrSourceFile MSF, std::unique_ptr &IRModule, llvm::GlobalVariable *&HashGlobal, - ArrayRef parallelOutputFilenames) { + ArrayRef parallelOutputFilenames, + llvm::StringSet<> &LinkerDirectives) { // FIXME: We shouldn't need to use the global context here, but // something is persisting across calls to performIRGeneration. auto &LLVMContext = getGlobalLLVMContext(); IRModule = MSF.is() ? performIRGeneration(IRGenOpts, *MSF.get(), std::move(SM), OutputFilename, PSPs, - LLVMContext, &HashGlobal) + LLVMContext, &HashGlobal, + &LinkerDirectives) : performIRGeneration(IRGenOpts, MSF.get(), std::move(SM), OutputFilename, PSPs, LLVMContext, parallelOutputFilenames, - &HashGlobal); + &HashGlobal, &LinkerDirectives); } static bool processCommandLineAndRunImmediately(CompilerInvocation &Invocation, @@ -1457,6 +1459,17 @@ static bool generateCode(CompilerInvocation &Invocation, EffectiveLanguageVersion, OutputFilename, Stats); } +static void collectLinkerDirectives(CompilerInvocation &Invocation, + ModuleOrSourceFile MSF, + llvm::StringSet<> &Symbols) { + auto tbdOpts = Invocation.getTBDGenOptions(); + tbdOpts.LinkerDirectivesOnly = true; + if (MSF.is()) + enumeratePublicSymbols(MSF.get(), Symbols, tbdOpts); + else + enumeratePublicSymbols(MSF.get(), Symbols, tbdOpts); +} + static bool performCompileStepsPostSILGen( CompilerInstance &Instance, CompilerInvocation &Invocation, std::unique_ptr SM, bool astGuaranteedToCorrespondToSIL, @@ -1585,6 +1598,8 @@ static bool performCompileStepsPostSILGen( return processCommandLineAndRunImmediately( Invocation, Instance, std::move(SM), MSF, observer, ReturnValue); + llvm::StringSet<> LinkerDirectives; + collectLinkerDirectives(Invocation, MSF, LinkerDirectives); StringRef OutputFilename = PSPs.OutputFilename; std::vector ParallelOutputFilenames = Invocation.getFrontendOptions().InputsAndOutputs.copyOutputFilenames(); @@ -1592,7 +1607,7 @@ static bool performCompileStepsPostSILGen( llvm::GlobalVariable *HashGlobal; generateIR( IRGenOpts, std::move(SM), PSPs, OutputFilename, MSF, IRModule, HashGlobal, - ParallelOutputFilenames); + ParallelOutputFilenames, LinkerDirectives); // Walk the AST for indexing after IR generation. Walking it before seems // to cause miscompilation issues. diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 562acba3de1b9..4bf920b5aae3f 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -998,7 +998,7 @@ static bool hasCodeCoverageInstrumentation(SILFunction &f, SILModule &m) { return f.getProfiler() && m.getOptions().EmitProfileCoverageMapping; } -void IRGenerator::emitGlobalTopLevel() { +void IRGenerator::emitGlobalTopLevel(llvm::StringSet<> *linkerDirectives) { // Generate order numbers for the functions in the SIL module that // correspond to definitions in the LLVM module. unsigned nextOrderNumber = 0; @@ -1018,7 +1018,11 @@ void IRGenerator::emitGlobalTopLevel() { CurrentIGMPtr IGM = getGenModule(wt.getProtocol()->getDeclContext()); ensureRelativeSymbolCollocation(wt); } - + if (linkerDirectives) { + for (auto &entry: *linkerDirectives) { + createLinkerDirectiveVariable(*PrimaryIGM, entry.getKey()); + } + } for (SILGlobalVariable &v : PrimaryIGM->getSILModule().getSILGlobals()) { Decl *decl = v.getDecl(); CurrentIGMPtr IGM = getGenModule(decl ? decl->getDeclContext() : nullptr); diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 16dac3169c5c2..4938a22b029fc 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -858,7 +858,8 @@ performIRGeneration(IRGenOptions &Opts, ModuleDecl *M, std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, llvm::LLVMContext &LLVMContext, SourceFile *SF = nullptr, - llvm::GlobalVariable **outModuleHash = nullptr) { + llvm::GlobalVariable **outModuleHash = nullptr, + llvm::StringSet<> *linkerDirectives = nullptr) { auto &Ctx = M->getASTContext(); assert(!Ctx.hadError()); @@ -879,8 +880,9 @@ performIRGeneration(IRGenOptions &Opts, ModuleDecl *M, { FrontendStatsTracer tracer(Ctx.Stats, "IRGen"); + // Emit the module contents. - irgen.emitGlobalTopLevel(); + irgen.emitGlobalTopLevel(linkerDirectives); if (SF) { IGM.emitSourceFile(*SF); @@ -1068,7 +1070,8 @@ struct LLVMCodeGenThreads { static void performParallelIRGeneration( IRGenOptions &Opts, swift::ModuleDecl *M, std::unique_ptr SILMod, StringRef ModuleName, int numThreads, - ArrayRef outputFilenames) { + ArrayRef outputFilenames, + llvm::StringSet<> *linkerDirectives) { IRGenerator irgen(Opts, *SILMod); @@ -1134,7 +1137,7 @@ static void performParallelIRGeneration( } // Emit the module contents. - irgen.emitGlobalTopLevel(); + irgen.emitGlobalTopLevel(linkerDirectives); for (auto *File : M->getFiles()) { if (auto *SF = dyn_cast(File)) { @@ -1262,18 +1265,21 @@ std::unique_ptr swift::performIRGeneration( StringRef ModuleName, const PrimarySpecificPaths &PSPs, llvm::LLVMContext &LLVMContext, ArrayRef parallelOutputFilenames, - llvm::GlobalVariable **outModuleHash) { + llvm::GlobalVariable **outModuleHash, + llvm::StringSet<> *LinkerDirectives) { if (SILMod->getOptions().shouldPerformIRGenerationInParallel() && !parallelOutputFilenames.empty()) { auto NumThreads = SILMod->getOptions().NumThreads; ::performParallelIRGeneration(Opts, M, std::move(SILMod), ModuleName, - NumThreads, parallelOutputFilenames); + NumThreads, parallelOutputFilenames, + LinkerDirectives); // TODO: Parallel LLVM compilation cannot be used if a (single) module is // needed as return value. return nullptr; } return ::performIRGeneration(Opts, M, std::move(SILMod), ModuleName, PSPs, - LLVMContext, nullptr, outModuleHash); + LLVMContext, nullptr, outModuleHash, + LinkerDirectives); } std::unique_ptr swift:: @@ -1281,10 +1287,11 @@ performIRGeneration(IRGenOptions &Opts, SourceFile &SF, std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, llvm::LLVMContext &LLVMContext, - llvm::GlobalVariable **outModuleHash) { + llvm::GlobalVariable **outModuleHash, + llvm::StringSet<> *LinkerDirectives) { return ::performIRGeneration(Opts, SF.getParentModule(), std::move(SILMod), ModuleName, PSPs, LLVMContext, &SF, - outModuleHash); + outModuleHash, LinkerDirectives); } void diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 1df95e74f923e..7bdcb858b3bb1 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -331,7 +331,7 @@ class IRGenerator { /// Emit functions, variables and tables which are needed anyway, e.g. because /// they are externally visible. - void emitGlobalTopLevel(); + void emitGlobalTopLevel(llvm::StringSet<> *LinkerDirectives); /// Emit references to each of the protocol descriptors defined in this /// IR module. diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index 37de480e9d986..a2402a16b3640 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -57,7 +57,10 @@ static bool isGlobalOrStaticVar(VarDecl *VD) { } void TBDGenVisitor::addSymbolInternal(StringRef name, - llvm::MachO::SymbolKind kind) { + llvm::MachO::SymbolKind kind, + bool isLinkerDirective) { + if (!isLinkerDirective && Opts.LinkerDirectivesOnly) + return; Symbols.addSymbol(kind, name, Targets); if (StringSymbols && kind == SymbolKind::GlobalSymbol) { auto isNewValue = StringSymbols->insert(name).second; @@ -102,7 +105,8 @@ void TBDGenVisitor::addLinkerDirectiveSymbols(StringRef name, llvm::SmallString<64> Buffer; llvm::raw_svector_ostream OS(Buffer); OS << "$ld$hide$os" << CurMaj << "." << CurMin << "$" << name; - addSymbolInternal(OS.str(), llvm::MachO::SymbolKind::GlobalSymbol); + addSymbolInternal(OS.str(), llvm::MachO::SymbolKind::GlobalSymbol, + /*LinkerDirective*/true); } } } @@ -686,6 +690,12 @@ static bool isApplicationExtensionSafe(const LangOptions &LangOpts) { llvm::sys::Process::GetEnv("LD_APPLICATION_EXTENSION_SAFE"); } +static bool hasLinkerDirective(Decl *D) { + if (D->getAttrs().hasAttribute()) + return true; + return false; +} + static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, StringSet *symbols, llvm::raw_ostream *os, @@ -734,6 +744,8 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, visitor.addMainIfNecessary(file); for (auto d : decls) { + if (opts.LinkerDirectivesOnly && !hasLinkerDirective(d)) + continue; visitor.TopLevelDecl = d; SWIFT_DEFER { visitor.TopLevelDecl = nullptr; }; visitor.visit(d); diff --git a/lib/TBDGen/TBDGenVisitor.h b/lib/TBDGen/TBDGenVisitor.h index 523ff6262d4b1..389528564cd65 100644 --- a/lib/TBDGen/TBDGenVisitor.h +++ b/lib/TBDGen/TBDGenVisitor.h @@ -57,7 +57,8 @@ class TBDGenVisitor : public ASTVisitor { Decl* TopLevelDecl = nullptr; private: - void addSymbolInternal(StringRef name, llvm::MachO::SymbolKind kind); + void addSymbolInternal(StringRef name, llvm::MachO::SymbolKind kind, + bool isLinkerDirective = false); void addLinkerDirectiveSymbols(StringRef name, llvm::MachO::SymbolKind kind); void addSymbol(StringRef name, llvm::MachO::SymbolKind kind = llvm::MachO::SymbolKind::GlobalSymbol); diff --git a/test/attr/Inputs/SymbolMove/LowLevel.swift b/test/attr/Inputs/SymbolMove/LowLevel.swift index 46450b44f3d71..fcc17c74e7f5f 100644 --- a/test/attr/Inputs/SymbolMove/LowLevel.swift +++ b/test/attr/Inputs/SymbolMove/LowLevel.swift @@ -1,11 +1,11 @@ -@available(OSX 10.8, *) -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) public func printMessageMoved() { print("Hello from LowLevel") } -@available(OSX 10.8, *) -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) public struct Entity { public let value = "LowLevel" public init() {} @@ -13,8 +13,8 @@ public struct Entity { } // =================== Move protocol =================================// -@available(OSX 10.8, *) -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) public protocol Box { associatedtype Item var ItemKind: String { get } @@ -22,22 +22,22 @@ public protocol Box { func shape() -> String } -@available(OSX 10.8, *) -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) extension Box { public func shape() -> String { return "round"} } -@available(OSX 10.8, *) -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) public struct Candy { public var kind = "candy" public init() {} } // =================== Move enum ============================ // -@available(OSX 10.8, *) -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) public enum LanguageKind: Int { case Cpp = 1 case Swift = 2 @@ -45,8 +45,8 @@ public enum LanguageKind: Int { } // =================== Move class ============================ // -@available(OSX 10.8, *) -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) open class Vehicle { public init() {} public var currentSpeed = 40.0 From 25376025ae442d6b9212ff207f944ed3d9e959e4 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 12 Dec 2019 12:06:08 -0800 Subject: [PATCH 012/478] IRGen: keep emitting protocol witness table symbols for refactored protocols When Protocol P and Struct S are in a same module and S conforms to P, the protocol witness table is emitted directly as a symbol. If we move P to a lower-level module, the protocol witness table symbol isn't emitted, breaking users' existing executable as a result. This change checks whether the protocol used to be defined in the same module and marks it as non-resilient if so. The compiler will continue emitting witness table as symbols to maintain cross-module ABI stability. --- lib/AST/Decl.cpp | 6 +++++- lib/IRGen/GenProto.cpp | 9 +++++++++ ...ttr_originally_definedin_backward_compatibility.swift | 6 +++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a9e8116e92d2a..dbc48a767d3f9 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3535,7 +3535,11 @@ bool NominalTypeDecl::isResilient(ModuleDecl *M, case ResilienceExpansion::Minimal: return isResilient(); case ResilienceExpansion::Maximal: - return M != getModuleContext() && isResilient(); + // We consider this decl belongs to the module either it's currently + // defined in this module or it's originally defined in this module, which + // is specified by @_originallyDefinedIn + return M != getModuleContext() && + M->getName().str() != getAlternateModuleName() && isResilient(); } llvm_unreachable("bad resilience expansion"); } diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index e61ad1c1674e4..5dff2dea7221f 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -828,6 +828,15 @@ bool IRGenModule::isResilientConformance( conformanceModule == conformance->getProtocol()->getParentModule()) return false; + // If the protocol WAS from the current module (@_originallyDefinedIn), we + // consider the conformance non-resilient, because we used to consider it + // non-resilient before the symbol moved. This is to ensure ABI stability + // across module boundaries. + if (conformanceModule == getSwiftModule() && + conformanceModule->getName().str() == + conformance->getProtocol()->getAlternateModuleName()) + return false; + // If the protocol and the conformance are in the same module and the // conforming type is not generic, they're not resilient. // diff --git a/test/attr/attr_originally_definedin_backward_compatibility.swift b/test/attr/attr_originally_definedin_backward_compatibility.swift index 4f79f63b641f7..c93dac9bbce2b 100644 --- a/test/attr/attr_originally_definedin_backward_compatibility.swift +++ b/test/attr/attr_originally_definedin_backward_compatibility.swift @@ -11,7 +11,7 @@ // RUN: mkdir -p %t/SDK/Frameworks/HighLevel.framework/Modules/HighLevel.swiftmodule // RUN: %target-build-swift-dylib(%t/SDK/Frameworks/HighLevel.framework/HighLevel) -module-name HighLevel -emit-module \ // RUN: -emit-module-path %t/SDK/Frameworks/HighLevel.framework/Modules/HighLevel.swiftmodule/%module-target-triple.swiftmodule \ -// RUN: %S/Inputs/SymbolMove/HighLevelOriginal.swift -Xlinker -install_name -Xlinker @rpath/HighLevel.framework/HighLevel +// RUN: %S/Inputs/SymbolMove/HighLevelOriginal.swift -Xlinker -install_name -Xlinker @rpath/HighLevel.framework/HighLevel -enable-library-evolution // --- Build an executable using the original high level framework // RUN: %target-build-swift -emit-executable %s -g -o %t/HighlevelRunner -F %t/SDK/Frameworks/ -framework HighLevel \ @@ -24,13 +24,13 @@ // RUN: mkdir -p %t/SDK/Frameworks/LowLevel.framework/Modules/LowLevel.swiftmodule // RUN: %target-build-swift-dylib(%t/SDK/Frameworks/LowLevel.framework/LowLevel) -module-name LowLevel -emit-module \ // RUN: -emit-module-path %t/SDK/Frameworks/LowLevel.framework/Modules/LowLevel.swiftmodule/%module-target-triple.swiftmodule \ -// RUN: %S/Inputs/SymbolMove/LowLevel.swift -Xlinker -install_name -Xlinker @rpath/LowLevel.framework/LowLevel +// RUN: %S/Inputs/SymbolMove/LowLevel.swift -Xlinker -install_name -Xlinker @rpath/LowLevel.framework/LowLevel -enable-library-evolution // --- Build high level framework. // RUN: mkdir -p %t/SDK/Frameworks/HighLevel.framework/Modules/HighLevel.swiftmodule // RUN: %target-build-swift-dylib(%t/SDK/Frameworks/HighLevel.framework/HighLevel) -module-name HighLevel -emit-module \ // RUN: -emit-module-path %t/SDK/Frameworks/HighLevel.framework/Modules/HighLevel.swiftmodule/%module-target-triple.swiftmodule \ -// RUN: %S/Inputs/SymbolMove/HighLevel.swift -F %t/SDK/Frameworks -Xlinker -reexport_framework -Xlinker LowLevel +// RUN: %S/Inputs/SymbolMove/HighLevel.swift -F %t/SDK/Frameworks -Xlinker -reexport_framework -Xlinker LowLevel -enable-library-evolution // --- Run the executable // RUN: %t/HighlevelRunner | %FileCheck %s -check-prefix=AFTER_MOVE From c31b9666a329299404f3a24c510b2641b9726f8f Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 12 Dec 2019 15:45:07 -0800 Subject: [PATCH 013/478] [test] IRGen: add a test for generating linker directive symbols $ld$hide --- .../original-defined-attr-linker-hide.swift | 26 +++++++++++++++++++ test/IRGen/original-defined-attr.swift | 3 +++ 2 files changed, 29 insertions(+) create mode 100644 test/IRGen/original-defined-attr-linker-hide.swift diff --git a/test/IRGen/original-defined-attr-linker-hide.swift b/test/IRGen/original-defined-attr-linker-hide.swift new file mode 100644 index 0000000000000..4a8332932bb99 --- /dev/null +++ b/test/IRGen/original-defined-attr-linker-hide.swift @@ -0,0 +1,26 @@ +// RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name CurrentModule -D CURRENT_MODULE | %FileCheck %s --check-prefix=CHECK-SAMEMAJOR --check-prefix=CHECK-DIFFMAJOR + +@available(OSX 10.8, *) +@_originallyDefinedIn(module: "OriginalModule", macOS 10.10) +public struct Entity { + public func addEntity(_ e: Entity) {} + public func removeEntity(_ e: Entity) {} +} + +// CHECK-SAMEMAJOR: $ld$hide$os10.8$_$s14OriginalModule6EntityVN +// CHECK-SAMEMAJOR: $ld$hide$os10.9$_$s14OriginalModule6EntityVN +// CHECK-SAMEMAJOR-NOT: $ld$hide$os10.10$_$s14OriginalModule6EntityVN + +@available(OSX 9.8, *) +@_originallyDefinedIn(module: "OriginalModule", macOS 10.10) +public struct OldEntity { + public func addEntity(_ e: Entity) {} + public func removeEntity(_ e: Entity) {} +} + +// CHECK-DIFFMAJOR: $ld$hide$os9.9$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR: $ld$hide$os9.13$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR: $ld$hide$os9.30$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR: $ld$hide$os10.8$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR: $ld$hide$os10.9$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR-NOT: $ld$hide$os10.10$_$s14OriginalModule9OldEntityVN diff --git a/test/IRGen/original-defined-attr.swift b/test/IRGen/original-defined-attr.swift index d586acb05d50d..c50a97e558056 100644 --- a/test/IRGen/original-defined-attr.swift +++ b/test/IRGen/original-defined-attr.swift @@ -3,12 +3,14 @@ #if CURRENT_MODULE +@available(OSX 10.8, *) @_originallyDefinedIn(module: "OriginalModule", macOS 10.15) public struct Entity { public func addEntity(_ e: Entity) {} public func removeEntity(_ e: Entity) {} } +@available(OSX 10.8, *) @_originallyDefinedIn(module: "OriginalModule", macOS 10.15) public protocol Movable { func MovableFuncFoo() @@ -16,6 +18,7 @@ public protocol Movable { public protocol Unmoveable {} +@available(OSX 10.8, *) @_originallyDefinedIn(module: "OriginalModule", macOS 10.15) public class MovedClass: Movable, Unmoveable { public func MovableFuncFoo() {} From 5d9e7b9e78d8888ad2cc38fc5da4fbf175254220 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 12 Dec 2019 16:08:16 -0800 Subject: [PATCH 014/478] [test] TBD: add a tbd gen test for generating linker directives --- test/TBD/linker-directives.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/TBD/linker-directives.swift diff --git a/test/TBD/linker-directives.swift b/test/TBD/linker-directives.swift new file mode 100644 index 0000000000000..1bc68b2255761 --- /dev/null +++ b/test/TBD/linker-directives.swift @@ -0,0 +1,17 @@ +// REQUIRES: VENDOR=apple +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %s -tbd-is-installapi -emit-tbd -emit-tbd-path %t/linker_directives.tbd +// RUN: %FileCheck %s --check-prefix CHECK-HAS --check-prefix CHECK-HAS-NOT < %t/linker_directives.tbd +// RUN: %target-swift-frontend -typecheck %s -emit-tbd -emit-tbd-path %t/linker_directives.tbd +// RUN: %FileCheck %s --check-prefix CHECK-HAS --check-prefix CHECK-HAS-NOT < %t/linker_directives.tbd + +@available(OSX 10.8, *) +@_originallyDefinedIn(module: "ToasterKit", macOS 10.15) +public func toast() {} + +// CHECK-HAS: $ld$hide$os10.14$_$s10ToasterKit5toastyyF +// CHECK-HAS: $ld$hide$os10.8$_$s10ToasterKit5toastyyF + +// CHECK-HAS-NOT-NOT: $ld$hide$os10.15$_$s10ToasterKit5toastyyF +// CHECK-HAS-NOT-NOT: $ld$hide$os10.7$_$s10ToasterKit5toastyyF From f098448421a96a7f7d2793d88e99a16e1dc3b8a2 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 12 Dec 2019 18:10:07 -0800 Subject: [PATCH 015/478] test: add a linker directive test for ios --- .../original-defined-attr-linker-hide-ios.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/IRGen/original-defined-attr-linker-hide-ios.swift diff --git a/test/IRGen/original-defined-attr-linker-hide-ios.swift b/test/IRGen/original-defined-attr-linker-hide-ios.swift new file mode 100644 index 0000000000000..02ddaf7932d1f --- /dev/null +++ b/test/IRGen/original-defined-attr-linker-hide-ios.swift @@ -0,0 +1,16 @@ +// RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name CurrentModule -D CURRENT_MODULE | %FileCheck %s + +// REQUIRES: OS=ios + +@available(iOS 5.0, OSX 10.10, *) +@_originallyDefinedIn(module: "OriginalModule", iOS 5.4, OSX 10.13) +public struct Entity { + public func addEntity(_ e: Entity) {} + public func removeEntity(_ e: Entity) {} +} + +// CHECK: $ld$hide$os5.0$_$s14OriginalModule6EntityVN +// CHECK: $ld$hide$os5.1$_$s14OriginalModule6EntityVN +// CHECK: $ld$hide$os5.2$_$s14OriginalModule6EntityVN +// CHECK: $ld$hide$os5.3$_$s14OriginalModule6EntityVN +// CHECK-NOT: $ld$hide$os5.4$_$s14OriginalModule6EntityVN From 66b4737ddc61d810bc8b63060d8f3ecd56d0a994 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 12 Dec 2019 21:33:12 -0800 Subject: [PATCH 016/478] TBDGen: use active platform versions to genearate linker directives --- include/swift/AST/Attr.h | 2 ++ lib/AST/Attr.cpp | 4 ++++ lib/TBDGen/TBDGen.cpp | 28 ++++++++++++++++++---------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index aa72a0ccb9cd0..a38b662e72795 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1607,6 +1607,8 @@ class OriginallyDefinedInAttr: public DeclAttribute { /// Indicates when the symbol was moved here. const llvm::VersionTuple MovedVersion; + /// Returns true if this attribute is active given the current platform. + bool isActivePlatform(const ASTContext &ctx) const; static bool classof(const DeclAttribute *DA) { return DA->getKind() == DAK_OriginallyDefinedIn; } diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 85fb3b9efafd5..624f9d54f6296 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1223,6 +1223,10 @@ bool AvailableAttr::isActivePlatform(const ASTContext &ctx) const { return isPlatformActive(Platform, ctx.LangOpts); } +bool OriginallyDefinedInAttr::isActivePlatform(const ASTContext &ctx) const { + return isPlatformActive(Platform, ctx.LangOpts); +} + bool AvailableAttr::isLanguageVersionSpecific() const { if (PlatformAgnostic == PlatformAgnosticAvailabilityKind::SwiftVersionSpecific) diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index a2402a16b3640..f62466853aab7 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -69,28 +69,38 @@ void TBDGenVisitor::addSymbolInternal(StringRef name, } } +static Optional getDeclMoveOSVersion(Decl *D) { + for (auto *attr: D->getAttrs()) { + if (auto *ODA = dyn_cast(attr)) { + if (ODA->isActivePlatform(D->getASTContext())) + return ODA->MovedVersion; + } + } + return None; +} + void TBDGenVisitor::addLinkerDirectiveSymbols(StringRef name, llvm::MachO::SymbolKind kind) { if (kind != llvm::MachO::SymbolKind::GlobalSymbol) return; if (!TopLevelDecl) return; - auto ODA = TopLevelDecl->getAttrs().getAttribute(); - if (!ODA) + auto MovedVer = getDeclMoveOSVersion(TopLevelDecl); + if (!MovedVer.hasValue()) return; + assert(MovedVer.hasValue()); unsigned Major[2]; unsigned Minor[2]; - Major[1] = ODA->MovedVersion.getMajor(); - Minor[1] = ODA->MovedVersion.getMinor().hasValue() ? - *ODA->MovedVersion.getMinor(): 0; + Major[1] = MovedVer->getMajor(); + Minor[1] = MovedVer->getMinor().hasValue() ? *MovedVer->getMinor(): 0; auto AvailRange = AvailabilityInference::availableRange(TopLevelDecl, TopLevelDecl->getASTContext()).getOSVersion(); assert(AvailRange.hasLowerEndpoint() && "cannot find the start point of availability"); if (!AvailRange.hasLowerEndpoint()) return; - assert(AvailRange.getLowerEndpoint() < ODA->MovedVersion); - if (AvailRange.getLowerEndpoint() >= ODA->MovedVersion) + assert(AvailRange.getLowerEndpoint() < *MovedVer); + if (AvailRange.getLowerEndpoint() >= *MovedVer) return; Major[0] = AvailRange.getLowerEndpoint().getMajor(); Minor[0] = AvailRange.getLowerEndpoint().getMinor().hasValue() ? @@ -691,9 +701,7 @@ static bool isApplicationExtensionSafe(const LangOptions &LangOpts) { } static bool hasLinkerDirective(Decl *D) { - if (D->getAttrs().hasAttribute()) - return true; - return false; + return getDeclMoveOSVersion(D).hasValue(); } static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, From a7e757bc1aadcd09ea080b719bfd40edf13c05dd Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Fri, 13 Dec 2019 12:38:41 -0800 Subject: [PATCH 017/478] [SwiftPM] Replace new-bootstrap with bootstrap --- .../swift_build_support/swift_build_support/products/swiftpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/swift_build_support/swift_build_support/products/swiftpm.py b/utils/swift_build_support/swift_build_support/products/swiftpm.py index 475483618f126..af619c147360a 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftpm.py +++ b/utils/swift_build_support/swift_build_support/products/swiftpm.py @@ -30,7 +30,7 @@ def should_build(self, host_target): def run_bootstrap_script(self, action, host_target, additional_params=[]): script_path = os.path.join( - self.source_dir, 'Utilities', 'new-bootstrap') + self.source_dir, 'Utilities', 'bootstrap') toolchain_path = self.install_toolchain_path() swiftc = os.path.join(toolchain_path, "usr", "bin", "swiftc") From c35c9ecc3d436c3f5d0f2553fc4b901e6ea7c512 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 13 Dec 2019 14:30:07 -0800 Subject: [PATCH 018/478] [SourceKit/CodeCompletion] Remove unnecessary sorting in completion codeCompleteOpen() has own sorting algorithm, codeComplete() calls this sortCompletionResults() in its callback. So this pre-sorting is completely unnecessary. --- tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index eb4b1fa48b761..d3912b1e2c6ed 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -100,7 +100,6 @@ struct SwiftCodeCompletionConsumer void handleResults(MutableArrayRef Results) override { assert(swiftContext.swiftASTContext); - CodeCompletionContext::sortCompletionResults(Results); handleResultsImpl(Results, swiftContext); } }; From 3a55c3c96e31947f9f3ce07107a0fe9c752658e6 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Fri, 13 Dec 2019 16:07:03 -0800 Subject: [PATCH 019/478] Front-end: teach the compiler to generate a .c file for $ld$add$os symbols. When symbols are moved to this module, this module declares them as HIDE for the OS versions prior to when the move happened. On the other hand, the original module should declare ADD them for these OS versions. An executable can choose the right library to link against depending on the deployment target. This is a walk-around that linker directives cannot specify other install name per symbol, we should eventually remove this. --- .../swift/Basic/SupplementaryOutputPaths.h | 12 +++- include/swift/Frontend/Frontend.h | 2 + include/swift/Option/FrontendOptions.td | 3 + .../ArgsToFrontendOutputsConverter.cpp | 10 +++- lib/Frontend/Frontend.cpp | 8 +++ lib/FrontendTool/FrontendTool.cpp | 56 +++++++++++++++++++ test/TBD/linker-directives.swift | 6 ++ 7 files changed, 93 insertions(+), 4 deletions(-) diff --git a/include/swift/Basic/SupplementaryOutputPaths.h b/include/swift/Basic/SupplementaryOutputPaths.h index 6907d8363d6f0..26c17e4627e90 100644 --- a/include/swift/Basic/SupplementaryOutputPaths.h +++ b/include/swift/Basic/SupplementaryOutputPaths.h @@ -149,6 +149,16 @@ struct SupplementaryOutputPaths { /// \sa swift::emitSwiftInterface std::string ModuleInterfaceOutputPath; + /// The path to a .c file where we should declare $ld$add symbols for those + /// symbols moved to the current module. + /// When symbols are moved to this module, this module declares them as HIDE + /// for the OS versions prior to when the move happened. On the other hand, the + /// original module should ADD them for these OS versions. An executable + /// can choose the right library to link against depending on the deployment target. + /// This is a walk-around that linker directives cannot specify other install + /// name per symbol, we should eventually remove this. + std::string LdAddCFilePath; + SupplementaryOutputPaths() = default; SupplementaryOutputPaths(const SupplementaryOutputPaths &) = default; @@ -158,7 +168,7 @@ struct SupplementaryOutputPaths { ReferenceDependenciesFilePath.empty() && SerializedDiagnosticsPath.empty() && LoadedModuleTracePath.empty() && TBDPath.empty() && ModuleInterfaceOutputPath.empty() && - ModuleSourceInfoOutputPath.empty(); + ModuleSourceInfoOutputPath.empty() && LdAddCFilePath.empty(); } }; } // namespace swift diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 129abe163b29f..12565e1dfe8f6 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -385,6 +385,8 @@ class CompilerInvocation { /// fail an assert if not in that mode. std::string getModuleInterfaceOutputPathForWholeModule() const; + std::string getLdAddCFileOutputPathForWholeModule() const; + SerializationOptions computeSerializationOptions(const SupplementaryOutputPaths &outs, bool moduleIsPublic); diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 4196942bc66c6..dd7e51fec72f6 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -640,4 +640,7 @@ def type_info_dump_filter_EQ : Joined<["-"], "type-info-dump-filter=">, Flags<[FrontendOption]>, HelpText<"One of 'all', 'resilient' or 'fragile'">; +def emit_ldadd_cfile_path + : Separate<["-"], "emit-ldadd-cfile-path">, MetaVarName<"">, + HelpText<"Generate .c file defining symbols to add back">; } // end let Flags = [FrontendOption, NoDriverOption, HelpHidden] diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp index 14296f5ad7227..fac378995366f 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp @@ -302,11 +302,12 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() options::OPT_emit_module_interface_path); auto moduleSourceInfoOutput = getSupplementaryFilenamesFromArguments( options::OPT_emit_module_source_info_path); - + auto ldAddCFileOutput = getSupplementaryFilenamesFromArguments( + options::OPT_emit_ldadd_cfile_path); if (!objCHeaderOutput || !moduleOutput || !moduleDocOutput || !dependenciesFile || !referenceDependenciesFile || !serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD || - !moduleInterfaceOutput || !moduleSourceInfoOutput) { + !moduleInterfaceOutput || !moduleSourceInfoOutput || !ldAddCFileOutput) { return None; } std::vector result; @@ -328,6 +329,7 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() sop.TBDPath = (*TBD)[i]; sop.ModuleInterfaceOutputPath = (*moduleInterfaceOutput)[i]; sop.ModuleSourceInfoOutputPath = (*moduleSourceInfoOutput)[i]; + sop.LdAddCFilePath = (*ldAddCFileOutput)[i]; result.push_back(sop); } return result; @@ -446,6 +448,7 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( sop.TBDPath = tbdPath; sop.ModuleInterfaceOutputPath = ModuleInterfaceOutputPath; sop.ModuleSourceInfoOutputPath = moduleSourceInfoOutputPath; + sop.LdAddCFilePath = pathsFromArguments.LdAddCFilePath; return sop; } @@ -546,7 +549,8 @@ SupplementaryOutputPathsComputer::readSupplementaryOutputFileMap() const { options::OPT_emit_loaded_module_trace_path, options::OPT_emit_module_interface_path, options::OPT_emit_module_source_info_path, - options::OPT_emit_tbd_path)) { + options::OPT_emit_tbd_path, + options::OPT_emit_ldadd_cfile_path)) { Diags.diagnose(SourceLoc(), diag::error_cannot_have_supplementary_outputs, A->getSpelling(), "-supplementary-output-file-map"); diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 2bfba22289d7c..09004274db7b3 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -124,6 +124,14 @@ std::string CompilerInvocation::getTBDPathForWholeModule() const { .SupplementaryOutputs.TBDPath; } +std::string +CompilerInvocation::getLdAddCFileOutputPathForWholeModule() const { + assert(getFrontendOptions().InputsAndOutputs.isWholeModule() && + "LdAdd cfile only makes sense when the whole module can be seen"); + return getPrimarySpecificPathsForAtMostOnePrimary() + .SupplementaryOutputs.LdAddCFilePath; +} + std::string CompilerInvocation::getModuleInterfaceOutputPathForWholeModule() const { assert(getFrontendOptions().InputsAndOutputs.isWholeModule() && diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 35acaca6f5d70..a1600614448f0 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1047,6 +1047,59 @@ static bool writeTBDIfNeeded(CompilerInvocation &Invocation, return writeTBD(Instance.getMainModule(), TBDPath, tbdOpts); } +static std::string changeToLdAdd(StringRef ldHide) { + SmallString<64> SymbolBuffer; + llvm::raw_svector_ostream OS(SymbolBuffer); + auto Parts = ldHide.split("$hide$"); + assert(!Parts.first.empty()); + assert(!Parts.second.empty()); + OS << Parts.first << "$add$" << Parts.second; + return OS.str().str(); +} + +static bool writeLdAddCFileIfNeeded(CompilerInvocation &Invocation, + CompilerInstance &Instance) { + auto frontendOpts = Invocation.getFrontendOptions(); + if (!frontendOpts.InputsAndOutputs.isWholeModule()) + return false; + auto Path = Invocation.getLdAddCFileOutputPathForWholeModule(); + if (Path.empty()) + return false; + if (!frontendOpts.InputsAndOutputs.isWholeModule()) { + Instance.getDiags().diagnose(SourceLoc(), + diag::tbd_only_supported_in_whole_module); + return true; + } + auto tbdOpts = Invocation.getTBDGenOptions(); + tbdOpts.LinkerDirectivesOnly = true; + llvm::StringSet<> ldSymbols; + auto *module = Instance.getMainModule(); + enumeratePublicSymbols(module, ldSymbols, tbdOpts); + std::error_code EC; + llvm::raw_fd_ostream OS(Path, EC, llvm::sys::fs::F_None); + if (EC) { + module->getASTContext().Diags.diagnose(SourceLoc(), + diag::error_opening_output, + Path, EC.message()); + return true; + } + OS << "// Automatically generated C source file from the Swift compiler \n" + << "// to add removed symbols back to the high-level framework for deployment\n" + << "// targets prior to the OS version when these symbols were moved to\n" + << "// a low-level framework " << module->getName().str() << ".\n\n"; + unsigned Idx = 0; + for (auto &S: ldSymbols) { + SmallString<32> NameBuffer; + llvm::raw_svector_ostream NameOS(NameBuffer); + NameOS << "ldAdd_" << Idx; + OS << "extern const char " << NameOS.str() << " __asm(\"" << + changeToLdAdd(S.getKey()) << "\");\n"; + OS << "const char " << NameOS.str() << " = 0;\n"; + ++ Idx; + } + return false; +} + static bool performCompileStepsPostSILGen( CompilerInstance &Instance, CompilerInvocation &Invocation, std::unique_ptr SM, bool astGuaranteedToCorrespondToSIL, @@ -1172,6 +1225,9 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( { hadAnyError |= writeTBDIfNeeded(Invocation, Instance); } + { + hadAnyError |= writeLdAddCFileIfNeeded(Invocation, Instance); + } return hadAnyError; } diff --git a/test/TBD/linker-directives.swift b/test/TBD/linker-directives.swift index 1bc68b2255761..97c5f8a028d19 100644 --- a/test/TBD/linker-directives.swift +++ b/test/TBD/linker-directives.swift @@ -15,3 +15,9 @@ public func toast() {} // CHECK-HAS-NOT-NOT: $ld$hide$os10.15$_$s10ToasterKit5toastyyF // CHECK-HAS-NOT-NOT: $ld$hide$os10.7$_$s10ToasterKit5toastyyF + +// RUN: %target-swift-frontend -typecheck %s -emit-tbd -emit-tbd-path %t/linker_directives.tbd -emit-ldadd-cfile-path %t/ldAdd.c -module-name AppKit +// RUN: %FileCheck %s --check-prefix CHECK-C-SYMBOL < %t/ldAdd.c + +// CHECK-C-SYMBOL: $ld$add$os10.14$_$s10ToasterKit5toastyyF +// CHECK-C-SYMBOL: $ld$add$os10.8$_$s10ToasterKit5toastyyF \ No newline at end of file From d0b4da6fc6791b920c8f07925a2263b19d34a15d Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Fri, 13 Dec 2019 21:56:27 -0800 Subject: [PATCH 020/478] test: mark linker directives tests as macosx only --- test/IRGen/original-defined-attr-linker-hide.swift | 1 + test/TBD/linker-directives.swift | 2 ++ 2 files changed, 3 insertions(+) diff --git a/test/IRGen/original-defined-attr-linker-hide.swift b/test/IRGen/original-defined-attr-linker-hide.swift index 4a8332932bb99..a6bb17141b875 100644 --- a/test/IRGen/original-defined-attr-linker-hide.swift +++ b/test/IRGen/original-defined-attr-linker-hide.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name CurrentModule -D CURRENT_MODULE | %FileCheck %s --check-prefix=CHECK-SAMEMAJOR --check-prefix=CHECK-DIFFMAJOR +// REQUIRES: OS=macosx @available(OSX 10.8, *) @_originallyDefinedIn(module: "OriginalModule", macOS 10.10) diff --git a/test/TBD/linker-directives.swift b/test/TBD/linker-directives.swift index 97c5f8a028d19..3e5b9e11c88e0 100644 --- a/test/TBD/linker-directives.swift +++ b/test/TBD/linker-directives.swift @@ -1,4 +1,6 @@ // REQUIRES: VENDOR=apple +// REQUIRES: OS=macosx + // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -typecheck %s -tbd-is-installapi -emit-tbd -emit-tbd-path %t/linker_directives.tbd From 594044a049784a4d094c6abda09416e1dfddc65f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 13 Dec 2019 18:05:58 -0500 Subject: [PATCH 021/478] Sema: Clean up handling of protocol operators with concrete operands In this case we would "devirtualize" the protocol requirement call by building the AST to model a direct reference to the witness. Previously this was done by recursively calling typeCheckExpression(), but the only thing this did was recover the correct substitutions for the call. Instead, we can just build the right SubstitutionMap directly. Unfortunately, while we serialize enough information in the AST to devirtualize calls at the SIL level, we do not for AST Exprs. This is because SIL devirtualization builds a reference to the witness thunk signature, which is an intermediate step between the protocol requirement and the witness. I get around this by deriving the substitutions from walking in parallel over the interface type of the witness, together with the inferred type of the call expression. --- lib/Sema/CSApply.cpp | 134 +++++++++++++++++++-------- test/SILGen/protocol_operators.swift | 60 ++++++++++++ 2 files changed, 156 insertions(+), 38 deletions(-) create mode 100644 test/SILGen/protocol_operators.swift diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 21e74782f3db7..ff599f150d0a2 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -26,6 +26,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Initializer.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" @@ -378,6 +379,66 @@ namespace { return base.getOldType(); } + // Returns None if the AST does not contain enough information to recover + // substitutions; this is different from an Optional(SubstitutionMap()), + // indicating a valid call to a non-generic operator. + Optional + getOperatorSubstitutions(ValueDecl *witness, Type refType) { + // We have to recover substitutions in this hacky way because + // the AST does not retain enough information to devirtualize + // calls like this. + auto witnessType = witness->getInterfaceType(); + + // Compute the substitutions. + auto *gft = witnessType->getAs(); + if (gft == nullptr) { + if (refType->isEqual(witnessType)) + return SubstitutionMap(); + return None; + } + + auto sig = gft->getGenericSignature(); + auto *env = sig->getGenericEnvironment(); + + witnessType = FunctionType::get(gft->getParams(), + gft->getResult(), + gft->getExtInfo()); + witnessType = env->mapTypeIntoContext(witnessType); + + TypeSubstitutionMap subs; + auto substType = witnessType->substituteBindingsTo( + refType, + [&](ArchetypeType *origType, CanType substType) -> CanType { + if (auto gpType = dyn_cast( + origType->getInterfaceType()->getCanonicalType())) + subs[gpType] = substType; + + return substType; + }); + + // If substitution failed, it means that the protocol requirement type + // and the witness type did not match up. The only time that this + // should happen is when the witness is defined in a base class and + // the actual call uses a derived class. For example, + // + // protocol P { func +(lhs: Self, rhs: Self) } + // class Base : P { func +(lhs: Base, rhs: Base) {} } + // class Derived : Base {} + // + // If we enter this code path with two operands of type Derived, + // we know we're calling the protocol requirement P.+, with a + // substituted type of (Derived, Derived) -> (). But the type of + // the witness is (Base, Base) -> (). Just bail out and make a + // witness method call in this rare case; SIL mandatory optimizations + // will likely devirtualize it anyway. + if (!substType) + return None; + + return SubstitutionMap::get(sig, + QueryTypeSubstitutionMap{subs}, + TypeChecker::LookUpConformance(cs.DC)); + } + public: /// Build a reference to the given declaration. Expr *buildDeclRef(SelectedOverload overload, DeclNameLoc loc, @@ -400,56 +461,53 @@ namespace { // Handle operator requirements found in protocols. if (auto proto = dyn_cast(decl->getDeclContext())) { - // If we don't have an archetype or existential, we have to call the - // witness. + // If we have a concrete conformance, build a call to the witness. + // // FIXME: This is awful. We should be able to handle this as a call to // the protocol requirement with Self == the concrete type, and SILGen // (or later) can devirtualize as appropriate. - if (!baseTy->is() && !baseTy->isAnyExistentialType()) { - auto conformance = - TypeChecker::conformsToProtocol( - baseTy, proto, cs.DC, - ConformanceCheckFlags::InExpression); - if (conformance.isConcrete()) { - if (auto witness = - conformance.getConcrete()->getWitnessDecl(decl)) { - // Hack up an AST that we can type-check (independently) to get - // it into the right form. - // FIXME: the hop through 'getDecl()' is because - // SpecializedProtocolConformance doesn't substitute into - // witnesses' ConcreteDeclRefs. - Type expectedFnType = simplifyType(overload.openedType); - assert(expectedFnType->isEqual( - fullType->castTo()->getResult()) && - "Cannot handle adjustments made to the opened type"); + auto conformance = + TypeChecker::conformsToProtocol( + baseTy, proto, cs.DC, + ConformanceCheckFlags::InExpression); + if (conformance.isConcrete()) { + if (auto witness = conformance.getConcrete()->getWitnessDecl(decl)) { + // The fullType was computed by substituting the protocol + // requirement so it always has a (Self) -> ... curried + // application. Strip it off if the witness was a top-level + // function. + Type refType; + if (witness->getDeclContext()->isTypeContext()) + refType = fullType; + else + refType = fullType->castTo()->getResult(); + + // Build the AST for the call to the witness. + auto subMap = getOperatorSubstitutions(witness, refType); + if (subMap) { + ConcreteDeclRef witnessRef(witness, *subMap); + auto declRefExpr = new (ctx) DeclRefExpr(witnessRef, loc, + /*Implicit=*/false); + declRefExpr->setFunctionRefKind(choice.getFunctionRefKind()); + cs.setType(declRefExpr, refType); + Expr *refExpr; if (witness->getDeclContext()->isTypeContext()) { + // If the operator is a type member, add the implicit + // (Self) -> ... call. Expr *base = TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy, ctx); - refExpr = new (ctx) MemberRefExpr(base, SourceLoc(), witness, - loc, /*Implicit=*/true); + cs.setType(base, MetatypeType::get(baseTy)); + + refExpr = new (ctx) DotSyntaxCallExpr(declRefExpr, + SourceLoc(), base); + auto refType = fullType->castTo()->getResult(); + cs.setType(refExpr, refType); } else { - auto declRefExpr = new (ctx) DeclRefExpr(witness, loc, - /*Implicit=*/false); - declRefExpr->setFunctionRefKind(choice.getFunctionRefKind()); refExpr = declRefExpr; } - auto resultTy = TypeChecker::typeCheckExpression( - refExpr, cs.DC, TypeLoc::withoutLoc(expectedFnType), - CTP_CannotFail); - if (!resultTy) - return nullptr; - - cs.cacheExprTypes(refExpr); - - // Remove an outer function-conversion expression. This - // happens when we end up referring to a witness for a - // superclass conformance, and 'Self' differs. - if (auto fnConv = dyn_cast(refExpr)) - refExpr = fnConv->getSubExpr(); - return forceUnwrapIfExpected(refExpr, choice, locator); } } diff --git a/test/SILGen/protocol_operators.swift b/test/SILGen/protocol_operators.swift new file mode 100644 index 0000000000000..fd11a62cecf8f --- /dev/null +++ b/test/SILGen/protocol_operators.swift @@ -0,0 +1,60 @@ +// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s + +infix operator +++ + +protocol Twig { + static func +++(lhs: Self, rhs: Self) +} + +struct Branch : Twig { + @_implements(Twig, +++(_:_:)) + static func doIt(_: Branch, _: Branch) {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators9useBranchyyAA0D0VF : $@convention(thin) (Branch) -> () { +// CHECK: function_ref @$s18protocol_operators6BranchV4doItyyAC_ACtFZ : $@convention(method) (Branch, Branch, @thin Branch.Type) -> () +// CHECK: return +func useBranch(_ b: Branch) { + b +++ b +} + +class Stick : Twig { + static func +++(lhs: Stick, rhs: Stick) {} +} + +class Stuck : Stick, ExpressibleByIntegerLiteral { + typealias IntegerLiteralType = Int + + required init(integerLiteral: Int) {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators8useStickyyAA5StuckC_AA0D0CtF : $@convention(thin) (@guaranteed Stuck, @guaranteed Stick) -> () { +// CHECK: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> () +// CHECK: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> () +// CHECK: witness_method $Stuck, #Twig."+++"!1 : (Self.Type) -> (Self, Self) -> () : $@convention(witness_method: Twig) <τ_0_0 where τ_0_0 : Twig> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> () +// CHECK: return +func useStick(_ a: Stuck, _ b: Stick) { + _ = a +++ b + _ = b +++ b + _ = a +++ 5 +} + +class Twine : Twig { + static func +++(lhs: Twine, rhs: Twine) {} +} + +class Rope : Twine, ExpressibleByIntegerLiteral { + typealias IntegerLiteralType = Int + + required init(integerLiteral: Int) {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators7useRopeyyAA0D0C_ADtF : $@convention(thin) (@guaranteed Rope, @guaranteed Rope) -> () { +// CHECK: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> () +// CHECK: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> () +// CHECK: witness_method $Rope, #Twig."+++"!1 : (Self.Type) -> (Self, Self) -> () : $@convention(witness_method: Twig) <τ_0_0 where τ_0_0 : Twig> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> () +func useRope(_ r: Rope, _ s: Rope) { + _ = r +++ s + _ = s +++ s + _ = r +++ 5 +} From a9106cafca1d8cb9cd56861b1c07bda7f290f80f Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 5 Dec 2019 13:28:37 -0800 Subject: [PATCH 022/478] [ConstraintSystem] Account for missing unwrap(s) in call to optional Objective-C members --- lib/Sema/CSSimplify.cpp | 20 ++++++++++++++++++++ test/Constraints/iuo_objc.swift | 16 ++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index bbea70f1a0f10..0a95e9f84c582 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -7427,6 +7427,26 @@ ConstraintSystem::simplifyApplicableFnConstraint( // Track how many times we do this so that we can record a fix for each. ++unwrapCount; } + + // Let's account for optional members concept from Objective-C + // which forms a disjunction for member type to check whether + // it would be possible to use optional type directly or it has + // to be force unwrapped (because such types are imported as IUO). + if (unwrapCount > 0 && desugar2->is()) { + auto *typeVar = desugar2->castTo(); + auto *locator = typeVar->getImpl().getLocator(); + if (locator->isLastElement()) { + auto *fix = ForceOptional::create(*this, origType2, desugar2, + getConstraintLocator(locator)); + if (recordFix(fix, /*impact=*/unwrapCount)) + return SolutionKind::Error; + + // Since the right-hand side of the constraint has been changed + // we have to re-generate this constraint to use new type. + flags |= TMF_GenerateConstraints; + return formUnsolved(); + } + } } // For a function, bind the output and convert the argument to the input. diff --git a/test/Constraints/iuo_objc.swift b/test/Constraints/iuo_objc.swift index 1d11b98423eca..22c5ae63f479e 100644 --- a/test/Constraints/iuo_objc.swift +++ b/test/Constraints/iuo_objc.swift @@ -9,7 +9,9 @@ func iuo_error(prop: IUOProperty) { // expected-note@-2{{coalesce}} // expected-note@-3{{force-unwrap}} let _: Coat? = prop.iuo.optional()! - // expected-error@-1 {{cannot invoke 'optional' with no arguments}} + // expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped to a value of type '() -> Coat?'}} + // expected-note@-2{{coalesce}} + // expected-note@-3{{force-unwrap}} let _: Coat? = prop.iuo.optional!() let _: Coat? = prop.iuo.optional!()! let _: Coat? = prop.iuo!.optional() @@ -17,7 +19,9 @@ func iuo_error(prop: IUOProperty) { // expected-note@-2{{coalesce}} // expected-note@-3{{force-unwrap}} let _: Coat? = prop.iuo!.optional()! - // expected-error@-1 {{cannot invoke 'optional' with no arguments}} + // expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped to a value of type '() -> Coat?'}} + // expected-note@-2{{coalesce}} + // expected-note@-3{{force-unwrap}} let _: Coat? = prop.iuo!.optional!() let _: Coat? = prop.iuo!.optional!()! let _: Coat = prop.iuo.optional() @@ -25,7 +29,9 @@ func iuo_error(prop: IUOProperty) { // expected-note@-2{{coalesce}} // expected-note@-3{{force-unwrap}} let _: Coat = prop.iuo.optional()! - // expected-error@-1 {{cannot invoke 'optional' with no arguments}} + // expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped to a value of type '() -> Coat?'}} + // expected-note@-2{{coalesce}} + // expected-note@-3{{force-unwrap}} let _: Coat = prop.iuo.optional!() let _: Coat = prop.iuo.optional!()! let _: Coat = prop.iuo!.optional() @@ -34,7 +40,9 @@ func iuo_error(prop: IUOProperty) { // expected-note@-3{{force-unwrap}} let _: Coat = prop.iuo!.optional()! - // expected-error@-1 {{cannot invoke 'optional' with no arguments}} + // expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped to a value of type '() -> Coat?'}} + // expected-note@-2{{coalesce}} + // expected-note@-3{{force-unwrap}} let _: Coat = prop.iuo!.optional!() let _: Coat = prop.iuo!.optional!()! From d77e34925f64907e80f18ba0be7abb76f909fed1 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 13 Dec 2019 14:04:26 -0800 Subject: [PATCH 023/478] [ConstraintSystem] Lift a restriction on fixing of non-function calls on Any/AnyObject Detect and diagnose situations where call is attempted directly on `Any` or `AnyObject` or member calls with `AnyObject` base which didn't match. --- lib/Sema/CSDiagnostics.cpp | 13 +++++++++++++ lib/Sema/CSSimplify.cpp | 5 ----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index f5f7d757baf76..25f1113a40d12 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -5598,6 +5598,19 @@ bool ExtraneousCallFailure::diagnoseAsError() { } } + if (auto *UDE = dyn_cast(anchor)) { + auto *baseExpr = UDE->getBase(); + auto *call = cast(getRawAnchor()); + + if (getType(baseExpr)->isAnyObject()) { + emitDiagnostic(anchor->getLoc(), diag::cannot_call_with_params, + UDE->getName().getBaseName().userFacingName(), + getType(call->getArg())->getString(), + isa(baseExpr)); + return true; + } + } + auto diagnostic = emitDiagnostic( anchor->getLoc(), diag::cannot_call_non_function_value, getType(anchor)); removeParensFixIt(diagnostic); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 0a95e9f84c582..94d4c7b9ea002 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -7520,11 +7520,6 @@ ConstraintSystem::simplifyApplicableFnConstraint( desugar2->is()) return SolutionKind::Error; - if (auto objectTy = desugar2->lookThroughAllOptionalTypes()) { - if (objectTy->isAny() || objectTy->isAnyObject()) - return SolutionKind::Error; - } - // If there are any type variables associated with arguments/result // they have to be marked as "holes". type1.visit([&](Type subType) { From 2db14a9148fde911e47b90faa323711a2c43d46f Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 13 Dec 2019 14:08:03 -0800 Subject: [PATCH 024/478] [CSDiag] NFC: Remove obsolete function diagnostics from `visitApplyExpr` --- lib/Sema/CSDiag.cpp | 223 -------------------------------------------- 1 file changed, 223 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 7657ec04cd3e2..0ac2fd371b8e5 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -201,12 +201,6 @@ class FailureDiagnosis :public ASTVisitor{ ContextualTypePurpose CTP, Type suggestedType = Type()); - /// Attempt to produce a diagnostic for a mismatch between a call's - /// type and its assumed contextual type. - bool diagnoseCallContextualConversionErrors(ApplyExpr *callEpxr, - Type contextualType, - ContextualTypePurpose CTP); - bool diagnoseImplicitSelfErrors(Expr *fnExpr, Expr *argExpr, CalleeCandidateInfo &CCI, ArrayRef argLabels); @@ -1671,36 +1665,6 @@ namespace { }; } // end anonymous namespace -/// Check if there failure associated with expression is related -/// to given contextual type. -bool FailureDiagnosis::diagnoseCallContextualConversionErrors( - ApplyExpr *callExpr, Type contextualType, ContextualTypePurpose CTP) { - if (!contextualType || contextualType->hasUnresolvedType()) - return false; - - auto typeCheckExpr = [&](Expr *expr, DeclContext *DC, - SmallPtrSetImpl &types) { - getPossibleTypesOfExpressionWithoutApplying( - expr, DC, types, FreeTypeVariableBinding::Disallow); - }; - - // First let's type-check expression without contextual type, and - // see if that's going to produce a type, if so, let's type-check - // again, this time using given contextual type. - SmallPtrSet withoutContextual; - typeCheckExpr(callExpr, CS.DC, withoutContextual); - - // If there are no types returned, it means that problem was - // nothing to do with contextual information, probably parameter/argument - // mismatch. - if (withoutContextual.empty()) - return false; - - Type exprType = withoutContextual.size() == 1 ? *withoutContextual.begin() : Type(); - return diagnoseContextualConversionError(callExpr, contextualType, CTP, - exprType); -} - // Check if there is a structural problem in the function expression // by performing type checking with the option to allow unresolved // type variables. If that is going to produce a function type with @@ -1731,45 +1695,8 @@ static bool shouldTypeCheckFunctionExpr(FailureDiagnosis &FD, DeclContext *DC, return true; } -// Check if any candidate of the overload set can accept a specified -// number of arguments, regardless of parameter type or label information. -static bool isViableOverloadSet(const CalleeCandidateInfo &CCI, - size_t numArgs) { - for (unsigned i = 0; i < CCI.size(); ++i) { - auto &&cand = CCI[i]; - auto funcDecl = dyn_cast_or_null(cand.getDecl()); - - // If we don't have a func decl or we haven't resolved its parameters, - // continue. The latter case can occur with `type(of:)`, which is introduced - // as a type variable. - if (!funcDecl || !cand.hasParameters()) - continue; - - auto params = cand.getParameters(); - bool hasVariadicParameter = false; - auto pairMatcher = [&](unsigned argIdx, unsigned paramIdx) { - hasVariadicParameter |= params[paramIdx].isVariadic(); - return true; - }; - - auto paramInfo = cand.getParameterListInfo(params); - InputMatcher IM(params, paramInfo); - auto result = IM.match(numArgs, pairMatcher); - if (result == InputMatcher::IM_Succeeded) - return true; - if (result == InputMatcher::IM_HasUnclaimedInput && hasVariadicParameter) - return true; - } - return false; -} - bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { - if (diagnoseCallContextualConversionErrors(callExpr, CS.getContextualType(), - CS.getContextualTypePurpose())) - return true; - auto *fnExpr = callExpr->getFn(); - auto originalFnType = CS.getType(callExpr->getFn()); if (shouldTypeCheckFunctionExpr(*this, CS.DC, fnExpr)) { // Type check the function subexpression to resolve a type for it if @@ -1792,162 +1719,12 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { auto fnType = getFuncType(CS.getType(fnExpr)); - // Let's see if this has to do with member vs. property error - // because sometimes when there is a member and a property declared - // on the nominal type with the same name. Type-checking function - // expression separately from arguments might produce solution for - // the property instead of the member. - if (!fnType->is() && - isa(callExpr->getFn())) { - fnExpr = callExpr->getFn(); - - SmallPtrSet types; - getPossibleTypesOfExpressionWithoutApplying(fnExpr, CS.DC, types); - - auto isFunctionType = [getFuncType](Type type) -> bool { - return type && getFuncType(type)->is(); - }; - - auto fnTypes = std::find_if(types.begin(), types.end(), isFunctionType); - if (fnTypes != types.end()) { - auto funcType = getFuncType(*fnTypes); - // If there is only one function type, let's use it. - if (std::none_of(std::next(fnTypes), types.end(), isFunctionType)) - fnType = funcType; - } else { - fnType = getFuncType(originalFnType); - } - } - - // If we have a contextual type, and if we have an ambiguously typed function - // result from our previous check, we re-type-check it using this contextual - // type to inform the result type of the callee. - // - // We only do this as a second pass because the first pass we just did may - // return something of obviously non-function-type. If this happens, we - // produce better diagnostics below by diagnosing this here rather than trying - // to peel apart the failed conversion to function type. - if (CS.getContextualType() && - (isUnresolvedOrTypeVarType(fnType) || - (fnType->is() && fnType->hasUnresolvedType()))) { - // FIXME: Prevent typeCheckChildIndependently from transforming expressions, - // because if we try to typecheck OSR expression with contextual type, - // it'll end up converting it into DeclRefExpr based on contextual info, - // instead let's try to get a type without applying and filter callee - // candidates later on. - CalleeListener listener(CS.getContextualType()); - - if (isa(fnExpr)) { - assert(!cast(fnExpr)->getReferencedDecl() && - "unexpected declaration reference"); - - ConcreteDeclRef decl = nullptr; - Type type = TypeChecker::getTypeOfExpressionWithoutApplying( - fnExpr, CS.DC, decl, FreeTypeVariableBinding::UnresolvedType, - &listener); - - if (type) - fnType = getFuncType(type); - } else { - fnExpr = typeCheckChildIndependently(callExpr->getFn(), Type(), - CTP_CalleeResult, TCC_ForceRecheck, - &listener); - if (!fnExpr) - return true; - - fnType = getFuncType(CS.getType(fnExpr)); - } - } - - // If we resolved a concrete expression for the callee, and it has - // non-function/non-metatype type, then we cannot call it! - if (!isUnresolvedOrTypeVarType(fnType) && - !fnType->is() && !fnType->is()) { - auto arg = callExpr->getArg(); - - // If the argument is a trailing ClosureExpr (i.e. {....}) and it is on - // the line after the callee, then it's likely the user forgot to - // write "do" before their brace stmt. - // Note that line differences of more than 1 are diagnosed during parsing. - if (auto *PE = dyn_cast(arg)) { - if (PE->hasTrailingClosure() && isa(PE->getSubExpr())) { - auto *closure = cast(PE->getSubExpr()); - auto &SM = CS.getASTContext().SourceMgr; - if (closure->hasAnonymousClosureVars() && - closure->getParameters()->size() == 0 && - 1 + SM.getLineNumber(callExpr->getFn()->getEndLoc()) == - SM.getLineNumber(closure->getStartLoc())) { - diagnose(closure->getStartLoc(), diag::brace_stmt_suggest_do) - .fixItInsert(closure->getStartLoc(), "do "); - return true; - } - } - } - - auto isExistentialMetatypeType = fnType->is(); - if (isExistentialMetatypeType) { - auto diag = diagnose(arg->getStartLoc(), - diag::missing_init_on_metatype_initialization); - diag.highlight(fnExpr->getSourceRange()); - return true; - } else { - auto diag = diagnose(arg->getStartLoc(), - diag::cannot_call_non_function_value, fnType); - diag.highlight(fnExpr->getSourceRange()); - - // If the argument is an empty tuple, then offer a - // fix-it to remove the empty tuple and use the value - // directly. - if (auto tuple = dyn_cast(arg)) { - if (tuple->getNumElements() == 0) { - diag.fixItRemove(arg->getSourceRange()); - } - } - return true; - } - } - bool hasTrailingClosure = callArgHasTrailingClosure(callExpr->getArg()); // Collect a full candidate list of callees based on the partially type // checked function. CalleeCandidateInfo calleeInfo(fnExpr, hasTrailingClosure, CS); - // In the case that function subexpression was resolved independently in - // the first place, the resolved type may not provide the best diagnostic. - // We consider the number of arguments to decide whether we'd go with it or - // stay with the original one. - if (fnExpr != callExpr->getFn()) { - bool isInstanceMethodAsCurriedMemberOnType = false; - if (!calleeInfo.empty()) { - auto &&cand = calleeInfo[0]; - auto decl = cand.getDecl(); - if (decl && decl->isInstanceMember() && !cand.skipCurriedSelf && - cand.getParameters().size() == 1) - isInstanceMethodAsCurriedMemberOnType = true; - } - - // In terms of instance method as curried member on type, we should not - // take the number of arguments into account. - if (!isInstanceMethodAsCurriedMemberOnType) { - size_t numArgs = 1; - auto arg = callExpr->getArg(); - if (auto tuple = dyn_cast(arg)) { - numArgs = tuple->getNumElements(); - } - - if (!isViableOverloadSet(calleeInfo, numArgs)) { - CalleeCandidateInfo calleeInfoOrig(callExpr->getFn(), - hasTrailingClosure, CS); - if (isViableOverloadSet(calleeInfoOrig, numArgs)) { - fnExpr = callExpr->getFn(); - fnType = getFuncType(CS.getType(fnExpr)); - calleeInfo = calleeInfoOrig; - } - } - } - } - // Filter list of the candidates based on the known function type. if (auto fn = fnType->getAs()) { using Closeness = CalleeCandidateInfo::ClosenessResultTy; From 5ed0641c594c6cb70cdf226659d550e5fa0723af Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 10 Dec 2019 22:03:40 -0800 Subject: [PATCH 025/478] [Runtime] Handle Error-conforming-to-NSObject casting fully. I missed a case where an Error-conforming class is dynamically casted to NSObject (via NSError). Fix it. --- stdlib/public/runtime/Casting.cpp | 3 ++- test/stdlib/ErrorBridged.swift | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 2955a276e63fe..c557d007cfb78 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -1301,7 +1301,8 @@ static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest, #if SWIFT_OBJC_INTEROP // If we're casting to NSError, we may need a representation change, // so fall into the general swift_dynamicCast path. - if (targetType == getNSErrorMetadata()) { + if (targetType == getNSErrorMetadata() || + targetType == getNSObjectMetadata()) { return swift_dynamicCast(dest, src, swift_getObjectType((HeapObject*)obj), targetType, flags); } diff --git a/test/stdlib/ErrorBridged.swift b/test/stdlib/ErrorBridged.swift index f7b666b6df1f7..f863ad582a818 100644 --- a/test/stdlib/ErrorBridged.swift +++ b/test/stdlib/ErrorBridged.swift @@ -785,6 +785,11 @@ ErrorBridgingTests.test("error-to-NSObject casts") { // "is" check expectTrue(error is NSObject) + + // Unconditional cast to a dictionary. + let dict = ["key" : NoisyError()] + let anyOfDict = dict as AnyObject + let dict2 = anyOfDict as! [String: NSObject] } } From 8261d6f57e1c52cdae514bd11d0f08b3551979c0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 20 Nov 2019 16:53:59 -0800 Subject: [PATCH 026/478] [Test] Go through the runtime for the "as? NSObject" check. This prevents runtime differences on as? from affecting this test. --- test/stdlib/BridgeIdAsAny.swift.gyb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/stdlib/BridgeIdAsAny.swift.gyb b/test/stdlib/BridgeIdAsAny.swift.gyb index 0caa12107f07b..60ca732179588 100644 --- a/test/stdlib/BridgeIdAsAny.swift.gyb +++ b/test/stdlib/BridgeIdAsAny.swift.gyb @@ -199,6 +199,11 @@ testCases = [ ] }% +/// Whether this can be safely casted to NSObject +func isNSObject(_ value: T) -> Bool { + return (value as? NSObject) != nil +} + % for testName, type, valueExpr, testFunc, conformsToError, conformsToHashable in testCases: BridgeAnything.test("${testName}") { autoreleasepool { @@ -210,7 +215,7 @@ BridgeAnything.test("${testName}") { let xInArray = [x] ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0]) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0]) - if (x as? NSObject) != nil { + if isNSObject(x) { ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0]) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0]) } @@ -219,7 +224,7 @@ BridgeAnything.test("${testName}") { let xInDictValue = ["key" : x] ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: AnyObject])["key"]!) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: AnyObject])!["key"]!) - if (x as? NSObject) != nil { + if isNSObject(x) { ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: NSObject])["key"]!) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: NSObject])!["key"]!) } @@ -231,7 +236,7 @@ BridgeAnything.test("${testName}") { // The NSObject version below can't test class LifetimeTracked. // ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [(AnyObject & Hashable): String]).keys.first!) // ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [(AnyObject & Hashable): String])!.keys.first!) - if (x as? NSObject) != nil { + if isNSObject(x) { ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [NSObject: String]).keys.first!) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [NSObject: String])!.keys.first!) } From 905c830c45627284c11370648bcb689f2d1e29bf Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 13 Dec 2019 23:48:04 -0800 Subject: [PATCH 027/478] [Runtime] Handle Error bridging as a last chance to cast to NSError/NSObject. Rather than attempting Error bridging early when trying to dynamically cast to NSError or NSObject, treat it as the *last* thing we do when all else fails. Push most of this code over into Objective-C-specific handling rather than #ifdef'd into the main casting logic to make that slightly more clear. One oddity of Error/NSError bridging is that a class that conforms to Error can be dynamically cast to NSObject via Error bridging. This has always been known to the static compiler, but the runtime itself was not always handling such a cast uniformly. Do so now, uniformly. However, this forced us to weaken an assertion, because casting a class type to NSError or NSObject can produce an object with a different identity. Fixes rdar://problem/57393991. --- stdlib/public/runtime/Casting.cpp | 94 ++++++---------------------- stdlib/public/runtime/ErrorObject.h | 22 +++++++ stdlib/public/runtime/ErrorObject.mm | 25 ++++++++ stdlib/public/runtime/SwiftObject.mm | 29 +++++++++ test/stdlib/BridgeIdAsAny.swift.gyb | 2 +- 5 files changed, 95 insertions(+), 77 deletions(-) diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index c557d007cfb78..e2b34ae6579a8 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -588,15 +588,6 @@ swift_dynamicCastMetatypeToObjectUnconditional(const Metadata *metatype, } } -// internal func _getErrorEmbeddedNSErrorIndirect( -// _ x: UnsafePointer) -> AnyObject? -#define getErrorEmbeddedNSErrorIndirect \ - MANGLE_SYM(s32_getErrorEmbeddedNSErrorIndirectyyXlSgSPyxGs0B0RzlF) -SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL -id getErrorEmbeddedNSErrorIndirect(const OpaqueValue *error, - const Metadata *T, - const WitnessTable *Error); - #endif /******************************************************************************/ @@ -1256,7 +1247,6 @@ static bool _dynamicCastUnknownClassIndirect(OpaqueValue *dest, // Okay, we're doing a conditional cast. void *result = const_cast(swift_dynamicCastUnknownClass(object, targetType)); - assert(result == nullptr || object == result); // If the cast failed, destroy the input and return false. if (!result) { @@ -1278,14 +1268,6 @@ static bool _dynamicCastUnknownClassIndirect(OpaqueValue *dest, /******************************** Existentials ********************************/ /******************************************************************************/ -#if SWIFT_OBJC_INTEROP -extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error); - -static const WitnessTable *findErrorWitness(const Metadata *srcType) { - return swift_conformsToProtocol(srcType, &PROTOCOL_DESCR_SYM(s5Error)); -} -#endif - /// Perform a dynamic cast from an existential type to some kind of /// class type. static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest, @@ -1298,15 +1280,6 @@ static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest, auto classContainer = reinterpret_cast(src); void *obj = classContainer->Value; -#if SWIFT_OBJC_INTEROP - // If we're casting to NSError, we may need a representation change, - // so fall into the general swift_dynamicCast path. - if (targetType == getNSErrorMetadata() || - targetType == getNSObjectMetadata()) { - return swift_dynamicCast(dest, src, swift_getObjectType((HeapObject*)obj), - targetType, flags); - } -#endif return _dynamicCastUnknownClassIndirect(dest, obj, targetType, flags); } case ExistentialTypeRepresentation::Opaque: { @@ -1815,32 +1788,6 @@ static bool _dynamicCastToFunction(OpaqueValue *dest, } } -/******************************************************************************/ -/****************************** Bridging NSError ******************************/ -/******************************************************************************/ - -#if SWIFT_OBJC_INTEROP -static id dynamicCastValueToNSError(OpaqueValue *src, - const Metadata *srcType, - const WitnessTable *srcErrorWitness, - DynamicCastFlags flags) { - // Check whether there is an embedded NSError. - if (auto embedded = getErrorEmbeddedNSErrorIndirect(src, srcType, - srcErrorWitness)) { - if (flags & DynamicCastFlags::TakeOnSuccess) - srcType->vw_destroy(src); - - return embedded; - } - - BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src, - /*isTake*/ flags & DynamicCastFlags::TakeOnSuccess); - auto *error = (SwiftError *)errorBox.object; - return _swift_stdlib_bridgeErrorToNSError(error); -} - -#endif - /******************************************************************************/ /********************************* Optionals **********************************/ /******************************************************************************/ @@ -2333,26 +2280,6 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, // Casts to class type. case MetadataKind::Class: case MetadataKind::ObjCClassWrapper: -#if SWIFT_OBJC_INTEROP - // If the destination type is an NSError or NSObject, and the source type - // is an Error, then the cast can succeed by NSError bridging. - if (targetType == getNSErrorMetadata() || - targetType == getNSObjectMetadata()) { - // Don't rebridge if the source is already some kind of NSError. - if (srcType->isAnyClass() - && swift_dynamicCastObjCClass(*reinterpret_cast(src), - static_cast(targetType)->Class)) - return _succeed(dest, src, srcType, flags); - if (auto srcErrorWitness = findErrorWitness(srcType)) { - auto error = dynamicCastValueToNSError(src, srcType, - srcErrorWitness, flags); - *reinterpret_cast(dest) = error; - return true; - } - } - LLVM_FALLTHROUGH; -#endif - case MetadataKind::ForeignClass: switch (srcType->getKind()) { case MetadataKind::Class: @@ -2388,6 +2315,21 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, srcBridgeWitness, flags); } + +#if SWIFT_OBJC_INTEROP + // If the destination type is an NSError or NSObject, and the source type + // is an Error, then the cast can succeed by NSError bridging. + if (targetType == getNSErrorMetadata() || + targetType == getNSObjectMetadata()) { + if (auto srcErrorWitness = findErrorWitness(srcType)) { + auto error = dynamicCastValueToNSError(src, srcType, + srcErrorWitness, flags); + *reinterpret_cast(dest) = error; + return true; + } + } +#endif + return _fail(src, srcType, targetType, flags); } @@ -2847,9 +2789,9 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src, // Handle Errors. } else if (auto srcErrorWitness = findErrorWitness(srcType)) { // Bridge the source value to an NSError. - auto box = swift_allocError(srcType, srcErrorWitness, src, consume) - .object; - return _swift_stdlib_bridgeErrorToNSError((SwiftError*)box); + auto flags = consume ? DynamicCastFlags::TakeOnSuccess + : DynamicCastFlags::Default; + return dynamicCastValueToNSError(src, srcType, srcErrorWitness, flags); } // Fall back to boxing. diff --git a/stdlib/public/runtime/ErrorObject.h b/stdlib/public/runtime/ErrorObject.h index b9f211a46d2a1..5e0b702cad5d2 100644 --- a/stdlib/public/runtime/ErrorObject.h +++ b/stdlib/public/runtime/ErrorObject.h @@ -253,6 +253,17 @@ Class getNSErrorClass(); /// Get the NSError metadata. const Metadata *getNSErrorMetadata(); +/// Find the witness table for the conformance of the given type to the +/// Error protocol, or return nullptr if it does not conform. +const WitnessTable *findErrorWitness(const Metadata *srcType); + +/// Dynamically cast a value whose conformance to the Error protocol is known +/// into an NSError instance. +id dynamicCastValueToNSError(OpaqueValue *src, + const Metadata *srcType, + const WitnessTable *srcErrorWitness, + DynamicCastFlags flags); + #endif SWIFT_RUNTIME_STDLIB_SPI @@ -263,4 +274,15 @@ const size_t _swift_lldb_sizeof_SwiftError; } // namespace swift +#if SWIFT_OBJC_INTEROP +// internal func _getErrorEmbeddedNSErrorIndirect( +// _ x: UnsafePointer) -> AnyObject? +#define getErrorEmbeddedNSErrorIndirect \ + MANGLE_SYM(s32_getErrorEmbeddedNSErrorIndirectyyXlSgSPyxGs0B0RzlF) +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +id getErrorEmbeddedNSErrorIndirect(const swift::OpaqueValue *error, + const swift::Metadata *T, + const swift::WitnessTable *Error); +#endif + #endif diff --git a/stdlib/public/runtime/ErrorObject.mm b/stdlib/public/runtime/ErrorObject.mm index b47e6bcb71d4e..941e839da3cc1 100644 --- a/stdlib/public/runtime/ErrorObject.mm +++ b/stdlib/public/runtime/ErrorObject.mm @@ -178,6 +178,31 @@ - (BOOL)isEqual:(id)other { swift_getObjCClassMetadata((const ClassMetadata *)getNSErrorClass())); } +extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error); + +const WitnessTable *swift::findErrorWitness(const Metadata *srcType) { + return swift_conformsToProtocol(srcType, &PROTOCOL_DESCR_SYM(s5Error)); +} + +id swift::dynamicCastValueToNSError(OpaqueValue *src, + const Metadata *srcType, + const WitnessTable *srcErrorWitness, + DynamicCastFlags flags) { + // Check whether there is an embedded NSError. + if (id embedded = getErrorEmbeddedNSErrorIndirect(src, srcType, + srcErrorWitness)) { + if (flags & DynamicCastFlags::TakeOnSuccess) + srcType->vw_destroy(src); + + return embedded; + } + + BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src, + /*isTake*/ flags & DynamicCastFlags::TakeOnSuccess); + auto *error = (SwiftError *)errorBox.object; + return _swift_stdlib_bridgeErrorToNSError(error); +} + static Class getAndBridgeSwiftNativeNSErrorClass() { Class nsErrorClass = swift::getNSErrorClass(); Class ourClass = [__SwiftNativeNSError class]; diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index 95ba09346ba44..6a2393e033e5e 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -35,6 +35,7 @@ #include "../SwiftShims/RuntimeShims.h" #include "../SwiftShims/AssertionReporting.h" #include "CompatibilityOverride.h" +#include "ErrorObject.h" #include "Private.h" #include "SwiftObject.h" #include "WeakReference.h" @@ -1098,6 +1099,20 @@ static bool isObjCForUnownedReference(void *value) { return object; } + // For casts to NSError or NSObject, we might need to bridge via the Error + // protocol. Try it now. + if (targetType == reinterpret_cast(getNSErrorClass()) || + targetType == reinterpret_cast([NSObject class])) { + auto srcType = swift_getObjCClassMetadata( + reinterpret_cast( + object_getClass(id_const_cast(object)))); + if (auto srcErrorWitness = findErrorWitness(srcType)) { + return dynamicCastValueToNSError((OpaqueValue*)&object, srcType, + srcErrorWitness, + DynamicCastFlags::TakeOnSuccess); + } + } + return nullptr; } @@ -1114,6 +1129,20 @@ static bool isObjCForUnownedReference(void *value) { return object; } + // For casts to NSError or NSObject, we might need to bridge via the Error + // protocol. Try it now. + if (targetType == reinterpret_cast(getNSErrorClass()) || + targetType == reinterpret_cast([NSObject class])) { + auto srcType = swift_getObjCClassMetadata( + reinterpret_cast( + object_getClass(id_const_cast(object)))); + if (auto srcErrorWitness = findErrorWitness(srcType)) { + return dynamicCastValueToNSError((OpaqueValue*)&object, srcType, + srcErrorWitness, + DynamicCastFlags::TakeOnSuccess); + } + } + Class sourceType = object_getClass(id_const_cast(object)); swift_dynamicCastFailure(reinterpret_cast(sourceType), targetType); diff --git a/test/stdlib/BridgeIdAsAny.swift.gyb b/test/stdlib/BridgeIdAsAny.swift.gyb index 60ca732179588..20edaac2f7fdb 100644 --- a/test/stdlib/BridgeIdAsAny.swift.gyb +++ b/test/stdlib/BridgeIdAsAny.swift.gyb @@ -201,7 +201,7 @@ testCases = [ /// Whether this can be safely casted to NSObject func isNSObject(_ value: T) -> Bool { - return (value as? NSObject) != nil + return (value is NSObject) && !(value is LifetimeTracked) } % for testName, type, valueExpr, testFunc, conformsToError, conformsToHashable in testCases: From 169dbcdc1744780e48a7688301a4e349407a4014 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 11 Dec 2019 10:19:37 -0800 Subject: [PATCH 028/478] Revert "Disable a test to unblock CI" This reverts commit 29a6d1066e765cdb3a5e1d7bdfb22310b2885664. --- test/stdlib/BridgeIdAsAny.swift.gyb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/stdlib/BridgeIdAsAny.swift.gyb b/test/stdlib/BridgeIdAsAny.swift.gyb index 20edaac2f7fdb..e0433b026d65c 100644 --- a/test/stdlib/BridgeIdAsAny.swift.gyb +++ b/test/stdlib/BridgeIdAsAny.swift.gyb @@ -175,10 +175,7 @@ protocol P {} %{ testCases = [ # testName type valueExpr testFunc conformsToError conformsToHashable - - # disabled to unblock CI: rdar://problem/57393991 - # ("classes", "LifetimeTracked", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True, True), - + ("classes", "LifetimeTracked", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True, True), ("strings", "String", '"vitameatavegamin"', "stringBridgesToEqualNSString", True, True), ("unbridged type", "KnownUnbridged", "KnownUnbridged()", "boxedTypeRoundTripsThroughDynamicCasting", False, True), ("tuple", "(Int, String)", '(1, "2")', "tupleCanBeDynamicallyCast", False, False), From 7466aa5f34b30d3679731e219bbfdad59889f795 Mon Sep 17 00:00:00 2001 From: Prashant Rane Date: Sat, 14 Dec 2019 16:26:55 +0530 Subject: [PATCH 029/478] Fix a typo in ${NEW} variable in Post-processing tools for diagnostics section While reading up I realized there is a typo in: `how to use utils/process-stats-dir.py`. The example asks to use OLD and NEW compiler but the documentation uses only ${OLD} compiler in example --- docs/CompilerPerformance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CompilerPerformance.md b/docs/CompilerPerformance.md index 97b23bb429b21..3f5599520711c 100644 --- a/docs/CompilerPerformance.md +++ b/docs/CompilerPerformance.md @@ -793,7 +793,7 @@ performance between two compilers, say `${OLD}/swiftc` and `${NEW}/swiftc`: ``` $ mkdir stats-old stats-new $ ${OLD}/swiftc -stats-output-dir stats-old test.swift -$ ${OLD}/swiftc -stats-output-dir stats-new test.swift +$ ${NEW}/swiftc -stats-output-dir stats-new test.swift $ utils/process-stats-dir.py --compare-stats-dirs stats-old stats-new old new delta_pct name 1402939 1430732 1.98 AST.NumASTBytesAllocated From f9eb34a5fae407e342164dc8e496d4a53478734a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 18 Nov 2019 17:05:46 -0800 Subject: [PATCH 030/478] [Constraint solver] Switch coercion to be solution-based. Rather than spinning up a new constraint system when performing an "as" coercion, perform the coercion with the known solution, because we already did all of the work to figure out how to perform the coercion. --- lib/Sema/CSApply.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 21e74782f3db7..d89d2d7bd1872 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -3482,12 +3482,9 @@ namespace { // Convert the subexpression. Expr *sub = expr->getSubExpr(); - solution.setExprTypes(sub); - - if (TypeChecker::convertToType(sub, toType, cs.DC)) + sub = solution.coerceToType(sub, toType, cs.getConstraintLocator(sub)); + if (!sub) return nullptr; - - cs.cacheExprTypes(sub); expr->setSubExpr(sub); cs.setType(expr, toType); From cb69e00b83c46c5322ca4b887d22c8dd3cf52400 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 19 Nov 2019 08:48:50 -0800 Subject: [PATCH 031/478] [Constraint application] Stop optimizing casts in the AST. Constraint application was rewriting conditional casts (as?) and forced casts (as!) into coercions (as) at the AST level when it determined that there was a coercion. Stop doing that: it's the optimizer's job to remove such casts. --- lib/Sema/CSApply.cpp | 41 +++++++---------------------------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index d89d2d7bd1872..45454cabb53af 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -3535,9 +3535,6 @@ namespace { return nullptr; case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: { - if (SuppressDiagnostics) - return nullptr; - if (cs.getType(sub)->isEqual(toType)) { ctx.Diags.diagnose(expr->getLoc(), diag::forced_downcast_noop, toType) .fixItRemove(SourceRange( @@ -3551,14 +3548,9 @@ namespace { "as"); } - // Transmute the checked cast into a coercion expression. - auto *result = - new (ctx) CoerceExpr(sub, expr->getLoc(), expr->getCastTypeLoc()); - cs.setType(result, toType); - cs.setType(result->getCastTypeLoc(), toType); - unsigned disjunctionChoice = - (castKind == CheckedCastKind::Coercion ? 0 : 1); - return visitCoerceExpr(result, disjunctionChoice); + expr->setCastKind(castKind); + cs.setType(expr, toType); + return expr; } // Valid casts. @@ -3611,37 +3603,18 @@ namespace { fromType, toType, castContextKind, cs.DC, expr->getLoc(), sub, expr->getCastTypeLoc().getSourceRange()); switch (castKind) { - /// Invalid cast. + // Invalid cast. case CheckedCastKind::Unresolved: expr->setCastKind(CheckedCastKind::ValueCast); break; case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: { - if (SuppressDiagnostics) - return nullptr; - ctx.Diags.diagnose(expr->getLoc(), diag::conditional_downcast_coercion, cs.getType(sub), toType); - - // Transmute the checked cast into a coercion expression. - auto *coerce = - new (ctx) CoerceExpr(sub, expr->getLoc(), expr->getCastTypeLoc()); - cs.setType(coerce, toType); - cs.setType(coerce->getCastTypeLoc(), toType); - unsigned disjunctionChoice = - (castKind == CheckedCastKind::Coercion ? 0 : 1); - Expr *result = visitCoerceExpr(coerce, disjunctionChoice); - if (!result) - return nullptr; - - // Wrap the result in an optional. Mark the optional injection as - // explicit, because the user did in fact write the '?' as part of - // 'as?', even though it wasn't necessary. - result = - new (ctx) InjectIntoOptionalExpr(result, OptionalType::get(toType)); - result->setImplicit(false); - return cs.cacheType(result); + expr->setCastKind(castKind); + cs.setType(expr, OptionalType::get(toType)); + return expr; } // Valid casts. From 5e30d0e02b41a9ac3f9ab321712e7b6669216a4b Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Sat, 14 Dec 2019 18:33:39 -0800 Subject: [PATCH 032/478] sema: diagnose bad interactions between @available and @_originallyDefinedIn --- include/swift/AST/DiagnosticsSema.def | 9 +++++ lib/Sema/TypeCheckAttr.cpp | 47 ++++++++++++++++++----- test/Sema/diag_originally_definedin.swift | 12 ++++++ 3 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 test/Sema/diag_originally_definedin.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 231508ca6a280..9a20318174057 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1466,6 +1466,15 @@ NOTE(option_set_empty_set_init,none, ERROR(originally_defined_in_dupe_platform,none, "duplicate version number for platform %0", (StringRef)) +ERROR(originally_definedin_topleve_decl,none, + "@%0 is only applicable to top-level decl", (StringRef)) + +ERROR(originally_definedin_need_available,none, + "need @available attribute for @%0", (StringRef)) + +ERROR(originally_definedin_must_after_available_version,none, + "moved version from @%0 must after introduced OS version", (StringRef)) + // Alignment attribute ERROR(alignment_not_power_of_two,none, "alignment value must be a power of two", ()) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index f5aff4e682c30..239d28ed61feb 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -252,7 +252,7 @@ class AttributeChecker : public AttributeVisitor { void visitImplementationOnlyAttr(ImplementationOnlyAttr *attr); void visitNonEphemeralAttr(NonEphemeralAttr *attr); - void checkOriginalDefinedInAttrs(ArrayRef Attrs); + void checkOriginalDefinedInAttrs(Decl *D, ArrayRef Attrs); }; } // end anonymous namespace @@ -1070,7 +1070,7 @@ void TypeChecker::checkDeclAttributes(Decl *D) { else Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute, attr); } - Checker.checkOriginalDefinedInAttrs(ODIAttrs); + Checker.checkOriginalDefinedInAttrs(D, ODIAttrs); } /// Returns true if the given method is an valid implementation of a @@ -2631,21 +2631,48 @@ void TypeChecker::checkParameterAttributes(ParameterList *params) { } } -void -AttributeChecker::checkOriginalDefinedInAttrs( +void AttributeChecker::checkOriginalDefinedInAttrs(Decl *D, ArrayRef Attrs) { - llvm::SmallSet AllPlatforms; + if (Attrs.empty()) + return; + auto &Ctx = D->getASTContext(); + OriginallyDefinedInAttr* theAttr = nullptr; // Attrs are in the reverse order of the source order. We need to visit them // in source order to diagnose the later attribute. - for (auto It = Attrs.rbegin(), End = Attrs.rend(); It != End; ++ It) { - auto *Attr = *It; - auto CurPlat = Attr->Platform; - if (!AllPlatforms.insert(CurPlat).second) { + for (auto *Attr: Attrs) { + if (!Attr->isActivePlatform(Ctx)) + continue; + if (theAttr) { // Only one version number is allowed for one platform name. - diagnose(Attr->AtLoc, diag::originally_defined_in_dupe_platform, + diagnose(theAttr->AtLoc, diag::originally_defined_in_dupe_platform, platformString(Attr->Platform)); + return; + } else { + theAttr = Attr; } } + if (!theAttr) + return; + assert(theAttr); + static StringRef AttrName = "_originallyDefinedIn"; + auto AtLoc = theAttr->AtLoc; + if (!D->getDeclContext()->isModuleScopeContext()) { + diagnose(AtLoc, diag::originally_definedin_topleve_decl, AttrName); + return; + } + auto AvailRange = AvailabilityInference::availableRange(D, Ctx); + if (!AvailRange.getOSVersion().hasLowerEndpoint()) { + diagnose(AtLoc, diag::originally_definedin_need_available, + AttrName); + return; + } + auto AvailBegin = AvailRange.getOSVersion().getLowerEndpoint(); + if (AvailBegin >= theAttr->MovedVersion) { + diagnose(AtLoc, + diag::originally_definedin_must_after_available_version, + AttrName); + return; + } } Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type, diff --git a/test/Sema/diag_originally_definedin.swift b/test/Sema/diag_originally_definedin.swift new file mode 100644 index 0000000000000..90fd72161bb31 --- /dev/null +++ b/test/Sema/diag_originally_definedin.swift @@ -0,0 +1,12 @@ +// RUN: %target-typecheck-verify-swift +// REQUIRES: OS=macosx + +@_originallyDefinedIn(module: "original", OSX 10.13) // expected-error {{need @available attribute for @_originallyDefinedIn}} +public func foo() {} + +@available(macOS 10.13, *) +@_originallyDefinedIn(module: "original", OSX 10.12) // expected-error {{moved version from @_originallyDefinedIn must after introduced OS version}} +public class C { + @_originallyDefinedIn(module: "original", OSX 10.13) // expected-error {{@_originallyDefinedIn is only applicable to top-level decl}} + public func foo() {} +} From 3cefb7b3f7ec5bf7fb1ca3c569e7bfbc23c00a79 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sat, 14 Dec 2019 22:15:01 -0800 Subject: [PATCH 033/478] validation-test: mark test as XFAIL TBD generation is not constrained to Darwin and it trips on the linker synthetic `__ImageBase`. XFAIL this until the TBD generation is either constrained or is taught to ignore the synthetic. This should temporarily allow us to enable validation tests on Windows. --- validation-test/execution/dsohandle-multi-module.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/validation-test/execution/dsohandle-multi-module.swift b/validation-test/execution/dsohandle-multi-module.swift index 01a28856bcf6c..734c599f0d318 100644 --- a/validation-test/execution/dsohandle-multi-module.swift +++ b/validation-test/execution/dsohandle-multi-module.swift @@ -9,6 +9,7 @@ // REQUIRES: executable_test // UNSUPPORTED: linux +// XFAIL: windows import first import second From e65e09e029365a207f180951db46904112bb01fb Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sun, 15 Dec 2019 10:26:17 -0800 Subject: [PATCH 034/478] [lit] Fix a typo causing us to not put a space in between an && and a run command. Noticed while debugging why a test was failing locally. --- test/lit.cfg | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/lit.cfg b/test/lit.cfg index a57ad1a3903f0..1b43328020d69 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -1498,21 +1498,21 @@ if not getattr(config, 'target_run_simple_swift', None): config.target_run_simple_swift_parameterized = \ (SubstituteCaptures('%%empty-directory(%%t) && ' '%s %s %%s \\1 -o %%t/a.out -module-name main && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run))) config.target_run_simple_swift = ( '%%empty-directory(%%t) && ' '%s %s %%s -o %%t/a.out -module-name main && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run)) config.target_run_stdlib_swift = ( '%%empty-directory(%%t) && ' '%s %s %%s -o %%t/a.out -module-name main ' '-Xfrontend -disable-access-control && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run)) config.target_run_simple_swiftgyb = ( @@ -1520,7 +1520,7 @@ if not getattr(config, 'target_run_simple_swift', None): '%%gyb %%s -o %%t/main.swift && ' '%%line-directive %%t/main.swift -- ' '%s %s %%t/main.swift -o %%t/a.out -module-name main && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%%line-directive %%t/main.swift -- ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run)) @@ -1530,7 +1530,7 @@ if not getattr(config, 'target_run_simple_swift', None): '%%line-directive %%t/main.swift -- ' '%s %s %%t/main.swift -o %%t/a.out -module-name main ' '-Xfrontend -disable-access-control && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%%line-directive %%t/main.swift -- ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run)) From c5747c3d9633f682e468e7638775e53263b420b1 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Fri, 13 Dec 2019 16:32:32 -0800 Subject: [PATCH 035/478] [NFC] Refactor name parsing into `Parser::parseDeclNameRef()` Gives this functionality a more self-documenting interface. --- include/swift/Parse/Parser.h | 38 ++++++ lib/Parse/ParseExpr.cpp | 229 +++++++++++++++++++++-------------- 2 files changed, 178 insertions(+), 89 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 9974e8212154c..8c5c1af114264 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1412,6 +1412,44 @@ class Parser { /// \param loc The location of the label (empty if it doesn't exist) void parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc); + enum class DeclNameFlag : uint8_t { + /// If passed, operator basenames are allowed. + AllowOperators = 1 << 0, + + /// If passed, names that coincide with keywords are allowed. Used after a + /// dot to enable things like '.init' and '.default'. + AllowKeywords = 1 << 1, + + /// If passed, 'deinit' and 'subscript' should be parsed as special names, + /// not ordinary identifiers. + UseSpecialNamesForDeinitAndSubscript = AllowKeywords | 1 << 2, + + /// If passed, compound names with argument lists are allowed, unless they + /// have empty argument lists. + AllowCompoundNames = 1 << 4, + + /// If passed, compound names with empty argument lists are allowed. + AllowZeroArgCompoundNames = AllowCompoundNames | 1 << 5, + }; + using DeclNameOptions = OptionSet; + + friend DeclNameOptions operator|(DeclNameFlag flag1, DeclNameFlag flag2) { + return DeclNameOptions(flag1) | flag2; + } + + /// Without \c DeclNameFlag::AllowCompoundNames, parse an + /// unqualified-decl-base-name. + /// + /// unqualified-decl-base-name: identifier + /// + /// With \c DeclNameFlag::AllowCompoundNames, parse an unqualified-base-name. + /// + /// unqualified-decl-name: + /// unqualified-decl-base-name + /// unqualified-decl-base-name '(' ((identifier | '_') ':') + ')' + DeclNameRef parseDeclNameRef(DeclNameLoc &loc, const Diagnostic &diag, + DeclNameOptions flags); + /// Parse an unqualified-decl-base-name. /// /// unqualified-decl-name: diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index b65596824c059..d61e4f13466bc 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2078,12 +2078,87 @@ void Parser::parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc) { } } -DeclNameRef Parser::parseUnqualifiedDeclBaseName( - bool afterDot, - DeclNameLoc &loc, - const Diagnostic &diag, - bool allowOperators, - bool allowDeinitAndSubscript) { +static bool tryParseArgLabelList(Parser &P, Parser::DeclNameOptions flags, + SourceLoc &lparenLoc, + SmallVectorImpl &argumentLabels, + SmallVectorImpl &argumentLabelLocs, + SourceLoc &rparenLoc) { + if (!flags.contains(Parser::DeclNameFlag::AllowCompoundNames)) + return false; + + // Is the current token a left paren? + if (!P.Tok.isFollowingLParen()) + return false; + + // Okay, let's look ahead and see if the next token is something that could + // be in an arg label list... + const Token &next = P.peekToken(); + + // A close parenthesis, if empty lists are allowed. + bool nextIsRParen = + flags.contains(Parser::DeclNameFlag::AllowZeroArgCompoundNames) && + next.is(tok::r_paren); + // An argument label. + bool nextIsArgLabel = next.canBeArgumentLabel() || next.is(tok::colon); + // An editor placeholder. + bool nextIsPlaceholder = Identifier::isEditorPlaceholder(next.getText()); + + if (!(nextIsRParen || nextIsArgLabel || nextIsPlaceholder)) + return false; + + // Try to parse a compound name. + SyntaxParsingContext ArgsCtxt(P.SyntaxContext, SyntaxKind::DeclNameArguments); + Parser::BacktrackingScope backtrack(P); + + lparenLoc = P.consumeToken(tok::l_paren); + while (P.Tok.isNot(tok::r_paren)) { + SyntaxParsingContext ArgCtxt(P.SyntaxContext, SyntaxKind::DeclNameArgument); + + // If we see a ':', the user forgot the '_'; + if (P.Tok.is(tok::colon)) { + P.diagnose(P.Tok, diag::empty_arg_label_underscore) + .fixItInsert(P.Tok.getLoc(), "_"); + argumentLabels.push_back(Identifier()); + argumentLabelLocs.push_back(P.consumeToken(tok::colon)); + } + + Identifier argName; + SourceLoc argLoc; + P.parseOptionalArgumentLabel(argName, argLoc); + if (argLoc.isValid()) { + argumentLabels.push_back(argName); + argumentLabelLocs.push_back(argLoc); + continue; + } + + // This is not a compound name. + // FIXME: Could recover better if we "know" it's a compound name. + ArgCtxt.setBackTracking(); + ArgsCtxt.setBackTracking(); + + return false; + } + + // We have a compound name. Cancel backtracking and build that name. + backtrack.cancelBacktrack(); + + if (argumentLabels.empty() && P.SyntaxContext->isEnabled()) + P.SyntaxContext->addSyntax( + ParsedSyntaxRecorder::makeBlankDeclNameArgumentList( + P.leadingTriviaLoc(), *P.SyntaxContext)); + else + ArgsCtxt.collectNodesInPlace(SyntaxKind::DeclNameArgumentList); + + rparenLoc = P.consumeToken(tok::r_paren); + + assert(argumentLabels.size() == argumentLabelLocs.size()); + + return true; +} + +DeclNameRef Parser::parseDeclNameRef(DeclNameLoc &loc, + const Diagnostic &diag, + DeclNameOptions flags) { // Consume the base name. DeclBaseName baseName; SourceLoc baseNameLoc; @@ -2092,17 +2167,21 @@ DeclNameRef Parser::parseUnqualifiedDeclBaseName( baseNameLoc = consumeIdentifier( &baseNameId, /*allowDollarIdentifier=*/true); baseName = baseNameId; - } else if (allowOperators && Tok.isAnyOperator()) { + } else if (flags.contains(DeclNameFlag::AllowOperators) && + Tok.isAnyOperator()) { baseName = Context.getIdentifier(Tok.getText()); baseNameLoc = consumeToken(); - } else if (afterDot && Tok.isKeyword()) { + } else if (flags.contains(DeclNameFlag::AllowKeywords) && Tok.isKeyword()) { + bool specialDeinitAndSubscript = + flags.contains(DeclNameFlag::UseSpecialNamesForDeinitAndSubscript); + // Syntax highlighting should treat this token as an identifier and // not as a keyword. if (Tok.is(tok::kw_init)) baseName = DeclBaseName::createConstructor(); - else if (allowDeinitAndSubscript &&Tok.is(tok::kw_deinit)) + else if (specialDeinitAndSubscript && Tok.is(tok::kw_deinit)) baseName = DeclBaseName::createDestructor(); - else if (allowDeinitAndSubscript &&Tok.is(tok::kw_subscript)) + else if (specialDeinitAndSubscript && Tok.is(tok::kw_subscript)) baseName = DeclBaseName::createSubscript(); else baseName = Context.getIdentifier(Tok.getText()); @@ -2115,95 +2194,67 @@ DeclNameRef Parser::parseUnqualifiedDeclBaseName( return DeclNameRef(); } - loc = DeclNameLoc(baseNameLoc); - return DeclNameRef(baseName); -} - - -DeclNameRef Parser::parseUnqualifiedDeclName(bool afterDot, - DeclNameLoc &loc, - const Diagnostic &diag, - bool allowOperators, - bool allowZeroArgCompoundNames, - bool allowDeinitAndSubscript) { - // Consume the base name. - auto baseName = parseUnqualifiedDeclBaseName(afterDot, loc, diag, - allowOperators, - allowDeinitAndSubscript); - - // If the next token isn't a following '(', we don't have a compound name. - if (!baseName || !Tok.isFollowingLParen()) - return baseName; - - // If the next token is a ')' then we have a 0-arg compound name. This is - // explicitly differentiated from "simple" (non-compound) name in DeclName. - // Unfortunately only some places in the grammar are ok with accepting this - // kind of name; in other places it's ambiguous with trailing calls. - if (allowZeroArgCompoundNames && peekToken().is(tok::r_paren)) { - SyntaxParsingContext ArgsCtxt(SyntaxContext, SyntaxKind::DeclNameArguments); - consumeToken(tok::l_paren); - if (SyntaxContext->isEnabled()) - SyntaxContext->addSyntax( - ParsedSyntaxRecorder::makeBlankDeclNameArgumentList( - leadingTriviaLoc(), *SyntaxContext)); - consumeToken(tok::r_paren); - SmallVector argumentLabels; - return baseName.withArgumentLabels(Context, argumentLabels); - } - - // If the token after that isn't an argument label or ':', we don't have a - // compound name. - if ((!peekToken().canBeArgumentLabel() && !peekToken().is(tok::colon)) || - Identifier::isEditorPlaceholder(peekToken().getText())) { - return baseName; - } - - // Try to parse a compound name. - SyntaxParsingContext ArgsCtxt(SyntaxContext, SyntaxKind::DeclNameArguments); - BacktrackingScope backtrack(*this); - + // Parse an argument list, if the flags allow it and it's present. SmallVector argumentLabels; SmallVector argumentLabelLocs; - SourceLoc lparenLoc = consumeToken(tok::l_paren); + SourceLoc lparenLoc; SourceLoc rparenLoc; - while (Tok.isNot(tok::r_paren)) { - SyntaxParsingContext ArgCtxt(SyntaxContext, SyntaxKind::DeclNameArgument); - // If we see a ':', the user forgot the '_'; - if (Tok.is(tok::colon)) { - diagnose(Tok, diag::empty_arg_label_underscore) - .fixItInsert(Tok.getLoc(), "_"); - argumentLabels.push_back(Identifier()); - argumentLabelLocs.push_back(consumeToken(tok::colon)); - } + bool hadArgList = tryParseArgLabelList(*this, flags, lparenLoc, + argumentLabels, argumentLabelLocs, + rparenLoc); - Identifier argName; - SourceLoc argLoc; - parseOptionalArgumentLabel(argName, argLoc); - if (argLoc.isValid()) { - argumentLabels.push_back(argName); - argumentLabelLocs.push_back(argLoc); - continue; - } + if (argumentLabelLocs.empty() || !hadArgList) + loc = DeclNameLoc(baseNameLoc); + else + loc = DeclNameLoc(Context, baseNameLoc, lparenLoc, argumentLabelLocs, + rparenLoc); - // This is not a compound name. - // FIXME: Could recover better if we "know" it's a compound name. - ArgCtxt.setBackTracking(); - ArgsCtxt.setBackTracking(); - return baseName; + if (!hadArgList) + return DeclNameRef(baseName); + + return DeclNameRef({ Context, baseName, argumentLabels }); +} + +DeclNameRef Parser::parseUnqualifiedDeclBaseName( + bool afterDot, + DeclNameLoc &loc, + const Diagnostic &diag, + bool allowOperators, + bool allowDeinitAndSubscript) { + DeclNameOptions flags = {}; + if (afterDot) + flags |= DeclNameFlag::AllowKeywords; + if (allowOperators) + flags |= DeclNameFlag::AllowOperators; + if (allowDeinitAndSubscript) { + assert(afterDot); + flags |= DeclNameFlag::UseSpecialNamesForDeinitAndSubscript; } - // We have a compound name. Cancel backtracking and build that name. - backtrack.cancelBacktrack(); - ArgsCtxt.collectNodesInPlace(SyntaxKind::DeclNameArgumentList); - rparenLoc = consumeToken(tok::r_paren); + return parseDeclNameRef(loc, diag, flags); +} - assert(!argumentLabels.empty() && "Logic above should prevent this"); - assert(argumentLabels.size() == argumentLabelLocs.size()); - loc = DeclNameLoc(Context, loc.getBaseNameLoc(), lparenLoc, argumentLabelLocs, - rparenLoc); - return baseName.withArgumentLabels(Context, argumentLabels); +DeclNameRef Parser::parseUnqualifiedDeclName(bool afterDot, + DeclNameLoc &loc, + const Diagnostic &diag, + bool allowOperators, + bool allowZeroArgCompoundNames, + bool allowDeinitAndSubscript) { + DeclNameOptions flags = DeclNameFlag::AllowCompoundNames; + if (afterDot) + flags |= DeclNameFlag::AllowKeywords; + if (allowOperators) + flags |= DeclNameFlag::AllowOperators; + if (allowDeinitAndSubscript) { + assert(afterDot); + flags |= DeclNameFlag::UseSpecialNamesForDeinitAndSubscript; + } + if (allowZeroArgCompoundNames) + flags |= DeclNameFlag::AllowZeroArgCompoundNames; + + return parseDeclNameRef(loc, diag, flags); } /// expr-identifier: From 9fa4303dd4ff3f80fc4a7988caf83a910b984c4d Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Sat, 14 Dec 2019 12:44:09 -0800 Subject: [PATCH 036/478] [NFC] Switch uses over to parseDeclNameRef() --- lib/Parse/ParseDecl.cpp | 41 +++++++++++++++++++---------------------- lib/Parse/ParseExpr.cpp | 26 ++++++++++++++------------ lib/Parse/ParseType.cpp | 5 ++--- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 9d4ededb03921..2fd88da0ac4c3 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -768,11 +768,10 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) { } if (!Status.shouldStopParsing()) { - MemberName = - parseUnqualifiedDeclName(/*afterDot=*/false, MemberNameLoc, - diag::attr_implements_expected_member_name, - /*allowOperators=*/true, - /*allowZeroArgCompoundNames=*/true); + MemberName = parseDeclNameRef(MemberNameLoc, + diag::attr_implements_expected_member_name, + DeclNameFlag::AllowZeroArgCompoundNames | + DeclNameFlag::AllowOperators); if (!MemberName) { Status.setIsParseError(); } @@ -1022,10 +1021,8 @@ bool Parser::parseDifferentiableAttributeArguments( SyntaxContext, SyntaxKind::FunctionDeclName); Diagnostic funcDiag(diag::attr_differentiable_expected_function_name.ID, { label }); - result.Name = - parseUnqualifiedDeclName(/*afterDot=*/false, result.Loc, - funcDiag, /*allowOperators=*/true, - /*allowZeroArgCompoundNames=*/true); + result.Name = parseDeclNameRef(result.Loc, funcDiag, + DeclNameFlag::AllowZeroArgCompoundNames | DeclNameFlag::AllowOperators); // If no trailing comma or 'where' clause, terminate parsing arguments. if (Tok.isNot(tok::comma, tok::kw_where)) terminateParsingArgs = true; @@ -1129,10 +1126,11 @@ ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, SyntaxKind::FunctionDeclName); // NOTE: Use `afterDot = true` and `allowDeinitAndSubscript = true` to // enable, e.g. `@derivative(of: init)` and `@derivative(of: subscript)`. - original.Name = parseUnqualifiedDeclName( - /*afterDot*/ true, original.Loc, - diag::attr_derivative_expected_original_name, /*allowOperators*/ true, - /*allowZeroArgCompoundNames*/ true, /*allowDeinitAndSubscript*/ true); + original.Name = parseDeclNameRef(original.Loc, + diag::attr_derivative_expected_original_name, + DeclNameFlag::AllowZeroArgCompoundNames | + DeclNameFlag::AllowKeywords | DeclNameFlag::AllowOperators | + DeclNameFlag::UseSpecialNamesForDeinitAndSubscript); } if (consumeIfTrailingComma()) return makeParserError(); @@ -2060,10 +2058,11 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, SyntaxKind::DeclName); DeclNameLoc loc; - replacedFunction = parseUnqualifiedDeclName( - true, loc, diag::attr_dynamic_replacement_expected_function, - /*allowOperators*/ true, /*allowZeroArgCompoundNames*/ true, - /*allowDeinitAndSubscript*/ true); + replacedFunction = parseDeclNameRef(loc, + diag::attr_dynamic_replacement_expected_function, + DeclNameFlag::AllowZeroArgCompoundNames | + DeclNameFlag::AllowKeywords | DeclNameFlag::AllowOperators | + DeclNameFlag::UseSpecialNamesForDeinitAndSubscript); } } @@ -2572,11 +2571,9 @@ bool Parser::parseConventionAttributeInternal( return true; } - DeclNameLoc unusedWitnessMethodProtocolLoc; - convention.WitnessMethodProtocol = parseUnqualifiedDeclBaseName( - /*afterDot=*/false, unusedWitnessMethodProtocolLoc, - diag::convention_attribute_witness_method_expected_protocol - ); + DeclNameLoc unusedLoc; + convention.WitnessMethodProtocol = parseDeclNameRef(unusedLoc, + diag::convention_attribute_witness_method_expected_protocol, {}); } // Parse the ')'. We can't use parseMatchingToken if we're in diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index d61e4f13466bc..1ef094ba47c50 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -644,6 +644,7 @@ ParserResult Parser::parseExprKeyPathObjC() { // Parse the sequence of unqualified-names. ParserStatus status; SourceLoc LastDotLoc; + DeclNameOptions flags = DeclNameFlag::AllowCompoundNames; while (true) { SyntaxParsingContext NamePieceCtx(SyntaxContext, SyntaxKind::ObjcNamePiece); // Handle code completion. @@ -652,10 +653,8 @@ ParserResult Parser::parseExprKeyPathObjC() { // Parse the next name. DeclNameLoc nameLoc; - bool afterDot = !components.empty(); - auto name = parseUnqualifiedDeclName( - afterDot, nameLoc, - diag::expr_keypath_expected_property_or_type); + DeclNameRef name = parseDeclNameRef(nameLoc, + diag::expr_keypath_expected_property_or_type, flags); if (!name) { status.setIsParseError(); break; @@ -666,6 +665,9 @@ ParserResult Parser::parseExprKeyPathObjC() { nameLoc.getBaseNameLoc()); components.push_back(component); + // After the first component, we can start parsing keywords. + flags |= DeclNameFlag::AllowKeywords; + // Handle code completion. if (Tok.is(tok::code_complete)) return handleCodeCompletion(SourceLoc()); @@ -1130,7 +1132,8 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, Diag<> D = isa(Result.get()) ? diag::expected_identifier_after_super_dot_expr : diag::expected_member_name; - DeclNameRef Name = parseUnqualifiedDeclName(/*afterDot=*/true, NameLoc, D); + auto Name = parseDeclNameRef(NameLoc, D, + DeclNameFlag::AllowKeywords | DeclNameFlag::AllowCompoundNames); if (!Name) return nullptr; SyntaxContext->createNodeInPlace(SyntaxKind::MemberAccessExpr); @@ -1580,8 +1583,8 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { return Result; } - Name = parseUnqualifiedDeclName(/*afterDot=*/true, NameLoc, - diag::expected_identifier_after_dot_expr); + Name = parseDeclNameRef(NameLoc, diag::expected_identifier_after_dot_expr, + DeclNameFlag::AllowKeywords | DeclNameFlag::AllowCompoundNames); if (!Name) return nullptr; SyntaxContext->createNodeInPlace(SyntaxKind::MemberAccessExpr); @@ -2267,8 +2270,8 @@ Expr *Parser::parseExprIdentifier() { // Parse the unqualified-decl-name. DeclNameLoc loc; - DeclNameRef name = parseUnqualifiedDeclName(/*afterDot=*/false, loc, - diag::expected_expr); + DeclNameRef name = parseDeclNameRef(loc, diag::expected_expr, + DeclNameFlag::AllowCompoundNames); SmallVector args; SourceLoc LAngleLoc, RAngleLoc; @@ -3082,9 +3085,8 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, SyntaxParsingContext operatorContext(SyntaxContext, SyntaxKind::IdentifierExpr); DeclNameLoc Loc; - auto OperName = parseUnqualifiedDeclBaseName(/*afterDot=*/false, Loc, - diag::expected_operator_ref, - /*allowOperators=*/true); + auto OperName = parseDeclNameRef(Loc, diag::expected_operator_ref, + DeclNameFlag::AllowOperators); if (!OperName) { return makeParserError(); } diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 6d7e84cd2c61f..5b9356a2594b4 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -667,9 +667,8 @@ ParserResult Parser::parseTypeIdentifier() { SourceLoc EndLoc; while (true) { DeclNameLoc Loc; - DeclNameRef Name = parseUnqualifiedDeclBaseName( - /*afterDot=*/false, Loc, - diag::expected_identifier_in_dotted_type); + DeclNameRef Name = + parseDeclNameRef(Loc, diag::expected_identifier_in_dotted_type, {}); if (!Name) Status.setIsParseError(); From b5f02c3d0c0a0f0b6bfcb271a4b10d9eb63ad628 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Sat, 14 Dec 2019 12:45:08 -0800 Subject: [PATCH 037/478] [NFC] Remove unused Parser::parseUnqualifiedDeclName methods --- include/swift/Parse/Parser.h | 33 ----------------------------- lib/Parse/ParseExpr.cpp | 41 ------------------------------------ 2 files changed, 74 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 8c5c1af114264..b1413b5eea164 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1450,39 +1450,6 @@ class Parser { DeclNameRef parseDeclNameRef(DeclNameLoc &loc, const Diagnostic &diag, DeclNameOptions flags); - /// Parse an unqualified-decl-base-name. - /// - /// unqualified-decl-name: - /// identifier - /// - /// \param afterDot Whether this identifier is coming after a period, which - /// enables '.init' and '.default' like expressions. - /// \param loc Will be populated with the location of the name. - /// \param diag The diagnostic to emit if this is not a name. - /// \param allowOperators Whether to allow operator basenames too. - DeclNameRef parseUnqualifiedDeclBaseName(bool afterDot, DeclNameLoc &loc, - const Diagnostic &diag, - bool allowOperators=false, - bool allowDeinitAndSubscript=false); - - /// Parse an unqualified-decl-name. - /// - /// unqualified-decl-name: - /// unqualified-decl-base-name - /// unqualified-decl-base-name '(' ((identifier | '_') ':') + ')' - /// - /// \param afterDot Whether this identifier is coming after a period, which - /// enables '.init' and '.default' like expressions. - /// \param loc Will be populated with the location of the name. - /// \param diag The diagnostic to emit if this is not a name. - /// \param allowOperators Whether to allow operator basenames too. - /// \param allowZeroArgCompoundNames Whether to allow empty argument lists. - DeclNameRef parseUnqualifiedDeclName(bool afterDot, DeclNameLoc &loc, - const Diagnostic &diag, - bool allowOperators=false, - bool allowZeroArgCompoundNames=false, - bool allowDeinitAndSubscript=false); - Expr *parseExprIdentifier(); Expr *parseExprEditorPlaceholder(Token PlaceholderTok, Identifier PlaceholderId); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 1ef094ba47c50..3733e73a27159 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2219,47 +2219,6 @@ DeclNameRef Parser::parseDeclNameRef(DeclNameLoc &loc, return DeclNameRef({ Context, baseName, argumentLabels }); } -DeclNameRef Parser::parseUnqualifiedDeclBaseName( - bool afterDot, - DeclNameLoc &loc, - const Diagnostic &diag, - bool allowOperators, - bool allowDeinitAndSubscript) { - DeclNameOptions flags = {}; - if (afterDot) - flags |= DeclNameFlag::AllowKeywords; - if (allowOperators) - flags |= DeclNameFlag::AllowOperators; - if (allowDeinitAndSubscript) { - assert(afterDot); - flags |= DeclNameFlag::UseSpecialNamesForDeinitAndSubscript; - } - - return parseDeclNameRef(loc, diag, flags); -} - - -DeclNameRef Parser::parseUnqualifiedDeclName(bool afterDot, - DeclNameLoc &loc, - const Diagnostic &diag, - bool allowOperators, - bool allowZeroArgCompoundNames, - bool allowDeinitAndSubscript) { - DeclNameOptions flags = DeclNameFlag::AllowCompoundNames; - if (afterDot) - flags |= DeclNameFlag::AllowKeywords; - if (allowOperators) - flags |= DeclNameFlag::AllowOperators; - if (allowDeinitAndSubscript) { - assert(afterDot); - flags |= DeclNameFlag::UseSpecialNamesForDeinitAndSubscript; - } - if (allowZeroArgCompoundNames) - flags |= DeclNameFlag::AllowZeroArgCompoundNames; - - return parseDeclNameRef(loc, diag, flags); -} - /// expr-identifier: /// unqualified-decl-name generic-args? Expr *Parser::parseExprIdentifier() { From bed2ce4ac5025cb19ea7eeec5b536ca9e73e9ae4 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Sun, 15 Dec 2019 23:44:38 -0800 Subject: [PATCH 038/478] [NFC] Improve wording and usage of subscript/deinit flag --- include/swift/Parse/Parser.h | 2 +- lib/Parse/ParseDecl.cpp | 8 ++++---- lib/Parse/ParseExpr.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index b1413b5eea164..86454a5177492 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1422,7 +1422,7 @@ class Parser { /// If passed, 'deinit' and 'subscript' should be parsed as special names, /// not ordinary identifiers. - UseSpecialNamesForDeinitAndSubscript = AllowKeywords | 1 << 2, + AllowKeywordsUsingSpecialNames = AllowKeywords | 1 << 2, /// If passed, compound names with argument lists are allowed, unless they /// have empty argument lists. diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 2fd88da0ac4c3..3ef22c70d78bd 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1129,8 +1129,8 @@ ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, original.Name = parseDeclNameRef(original.Loc, diag::attr_derivative_expected_original_name, DeclNameFlag::AllowZeroArgCompoundNames | - DeclNameFlag::AllowKeywords | DeclNameFlag::AllowOperators | - DeclNameFlag::UseSpecialNamesForDeinitAndSubscript); + DeclNameFlag::AllowKeywordsUsingSpecialNames | + DeclNameFlag::AllowOperators); } if (consumeIfTrailingComma()) return makeParserError(); @@ -2061,8 +2061,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, replacedFunction = parseDeclNameRef(loc, diag::attr_dynamic_replacement_expected_function, DeclNameFlag::AllowZeroArgCompoundNames | - DeclNameFlag::AllowKeywords | DeclNameFlag::AllowOperators | - DeclNameFlag::UseSpecialNamesForDeinitAndSubscript); + DeclNameFlag::AllowKeywordsUsingSpecialNames | + DeclNameFlag::AllowOperators); } } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 3733e73a27159..e093eac765b0a 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2176,7 +2176,7 @@ DeclNameRef Parser::parseDeclNameRef(DeclNameLoc &loc, baseNameLoc = consumeToken(); } else if (flags.contains(DeclNameFlag::AllowKeywords) && Tok.isKeyword()) { bool specialDeinitAndSubscript = - flags.contains(DeclNameFlag::UseSpecialNamesForDeinitAndSubscript); + flags.contains(DeclNameFlag::AllowKeywordsUsingSpecialNames); // Syntax highlighting should treat this token as an identifier and // not as a keyword. From 89b12ae28f6003110ab81de1518ba53a8d53297e Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 16 Dec 2019 11:24:04 +0100 Subject: [PATCH 039/478] Remove an unused member field in the SILLinkerVisitor. NFC --- lib/SIL/Linker.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/SIL/Linker.h b/lib/SIL/Linker.h index 12ad57ae57fff..2901959ddfbc6 100644 --- a/lib/SIL/Linker.h +++ b/lib/SIL/Linker.h @@ -33,10 +33,6 @@ class SILLinkerVisitor : public SILInstructionVisitor { /// Worklist of SILFunctions we are processing. llvm::SmallVector Worklist; - /// A list of callees of the current instruction being visited. cleared after - /// every instruction is visited. - llvm::SmallVector FunctionDeserializationWorklist; - /// The current linking mode. LinkingMode Mode; @@ -45,8 +41,7 @@ class SILLinkerVisitor : public SILInstructionVisitor { public: SILLinkerVisitor(SILModule &M, SILModule::LinkingMode LinkingMode) - : Mod(M), Worklist(), FunctionDeserializationWorklist(), - Mode(LinkingMode), Changed(false) {} + : Mod(M), Worklist(), Mode(LinkingMode), Changed(false) {} /// Process F, recursively deserializing any thing F may reference. /// Returns true if any deserialization was performed. From 23cc0790a7b65661c33a8409c7b0c5d362304b1a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 16 Dec 2019 08:46:33 -0800 Subject: [PATCH 040/478] [Dynamic casting] Fix overrelease on unsuccessful cast to an Error type. Fixes SR-9207 / rdar://problem/45961622, SR-10478 / rdar://problem/49906841, and rdar://problem/50665156. --- stdlib/public/runtime/Casting.cpp | 5 ++++- test/stdlib/ErrorBridged.swift | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index e2b34ae6579a8..d7cb076505cb9 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -2382,7 +2382,10 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, #if SWIFT_OBJC_INTEROP // If the source is an NSError, and the target is a bridgeable // Error, try to bridge. - if (tryDynamicCastNSErrorToValue(dest, src, srcType, targetType, flags)) { + auto innerFlags = flags - DynamicCastFlags::Unconditional + - DynamicCastFlags::DestroyOnFailure; + if (tryDynamicCastNSErrorToValue(dest, src, srcType, targetType, + innerFlags)) { return true; } #endif diff --git a/test/stdlib/ErrorBridged.swift b/test/stdlib/ErrorBridged.swift index f863ad582a818..da284173bef6f 100644 --- a/test/stdlib/ErrorBridged.swift +++ b/test/stdlib/ErrorBridged.swift @@ -820,4 +820,20 @@ ErrorBridgingTests.test("CFError-to-Error casts") { } } +enum MyError: Error { + case someThing +} + +ErrorBridgingTests.test("SR-9207 crash in failed cast to NSError") { + + if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { + let error = MyError.someThing + let foundationError = error as NSError + + if let urlError = foundationError as? URLError { + expectUnreachable() + } + } +} + runAllTests() From 7d09aee9a164223ad1d2a04c4081342c08f6bc32 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 16 Dec 2019 09:10:14 -0800 Subject: [PATCH 041/478] ClosureLifetimeFixup: Handle undef partial_apply arguments gracefully We have to handle undef partial_apply arguments to handle the following source gracefully during the diagnosis pipeline. ``` class TestUndefined { private var stringList: [String]! func dontCrash(strings: [String]) { assert(stringList.allSatisfy({ $0 == stringList.first!})) let stringList = strings.filter({ $0 == "a" }) } } ``` rdar://57893008 --- .../Mandatory/ClosureLifetimeFixup.cpp | 7 ++++-- test/SILOptimizer/closure-lifetime-fixup.sil | 23 +++++++++++++++++++ .../closure_lifetime_fixup_undef.swift | 13 +++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 test/SILOptimizer/closure_lifetime_fixup_undef.swift diff --git a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp index d0c9c67e5c49b..6a9ac0aa51d88 100644 --- a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp +++ b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp @@ -458,6 +458,9 @@ static bool tryRewriteToPartialApplyStack( saveDeleteInst(convertOrPartialApply); saveDeleteInst(origPA); + // Only insert destroys for defined partial_apply arguments. + auto isDefined = [](SILValue arg) -> bool { return !isa(arg); }; + // Insert destroys of arguments after the apply and the dealloc_stack. if (auto *apply = dyn_cast(singleApplyUser)) { auto insertPt = std::next(SILBasicBlock::iterator(apply)); @@ -466,12 +469,12 @@ static bool tryRewriteToPartialApplyStack( return true; SILBuilderWithScope b3(insertPt); b3.createDeallocStack(loc, newPA); - insertDestroyOfCapturedArguments(newPA, b3); + insertDestroyOfCapturedArguments(newPA, b3, isDefined); } else if (auto *tai = dyn_cast(singleApplyUser)) { for (auto *succBB : tai->getSuccessorBlocks()) { SILBuilderWithScope b3(succBB->begin()); b3.createDeallocStack(loc, newPA); - insertDestroyOfCapturedArguments(newPA, b3); + insertDestroyOfCapturedArguments(newPA, b3, isDefined); } } else { llvm_unreachable("Unknown FullApplySite instruction kind"); diff --git a/test/SILOptimizer/closure-lifetime-fixup.sil b/test/SILOptimizer/closure-lifetime-fixup.sil index caf2dcc47136a..6ece4cb4e2ec7 100644 --- a/test/SILOptimizer/closure-lifetime-fixup.sil +++ b/test/SILOptimizer/closure-lifetime-fixup.sil @@ -155,3 +155,26 @@ bb1: %86 = tuple () return %86 : $() } + +sil [ossa] @closureImpl : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> Bool +sil [ossa] @useClosure : $@convention(thin) (@noescape @callee_guaranteed () -> Bool) -> () + +// Don't crash. +// CHECK-LABEL: sil hidden [ossa] @testUndefined +// CHECK: [[PA:%.*]] = partial_apply [callee_guaranteed] [on_stack] +// CHECK: mark_dependence +// CHECK: mark_dependence +// CHECK: dealloc_stack [[PA]] : $@noescape @callee_guaranteed () -> Bool +// CHECK: destroy_value +sil hidden [ossa] @testUndefined : $@convention(method) (@guaranteed Klass, @guaranteed Klass) -> () { +bb0(%0 : @guaranteed $Klass, %1 : @guaranteed $Klass): + %4 = function_ref @closureImpl : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> Bool + %5 = copy_value %1 : $Klass + %6 = partial_apply [callee_guaranteed] %4(%5, undef) : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> Bool + %7 = convert_escape_to_noescape [not_guaranteed] %6 : $@callee_guaranteed () -> Bool to $@noescape @callee_guaranteed () -> Bool + %21 = function_ref @useClosure : $@convention(thin) (@noescape @callee_guaranteed () -> Bool) -> () + %22 = apply %21(%7) : $@convention(thin) (@noescape @callee_guaranteed () -> Bool) -> () + destroy_value %6 : $@callee_guaranteed () -> Bool + %42 = tuple () + return %42 : $() +} diff --git a/test/SILOptimizer/closure_lifetime_fixup_undef.swift b/test/SILOptimizer/closure_lifetime_fixup_undef.swift new file mode 100644 index 0000000000000..cf27396999f52 --- /dev/null +++ b/test/SILOptimizer/closure_lifetime_fixup_undef.swift @@ -0,0 +1,13 @@ +// RUN: not %target-swift-frontend %s -sil-verify-all -c 2>&1 | %FileCheck %s + +// Report the error but don't crash. +// CHECK: error: closure captures 'stringList' before it is declared + +class TestUndefined { + private var stringList: [String]! + + func dontCrash(strings: [String]) { + assert(stringList.allSatisfy({ $0 == stringList.first!})) + let stringList = strings.filter({ $0 == "a" }) + } +} From cefc03f0e279f0fc94058e931bc12af72c2bba4f Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 12 Dec 2019 12:53:02 -0800 Subject: [PATCH 042/478] [CS] Resolve callees for key path dynamic members Change the locator for the applicable fn constraint for the inner subscript reference in an implicit `outer[dynamicMember: \Inner.[0]]` expr such that it uses the member locator with a KeyPathDynamicMember element. Then add a case to `getCalleeLocator` to handle these locators. We also need to handle this specially in `getArgumentInfoLocator` due to the fact that the argument labels for the inner subscript reference correspond to those written by the user for the outer subscript reference. Resolves SR-11933. Resolves rdar://problem/57824025. --- lib/Sema/CSSimplify.cpp | 4 +-- lib/Sema/ConstraintSystem.cpp | 30 +++++++++++++++++-- .../keypath_dynamic_member_lookup.swift | 27 ++++++++++++++++- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index bbea70f1a0f10..35e36c9c68a3d 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -788,9 +788,7 @@ getCalleeDeclAndArgs(ConstraintSystem &cs, // Our remaining path can only be 'ApplyArgument'. auto path = callLocator->getPath(); - if (!path.empty() && - !(path.size() <= 2 && - path.back().getKind() == ConstraintLocator::ApplyArgument)) + if (!path.empty() && !path.back().is()) return formUnknownCallee(); // Dig out the callee information. diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index a0ef70251cb2d..1f1e2c484ef33 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -438,6 +438,18 @@ ConstraintSystem::getCalleeLocator(ConstraintLocator *locator, auto *anchor = locator->getAnchor(); assert(anchor && "Expected an anchor!"); + auto path = locator->getPath(); + { + // If we have a locator for a member found through key path dynamic member + // lookup, then we need to chop off the elements after the + // KeyPathDynamicMember element to get the callee locator. + auto iter = path.rbegin(); + if (locator->findLast(iter)) { + auto newPath = path.drop_back(iter - path.rbegin()); + return getConstraintLocator(anchor, newPath); + } + } + // If we have a locator that starts with a key path component element, we // may have a callee given by a property or subscript component. if (auto componentElt = @@ -2152,8 +2164,10 @@ void ConstraintSystem::bindOverloadType( auto adjustedFnTy = FunctionType::get(fnType->getParams(), subscriptResultTy); - addConstraint(ConstraintKind::ApplicableFunction, adjustedFnTy, memberTy, - applicableFn->getLocator()); + ConstraintLocatorBuilder kpLocBuilder(keyPathLoc); + addConstraint( + ConstraintKind::ApplicableFunction, adjustedFnTy, memberTy, + kpLocBuilder.withPathElement(ConstraintLocator::ApplyFunction)); addConstraint(ConstraintKind::Bind, dynamicResultTy, fnType->getResult(), keyPathLoc); @@ -3424,6 +3438,18 @@ ConstraintSystem::getArgumentInfoLocator(ConstraintLocator *locator) { if (auto *UME = dyn_cast(anchor)) return getConstraintLocator(UME); + auto path = locator->getPath(); + { + // If this is for a dynamic member reference, the argument info is for the + // original call-site, which we can get by stripping away the + // KeyPathDynamicMember elements. + auto iter = path.begin(); + if (locator->findFirst(iter)) { + ArrayRef newPath(path.begin(), iter); + return getConstraintLocator(anchor, newPath); + } + } + return getCalleeLocator(locator); } diff --git a/test/Constraints/keypath_dynamic_member_lookup.swift b/test/Constraints/keypath_dynamic_member_lookup.swift index 021434bd58488..68b7eac7f0c07 100644 --- a/test/Constraints/keypath_dynamic_member_lookup.swift +++ b/test/Constraints/keypath_dynamic_member_lookup.swift @@ -439,7 +439,6 @@ struct SR_11896_Immutable { } } - // CHECK-LABEL: sil hidden @$s29keypath_dynamic_member_lookup21testKeyPathMutabilityyyF : $@convention(thin) () -> () func testKeyPathMutability() { // CHECK: keypath $KeyPath, (root $SR_11896_Base; stored_property #SR_11896_Base.mutable : $Int) @@ -458,3 +457,29 @@ func testKeyPathMutability() { // CHECK: keypath $KeyPath, (root $SR_11896_Immutable; gettable_property $Int _ = \SR_11896_Immutable.immutable } + +// SR-11933: Make sure we properly handle default arguments. +struct HasDefaultedSubscript { + subscript(_ x: Int = 0) -> Int { x } +} + +@dynamicMemberLookup +struct SR_11933 { + subscript(dynamicMember kp: KeyPath) -> Int { 0 } +} + +// CHECK-LABEL: sil hidden @$s29keypath_dynamic_member_lookup28testDynamicMemberWithDefaultyyAA8SR_11933VF : $@convention(thin) (SR_11933) -> () +func testDynamicMemberWithDefault(_ x: SR_11933) { + // CHECK: [[DEF_FN:%[0-9]+]] = function_ref @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icipfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF_ARG:%[0-9]+]] = apply [[DEF_FN]]() + // CHECK: [[KP:%[0-9]+]] = keypath $KeyPath, (root $HasDefaultedSubscript; gettable_property $Int, id @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icig : $@convention(method) (Int, HasDefaultedSubscript) -> Int, getter @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icipACTK : $@convention(thin) (@in_guaranteed HasDefaultedSubscript, UnsafeRawPointer) -> @out Int, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[DEF_ARG]]) + // CHECK: [[SUB_GET:%[0-9]+]] = function_ref @$s29keypath_dynamic_member_lookup8SR_11933V0B6MemberSis7KeyPathCyAA21HasDefaultedSubscriptVSiG_tcig : $@convention(method) (@guaranteed KeyPath, SR_11933) -> Int + // CHECK: apply [[SUB_GET]]([[KP]], {{%[0-9]+}}) + _ = x[] + + // CHECK: [[DEF_FN:%[0-9]+]] = function_ref @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icipfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF_ARG:%[0-9]+]] = apply [[DEF_FN]]() + // CHECK: [[INNER_KP:%[0-9]+]] = keypath $KeyPath, (root $HasDefaultedSubscript; gettable_property $Int, id @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icig : $@convention(method) (Int, HasDefaultedSubscript) -> Int, getter @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icipACTK : $@convention(thin) (@in_guaranteed HasDefaultedSubscript, UnsafeRawPointer) -> @out Int, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[DEF_ARG]]) + // CHECK: [[OUTER_KP:%[0-9]+]] = keypath $KeyPath, (root $SR_11933; gettable_property $Int, id @$s29keypath_dynamic_member_lookup8SR_11933V0B6MemberSis7KeyPathCyAA21HasDefaultedSubscriptVSiG_tcig : $@convention(method) (@guaranteed KeyPath, SR_11933) -> Int, getter @$s29keypath_dynamic_member_lookup8SR_11933V0B6MemberSis7KeyPathCyAA21HasDefaultedSubscriptVSiG_tcipACTK : $@convention(thin) (@in_guaranteed SR_11933, UnsafeRawPointer) -> @out Int, indices [%$0 : $KeyPath : $KeyPath], indices_equals @$ss7KeyPathCy29keypath_dynamic_member_lookup21HasDefaultedSubscriptVSiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$ss7KeyPathCy29keypath_dynamic_member_lookup21HasDefaultedSubscriptVSiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[INNER_KP]]) + _ = \SR_11933.[] +} From 8a3e4a2fe8ef9fba532d77cd9d224e1307e6d554 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Mon, 16 Dec 2019 10:39:50 -0800 Subject: [PATCH 043/478] Update the Xcode version to 11.3 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 424c27e2e3daa..3d998e0197640 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Please make sure you use Python 2.x. Python 3.x is not supported currently. #### macOS -To build for macOS, you need [Xcode 11.2](https://developer.apple.com/xcode/downloads/). +To build for macOS, you need [Xcode 11.3](https://developer.apple.com/xcode/downloads/). The required version of Xcode changes frequently, and is often a beta release. Check this document or the host information on for the current required version. From 84ff97ac68ec61ed628c66fc4a35210331cb3990 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 16 Dec 2019 11:28:14 -0800 Subject: [PATCH 044/478] [NFC] Drop a dead overload of DynamicSubscriptExpr::create --- include/swift/AST/Expr.h | 11 ----------- lib/AST/Expr.cpp | 27 --------------------------- 2 files changed, 38 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 2f616623e3009..7afd8e75db84b 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -1741,17 +1741,6 @@ class DynamicSubscriptExpr final llvm::function_ref getType = [](const Expr *E) -> Type { return E->getType(); }); - /// Create a new dynamic subscript. - static DynamicSubscriptExpr *create(ASTContext &ctx, Expr *base, - SourceLoc lSquareLoc, - ArrayRef indexArgs, - ArrayRef indexArgLabels, - ArrayRef indexArgLabelLocs, - SourceLoc rSquareLoc, - Expr *trailingClosure, - ConcreteDeclRef decl, - bool implicit); - /// getIndex - Retrieve the index of the subscript expression, i.e., the /// "offset" into the base value. Expr *getIndex() const { return Index; } diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 7eb779525f5cd..9296e947989cb 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1504,33 +1504,6 @@ DynamicSubscriptExpr::create(ASTContext &ctx, Expr *base, Expr *index, hasTrailingClosure, decl, implicit); } -DynamicSubscriptExpr * -DynamicSubscriptExpr::create(ASTContext &ctx, Expr *base, SourceLoc lSquareLoc, - ArrayRef indexArgs, - ArrayRef indexArgLabels, - ArrayRef indexArgLabelLocs, - SourceLoc rSquareLoc, - Expr *trailingClosure, - ConcreteDeclRef decl, - bool implicit) { - SmallVector indexArgLabelsScratch; - SmallVector indexArgLabelLocsScratch; - Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, - indexArgLabelLocs, rSquareLoc, - trailingClosure, implicit, - indexArgLabelsScratch, - indexArgLabelLocsScratch); - - size_t size = totalSizeToAlloc(indexArgLabels, indexArgLabelLocs, - trailingClosure != nullptr); - - void *memory = ctx.Allocate(size, alignof(DynamicSubscriptExpr)); - return new (memory) DynamicSubscriptExpr(base, index, indexArgLabels, - indexArgLabelLocs, - trailingClosure != nullptr, - decl, implicit); -} - UnresolvedMemberExpr::UnresolvedMemberExpr(SourceLoc dotLoc, DeclNameLoc nameLoc, DeclNameRef name, Expr *argument, From 1534b3659def796757b65d7088b5b6be5e25e418 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 16 Dec 2019 11:28:54 -0800 Subject: [PATCH 045/478] [NFC] Const-qualify SelectedOverload's members --- lib/Sema/CSApply.cpp | 3 ++- lib/Sema/ConstraintSystem.h | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index e9d0a412b314a..2e440c8dc43c1 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -4485,7 +4485,8 @@ namespace { } void buildKeyPathSubscriptComponent( - SelectedOverload &overload, SourceLoc componentLoc, Expr *indexExpr, + const SelectedOverload &overload, + SourceLoc componentLoc, Expr *indexExpr, ArrayRef labels, ConstraintLocator *locator, SmallVectorImpl &components) { auto subscript = cast(overload.choice.getDecl()); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 493dc657f4246..bcbefc2315b20 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -471,19 +471,19 @@ enum class SolutionCompareResult { /// declaration was opened, which may involve type variables. struct SelectedOverload { /// The overload choice. - OverloadChoice choice; + const OverloadChoice choice; /// The opened type of the base of the reference to this overload, if /// we're referencing a member. - Type openedFullType; + const Type openedFullType; /// The opened type produced by referring to this overload. - Type openedType; + const Type openedType; /// The type that this overload binds. Note that this may differ from /// openedType, for example it will include any IUO unwrapping that has taken /// place. - Type boundType; + const Type boundType; }; /// Provides information about the application of a function argument to a From 7dd429b67f052398b853ea8154c2f660760914cc Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 16 Dec 2019 11:31:14 -0800 Subject: [PATCH 046/478] [NFC] Refactor buildSubscript a bit Have callers resolve the SelectedOverload instead of the callee. Then make the error paths more apparent, and flush Optional wherever it can be found. --- lib/Sema/CSApply.cpp | 128 ++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 70 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 2e440c8dc43c1..e9e409e069f43 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -1228,41 +1228,13 @@ namespace { bool hasTrailingClosure, ConstraintLocatorBuilder locator, bool isImplicit, AccessSemantics semantics, - Optional selected = None) { - - // Determine the declaration selected for this subscript operation. - if (!selected) - selected = solution.getOverloadChoiceIfAvailable( - cs.getConstraintLocator( - locator.withPathElement( - ConstraintLocator::SubscriptMember))); - - // Handles situation where there was a solution available but it didn't - // have a proper overload selected from subscript call, might be because - // solver was allowed to return free or unresolved types, which can - // happen while running diagnostics on one of the expressions. - if (!selected.hasValue()) { - auto &de = cs.getASTContext().Diags; - auto baseType = cs.getType(base); - - if (auto errorType = baseType->getAs()) { - de.diagnose(base->getLoc(), diag::cannot_subscript_base, - errorType->getOriginalType()) - .highlight(base->getSourceRange()); - } else { - de.diagnose(base->getLoc(), diag::cannot_subscript_ambiguous_base) - .highlight(base->getSourceRange()); - } - - return nullptr; - } - + const SelectedOverload &selected) { // Build the new subscript. auto newSubscript = buildSubscriptHelper(base, index, argLabels, - *selected, hasTrailingClosure, + selected, hasTrailingClosure, locator, isImplicit, semantics); - if (selected->choice.getKind() == OverloadChoiceKind::DeclViaDynamic) { + if (selected.choice.getKind() == OverloadChoiceKind::DeclViaDynamic) { // Rewrite for implicit unwrapping if the solution requires it. auto *dynamicLocator = cs.getConstraintLocator( locator, {ConstraintLocator::SubscriptMember, @@ -1277,19 +1249,19 @@ namespace { } } - if (selected->choice.isDecl()) { + if (selected.choice.isDecl()) { auto locatorKind = ConstraintLocator::SubscriptMember; - if (selected->choice.getKind() == + if (selected.choice.getKind() == OverloadChoiceKind::DynamicMemberLookup) locatorKind = ConstraintLocator::Member; - if (selected->choice.getKind() == + if (selected.choice.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup && !isa(locator.getAnchor())) locatorKind = ConstraintLocator::Member; newSubscript = - forceUnwrapIfExpected(newSubscript, selected->choice, + forceUnwrapIfExpected(newSubscript, selected.choice, locator.withPathElement(locatorKind)); } @@ -1298,7 +1270,7 @@ namespace { Expr *buildSubscriptHelper(Expr *base, Expr *index, ArrayRef argLabels, - SelectedOverload &selected, + const SelectedOverload &selected, bool hasTrailingClosure, ConstraintLocatorBuilder locator, bool isImplicit, AccessSemantics semantics) { @@ -2862,8 +2834,29 @@ namespace { cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember); auto overload = solution.getOverloadChoiceIfAvailable(memberLocator); - if (overload && overload->choice.getKind() == - OverloadChoiceKind::KeyPathDynamicMemberLookup) { + // Handles situation where there was a solution available but it didn't + // have a proper overload selected from subscript call, might be because + // solver was allowed to return free or unresolved types, which can + // happen while running diagnostics on one of the expressions. + if (!overload) { + const auto *base = expr->getBase(); + auto &de = cs.getASTContext().Diags; + auto baseType = cs.getType(base); + + if (auto errorType = baseType->getAs()) { + de.diagnose(base->getLoc(), diag::cannot_subscript_base, + errorType->getOriginalType()) + .highlight(base->getSourceRange()); + } else { + de.diagnose(base->getLoc(), diag::cannot_subscript_ambiguous_base) + .highlight(base->getSourceRange()); + } + + return nullptr; + } + + if (overload->choice.getKind() == + OverloadChoiceKind::KeyPathDynamicMemberLookup) { return buildDynamicMemberLookupRef( expr, expr->getBase(), expr->getIndex()->getStartLoc(), SourceLoc(), *overload, memberLocator); @@ -2872,7 +2865,7 @@ namespace { return buildSubscript( expr->getBase(), expr->getIndex(), expr->getArgumentLabels(), expr->hasTrailingClosure(), cs.getConstraintLocator(expr), - expr->isImplicit(), expr->getAccessSemantics(), overload); + expr->isImplicit(), expr->getAccessSemantics(), *overload); } /// "Finish" an array expression by filling in the semantic expression. @@ -2967,11 +2960,14 @@ namespace { } Expr *visitDynamicSubscriptExpr(DynamicSubscriptExpr *expr) { + auto *memberLocator = + cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember); return buildSubscript(expr->getBase(), expr->getIndex(), expr->getArgumentLabels(), expr->hasTrailingClosure(), cs.getConstraintLocator(expr), - expr->isImplicit(), AccessSemantics::Ordinary); + expr->isImplicit(), AccessSemantics::Ordinary, + solution.getOverloadChoice(memberLocator)); } Expr *visitTupleElementExpr(TupleElementExpr *expr) { @@ -4193,8 +4189,6 @@ namespace { } auto kind = origComponent.getKind(); - Optional foundDecl; - auto locator = cs.getConstraintLocator( E, LocatorPathElt::KeyPathComponent(i)); @@ -4207,48 +4201,42 @@ namespace { // If this is an unresolved link, make sure we resolved it. if (kind == KeyPathExpr::Component::Kind::UnresolvedProperty || kind == KeyPathExpr::Component::Kind::UnresolvedSubscript) { - foundDecl = solution.getOverloadChoiceIfAvailable(locator); - // Leave the component unresolved if the overload was not resolved. - if (foundDecl) { - isDynamicMember = - foundDecl->choice.getKind() == - OverloadChoiceKind::DynamicMemberLookup || - foundDecl->choice.getKind() == - OverloadChoiceKind::KeyPathDynamicMemberLookup; - - // If this was a @dynamicMemberLookup property, then we actually - // form a subscript reference, so switch the kind. - if (isDynamicMember) { - kind = KeyPathExpr::Component::Kind::UnresolvedSubscript; - } + auto foundDecl = solution.getOverloadChoiceIfAvailable(locator); + if (!foundDecl) { + // If we couldn't resolve the component, leave it alone. + resolvedComponents.push_back(origComponent); + baseTy = origComponent.getComponentType(); + continue; + } + + isDynamicMember = + foundDecl->choice.getKind() == + OverloadChoiceKind::DynamicMemberLookup || + foundDecl->choice.getKind() == + OverloadChoiceKind::KeyPathDynamicMemberLookup; + + // If this was a @dynamicMemberLookup property, then we actually + // form a subscript reference, so switch the kind. + if (isDynamicMember) { + kind = KeyPathExpr::Component::Kind::UnresolvedSubscript; } } switch (kind) { case KeyPathExpr::Component::Kind::UnresolvedProperty: { - // If we couldn't resolve the component, leave it alone. - if (!foundDecl) { - resolvedComponents.push_back(origComponent); - break; - } - - buildKeyPathPropertyComponent(*foundDecl, origComponent.getLoc(), + buildKeyPathPropertyComponent(solution.getOverloadChoice(locator), + origComponent.getLoc(), locator, resolvedComponents); break; } case KeyPathExpr::Component::Kind::UnresolvedSubscript: { - // Leave the component unresolved if the overload was not resolved. - if (!foundDecl) { - resolvedComponents.push_back(origComponent); - break; - } - ArrayRef subscriptLabels; if (!isDynamicMember) subscriptLabels = origComponent.getSubscriptLabels(); buildKeyPathSubscriptComponent( - *foundDecl, origComponent.getLoc(), origComponent.getIndexExpr(), + solution.getOverloadChoice(locator), + origComponent.getLoc(), origComponent.getIndexExpr(), subscriptLabels, locator, resolvedComponents); break; } From a78bb3d49f160e0e5cd4dfe4febd6b0f0ed446e0 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 16 Dec 2019 12:06:14 -0800 Subject: [PATCH 047/478] Instead of just fixing the ClosureLifetimeFixup change releasePartialApplyCapturedArg to handle undef captures --- lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp | 7 ++----- lib/SILOptimizer/Utils/InstOptUtils.cpp | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp index 6a9ac0aa51d88..d0c9c67e5c49b 100644 --- a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp +++ b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp @@ -458,9 +458,6 @@ static bool tryRewriteToPartialApplyStack( saveDeleteInst(convertOrPartialApply); saveDeleteInst(origPA); - // Only insert destroys for defined partial_apply arguments. - auto isDefined = [](SILValue arg) -> bool { return !isa(arg); }; - // Insert destroys of arguments after the apply and the dealloc_stack. if (auto *apply = dyn_cast(singleApplyUser)) { auto insertPt = std::next(SILBasicBlock::iterator(apply)); @@ -469,12 +466,12 @@ static bool tryRewriteToPartialApplyStack( return true; SILBuilderWithScope b3(insertPt); b3.createDeallocStack(loc, newPA); - insertDestroyOfCapturedArguments(newPA, b3, isDefined); + insertDestroyOfCapturedArguments(newPA, b3); } else if (auto *tai = dyn_cast(singleApplyUser)) { for (auto *succBB : tai->getSuccessorBlocks()) { SILBuilderWithScope b3(succBB->begin()); b3.createDeallocStack(loc, newPA); - insertDestroyOfCapturedArguments(newPA, b3, isDefined); + insertDestroyOfCapturedArguments(newPA, b3); } } else { llvm_unreachable("Unknown FullApplySite instruction kind"); diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 9c97afaa3eb43..4515a5230ad94 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -1020,7 +1020,7 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, // possible for that value. // If we have qualified ownership, we should just emit a destroy value. - if (arg->getFunction()->hasOwnership()) { + if (builder.getFunction().hasOwnership()) { callbacks.createdNewInst(builder.createDestroyValue(loc, arg)); return; } From c842fee0a43250a122e9e4938fb7fe7b17a95875 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 12 Dec 2019 14:12:35 -0800 Subject: [PATCH 048/478] [AutoDiff upstream] Add `@transpose(of:)` attribute. The `@transpose(of:)` attribute registers a function as a transpose of another function. This patch adds the `@transpose(of:)` attribute definition, syntax, parsing, and printing. Resolves TF-827. Todos: - Type-checking (TF-830, TF-1060). - Enable serialization (TF-838). - Use module-qualified names instead of custom qualified name syntax/parsing (TF-1066). --- include/swift/AST/Attr.def | 5 + include/swift/AST/Attr.h | 83 +++++++ include/swift/AST/DiagnosticsParse.def | 7 +- include/swift/Parse/Parser.h | 34 ++- lib/AST/Attr.cpp | 98 +++++++- lib/Parse/ParseDecl.cpp | 218 +++++++++++++++--- lib/Parse/ParseType.cpp | 43 +++- lib/Sema/TypeCheckAttr.cpp | 3 + lib/Sema/TypeCheckDeclOverride.cpp | 1 + lib/Serialization/ModuleFormat.h | 8 + lib/Serialization/Serialization.cpp | 26 +++ .../Parse/derivative_attr_parse.swift | 3 +- .../AutoDiff/Parse/transpose_attr_parse.swift | 92 ++++++++ .../Serialization/transpose_attr.swift | 99 ++++++++ .../round_trip_parse_gen.swift.withkinds | 26 ++- .../Syntax/round_trip_parse_gen.swift | 22 ++ utils/gyb_syntax_support/AttributeNodes.py | 64 ++++- .../NodeSerializationCodes.py | 1 + 18 files changed, 767 insertions(+), 66 deletions(-) create mode 100644 test/AutoDiff/Parse/transpose_attr_parse.swift create mode 100644 test/AutoDiff/Serialization/transpose_attr.swift diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index dd328b9962b56..4f40cdc2b04bc 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -540,6 +540,11 @@ DECL_ATTR(_implicitly_synthesizes_nested_requirement, ImplicitlySynthesizesNeste ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 98) +DECL_ATTR(transpose, Transpose, + OnFunc | LongAttribute | AllowMultipleAttributes | + ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, + 99) + #undef TYPE_ATTR #undef DECL_ATTR_ALIAS #undef CONTEXTUAL_DECL_ATTR_ALIAS diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 896396d8c78cd..234f3611f9e7b 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1858,6 +1858,89 @@ class DerivativeAttr final } }; +/// The `@transpose` attribute registers a function as a transpose of another +/// function-like declaration: a 'func', 'init', 'subscript', or 'var' computed +/// property declaration. +/// +/// The `@transpose` attribute also has a `wrt:` clause specifying the +/// parameters that are transposed "with respect to", i.e. the transposed +/// parameters. +/// +/// Examples: +/// @transpose(of: foo) +/// @transpose(of: +, wrt: (lhs, rhs)) +class TransposeAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + + /// The base type of the original function. + /// This is non-null only when the original function is not top-level (i.e. it + /// is an instance/static method). + TypeRepr *BaseTypeRepr; + /// The original function name. + DeclNameRefWithLoc OriginalFunctionName; + /// The original function declaration, resolved by the type checker. + AbstractFunctionDecl *OriginalFunction = nullptr; + /// The number of parsed parameters specified in 'wrt:'. + unsigned NumParsedParameters = 0; + /// The transposed parameters' indices, resolved by the type checker. + IndexSubset *ParameterIndices = nullptr; + + explicit TransposeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameRefWithLoc original, + ArrayRef params); + + explicit TransposeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameRefWithLoc original, + IndexSubset *indices); + +public: + static TransposeAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameRefWithLoc original, + ArrayRef params); + + static TransposeAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameRefWithLoc original, + IndexSubset *indices); + + TypeRepr *getBaseTypeRepr() const { return BaseTypeRepr; } + DeclNameRefWithLoc getOriginalFunctionName() const { + return OriginalFunctionName; + } + AbstractFunctionDecl *getOriginalFunction() const { + return OriginalFunction; + } + void setOriginalFunction(AbstractFunctionDecl *decl) { + OriginalFunction = decl; + } + + /// The parsed transposed parameters, i.e. the list of parameters specified in + /// 'wrt:'. + ArrayRef getParsedParameters() const { + return {getTrailingObjects(), NumParsedParameters}; + } + MutableArrayRef getParsedParameters() { + return {getTrailingObjects(), NumParsedParameters}; + } + size_t numTrailingObjects(OverloadToken) const { + return NumParsedParameters; + } + + IndexSubset *getParameterIndices() const { + return ParameterIndices; + } + void setParameterIndices(IndexSubset *parameterIndices) { + ParameterIndices = parameterIndices; + } + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Transpose; + } +}; + /// Attributes that may be applied to declarations. class DeclAttributes { /// Linked list of declaration attributes. diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 9f26b49408557..bcbfa22da4787 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1560,9 +1560,12 @@ ERROR(expected_colon_after_label,PointsToFirstBadToken, ERROR(diff_params_clause_expected_parameter,PointsToFirstBadToken, "expected a parameter, which can be a function parameter name, " "parameter index, or 'self'", ()) +ERROR(diff_params_clause_expected_parameter_unnamed,PointsToFirstBadToken, + "expected a parameter, which can be a function parameter index or 'self'", + ()) -// derivative -ERROR(attr_derivative_expected_original_name,PointsToFirstBadToken, +// Automatic differentiation attributes +ERROR(autodiff_attr_expected_original_decl_name,PointsToFirstBadToken, "expected an original function name", ()) //------------------------------------------------------------------------------ diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 9974e8212154c..93642aa077687 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -994,14 +994,22 @@ class Parser { Optional &vjpSpec, TrailingWhereClause *&whereClause); - /// Parse a differentiation parameters clause. + /// Parse a differentiation parameters clause, i.e. the 'wrt:' clause in + /// `@differentiable` and `@derivative` attributes. + /// If `allowNamedParameters` is false, allow only index parameters and + /// 'self'. bool parseDifferentiationParametersClause( - SmallVectorImpl ¶ms, StringRef attrName); + SmallVectorImpl ¶ms, StringRef attrName, + bool allowNamedParameters = true); /// Parse the @derivative attribute. ParserResult parseDerivativeAttribute(SourceLoc AtLoc, SourceLoc Loc); + /// Parse the @transpose attribute. + ParserResult parseTransposeAttribute(SourceLoc AtLoc, + SourceLoc Loc); + /// Parse a specific attribute. ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc); @@ -1143,7 +1151,19 @@ class Parser { SourceLoc &LAngleLoc, SourceLoc &RAngleLoc); - ParserResult parseTypeIdentifier(); + /// Parses a type identifier (e.g. 'Foo' or 'Foo.Bar.Baz'). + /// + /// When `isParsingQualifiedDeclBaseType` is true: + /// - Parses and returns the base type for a qualified declaration name, + /// positioning the parser at the '.' before the final declaration name. + // This position is important for parsing final declaration names like + // '.init' via `parseUnqualifiedDeclName`. + /// - For example, 'Foo.Bar.f' parses as 'Foo.Bar' and the parser is + /// positioned at '.f'. + /// - If there is no base type qualifier (e.g. when parsing just 'f'), returns + /// an empty parser error. + ParserResult parseTypeIdentifier( + bool isParsingQualifiedDeclBaseType = false); ParserResult parseOldStyleProtocolComposition(); ParserResult parseAnyType(); ParserResult parseSILBoxType(GenericParamList *generics, @@ -1366,6 +1386,14 @@ class Parser { bool canParseTypedPattern(); + /// Returns true if a base type for a qualified declaration name can be + /// parsed. + /// Examples: + /// 'Foo.f' -> true + /// 'Foo.Bar.f' -> true + /// 'f' -> false + bool canParseBaseTypeForQualifiedDeclName(); + //===--------------------------------------------------------------------===// // Expression Parsing ParserResult parseExpr(Diag<> ID) { diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index d8eaa5a42063d..784462e6a91db 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -366,11 +366,25 @@ static void printShortFormAvailable(ArrayRef Attrs, Printer.printNewline(); } -// Returns the differentiation parameters clause string for the given function, -// parameter indices, and parsed parameters. +/// Printing style for a differentiation parameter in a `wrt:` differentiation +/// parameters clause. Used for printing `@differentiable`, `@derivative`, and +/// `@transpose` attributes. +enum class DifferentiationParameterPrintingStyle { + /// Print parameter by name. + /// Used for `@differentiable` and `@derivative` attribute. + Name, + /// Print parameter by index. + /// Used for `@transpose` attribute. + Index +}; + +/// Returns the differentiation parameters clause string for the given function, +/// parameter indices, parsed parameters, . Use the parameter indices if +/// specified; otherwise, use the parsed parameters. static std::string getDifferentiationParametersClauseString( const AbstractFunctionDecl *function, IndexSubset *paramIndices, - ArrayRef parsedParams) { + ArrayRef parsedParams, + DifferentiationParameterPrintingStyle style) { assert(function); bool isInstanceMethod = function->isInstanceMember(); std::string result; @@ -392,7 +406,14 @@ static std::string getDifferentiationParametersClauseString( } // Print remaining differentiation parameters. interleave(parameters.set_bits(), [&](unsigned index) { - printer << function->getParameters()->get(index)->getName().str(); + switch (style) { + case DifferentiationParameterPrintingStyle::Name: + printer << function->getParameters()->get(index)->getName().str(); + break; + case DifferentiationParameterPrintingStyle::Index: + printer << index; + break; + } }, [&] { printer << ", "; }); if (parameterCount > 1) printer << ')'; @@ -425,11 +446,11 @@ static std::string getDifferentiationParametersClauseString( return printer.str(); } -// Print the arguments of the given `@differentiable` attribute. -// - If `omitWrtClause` is true, omit printing the `wrt:` differentiation -// parameters clause. -// - If `omitDerivativeFunctions` is true, omit printing the JVP/VJP derivative -// functions. +/// Print the arguments of the given `@differentiable` attribute. +/// - If `omitWrtClause` is true, omit printing the `wrt:` differentiation +/// parameters clause. +/// - If `omitDerivativeFunctions` is true, omit printing the JVP/VJP derivative +/// functions. static void printDifferentiableAttrArguments( const DifferentiableAttr *attr, ASTPrinter &printer, PrintOptions Options, const Decl *D, bool omitWrtClause = false, @@ -465,7 +486,8 @@ static void printDifferentiableAttrArguments( // Print differentiation parameters clause, unless it is to be omitted. if (!omitWrtClause) { auto diffParamsString = getDifferentiationParametersClauseString( - original, attr->getParameterIndices(), attr->getParsedParameters()); + original, attr->getParameterIndices(), attr->getParsedParameters(), + DifferentiationParameterPrintingStyle::Name); // Check whether differentiation parameter clause is empty. // Handles edge case where resolved parameter indices are unset and // parsed parameters are empty. This case should never trigger for @@ -897,6 +919,21 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, break; } + case DAK_Transpose: { + Printer.printAttrName("@transpose"); + Printer << "(of: "; + auto *attr = cast(this); + Printer << attr->getOriginalFunctionName().Name; + auto *transpose = cast(D); + auto transParamsString = getDifferentiationParametersClauseString( + transpose, attr->getParameterIndices(), attr->getParsedParameters(), + DifferentiationParameterPrintingStyle::Index); + if (!transParamsString.empty()) + Printer << ", " << transParamsString; + Printer << ')'; + break; + } + case DAK_ImplicitlySynthesizesNestedRequirement: Printer.printAttrName("@_implicitly_synthesizes_nested_requirement"); Printer << "(\"" << cast(this)->Value << "\")"; @@ -1040,6 +1077,8 @@ StringRef DeclAttribute::getAttrName() const { return "differentiable"; case DAK_Derivative: return "derivative"; + case DAK_Transpose: + return "transpose"; } llvm_unreachable("bad DeclAttrKind"); } @@ -1497,6 +1536,45 @@ DerivativeAttr *DerivativeAttr::create(ASTContext &context, bool implicit, std::move(originalName), indices); } +TransposeAttr::TransposeAttr(bool implicit, SourceLoc atLoc, + SourceRange baseRange, TypeRepr *baseTypeRepr, + DeclNameRefWithLoc originalName, + ArrayRef params) + : DeclAttribute(DAK_Transpose, atLoc, baseRange, implicit), + BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), + NumParsedParameters(params.size()) { + std::uninitialized_copy(params.begin(), params.end(), + getTrailingObjects()); +} + +TransposeAttr::TransposeAttr(bool implicit, SourceLoc atLoc, + SourceRange baseRange, TypeRepr *baseTypeRepr, + DeclNameRefWithLoc originalName, IndexSubset *indices) + : DeclAttribute(DAK_Transpose, atLoc, baseRange, implicit), + BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), + ParameterIndices(indices) {} + +TransposeAttr *TransposeAttr::create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, + DeclNameRefWithLoc originalName, + ArrayRef params) { + unsigned size = totalSizeToAlloc(params.size()); + void *mem = context.Allocate(size, alignof(TransposeAttr)); + return new (mem) TransposeAttr(implicit, atLoc, baseRange, baseType, + std::move(originalName), params); +} + +TransposeAttr *TransposeAttr::create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, + DeclNameRefWithLoc originalName, + IndexSubset *indices) { + void *mem = context.Allocate(sizeof(TransposeAttr), alignof(TransposeAttr)); + return new (mem) TransposeAttr(implicit, atLoc, baseRange, baseType, + std::move(originalName), indices); +} + ImplementsAttr::ImplementsAttr(SourceLoc atLoc, SourceRange range, TypeLoc ProtocolType, DeclName MemberName, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 9d4ededb03921..a22b8a9137794 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -862,6 +862,7 @@ static bool errorAndSkipUntilConsumeRightParen(Parser &P, StringRef attrName, }; /// Parse a differentiation parameters 'wrt:' clause, returning true on error. +/// If `allowNamedParameters` is false, allow only index parameters and 'self'. /// /// \verbatim /// differentiation-params-clause: @@ -869,10 +870,11 @@ static bool errorAndSkipUntilConsumeRightParen(Parser &P, StringRef attrName, /// differentiation-params: /// '(' differentiation-param (',' differentiation-param)* ')' /// differentiation-param: -/// 'self' | identifier +/// 'self' | identifier | [0-9]+ /// \endverbatim bool Parser::parseDifferentiationParametersClause( - SmallVectorImpl ¶ms, StringRef attrName) { + SmallVectorImpl ¶ms, StringRef attrName, + bool allowNamedParameters) { SyntaxParsingContext DiffParamsClauseContext( SyntaxContext, SyntaxKind::DifferentiationParamsClause); consumeToken(tok::identifier); @@ -888,34 +890,38 @@ bool Parser::parseDifferentiationParametersClause( SyntaxContext, SyntaxKind::DifferentiationParam); SourceLoc paramLoc; switch (Tok.getKind()) { - case tok::identifier: { - Identifier paramName; - if (parseIdentifier(paramName, paramLoc, - diag::diff_params_clause_expected_parameter)) - return true; - params.push_back(ParsedAutoDiffParameter::getNamedParameter( - paramLoc, paramName)); - break; - } - case tok::integer_literal: { - unsigned paramNum; - if (parseUnsignedInteger( - paramNum, paramLoc, - diag::diff_params_clause_expected_parameter)) - return true; - - params.push_back(ParsedAutoDiffParameter::getOrderedParameter( - paramLoc, paramNum)); - break; - } - case tok::kw_self: { - paramLoc = consumeToken(tok::kw_self); - params.push_back(ParsedAutoDiffParameter::getSelfParameter(paramLoc)); - break; + case tok::identifier: { + // If named parameters are not allowed, diagnose. + if (!allowNamedParameters) { + diagnose(Tok, diag::diff_params_clause_expected_parameter_unnamed); + return true; } - default: - diagnose(Tok, diag::diff_params_clause_expected_parameter); + Identifier paramName; + if (parseIdentifier(paramName, paramLoc, + diag::diff_params_clause_expected_parameter)) + return true; + params.push_back(ParsedAutoDiffParameter::getNamedParameter( + paramLoc, paramName)); + break; + } + case tok::integer_literal: { + unsigned paramNum; + if (parseUnsignedInteger( + paramNum, paramLoc, + diag::diff_params_clause_expected_parameter)) return true; + params.push_back(ParsedAutoDiffParameter::getOrderedParameter( + paramLoc, paramNum)); + break; + } + case tok::kw_self: { + paramLoc = consumeToken(tok::kw_self); + params.push_back(ParsedAutoDiffParameter::getSelfParameter(paramLoc)); + break; + } + default: + diagnose(Tok, diag::diff_params_clause_expected_parameter); + return true; } if (parseTrailingComma && Tok.isNot(tok::r_paren)) return parseToken(tok::comma, diag::attr_expected_comma, attrName, @@ -1081,6 +1087,66 @@ bool Parser::parseDifferentiableAttributeArguments( return false; } +/// Helper function that parses 'type-identifier' for `parseQualifiedDeclName`. +/// Returns true on error. Sets `baseType` to the parsed base type if present, +/// or to `nullptr` if not. A missing base type is not considered an error. +static bool parseBaseTypeForQualifiedDeclName(Parser &P, TypeRepr *&baseType) { + baseType = nullptr; + + if (!P.canParseBaseTypeForQualifiedDeclName()) + return false; + + auto result = P.parseTypeIdentifier(/*isParsingQualifiedDeclName*/ true); + if (result.isNull()) + return true; + + // Consume a leading period. This is relevant only for qualified operators. + // TODO(TF-1065): Consider disallowing qualified operator names. There is no + // precedent for qualified operator syntax elsewhere in Swift. + if (P.startsWithSymbol(P.Tok, '.')) { + assert(P.Tok.isAnyOperator() && + "Only operators should have leading period here"); + P.consumeStartingCharacterOfCurrentToken(tok::period); + } + + baseType = result.getPtrOrNull(); + return false; +} + +/// parseQualifiedDeclName +/// +/// \verbatim +/// qualified-decl-name: +/// type-identifier? unqualified-decl-name +/// type-identifier: +/// identifier generic-args? ('.' identifier generic-args?)* +/// \endverbatim +/// +/// Parses an optional base type, followed by a declaration name. +/// Returns true on error (if declaration name could not be parsed). +// TODO(TF-1066): Use module qualified name syntax/parsing instead of custom +// qualified name syntax/parsing. +static bool parseQualifiedDeclName(Parser &P, Diag<> nameParseError, + TypeRepr *&baseType, + DeclNameRefWithLoc &original) { + SyntaxParsingContext DeclNameContext(P.SyntaxContext, + SyntaxKind::QualifiedDeclName); + // Parse base type. + if (parseBaseTypeForQualifiedDeclName(P, baseType)) + return true; + // Parse final declaration name. + // Note: Use `afterDot = true` and `allowDeinitAndSubscript = true` to enable + // initializer and subscript lookup. + original.Name = + P.parseUnqualifiedDeclName(/*afterDot*/ true, original.Loc, + nameParseError, /*allowOperators*/ true, + /*allowZeroArgCompoundNames*/ true, + /*allowDeinitAndSubscript*/ true); + // The base type is optional, but the final unqualified declaration name is + // not. If name could not be parsed, return true for error. + return !original.Name; +} + /// Parse a `@derivative(of:)` attribute, returning true on error. /// /// \verbatim @@ -1091,6 +1157,7 @@ ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, SourceLoc loc) { StringRef AttrName = "derivative"; SourceLoc lParenLoc = loc, rParenLoc = loc; + TypeRepr *baseType = nullptr; DeclNameRefWithLoc original; SmallVector params; @@ -1124,15 +1191,11 @@ ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, return makeParserError(); } { - // Parse the name of the function. - SyntaxParsingContext FuncDeclNameContext(SyntaxContext, - SyntaxKind::FunctionDeclName); - // NOTE: Use `afterDot = true` and `allowDeinitAndSubscript = true` to - // enable, e.g. `@derivative(of: init)` and `@derivative(of: subscript)`. - original.Name = parseUnqualifiedDeclName( - /*afterDot*/ true, original.Loc, - diag::attr_derivative_expected_original_name, /*allowOperators*/ true, - /*allowZeroArgCompoundNames*/ true, /*allowDeinitAndSubscript*/ true); + // Parse the optionally qualified function name. + if (parseQualifiedDeclName( + *this, diag::autodiff_attr_expected_original_decl_name, + baseType, original)) + return makeParserError(); } if (consumeIfTrailingComma()) return makeParserError(); @@ -1152,6 +1215,76 @@ ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, SourceRange(loc, rParenLoc), original, params)); } +/// Parse a `@transpose(of:)` attribute, returning true on error. +/// +/// \verbatim +/// transpose-attribute-arguments: +/// '(' 'of' ':' decl-name (',' transposed-params-clause)? ')' +/// \endverbatim +ParserResult Parser::parseTransposeAttribute(SourceLoc atLoc, + SourceLoc loc) { + StringRef AttrName = "transpose"; + SourceLoc lParenLoc = loc, rParenLoc = loc; + TypeRepr *baseType = nullptr; + DeclNameRefWithLoc original; + SmallVector params; + + // Parse trailing comma, if it exists, and check for errors. + auto consumeIfTrailingComma = [&]() -> bool { + if (!consumeIf(tok::comma)) return false; + // Diagnose trailing comma before ')'. + if (Tok.is(tok::r_paren)) { + diagnose(Tok, diag::unexpected_separator, ","); + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + } + // Check that token after comma is 'wrt:'. + if (isIdentifier(Tok, "wrt")) + return false; + diagnose(Tok, diag::attr_expected_label, "wrt", AttrName); + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + }; + + // Parse '('. + if (!consumeIf(tok::l_paren, lParenLoc)) { + diagnose(getEndOfPreviousLoc(), diag::attr_expected_lparen, AttrName, + /*DeclModifier*/ false); + return makeParserError(); + } + { + SyntaxParsingContext ContentContext( + SyntaxContext, SyntaxKind::DerivativeRegistrationAttributeArguments); + // Parse the 'of:' label and colon. + if (parseSpecificIdentifier("of", diag::attr_missing_label, "of", + AttrName) || + parseToken(tok::colon, diag::expected_colon_after_label, "of")) { + return makeParserError(); + } + { + // Parse the optionally qualified function name. + if (parseQualifiedDeclName( + *this, diag::autodiff_attr_expected_original_decl_name, + baseType, original)) + return makeParserError(); + } + if (consumeIfTrailingComma()) + return makeParserError(); + // Parse the optional 'wrt' transposed parameters clause. + if (Tok.is(tok::identifier) && Tok.getText() == "wrt" && + parseDifferentiationParametersClause(params, AttrName, + /*allowNamedParameters*/ false)) + return makeParserError(); + } + // Parse ')'. + if (!consumeIf(tok::r_paren, rParenLoc)) { + diagnose(getEndOfPreviousLoc(), diag::attr_expected_rparen, AttrName, + /*DeclModifier*/ false); + return makeParserError(); + } + return ParserResult(TransposeAttr::create( + Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), baseType, + original, params)); +} + void Parser::parseObjCSelector(SmallVector &Names, SmallVector &NameLocs, bool &IsNullarySelector) { @@ -2123,6 +2256,17 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } + case DAK_Transpose: { + // `@transpose` in a local scope is not allowed. + if (CurDeclContext->isLocalContext()) + diagnose(Loc, diag::attr_only_at_non_local_scope, '@' + AttrName.str()); + + auto Attr = parseTransposeAttribute(AtLoc, Loc); + if (Attr.isNonNull()) + Attributes.add(Attr.get()); + break; + } + case DAK_ProjectedValueProperty: { if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 6d7e84cd2c61f..1a560a8fa74d4 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -633,12 +633,42 @@ ParserStatus Parser::parseGenericArguments(SmallVectorImpl &Args, return makeParserSuccess(); } +/// Returns true if a base type for a qualified declaration name can be +/// parsed. +/// +/// Examples: +/// 'Foo.f' -> true +/// 'Foo.Bar.f' -> true +/// 'f' -> false, no base type +bool Parser::canParseBaseTypeForQualifiedDeclName() { + BacktrackingScope backtrack(*this); + + // First, parse a single type identifier component. + if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any)) + return false; + consumeToken(); + if (startsWithLess(Tok)) { + if (!canParseGenericArguments()) + return false; + } + + // If the next token is a period or starts with a period, then this can be + // parsed as a type qualifier. + return startsWithSymbol(Tok, '.'); +} + /// parseTypeIdentifier /// /// type-identifier: /// identifier generic-args? ('.' identifier generic-args?)* /// -ParserResult Parser::parseTypeIdentifier() { +ParserResult +Parser::parseTypeIdentifier(bool isParsingQualifiedDeclBaseType) { + // If parsing a qualified declaration name, return error if base type cannot + // be parsed. + if (isParsingQualifiedDeclBaseType && !canParseBaseTypeForQualifiedDeclName()) + return makeParserError(); + if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_Self)) { // is this the 'Any' type if (Tok.is(tok::kw_Any)) { @@ -704,7 +734,18 @@ ParserResult Parser::parseTypeIdentifier() { } if (!peekToken().isContextualKeyword("Type") && !peekToken().isContextualKeyword("Protocol")) { + // Consume the period. consumeToken(); + // If parsing a qualified declaration name, break before parsing the + // final declaration name component. + if (isParsingQualifiedDeclBaseType) { + BacktrackingScope backtrack(*this); + // If qualified name base type cannot be parsed from the current + // point (i.e. the next type identifier is not followed by a '.'), + // then the next identifier is the final declaration name component. + if (!canParseBaseTypeForQualifiedDeclName()) + break; + } continue; } } else if (Tok.is(tok::code_complete)) { diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 600503aa5ffe8..2829351165274 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -119,6 +119,9 @@ class AttributeChecker : public AttributeVisitor { // TODO(TF-828): Upstream `@differentiable` attribute type-checking from // tensorflow branch. IGNORED_ATTR(Differentiable) + // TODO(TF-830): Upstream `@transpose` attribute type-checking from tensorflow + // branch. + IGNORED_ATTR(Transpose) #undef IGNORED_ATTR void visitAlignmentAttr(AlignmentAttr *attr) { diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 2aa54dcd385a8..15cc86f0d2322 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1339,6 +1339,7 @@ namespace { // Differentiation-related attributes. UNINTERESTING_ATTR(Differentiable) UNINTERESTING_ATTR(Derivative) + UNINTERESTING_ATTR(Transpose) // These can't appear on overridable declarations. UNINTERESTING_ATTR(Prefix) diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 75f5f99815c3c..284004efed076 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -1790,6 +1790,14 @@ namespace decls_block { BCArray> // Differentiation parameter indices' bitvector. >; + using TransposeDeclAttrLayout = BCRecordLayout< + Transpose_DECL_ATTR, + BCFixed<1>, // Implicit flag. + IdentifierIDField, // Original name. + DeclIDField, // Original function declaration. + BCArray> // Transposed parameter indices' bitvector. + >; + #define SIMPLE_DECL_ATTR(X, CLASS, ...) \ using CLASS##DeclAttrLayout = BCRecordLayout< \ CLASS##_DECL_ATTR, \ diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 22074ba3f64bf..0ddc078c56672 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2364,6 +2364,32 @@ class Serializer::DeclSerializer : public DeclVisitor { origDeclID, derivativeKind, indices); return; } + + case DAK_Transpose: { + auto abbrCode = S.DeclTypeAbbrCodes[TransposeDeclAttrLayout::Code]; + auto *attr = cast(DA); + // NOTE(TF-838): `@transpose` attribute serialization is blocked by + // `@transpose` attribute type-checking (TF-830), which resolves + // the original declaration. + if (!attr->getOriginalFunction()) + return; + assert(attr->getOriginalFunction() && + "`@transpose` attribute should have original declaration set " + "during construction or parsing"); + auto origName = attr->getOriginalFunctionName().Name.getBaseName(); + IdentifierID origNameId = S.addDeclBaseNameRef(origName); + DeclID origDeclID = S.addDeclRef(attr->getOriginalFunction()); + auto *parameterIndices = attr->getParameterIndices(); + assert(parameterIndices && "Parameter indices must be resolved"); + SmallVector indices; + for (unsigned i : range(parameterIndices->getCapacity())) + indices.push_back(parameterIndices->contains(i)); + TransposeDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(), origNameId, + origDeclID, indices); + return; + } + case DAK_ImplicitlySynthesizesNestedRequirement: { auto *theAttr = cast(DA); auto abbrCode = S.DeclTypeAbbrCodes[ImplicitlySynthesizesNestedRequirementDeclAttrLayout::Code]; diff --git a/test/AutoDiff/Parse/derivative_attr_parse.swift b/test/AutoDiff/Parse/derivative_attr_parse.swift index 8d144e5e3779e..5bdf00cfb303d 100644 --- a/test/AutoDiff/Parse/derivative_attr_parse.swift +++ b/test/AutoDiff/Parse/derivative_attr_parse.swift @@ -28,8 +28,7 @@ func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { /// Bad -// expected-error @+3 {{expected an original function name}} -// expected-error @+2 {{expected ')' in 'derivative' attribute}} +// expected-error @+2 {{expected an original function name}} // expected-error @+1 {{expected declaration}} @derivative(of: 3) func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { diff --git a/test/AutoDiff/Parse/transpose_attr_parse.swift b/test/AutoDiff/Parse/transpose_attr_parse.swift new file mode 100644 index 0000000000000..6f84e962ead3b --- /dev/null +++ b/test/AutoDiff/Parse/transpose_attr_parse.swift @@ -0,0 +1,92 @@ +// RUN: %target-swift-frontend -parse -verify %s + +/// Good + +@transpose(of: foo) +func transpose(v: Float) -> Float + +@transpose(of: foo(_:_:)) +func transpose(v: Float) -> Float + +@transpose(of: wrt, wrt: 0) +func transpose(v: Float) -> Float + +@transpose(of: foo, wrt: 0) +func transpose(v: Float) -> Float + +@transpose(of: foo, wrt: (0, 1)) +func transpose(v: Float) -> (Float, Float) + +@transpose(of: foo, wrt: (self, 0, 1, 2)) +func transpose(v: Float) -> (Float, Float, Float, Float) + +// Qualified declaration. +@transpose(of: A.B.C.foo(x:y:_:z:)) +func transpose(v: Float) -> Float + +// Qualified operator. +// TODO(TF-1065): Consider disallowing qualified operators. +@transpose(of: Swift.Float.+) +func transpose(v: Float) -> Float + +// Qualified leading-period operator (confusing). +// TODO(TF-1065): Consider disallowing qualified operators. +@transpose(of: Swift.Float..<) +func transpose(v: Float) -> Float + +// `init` keyword edge case. +@transpose(of: Swift.Float.init(_:)) +func transpose(v: Float) -> Float + +// `subscript` keyword edge case. +@transpose(of: Swift.Array.subscript(_:)) +func transpose(v: Float) -> Float + +/// Bad + +// expected-error @+2 {{expected an original function name}} +// expected-error @+1 {{expected declaration}} +@transpose(of: 3) +func transpose(v: Float) -> Float + +// expected-error @+1 {{expected label 'wrt:' in '@transpose' attribute}} +@transpose(of: foo, blah) +func transpose(v: Float) -> Float + +// expected-error @+1 {{expected a colon ':' after 'wrt'}} +@transpose(of: foo, wrt) +func transpose(v: Float) -> Float + +// expected-error @+1 {{unexpected ',' separator}} +@transpose(of: foo,) +func transpose(v: Float) -> Float + +// expected-error @+2 {{expected ')' in 'transpose' attribute}} +// expected-error @+1 {{expected declaration}} +@transpose(of: foo, wrt: 0,) +func transpose(v: Float) -> Float + +// expected-error @+1 {{expected a parameter, which can be a function parameter index or 'self'}} +@transpose(of: foo, wrt: v) +func transpose(v: Float) -> Float + +// expected-error @+1 {{expected a parameter, which can be a function parameter index or 'self'}} +@transpose(of: foo, wrt: (0, v)) +func transpose(v: Float) -> Float + +// expected-error @+2 {{expected ')' in 'transpose' attribute}} +// expected-error @+1 {{expected declaration}} +@transpose(of: Swift.Float.+(_:_)) +func transpose(v: Float) -> Float + +// expected-error @+2 {{expected ')' in 'transpose' attribute}} +// expected-error @+1 {{expected declaration}} +@transpose(of: Swift.Float.+.a) +func transpose(v: Float) -> Float + +func testLocalTransposeRegistration() { + // Transpose registration can only be non-local. + // expected-error @+1 {{attribute '@transpose' can only be used in a non-local scope}} + @transpose(of: +) + func transpose(_ x: Float) -> (Float, Float) +} diff --git a/test/AutoDiff/Serialization/transpose_attr.swift b/test/AutoDiff/Serialization/transpose_attr.swift new file mode 100644 index 0000000000000..80bc7a220586b --- /dev/null +++ b/test/AutoDiff/Serialization/transpose_attr.swift @@ -0,0 +1,99 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -enable-experimental-differentiable-programming %s -emit-module -parse-as-library -o %t +// RUN: llvm-bcanalyzer %t/transpose_attr.swiftmodule | %FileCheck %s -check-prefix=BCANALYZER +// RUN: %target-sil-opt -enable-experimental-differentiable-programming -disable-sil-linking -enable-sil-verify-all %t/transpose_attr.swiftmodule -o - | %FileCheck %s + +// BCANALYZER-NOT: UnknownCode +// REQUIRES: differentiable_programming + +// TODO(TF-838): Enable this test. +// Blocked by TF-830: `@transpose` attribute type-checking. +// XFAIL: * + +import _Differentiation + +// Dummy `Differentiable`-conforming type. +struct S: Differentiable & AdditiveArithmetic { + static var zero: S { S() } + static func + (_: S, _: S) -> S { S() } + static func - (_: S, _: S) -> S { S() } + typealias TangentVector = S +} + +// Test top-level functions. + +func top1(_ x: S) -> S { + x +} +// CHECK: @transpose(of: top1, wrt: 0) +@transpose(of: top1, wrt: 0) +func transposeTop1(v: S) -> S { + v +} + +func top2(_ x: T, _ i: Int, _ y: U) -> U { + y +} +// CHECK: @transpose(of: top2, wrt: (0, 2)) +@transpose(of: top2, wrt: (0, 2)) +func transposeTop2(_ int: Int, v: U) -> (T, U) +where T: Differentiable, U: Differentiable, + T == T.TangentVector, U == U.TangentVector { + (.zero, v) +} + +// Test instance methods. + +extension S { + func instanceMethod(_ other: S) -> S { + self + other + } + + // CHECK: @transpose(of: instanceMethod, wrt: 0) + @transpose(of: instanceMethod, wrt: 0) + func transposeInstanceMethod(v: S) -> (S, S) { + (v, v) + } + + // CHECK: @transpose(of: instanceMethod, wrt: self) + @transpose(of: instanceMethod, wrt: self) + func transposeInstanceMethodWrtSelf(v: S) -> (S, S) { + (v, v) + } +} + +// Test static methods. + +extension S { + static func staticMethod(x: S) -> S { + x + } + + // CHECK: @transpose(of: staticMethod, wrt: 0) + @transpose(of: staticMethod, wrt: 0) + func transposeStaticMethod(_: S.Type) -> S { + self + } +} + +// Test computed properties. +extension S { + var computedProperty: S { self } + + // CHECK: @transpose(of: computedProperty, wrt: self) + @transpose(of: computedProperty, wrt: self) + func transposeProperty() -> Self { + self + } +} + +// Test subscripts. +extension S { + subscript(x: T) -> Self { self } + + // CHECK: @transpose(of: subscript, wrt: self) + @transpose(of: subscript(_:), wrt: self) + func transposeSubscript(x: T) -> Self { + self + } +} diff --git a/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds index 8aa4210d41c35..87c1cab0cd250 100644 --- a/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds +++ b/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds @@ -26,14 +26,36 @@ func bar(_ x: wrt: (self, x, y), jvp: bar, vjp: foo(_:_:) where T : FloatingPoint) func bar<T : Numeric>(_ x: T, y: T) -> T { return 1 } -@derivative(of: -) +@derivative(of: -) func negateDerivative(_ x: Float) -> (value: Float, pullback: (Float) -> Float) { return (-x, { v in -v }) } -@derivative(of: baz(label:_:), wrt: (x)) +@derivative(of: baz(label:_:), wrt: (x)) func bazDerivative(_ x: Float, y: Float) -> (value: Float, pullback: (Float) -> Float) { return (x, { v in v }) +} + +@transpose(of: -) +func negateDerivative(_ x: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (-x, { v in -v }) +} + +@derivative(of: baz(label:_:), wrt: (x)) +func bazDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + +@transpose(of: +) +func addTranspose(_ v: Float) -> (Float, Float) { + return (v, v) +} + +@transpose(of: -, wrt: (0, 1)) +func subtractTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) } diff --git a/test/AutoDiff/Syntax/round_trip_parse_gen.swift b/test/AutoDiff/Syntax/round_trip_parse_gen.swift index 9e2d4b0a9fc08..0ebe54b7de2cc 100644 --- a/test/AutoDiff/Syntax/round_trip_parse_gen.swift +++ b/test/AutoDiff/Syntax/round_trip_parse_gen.swift @@ -37,3 +37,25 @@ func bazDerivative(_ x: Float, y: Float) -> (value: Float, pullback: (Float) -> Float) { return (x, { v in v }) } + +@transpose(of: -) +func negateDerivative(_ x: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (-x, { v in -v }) +} + +@derivative(of: baz(label:_:), wrt: (x)) +func bazDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + +@transpose(of: +) +func addTranspose(_ v: Float) -> (Float, Float) { + return (v, v) +} + +@transpose(of: -, wrt: (0, 1)) +func subtractTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} diff --git a/utils/gyb_syntax_support/AttributeNodes.py b/utils/gyb_syntax_support/AttributeNodes.py index fbfbdac2e233b..16d9fa095747b 100644 --- a/utils/gyb_syntax_support/AttributeNodes.py +++ b/utils/gyb_syntax_support/AttributeNodes.py @@ -261,11 +261,11 @@ Node('DifferentiationParamList', kind='SyntaxCollection', element='DifferentiationParam'), - # differentiation-param -> ('self' | identifer) ','? + # differentiation-param -> ('self' | identifer | integer-literal) ','? Node('DifferentiationParam', kind='Syntax', description=''' - A differentiation parameter: either the "self" identifier or a - function parameter name. + A differentiation parameter: either the "self" identifier, a function + parameter name, or a function parameter index. ''', traits=['WithTrailingComma'], children=[ @@ -273,6 +273,7 @@ node_choices=[ Child('Self', kind='SelfToken'), Child('Name', kind='IdentifierToken'), + Child('Index', kind='IntegerLiteralToken'), ]), Child('TrailingComma', kind='CommaToken', is_optional=True), ]), @@ -295,14 +296,16 @@ ]), # The argument of the derivative registration attribute - # '@derivative(of: ...)'. + # '@derivative(of: ...)' and the transpose registration attribute + # '@transpose(of: ...)'. + # # derivative-registration-attr-arguments -> # 'of' ':' func-decl-name ','? differentiation-params-clause? Node('DerivativeRegistrationAttributeArguments', kind='Syntax', description=''' - The arguments for the '@derivative(of:)' attribute: the 'of:' label, - the original declaration name, and an optional differentiation - parameter list. + The arguments for the '@derivative(of:)' and '@transpose(of:)' + attributes: the 'of:' label, the original declaration name, and an + optional differentiation parameter list. ''', children=[ Child('OfLabel', kind='IdentifierToken', text_choices=['of'], @@ -311,13 +314,56 @@ The colon separating the "of" label and the original declaration name. '''), - Child('Original', kind='FunctionDeclName', - description='The referenced original declaration.'), + Child('OriginalDeclName', kind='QualifiedDeclName', + description='The referenced original declaration name.'), Child('Comma', kind='CommaToken', is_optional=True), Child('DiffParams', kind='DifferentiationParamsClause', is_optional=True), ]), + # An optionally qualified declaration name. + # Currently used only for `@derivative` and `@transpose` attribute. + # TODO(TF-1066): Use module qualified name syntax/parsing instead of custom + # qualified name syntax/parsing. + # + # qualified-decl-name -> + # base-type? '.'? (identifier | operator) decl-name-arguments? + # base-type -> + # member-type-identifier | base-type-identifier + Node('QualifiedDeclName', kind='Syntax', + description=''' + An optionally qualified function declaration name (e.g. `+(_:_:)`, + `A.B.C.foo(_:_:)`). + ''', + children=[ + Child('BaseType', kind='Type', description=''' + The base type of the qualified name, optionally specified. + ''', + node_choices=[ + Child('MemberType', kind='MemberTypeIdentifier'), + Child('SimpleType', kind='SimpleTypeIdentifier'), + ], is_optional=True), + Child('Dot', kind='Token', + token_choices=[ + 'PeriodToken', 'PrefixPeriodToken' + ], is_optional=True), + Child('Name', kind='Syntax', description=''' + The base name of the referenced function. + ''', + node_choices=[ + Child('Identifier', kind='IdentifierToken'), + Child('PrefixOperator', kind='PrefixOperatorToken'), + Child('PostfixOperator', kind='PostfixOperatorToken'), + Child('SpacedBinaryOperator', + kind='SpacedBinaryOperatorToken'), + ]), + Child('Arguments', kind='DeclNameArguments', + is_optional=True, description=''' + The argument labels of the referenced function, optionally + specified. + '''), + ]), + # func-decl-name -> (identifier | operator) decl-name-arguments? # NOTE: This is duplicated with `DeclName` above. Change `DeclName` # description and use it if possible. diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 9d30f23d647c0..5bcadc30f4a35 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -243,6 +243,7 @@ 'FunctionDeclName': 239, 'PoundFilePathExpr': 240, 'DerivativeRegistrationAttributeArguments': 241, + 'QualifiedDeclName': 242, } From 9c638ae60dcac3b7bfcf7b3ecc1ea384ccd7ea97 Mon Sep 17 00:00:00 2001 From: tbkka Date: Mon, 16 Dec 2019 12:26:22 -0800 Subject: [PATCH 049/478] SR-5289: Teach Mirror how to handle unowned/unmanaged references (#28368) * SR-5289: Support reflecting weak, unowned, and unmanaged refs This refactors how we handle reference ownership when reflecting fields of struct and class objects. There are now explicit paths for each type of reference and some simple exhaustiveness checks to fail the build if a new reference type is added in the future without updating this logic. --- include/swift/ABI/Metadata.h | 4 + include/swift/ABI/MetadataValues.h | 49 ---- include/swift/Runtime/ExistentialContainer.h | 2 + stdlib/public/runtime/Metadata.cpp | 7 + stdlib/public/runtime/Private.h | 12 +- stdlib/public/runtime/ReflectionMirror.mm | 169 +++++++------- test/stdlib/Mirror.swift | 234 ++++++++++++++++++- 7 files changed, 335 insertions(+), 142 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 34b0c0f1affc8..6bcdaf8d87a3a 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -160,6 +160,7 @@ using TargetRelativeIndirectablePointer struct HeapObject; class WeakReference; +struct UnownedReference; template struct TargetMetadata; using Metadata = TargetMetadata; @@ -645,6 +646,9 @@ struct TargetMetadata { // NOTE: This *is* a box for copy-on-write existentials. OpaqueValue *allocateBoxForExistentialIn(ValueBuffer *Buffer) const; + // Deallocate an out-of-line buffer box if one is present. + void deallocateBoxForExistentialIn(ValueBuffer *Buffer) const; + /// Get the nominal type descriptor if this metadata describes a nominal type, /// or return null if it does not. ConstTargetMetadataPointer diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index cc15eed09bb8c..e6c869c1ad97a 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -913,55 +913,6 @@ class TargetTupleTypeFlags { }; using TupleTypeFlags = TargetTupleTypeFlags; -/// Field types and flags as represented in a nominal type's field/case type -/// vector. -class FieldType { - typedef uintptr_t int_type; - // Type metadata is always at least pointer-aligned, so we get at least two - // low bits to stash flags. We could use three low bits on 64-bit, and maybe - // some high bits as well. - enum : int_type { - Indirect = 1, - Weak = 2, - - TypeMask = ((uintptr_t)-1) & ~(alignof(void*) - 1), - }; - int_type Data; - - constexpr FieldType(int_type Data) : Data(Data) {} -public: - constexpr FieldType() : Data(0) {} - FieldType withType(const Metadata *T) const { - return FieldType((Data & ~TypeMask) | (uintptr_t)T); - } - - constexpr FieldType withIndirect(bool indirect) const { - return FieldType((Data & ~Indirect) - | (indirect ? Indirect : 0)); - } - - constexpr FieldType withWeak(bool weak) const { - return FieldType((Data & ~Weak) - | (weak ? Weak : 0)); - } - - bool isIndirect() const { - return bool(Data & Indirect); - } - - bool isWeak() const { - return bool(Data & Weak); - } - - const Metadata *getType() const { - return (const Metadata *)(Data & TypeMask); - } - - int_type getIntValue() const { - return Data; - } -}; - /// Flags for exclusivity-checking operations. enum class ExclusivityFlags : uintptr_t { Read = 0x0, diff --git a/include/swift/Runtime/ExistentialContainer.h b/include/swift/Runtime/ExistentialContainer.h index 7e1d733395790..bf7e63c7cb825 100644 --- a/include/swift/Runtime/ExistentialContainer.h +++ b/include/swift/Runtime/ExistentialContainer.h @@ -95,6 +95,8 @@ struct ClassExistentialContainerImpl { using ClassExistentialContainer = ClassExistentialContainerImpl; using WeakClassExistentialContainer = ClassExistentialContainerImpl; +using UnownedClassExistentialContainer = + ClassExistentialContainerImpl; } // end swift namespace diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 66f1dc053d182..1b1a0f5a1c101 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -3862,6 +3862,13 @@ template <> OpaqueValue *Metadata::allocateBoxForExistentialIn(ValueBuffer *buff return refAndValueAddr.buffer; } +template <> void Metadata::deallocateBoxForExistentialIn(ValueBuffer *buffer) const { + auto *vwt = getValueWitnesses(); + if (vwt->isValueInline()) + return; + swift_deallocBox(reinterpret_cast(buffer->PrivateData[0])); +} + template <> OpaqueValue *Metadata::allocateBufferIn(ValueBuffer *buffer) const { auto *vwt = getValueWitnesses(); if (vwt->isValueInline()) diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index 9bd4a808876fa..11b82d09c3578 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -50,8 +50,10 @@ class TypeReferenceOwnership { #define REF_STORAGE(Name, ...) \ void set##Name() { Data |= Name; } \ - bool is##Name() const { return Data & Name; } + bool is##Name() const { return Data == Name; } #include "swift/AST/ReferenceStorage.def" + + bool isStrong() const { return Data == 0; } }; /// Type information consists of metadata and its ownership info, @@ -76,9 +78,11 @@ class TypeInfo { const Metadata *getMetadata() const { return Response.Value; } MetadataResponse getResponse() const { return Response; } - bool isWeak() const { return ReferenceOwnership.isWeak(); } - bool isUnowned() const { return ReferenceOwnership.isUnowned(); } - bool isUnmanaged() const { return ReferenceOwnership.isUnmanaged(); } +#define REF_STORAGE(Name, ...) \ + bool is##Name() const { return ReferenceOwnership.is##Name(); } +#include "swift/AST/ReferenceStorage.def" + + bool isStrong() const { return ReferenceOwnership.isStrong(); } TypeReferenceOwnership getReferenceOwnership() const { return ReferenceOwnership; diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.mm index 310bd403d3b29..32dd7e7398cab 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.mm @@ -89,6 +89,29 @@ - (id)debugQuickLookObject; namespace { +class FieldType { + const Metadata *type; + bool indirect; + TypeReferenceOwnership referenceOwnership; +public: + + constexpr FieldType() : type(nullptr), indirect(false), referenceOwnership() { } + constexpr FieldType(const Metadata *T) : type(T), indirect(false), referenceOwnership() { } + + static constexpr FieldType untypedEnumCase(bool indirect) { + FieldType type{}; + type.indirect = indirect; + return type; + } + const Metadata *getType() const { return type; } + const TypeReferenceOwnership getReferenceOwnership() const { return referenceOwnership; } + bool isIndirect() const { return indirect; } + void setIndirect(bool value) { indirect = value; } + void setReferenceOwnership(TypeReferenceOwnership newOwnership) { + referenceOwnership = newOwnership; + } +}; + /// The layout of Any. using Any = OpaqueExistentialContainer; @@ -123,58 +146,63 @@ - (id)debugQuickLookObject; return std::make_tuple(T, Value); } -static bool loadSpecialReferenceStorage(OpaqueValue *fieldData, - const FieldType fieldType, - Any *outValue) { - // isWeak() implies a reference type via Sema. - if (!fieldType.isWeak()) - return false; - - auto type = fieldType.getType(); +static void copyWeakFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { assert(type->getKind() == MetadataKind::Optional); + auto *srcContainer = reinterpret_cast(fieldData); + auto *destClassContainer = reinterpret_cast(destContainer); + destClassContainer->Value = swift_unknownObjectWeakLoadStrong(&srcContainer->Value); + auto witnessTablesSize = type->vw_size() - sizeof(WeakClassExistentialContainer); + memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize); +} - auto *weakField = reinterpret_cast(fieldData); - auto *strongValue = swift_unknownObjectWeakLoadStrong(weakField); - - // Now that we have a strong reference, we need to create a temporary buffer - // from which to copy the whole value, which might be a native class-bound - // existential, which means we also need to copy n witness tables, for - // however many protocols are in the protocol composition. For example, if we - // are copying a: - // weak var myWeakProperty : (Protocol1 & Protocol2)? - // then we need to copy three values: - // - the instance - // - the witness table for Protocol1 - // - the witness table for Protocol2 - - auto *weakContainer = - reinterpret_cast(fieldData); - - // Create a temporary existential where we can put the strong reference. - // The allocateBuffer value witness requires a ValueBuffer to own the - // allocated storage. - ValueBuffer temporaryBuffer; - - auto *temporaryValue = reinterpret_cast( - type->allocateBufferIn(&temporaryBuffer)); - - // Now copy the entire value out of the parent, which will include the - // witness tables. - temporaryValue->Value = strongValue; - auto valueWitnessesSize = type->getValueWitnesses()->getSize() - - sizeof(WeakClassExistentialContainer); - memcpy(temporaryValue->getWitnessTables(), weakContainer->getWitnessTables(), - valueWitnessesSize); - - outValue->Type = type; - auto *opaqueValueAddr = type->allocateBoxForExistentialIn(&outValue->Buffer); - type->vw_initializeWithCopy(opaqueValueAddr, - reinterpret_cast(temporaryValue)); - - type->deallocateBufferIn(&temporaryBuffer); - swift_unknownObjectRelease(strongValue); - - return true; +static void copyUnownedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { + auto *srcContainer = reinterpret_cast(fieldData); + auto *destClassContainer = reinterpret_cast(destContainer); + destClassContainer->Value = swift_unknownObjectUnownedLoadStrong(&srcContainer->Value); + auto witnessTablesSize = type->vw_size() - sizeof(UnownedClassExistentialContainer); + memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize); +} + +static void copyUnmanagedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { + // Also known as "unowned(unsafe)". + // This is simpler than the unowned/weak cases because unmanaged + // references are fundamentally the same as strong ones, so we + // can use the regular strong reference support that already + // knows how to handle existentials and Obj-C references. + type->vw_initializeWithCopy(destContainer, fieldData); +} + +static AnyReturn copyFieldContents(OpaqueValue *fieldData, + const FieldType fieldType) { + Any outValue; + auto *type = fieldType.getType(); + outValue.Type = type; + auto ownership = fieldType.getReferenceOwnership(); + auto *destContainer = type->allocateBoxForExistentialIn(&outValue.Buffer); + + if (ownership.isStrong()) { + type->vw_initializeWithCopy(destContainer, fieldData); + } + + // Generate a conditional clause for every known ownership type. + // If this causes errors, it's because someone added a new ownership type + // to ReferenceStorage.def and missed some related updates. +#define REF_STORAGE(Name, ...) \ + else if (ownership.is##Name()) { \ + copy##Name##FieldContents(destContainer, type, fieldData); \ + } +#include "swift/AST/ReferenceStorage.def" + + else { + // The field was declared with a reference type we don't understand. + warning(0, "Value with unrecognized reference type is reflected as ()"); + // Clean up the buffer allocated above + type->deallocateBoxForExistentialIn(&outValue.Buffer); + // Return an existential containing Void + outValue.Type = &METADATA_SYM(EMPTY_TUPLE_MANGLING); + } + + return AnyReturn(outValue); } @@ -310,11 +338,7 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { "type '%*s' that claims to be reflectable. Its fields will show up as " "'unknown' in Mirrors\n", (int)typeName.length, typeName.data); - return {"unknown", - FieldType() - .withType(&METADATA_SYM(EMPTY_TUPLE_MANGLING)) - .withIndirect(false) - .withWeak(false)}; + return {"unknown", FieldType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))}; }; auto *baseDesc = base->getTypeContextDescriptor(); @@ -325,14 +349,13 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { if (!fields) return failedToFindMetadata(); - const FieldDescriptor &descriptor = *fields; - auto &field = descriptor.getFields()[index]; + auto &field = fields->getFields()[index]; // Bounds are always valid as the offset is constant. auto name = field.getFieldName(); // Enum cases don't always have types. if (!field.hasMangledTypeName()) - return {name, FieldType().withIndirect(field.isIndirectCase())}; + return {name, FieldType::untypedEnumCase(field.isIndirectCase())}; auto typeName = field.getMangledTypeName(); @@ -360,10 +383,10 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { (int)typeName.size(), typeName.data()); } - return {name, FieldType() - .withType(typeInfo.getMetadata()) - .withIndirect(field.isIndirectCase()) - .withWeak(typeInfo.isWeak())}; + auto fieldType = FieldType(typeInfo.getMetadata()); + fieldType.setIndirect(field.isIndirectCase()); + fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership()); + return {name, fieldType}; } // Implementation for structs. @@ -397,7 +420,6 @@ AnyReturn subscript(intptr_t i, const char **outName, // Load the offset from its respective vector. auto fieldOffset = Struct->getFieldOffsets()[i]; - Any result; StringRef name; FieldType fieldInfo; std::tie(name, fieldInfo) = getFieldAt(type, i); @@ -409,15 +431,7 @@ AnyReturn subscript(intptr_t i, const char **outName, auto *bytes = reinterpret_cast(value); auto *fieldData = reinterpret_cast(bytes + fieldOffset); - bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result); - if (!didLoad) { - result.Type = fieldInfo.getType(); - auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer); - result.Type->vw_initializeWithCopy(opaqueValueAddr, - const_cast(fieldData)); - } - - return AnyReturn(result); + return copyFieldContents(fieldData, fieldInfo); } }; @@ -559,7 +573,6 @@ AnyReturn subscript(intptr_t i, const char **outName, #endif } - Any result; StringRef name; FieldType fieldInfo; std::tie(name, fieldInfo) = getFieldAt(type, i); @@ -571,15 +584,7 @@ AnyReturn subscript(intptr_t i, const char **outName, *outName = name.data(); *outFreeFunc = nullptr; - bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result); - if (!didLoad) { - result.Type = fieldInfo.getType(); - auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer); - result.Type->vw_initializeWithCopy(opaqueValueAddr, - const_cast(fieldData)); - } - - return AnyReturn(result); + return copyFieldContents(fieldData, fieldInfo); } #if SWIFT_OBJC_INTEROP diff --git a/test/stdlib/Mirror.swift b/test/stdlib/Mirror.swift index b2b09fe97a1f2..108f5f4c73f83 100644 --- a/test/stdlib/Mirror.swift +++ b/test/stdlib/Mirror.swift @@ -509,6 +509,226 @@ mirrors.test("struct/WrapNSArray") { #endif // _runtime(_ObjC) +//===--- Weak and Unowned References --------------------------------------===// + +// Check that Mirror correctly reflects weak/unowned refs to both +// Swift and ObjC objects from Swift structs and classes. + +protocol WeakUnownedTestsP1: class { + func f1() -> Int +} + +protocol WeakUnownedTestsP2 { + func f2() -> String +} + +class WeakUnownedSwiftClass: WeakUnownedTestsP1, WeakUnownedTestsP2 { + let tracker = LifetimeTracked(0) + func f1() -> Int { return 2 } + func f2() -> String { return "b" } +} + +#if _runtime(_ObjC) +@objc class WeakUnownedObjCClass: NSObject, WeakUnownedTestsP1, WeakUnownedTestsP2 { + let tracker = LifetimeTracked(0) + func f1() -> Int { return 2 } + func f2() -> String { return "b" } +} +#endif + +// The four tests below populate objects with different types +// but identical overall structure. +// This function is used by all four to verify that the resulting +// Mirror objects have the expected entries. +func verifyWeakUnownedReflection + + (_ m: Mirror, expectedClass: ExpectedClass.Type ) +{ + let i = m.children.makeIterator() + + func verifyClassField(child: (label: String?, value: Any), name: String) { + expectEqual(child.label, name) + let v = child.value as? ExpectedClass + expectNotNil(v) + expectEqual(v!.f1(), 2) + } + + func verifyExistentialField(child: (label: String?, value: Any), name: String) { + expectEqual(child.label, name) + expectNotNil(child.value) + + // FIXME: These casts are currently broken (Dec 2019) + // Once they are fixed, enable additional checks: + //let vp1 = child.value as? WeakUnownedTestsP1 + //expectNotNil(vp1) + //expectEqual(vp1!.f1(), 2) + //let vp2 = child.value as? WeakUnownedTestsP2 + //expectNotNil(vp2) + //expectEqual(vp2!.f2(), "b") + + let v = child.value as? ExpectedClass + expectNotNil(v) + expectEqual(v!.f1(), 2) + let m = Mirror(reflecting: v!) + expectEqual(m.displayStyle, .`class`) + // TODO: Find a way to verify that the existential wrapper carries + // the expected protocol witnesses. The current Swift runtime does + // a very good job of hiding this from users. + } + + verifyClassField(child: i.next()!, name: "strong_class") + verifyExistentialField(child: i.next()!, name: "strong_existential") + verifyClassField(child: i.next()!, name: "weak_class") + verifyExistentialField(child: i.next()!, name: "weak_existential") + verifyClassField(child: i.next()!, name: "unowned_safe_class") + verifyExistentialField(child: i.next()!, name: "unowned_safe_existential") + + verifyClassField(child: i.next()!, name: "unowned_unsafe_class") + verifyExistentialField(child: i.next()!, name: "unowned_unsafe_existential") + expectNil(i.next()) + + // The original bug report from SR-5289 crashed when the print() code + // attempted to reflect the contents of an unowned field. + // The tests above _should_ suffice to check this, but let's print everything + // anyway just to be sure. + for c in m.children { + print(c.label ?? "?", c.value) + } +} + +#if _runtime(_ObjC) +// Related: SR-5289 reported a crash when using Mirror to inspect Swift +// class objects containing unowned pointers to Obj-C class objects. +mirrors.test("Weak and Unowned Obj-C refs in class (SR-5289)") { + class SwiftClassWithWeakAndUnowned { + var strong_class: WeakUnownedObjCClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedObjCClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedObjCClass + unowned(safe) let unowned_safe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + unowned(unsafe) let unowned_unsafe_class: WeakUnownedObjCClass + unowned(unsafe) let unowned_unsafe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + + init(_ objc: WeakUnownedObjCClass) { + self.strong_class = objc + self.strong_existential = objc + self.weak_class = objc + self.weak_existential = objc + self.unowned_safe_class = objc + self.unowned_safe_existential = objc + self.unowned_unsafe_class = objc + self.unowned_unsafe_existential = objc + } + } + + let objc = WeakUnownedObjCClass() + let classWithReferences = SwiftClassWithWeakAndUnowned(objc) + let m = Mirror(reflecting: classWithReferences) + expectEqual(m.displayStyle, .`class`) + expectEqual(m.description, "Mirror for SwiftClassWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftClassWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedObjCClass.self) +} + +mirrors.test("Weak and Unowned Obj-C refs in struct") { + struct SwiftStructWithWeakAndUnowned { + var strong_class: WeakUnownedObjCClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedObjCClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedObjCClass + unowned(safe) let unowned_safe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + unowned(unsafe) let unowned_unsafe_class: WeakUnownedObjCClass + unowned(unsafe) let unowned_unsafe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + + init(_ objc: WeakUnownedObjCClass) { + self.strong_class = objc + self.strong_existential = objc + self.weak_class = objc + self.weak_existential = objc + self.unowned_safe_class = objc + self.unowned_safe_existential = objc + self.unowned_unsafe_class = objc + self.unowned_unsafe_existential = objc + } + } + + let objc = WeakUnownedObjCClass() + let structWithReferences = SwiftStructWithWeakAndUnowned(objc) + let m = Mirror(reflecting: structWithReferences) + expectEqual(m.displayStyle, .`struct`) + expectEqual(m.description, "Mirror for SwiftStructWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftStructWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedObjCClass.self) +} + +#endif + +mirrors.test("Weak and Unowned Swift refs in class") { + class SwiftClassWithWeakAndUnowned { + var strong_class: WeakUnownedSwiftClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedSwiftClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedSwiftClass + unowned(safe) let unowned_safe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + unowned(unsafe) let unowned_unsafe_class: WeakUnownedSwiftClass + unowned(unsafe) let unowned_unsafe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + + init(_ swift: WeakUnownedSwiftClass) { + self.strong_class = swift + self.strong_existential = swift + self.weak_class = swift + self.weak_existential = swift + self.unowned_safe_class = swift + self.unowned_safe_existential = swift + self.unowned_unsafe_class = swift + self.unowned_unsafe_existential = swift + } + } + + let swift = WeakUnownedSwiftClass() + let classWithReferences = SwiftClassWithWeakAndUnowned(swift) + let m = Mirror(reflecting: classWithReferences) + expectEqual(m.displayStyle, .`class`) + expectEqual(m.description, "Mirror for SwiftClassWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftClassWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedSwiftClass.self) +} + +mirrors.test("Weak and Unowned Swift refs in struct") { + struct SwiftStructWithWeakAndUnowned { + var strong_class: WeakUnownedSwiftClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedSwiftClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedSwiftClass + unowned(safe) let unowned_safe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + unowned(unsafe) let unowned_unsafe_class: WeakUnownedSwiftClass + unowned(unsafe) let unowned_unsafe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + + init(_ swift: WeakUnownedSwiftClass) { + self.strong_class = swift + self.strong_existential = swift + self.weak_class = swift + self.weak_existential = swift + self.unowned_safe_class = swift + self.unowned_safe_existential = swift + self.unowned_unsafe_class = swift + self.unowned_unsafe_existential = swift + } + } + + let swift = WeakUnownedSwiftClass() + let structWithReferences = SwiftStructWithWeakAndUnowned(swift) + let m = Mirror(reflecting: structWithReferences) + expectEqual(m.displayStyle, .`struct`) + expectEqual(m.description, "Mirror for SwiftStructWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftStructWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedSwiftClass.self) +} + //===--- Suppressed Superclass Mirrors ------------------------------------===// mirrors.test("Class/Root/NoSuperclassMirror") { class B : CustomReflectable { @@ -854,7 +1074,7 @@ struct GenericStructWithDefaultMirror { mirrors.test("Struct/Generic/DefaultMirror") { do { - var value = GenericStructWithDefaultMirror( + let value = GenericStructWithDefaultMirror( first: 123, second: ["abc", 456, 789.25]) var output = "" @@ -1616,7 +1836,7 @@ mirrors.test("Float") { } do { - var input: Float = 42.125 + let input: Float = 42.125 var output = "" dump(input, to: &output) @@ -1649,7 +1869,7 @@ mirrors.test("Double") { } do { - var input: Double = 42.125 + let input: Double = 42.125 var output = "" dump(input, to: &output) @@ -1745,9 +1965,9 @@ mirrors.test("FieldNamesBug") { } mirrors.test("MirrorMirror") { - var object = 1 - var mirror = Mirror(reflecting: object) - var mirrorMirror = Mirror(reflecting: mirror) + let object = 1 + let mirror = Mirror(reflecting: object) + let mirrorMirror = Mirror(reflecting: mirror) expectEqual(0, mirrorMirror.children.count) } @@ -1755,7 +1975,7 @@ mirrors.test("MirrorMirror") { mirrors.test("OpaquePointer/null") { // Don't crash on null pointers. rdar://problem/19708338 let pointer: OpaquePointer? = nil - let mirror = Mirror(reflecting: pointer) + let mirror = Mirror(reflecting: pointer as Any) expectEqual(0, mirror.children.count) } From 046c849949bf8b10f120a6e496f25eb4b91469f2 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Mon, 16 Dec 2019 13:40:21 -0800 Subject: [PATCH 050/478] Add an option to disable ClangImporter imports form source. This is primarily meant to used for testing LLDB's DWARFImporterDelegate, however, this could become the default option for LLDB once DWARFImporterDelegate is sufficiently mature. --- .../ClangImporter/ClangImporterOptions.h | 4 ++++ include/swift/Option/FrontendOptions.td | 4 ++++ lib/ClangImporter/ClangImporter.cpp | 24 ++++++++++++------- lib/ClangImporter/ImporterImpl.h | 12 ++++++++-- lib/Frontend/CompilerInvocation.cpp | 5 ++++ .../ClangImporter/disable-source-import.swift | 18 ++++++++++++++ 6 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 test/ClangImporter/disable-source-import.swift diff --git a/include/swift/ClangImporter/ClangImporterOptions.h b/include/swift/ClangImporter/ClangImporterOptions.h index 4bf47c4d3575f..14e593daf88a0 100644 --- a/include/swift/ClangImporter/ClangImporterOptions.h +++ b/include/swift/ClangImporter/ClangImporterOptions.h @@ -99,6 +99,10 @@ class ClangImporterOptions { /// When set, don't enforce warnings with -Werror. bool DebuggerSupport = false; + /// When set, ClangImporter is disabled, and all requests go to the + /// DWARFImporter delegate. + bool DisableSourceImport = false; + /// 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/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 4196942bc66c6..38dbf549ba402 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -268,6 +268,10 @@ def debug_crash_after_parse : Flag<["-"], "debug-crash-after-parse">, def debugger_support : Flag<["-"], "debugger-support">, HelpText<"Process swift code as if running in the debugger">; +def disable_clangimporter_source_import : Flag<["-"], + "disable-clangimporter-source-import">, + HelpText<"Disable ClangImporter and forward all requests straight the DWARF importer.">; + def disable_arc_opts : Flag<["-"], "disable-arc-opts">, HelpText<"Don't run SIL ARC optimization passes.">; def disable_ossa_opts : Flag<["-"], "disable-ossa-opts">, diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 667499a9640eb..d3ffc06ccb67f 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -1757,18 +1757,24 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( return finishLoadingClangModule(clangModule, /*preferOverlay=*/false); } -ModuleDecl *ClangImporter::loadModule( - SourceLoc importLoc, - ArrayRef> path) { - ModuleDecl *MD = Impl.loadModuleClang(importLoc, path); +ModuleDecl * +ClangImporter::loadModule(SourceLoc importLoc, + ArrayRef> path) { + return Impl.loadModule(importLoc, path); +} + +ModuleDecl *ClangImporter::Implementation::loadModule( + SourceLoc importLoc, ArrayRef> path) { + ModuleDecl *MD = nullptr; + if (!DisableSourceImport) + MD = loadModuleClang(importLoc, path); if (!MD) - MD = Impl.loadModuleDWARF(importLoc, path); + MD = loadModuleDWARF(importLoc, path); return MD; } ModuleDecl *ClangImporter::Implementation::finishLoadingClangModule( - const clang::Module *clangModule, - bool findOverlay) { + const clang::Module *clangModule, bool findOverlay) { assert(clangModule); // Bump the generation count. @@ -1954,6 +1960,7 @@ ClangImporter::Implementation::Implementation( BridgingHeaderLookupTable(new SwiftLookupTable(nullptr)), BuffersForDiagnostics(ctx.SourceMgr), platformAvailability(ctx.LangOpts), nameImporter(), + DisableSourceImport(opts.DisableSourceImport), DWARFImporter(dwarfImporterDelegate) {} ClangImporter::Implementation::~Implementation() { @@ -2613,7 +2620,8 @@ void ClangImporter::lookupTypeDecl( clang::LookupResult lookupResult(sema, clangName, clang::SourceLocation(), lookupKind); bool foundViaClang = false; - if (sema.LookupName(lookupResult, /*Scope=*/nullptr)) { + if (!Impl.DisableSourceImport && + sema.LookupName(lookupResult, /*Scope=*/nullptr)) { for (auto clangDecl : lookupResult) { if (!isa(clangDecl) && !isa(clangDecl) && diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 79d0e175e211d..dc09261781c61 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -605,6 +605,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation bool shouldIgnoreBridgeHeaderTopLevelDecl(clang::Decl *D); private: + /// When set, ClangImporter is disabled, and all requests go to the + /// DWARFImporter delegate. + bool DisableSourceImport; + /// The DWARF importer delegate, if installed. DWARFImporterDelegate *DWARFImporter = nullptr; public: @@ -614,7 +618,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation private: /// The list of Clang modules found in the debug info. llvm::DenseMap DWARFModuleUnits; -public: + /// Load a module using the clang::CompilerInstance. ModuleDecl *loadModuleClang(SourceLoc importLoc, ArrayRef> path); @@ -624,7 +628,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation ModuleDecl *loadModuleDWARF(SourceLoc importLoc, ArrayRef> path); - +public: + /// Load a module using either method. + ModuleDecl *loadModule(SourceLoc importLoc, + ArrayRef> path); + void recordImplicitUnwrapForDecl(ValueDecl *decl, bool isIUO) { if (!isIUO) return; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 7c3188f0dc168..355aeff500586 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -638,7 +638,12 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, if (Args.hasArg(OPT_warnings_as_errors)) Opts.ExtraArgs.push_back("-Werror"); + Opts.DebuggerSupport |= Args.hasArg(OPT_debugger_support); + + Opts.DisableSourceImport |= + Args.hasArg(OPT_disable_clangimporter_source_import); + return false; } diff --git a/test/ClangImporter/disable-source-import.swift b/test/ClangImporter/disable-source-import.swift new file mode 100644 index 0000000000000..826a2b10d2e24 --- /dev/null +++ b/test/ClangImporter/disable-source-import.swift @@ -0,0 +1,18 @@ +// RUN: %empty-directory(%t.mcp) + +// This should fail only if -disable-clangimporter-source-import is present. + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: -enable-objc-interop -typecheck -I %S/Inputs/custom-modules \ +// RUN: -module-cache-path %t.mcp %s | %FileCheck --allow-empty %s + +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: -enable-objc-interop -typecheck -o - -I %S/Inputs/custom-modules \ +// RUN: -module-cache-path %t.mcp -disable-clangimporter-source-import %s \ +// RUN: 2>&1 | %FileCheck --check-prefix=ERROR %s + +import ExternIntX + +public let y = x // ERROR: error + +// CHECK-NOT: error From e7170bd7517b1b87d52796783ac19589b9a5c0c1 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 30 Nov 2019 17:36:28 -0800 Subject: [PATCH 051/478] Add Qualified Lookup Requests --- include/swift/AST/Module.h | 5 ++ include/swift/AST/NameLookupRequests.h | 39 ++++++++++++++ include/swift/AST/NameLookupTypeIDZone.def | 25 ++++++--- include/swift/Basic/Statistics.def | 6 --- lib/AST/NameLookup.cpp | 61 +++++++++++++--------- test/decl/circularity.swift | 2 +- test/decl/class/circular_inheritance.swift | 9 ++-- 7 files changed, 104 insertions(+), 43 deletions(-) diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 456fa92a53dad..8f4a28ab2da87 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -629,6 +629,11 @@ inline bool DeclContext::isModuleScopeContext() const { return isModuleContext(); } +/// Extract the source location from the given module declaration. +inline SourceLoc extractNearestSourceLoc(const ModuleDecl *mod) { + return extractNearestSourceLoc(static_cast(mod)); +} + } // end namespace swift namespace llvm { diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index d1ed1fd5a06cd..47f7a96a82d1f 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -405,6 +405,45 @@ class AnyObjectLookupRequest NLOptions options) const; }; +class ModuleQualifiedLookupRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + const DeclContext *DC, + ModuleDecl *mod, DeclNameRef name, + NLOptions opts) const; +}; + +class QualifiedLookupRequest + : public SimpleRequest, + DeclNameRef, NLOptions), + CacheKind::Uncached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, const DeclContext *DC, + SmallVector decls, + DeclNameRef name, + NLOptions opts) const; +}; + #define SWIFT_TYPEID_ZONE NameLookup #define SWIFT_TYPEID_HEADER "swift/AST/NameLookupTypeIDZone.def" #include "swift/Basic/DefineTypeIDZone.h" diff --git a/include/swift/AST/NameLookupTypeIDZone.def b/include/swift/AST/NameLookupTypeIDZone.def index c0cf8900243c5..df16be2b66aa1 100644 --- a/include/swift/AST/NameLookupTypeIDZone.def +++ b/include/swift/AST/NameLookupTypeIDZone.def @@ -15,6 +15,9 @@ // //===----------------------------------------------------------------------===// +SWIFT_REQUEST(NameLookup, AnyObjectLookupRequest, + QualifiedLookupResult(const DeclContext *, DeclName, NLOptions), + Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, CustomAttrNominalRequest, NominalTypeDecl *(CustomAttr *, DeclContext *), Cached, NoLocationInfo) @@ -34,6 +37,20 @@ SWIFT_REQUEST(NameLookup, InheritedDeclsReferencedRequest, DirectlyReferencedTypeDecls( llvm::PointerUnion, unsigned), Uncached, HasNearestLocation) +SWIFT_REQUEST(NameLookup, LookupInModuleRequest, + QualifiedLookupResult(const DeclContext *, DeclName, NLKind, + namelookup::ResolutionKind, + const DeclContext *), + Uncached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, ModuleQualifiedLookupRequest, + QualifiedLookupResult( + const DeclContext *, ModuleDecl *, DeclNameRef, NLOptions), + Uncached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, QualifiedLookupRequest, + QualifiedLookupResult( + const DeclContext *, SmallVector, + DeclNameRef, NLOptions), + Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, SelfBoundsFromWhereClauseRequest, SelfBounds(llvm::PointerUnion), Uncached, NoLocationInfo) @@ -48,11 +65,3 @@ SWIFT_REQUEST(NameLookup, UnderlyingTypeDeclsReferencedRequest, SWIFT_REQUEST(NameLookup, UnqualifiedLookupRequest, LookupResult(UnqualifiedLookupDescriptor), Uncached, NoLocationInfo) -SWIFT_REQUEST(NameLookup, LookupInModuleRequest, - QualifiedLookupResult(const DeclContext *, DeclName, NLKind, - namelookup::ResolutionKind, - const DeclContext *), - Uncached, NoLocationInfo) -SWIFT_REQUEST(NameLookup, AnyObjectLookupRequest, - QualifiedLookupResult(const DeclContext *, DeclName, NLOptions), - Uncached, NoLocationInfo) diff --git a/include/swift/Basic/Statistics.def b/include/swift/Basic/Statistics.def index 725c082b8d877..657568f3ea3a0 100644 --- a/include/swift/Basic/Statistics.def +++ b/include/swift/Basic/Statistics.def @@ -138,12 +138,6 @@ FRONTEND_STATISTIC(AST, NumPrefixOperators) /// Number of precedence groups in the AST context. FRONTEND_STATISTIC(AST, NumPrecedenceGroups) -/// Number of qualified lookups into a nominal type. -FRONTEND_STATISTIC(AST, NumLookupQualifiedInNominal) - -/// Number of qualified lookups into a module. -FRONTEND_STATISTIC(AST, NumLookupQualifiedInModule) - /// Number of local lookups into a module. FRONTEND_STATISTIC(AST, NumModuleLookupValue) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index afdcc0b40b313..848f40dbd11ee 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1587,17 +1587,24 @@ bool DeclContext::lookupQualified(ArrayRef typeDecls, DeclNameRef member, NLOptions options, SmallVectorImpl &decls) const { - using namespace namelookup; assert(decls.empty() && "additive lookup not supported"); + QualifiedLookupRequest req{this, {typeDecls.begin(), typeDecls.end()}, + member, options}; + decls = evaluateOrDefault(getASTContext().evaluator, req, {}); + return !decls.empty(); +} - auto *stats = getASTContext().Stats; - if (stats) - stats->getFrontendCounters().NumLookupQualifiedInNominal++; +llvm::Expected +QualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, + SmallVector typeDecls, + DeclNameRef member, NLOptions options) const { + using namespace namelookup; + QualifiedLookupResult decls; // Configure lookup and dig out the tracker. ReferencedNameTracker *tracker = nullptr; bool isLookupCascading; - configureLookup(this, options, tracker, isLookupCascading); + configureLookup(DC, options, tracker, isLookupCascading); // Tracking for the nominal types we'll visit. SmallVector stack; @@ -1645,8 +1652,7 @@ bool DeclContext::lookupQualified(ArrayRef typeDecls, if ((options & NL_OnlyTypes) && !isa(decl)) continue; - if (isAcceptableLookupResult(this, options, decl, - onlyCompleteObjectInits)) + if (isAcceptableLookupResult(DC, options, decl, onlyCompleteObjectInits)) decls.push_back(decl); } @@ -1706,34 +1712,40 @@ bool DeclContext::lookupQualified(ArrayRef typeDecls, } } - pruneLookupResultSet(this, options, decls); - if (auto *debugClient = this->getParentModule()->getDebugClient()) { - debugClient->finishLookupInNominals(this, typeDecls, member.getFullName(), + pruneLookupResultSet(DC, options, decls); + if (auto *debugClient = DC->getParentModule()->getDebugClient()) { + debugClient->finishLookupInNominals(DC, typeDecls, member.getFullName(), options, decls); } - // We're done. Report success/failure. - return !decls.empty(); + + return decls; } bool DeclContext::lookupQualified(ModuleDecl *module, DeclNameRef member, NLOptions options, SmallVectorImpl &decls) const { - using namespace namelookup; + assert(decls.empty() && "additive lookup not supported"); + ModuleQualifiedLookupRequest req{this, module, member, options}; + decls = evaluateOrDefault(getASTContext().evaluator, req, {}); + return !decls.empty(); +} - auto &ctx = getASTContext(); - auto *stats = ctx.Stats; - if (stats) - stats->getFrontendCounters().NumLookupQualifiedInModule++; +llvm::Expected +ModuleQualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, + ModuleDecl *module, DeclNameRef member, + NLOptions options) const { + using namespace namelookup; + QualifiedLookupResult decls; // Configure lookup and dig out the tracker. ReferencedNameTracker *tracker = nullptr; bool isLookupCascading; - configureLookup(this, options, tracker, isLookupCascading); + configureLookup(DC, options, tracker, isLookupCascading); auto kind = (options & NL_OnlyTypes ? ResolutionKind::TypesOnly : ResolutionKind::Overloadable); - auto topLevelScope = getModuleScopeContext(); + auto topLevelScope = DC->getModuleScopeContext(); if (module == topLevelScope->getParentModule()) { if (tracker) { recordLookupOfTopLevelName(topLevelScope, member.getFullName(), @@ -1748,6 +1760,7 @@ bool DeclContext::lookupQualified(ModuleDecl *module, DeclNameRef member, // anything in this one. // Perform the lookup in all imports of this module. + auto &ctx = DC->getASTContext(); auto accessPaths = ctx.getImportCache().getAllVisibleAccessPaths( module, topLevelScope); if (llvm::any_of(accessPaths, @@ -1760,14 +1773,14 @@ bool DeclContext::lookupQualified(ModuleDecl *module, DeclNameRef member, } } - pruneLookupResultSet(this, options, decls); + pruneLookupResultSet(DC, options, decls); - if (auto *debugClient = this->getParentModule()->getDebugClient()) { - debugClient->finishLookupInModule(this, module, member.getFullName(), + if (auto *debugClient = DC->getParentModule()->getDebugClient()) { + debugClient->finishLookupInModule(DC, module, member.getFullName(), options, decls); } - // We're done. Report success/failure. - return !decls.empty(); + + return decls; } llvm::Expected diff --git a/test/decl/circularity.swift b/test/decl/circularity.swift index 69731c3725c5c..fedb35a291b9a 100644 --- a/test/decl/circularity.swift +++ b/test/decl/circularity.swift @@ -96,7 +96,7 @@ class C4 { required init(x: Int) {} } -class D4 : C4, P1 { // expected-note {{through reference here}} +class D4 : C4, P1 { // expected-note 2 {{through reference here}} required init(x: X) { // expected-error {{circular reference}} // expected-note@-1 2{{through reference here}} super.init(x: x) diff --git a/test/decl/class/circular_inheritance.swift b/test/decl/class/circular_inheritance.swift index 0e0ceba0194b4..202e5a9ffe13f 100644 --- a/test/decl/class/circular_inheritance.swift +++ b/test/decl/class/circular_inheritance.swift @@ -8,12 +8,12 @@ // Check that we produced superclass type requests. // RUN: %{python} %utils/process-stats-dir.py --evaluate 'SuperclassTypeRequest == 18' %t/stats-dir -class Left // expected-error {{circular reference}} +class Left // expected-error {{circular reference}} expected-note {{through reference here}} : Right.Hand { // expected-note {{through reference here}} class Hand {} } -class Right // expected-note {{through reference here}} +class Right // expected-note 2 {{through reference here}} : Left.Hand { // expected-note {{through reference here}} class Hand {} } @@ -35,13 +35,13 @@ class Outer { class Inner : Outer {} } -class Outer2 // expected-error {{circular reference}} +class Outer2 // expected-error {{circular reference}} expected-note {{through reference here}} : Outer2.Inner { // expected-note {{through reference here}} class Inner {} } -class Outer3 // expected-error {{circular reference}} +class Outer3 // expected-error {{circular reference}} expected-note {{through reference here}} : Outer3.Inner { // expected-note {{through reference here}} class Inner {} } @@ -79,6 +79,7 @@ class WithDesignatedInit : WithDesignatedInit { // expected-error@-1 {{'WithDesignatedInit' inherits from itself}} // expected-error@-2 {{circular reference}} // expected-note@-3 {{through reference here}} + // expected-note@-4 2 {{through reference here}} init(x: Int) {} // expected-error {{circular reference}} } From f22fb813261a9deadd8f0eea48c334fa53904c57 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 16 Dec 2019 15:32:12 -0800 Subject: [PATCH 052/478] Block additive lookup in directReferencesForQualifiedTypeLookup --- lib/AST/NameLookup.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 848f40dbd11ee..48c4fdd102be2 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -2010,7 +2010,9 @@ directReferencesForQualifiedTypeLookup(Evaluator &evaluator, auto innerOptions = options; innerOptions &= ~NL_RemoveOverridden; innerOptions &= ~NL_RemoveNonVisible; - dc->lookupQualified(module, name, innerOptions, members); + SmallVector moduleMembers; + dc->lookupQualified(module, name, innerOptions, moduleMembers); + members.append(moduleMembers.begin(), moduleMembers.end()); } addResults(members); From 257c93a18304b62c943fdaf1ac7708518969d576 Mon Sep 17 00:00:00 2001 From: David Smith Date: Mon, 16 Dec 2019 14:53:36 -0800 Subject: [PATCH 053/478] Use -[NSSet getObjects:] instead of -[NSSet getObjects:count:] to maintain compatibility with existing code passing NSArrays into this function --- stdlib/public/core/SetBridging.swift | 2 +- stdlib/public/core/ShadowProtocols.swift | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/SetBridging.swift b/stdlib/public/core/SetBridging.swift index 75332860ec8bd..85ce73edb5213 100644 --- a/stdlib/public/core/SetBridging.swift +++ b/stdlib/public/core/SetBridging.swift @@ -20,7 +20,7 @@ internal func _stdlib_NSSet_allObjects(_ object: AnyObject) -> _BridgingBuffer { let nss = unsafeBitCast(object, to: _NSSet.self) let count = nss.count let storage = _BridgingBuffer(count) - nss.getObjects(storage.baseAddress, count: count) + nss.getObjects(storage.baseAddress) return storage } diff --git a/stdlib/public/core/ShadowProtocols.swift b/stdlib/public/core/ShadowProtocols.swift index bdffbd02ff4e6..0e9bb7128604c 100644 --- a/stdlib/public/core/ShadowProtocols.swift +++ b/stdlib/public/core/ShadowProtocols.swift @@ -175,6 +175,10 @@ internal protocol _NSSet: _NSSetCore { _ buffer: UnsafeMutablePointer, count: Int ) + + @objc(getObjects:) func getObjects( + _ buffer: UnsafeMutablePointer + ) } /// A shadow for the API of NSNumber we will use in the core From f7472e2c4577abedf7eadc951c07e7b12927f5e7 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 16 Dec 2019 16:04:09 -0800 Subject: [PATCH 054/478] EscapeAnalysis: Add PointerKind and interior/reference flags Categorize three kinds of pointers: NoPointer (don't create a node) ReferenceOnly (safe to make normal assumptions) AnyPointer (may have addresses, rawpointers, or any mix of thoses with references) Flag ConnectionGraph nodes as - hasReferenceOnly - isInterior An interior node always has an additional content node. All sorts of arbitrary node merging is supported. Nodes with totally different properties can be safely merged. Interior nodes can safely be merged with their field content (which does happen surprisingly often). Alias analysis will use these flags to safely make assumptions about properties of the connection graph. --- .../SILOptimizer/Analysis/EscapeAnalysis.h | 319 +++++--- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 755 ++++++++++-------- 2 files changed, 644 insertions(+), 430 deletions(-) diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index 9c4b364383d11..0c68b94f9984e 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -43,18 +43,20 @@ /// return a /// /// Generates the following connection graph, where 'a' is in the SILValue %0: -/// Val %0 Esc: R, Succ: (%0.1) // Represents 'a', and points to 'a's content -/// Con %0.1 Esc: G, Succ: // Represents the content of 'a' -/// Ret Esc: R, Succ: %0 // The returned value, aliased with 'a' +/// Val [ref] %1 Esc: R, Succ: (%1) // Reference 'a'; points to 'a's object +/// Con [int] %2 Esc: R, Succ: (%2.1) // Object pointed to by 'a' +/// Con %2.1 Esc: G, Succ: // Fields in the object +/// Ret Esc: R, Succ: %0 // The returned value, aliased with 'a' /// /// Each node has an escaping state: None, (R)eturn, (A)rguments, or (G)lobal. /// These states form a lattice in which None is the most refined, or top, state /// and Global is the least refined, or bottom, state. Merging nodes performs a -/// meet operation on their escaping states. At a call site, the callee graph is -/// merged with the callee graph by merging the respective call argument -/// nodes. A node has a "Return" escaping state if it only escapes by being -/// returned from the current function. A node has an "Argument" escaping state -/// if only escapes by being passed as an incoming argument to this function. +/// meet operation on their escaping states, moving the state down in the +/// lattice. At a call site, the callee graph is merged with the caller graph by +/// merging the respective call argument nodes. A node has a "Return" escaping +/// state if it only escapes by being returned from the current function. A node +/// has an "Argument" escaping state if only escapes by being passed as an +/// incoming argument to this function. /// /// A directed edge between two connection graph nodes indicates that the memory /// represented by the destination node memory is reachable via an address @@ -81,30 +83,37 @@ /// /// Generates the following connection graph, where the alloc_stack for variable /// 'a' is in the SILValue %0 and class allocation returns SILValue %3. -/// Val %0 Esc: G, Succ: (%0.1) -/// Con %0.1 Esc: G, Succ: %3 -/// Val %3 Esc: G, Succ: (%3.1) -/// Con %3.1 Esc: G, Succ: -/// Ret Esc: R, Succ: %3 +/// +/// Val %0 Esc: , Succ: (%0.1) // Stack address of 'a' +/// Con [ref] %0.1 Esc: , Succ: %3 // Local reference 'a', aliased with %3 +/// Val [ref] %3 Esc: , Succ: (%4) // Local instance 'a', stored in %0.1 +/// Con [int] %4 Esc: G, Succ: (%4.1) // Object, escapes +/// Con %4.1 Esc: G, Succ: // Fields, escapes +/// Ret Esc: , Succ: (%4), %3 /// /// The value node for variable 'a' now points to local variable storage /// (%0.1). That local variable storage contains a reference. Assignment into /// that reference creates a defer edge to the allocated reference (%3). The -/// allocated reference in turn points to the object storage (%3.1). +/// allocated reference in turn points to the object storage (%4). /// -/// Note that a variable holding a single class reference and a variable -/// holding a non-trivial struct has the same graph representation. The -/// variable's content node only represents the value of the references, not the -/// memory pointed-to by the reference. +/// Note that a variable holding a single class reference and a variable holding +/// a non-trivial struct will have the same graph representation. A '[ref]' flag +/// on a node indicates that the all pointer-type fields that may be stored +/// inside the memory represented by that node are references. This allows alias +/// analysis to assume the object the node points to will not be released when +/// the node's memory is released as long as there are subsequent accesses to +/// the object accessed via a different path in the connection graph. /// /// A pointsTo edge does not necessarily indicate pointer indirection. It may -/// simply represent a derived address within the same object. This allows -/// escape analysis to view an object's memory in layers, each with separate -/// escaping properties. For example, a class object's first-level content node -/// represents the object header including the metadata pointer and reference -/// count. An object's second level content node only represents the -/// reference-holding fields within that object. Consider the connection graph -/// for a class with properties: +/// simply represent a derived address within the same object. A node that +/// points to the same logical object must be flagged as an interior node +/// ('[int]'). Interior nodes always have pointsTo content representing the rest +/// of the object. This allows escape analysis to view an object's memory in +/// layers, each with separate escaping properties. For example, a class +/// object's first-level content node represents the object header including the +/// metadata pointer and reference count. An object's second level content node +/// only represents the reference-holding fields within that object. Consider +/// the connection graph for a class with properties: /// /// class HasObj { /// var obj: AnyObject @@ -114,35 +123,37 @@ /// } /// /// Which generates this graph where the argument 'h' is %0, and 'o' is %1: -/// Arg %0 Esc: A, Succ: (%0.1) -/// Con %0.1 Esc: A, Succ: (%0.2) -/// Con %0.2 Esc: A, Succ: %1 -/// Arg %1 Esc: A, Succ: (%1.1) -/// Con %1.1 Esc: A, Succ: (%1.2) -/// Con %1.2 Esc: G, Succ: /// -/// Node %0.1 represents the header of 'h', including reference count and -/// metadata pointer. This node points to %0.2 which represents the 'obj' -/// property. The assignment 'h.obj = o' creates a defer edge from %0.2 to -/// %1. Similarly, %1.1 represents the header of 'o', and %1.2 represents any -/// potential nontrivial properties in 'o' which may have escaped globally when -/// 'o' was released. +/// Arg [ref] %0 Esc: A, Succ: (%6) // 'h' +/// Arg [ref] %1 Esc: A, Succ: (%1.1) // 'o' +/// Con [int] %1.1 Esc: A, Succ: (%1.2) // 'o' object +/// Con %1.2 Esc: A, Succ: (%1.3) // 'o' fields +/// Con %1.3 Esc: G, Succ: // memory 'h.obj' may point to +/// Con [int] %6 Esc: A, Succ: (%7) // 'h' object +/// Con %7 Esc: A, Succ: %1 // 'h.obj' +/// +/// Node %1.1 represents the header of 'o', including reference count and +/// metadata pointer. This node points to %1.2 which represents the 'obj' +/// property. '%1.3' represents any potential nontrivial properties in 'o' which +/// may have escaped globally when 'o' was released. '%6' is a ref_element_addr +/// accessing 'h.obj'. '%7' is a load of 'h.obj'. The assignment 'h.obj = o' +/// creates a defer edge from '%7' to '%1'. /// /// The connection graph is constructed by summarizing all memory operations in /// a flow-insensitive way. Hint: ConGraph->viewCG() displays the Dot-formatted /// connection graph. /// -/// In addition to the connection graph, EscapeAnalysis stores information about -/// "use points". Each release operation is a use points. These instructions are -/// recorded in a table and given an ID. Each connection graph node stores a -/// bitset indicating the use points reachable via the CFG by that node. This -/// provides some flow-sensitive information on top of the otherwise flow -/// insensitive connection graph. -/// -/// Note: storing bitsets in each node may be unnecessary overhead since the -/// same information can be obtained with a graph traversal, typically of only -/// 1-3 hops. -// ===---------------------------------------------------------------------===// +/// In addition to the connection graph, EscapeAnalysis caches information about +/// "use points". Each release operation in which the released reference can be +/// identified is a considered a use point. The use point instructions are +/// recorded in a table and given an ID. Each connection graph content node +/// stores a bitset indicating the use points that may release references that +/// point to by that content node. Correctness relies on an invariant: for each +/// reference-type value in the function, all points in the function which may +/// release the reference must be identified as use points of the node that the +/// value points to. If the reference-type value may be released any other +/// way, then its content node must be marked escaping. +/// ===---------------------------------------------------------------------===// #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ESCAPEANALYSIS_H_ #define SWIFT_SILOPTIMIZER_ANALYSIS_ESCAPEANALYSIS_H_ @@ -234,6 +245,13 @@ class EscapeAnalysis : public BottomUpIPAnalysis { Global }; + // Must be ordered from most precise to least precise. A meet across memory + // locations (such as aggregate fields) always moves down. + enum PointerKind { NoPointer, ReferenceOnly, AnyPointer }; + static bool canOnlyContainReferences(PointerKind pointerKind) { + return pointerKind <= ReferenceOnly; + } + public: class CGNode; class ConnectionGraph; @@ -320,25 +338,52 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// is completely unlinked from the graph, bool isMerged = false; - /// True if this is a content node that owns a reference count. Such a - /// content node necessarilly keeps alive all content it points to until it - /// is released. This can be conservatively false. - bool hasRC = false; + /// True if this node's pointsTo content is part of the same object with + /// respect to SIL memory access. When this is true, then this node must + /// have a content node. + /// + /// When this flag is false, it provides a "separate access guarantee" for + /// stronger analysis. If the base object for a SIL memory access is mapped + /// to this node, then the accessed memory must have the same escaping + /// properties as the base object. In contrast, when this is true, the + /// connection graph may model the escaping properties of the base object + /// separately from the accessed memory. + bool isInteriorFlag; + + /// True if this node can only point to other nodes via a reference, not an + /// address or raw pointer. + /// + /// When this flag is true, it provides a "separate lifetime guarantee". Any + /// reference modeled by this node keeps alive all pointsTo content until + /// it is released. e.g. Given two nodes that both pointTo the same + /// content, releasing the value associated one node cannot free that + /// content as long as the released node is reference-only and the value + /// associated with the other node is accessed later. + /// + /// Note that an object field may contain a raw pointer which could point + /// anywhere, even back into the same object. In that case + /// hasReferenceOnlyFlag must be false. + /// + /// Typically, when this is true, the node represents at least one + /// reference, however, a return node may be created even when the return + /// type is NoPointer. + bool hasReferenceOnlyFlag; /// The type of the node (mainly distinguishes between content and value /// nodes). NodeType Type; /// The constructor. - CGNode(ValueBase *V, NodeType Type, bool hasRC) - : mappedValue(V), UsePoints(0), hasRC(hasRC), Type(Type) { + CGNode(ValueBase *V, NodeType Type, bool isInterior, bool hasReferenceOnly) + : mappedValue(V), UsePoints(0), isInteriorFlag(isInterior), + hasReferenceOnlyFlag(hasReferenceOnly), Type(Type) { switch (Type) { case NodeType::Argument: case NodeType::Value: - assert(V); + assert(V && !isInteriorFlag); break; case NodeType::Return: - assert(!V); + assert(!V && !isInteriorFlag); break; case NodeType::Content: // A content node representing the returned value has no associated @@ -360,6 +405,8 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return Changed; } + void mergeFlags(bool isInterior, bool hasReferenceOnly); + // Merge the properties of \p fromNode into this node. void mergeProperties(CGNode *fromNode); @@ -454,10 +501,18 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Return true if this node represents content. bool isContent() const { return Type == NodeType::Content; } - /// Return true if this node represents an entire reference counted object. - bool hasRefCount() const { return hasRC; } + /// Return true if this node pointsTo the same object. + bool isInterior() const { return isInteriorFlag; } - void setRefCount(bool rc) { hasRC = rc; } + void setInterior(bool isInterior) { isInteriorFlag = isInterior; } + + /// Return true if this node can only point to another node via a reference, + /// as opposed to an address or raw pointer. + bool hasReferenceOnly() const { return hasReferenceOnlyFlag; } + + void setHasReferenceOnly(bool hasReferenceOnly) { + hasReferenceOnlyFlag = hasReferenceOnly; + } /// Returns the escape state. EscapeState getEscapeState() const { return State; } @@ -614,11 +669,13 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Allocates a node of a given type. /// - /// hasRC is set for Content nodes based on the type and origin of - /// the pointer. - CGNode *allocNode(ValueBase *V, NodeType Type, bool hasRC = false) { - assert(Type == NodeType::Content || !hasRC); - CGNode *Node = new (NodeAllocator.Allocate()) CGNode(V, Type, hasRC); + /// isInterior is always false for non-content nodes and is set for content + /// nodes based on the type and origin of the pointer. + CGNode *allocNode(ValueBase *V, NodeType Type, bool isInterior, + bool isReference) { + assert((Type == NodeType::Content) || !isInterior); + CGNode *Node = new (NodeAllocator.Allocate()) + CGNode(V, Type, isInterior, isReference); Nodes.push_back(Node); return Node; } @@ -665,6 +722,9 @@ class EscapeAnalysis : public BottomUpIPAnalysis { } } + // Helper for getNode and getValueContent. + CGNode *getOrCreateNode(ValueBase *V, PointerKind pointerKind); + /// Gets or creates a node for a value \p V. /// If V is a projection(-path) then the base of the projection(-path) is /// taken. This means the node is always created for the "outermost" value @@ -672,9 +732,14 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Returns null, if V is not a "pointer". CGNode *getNode(ValueBase *V, bool createIfNeeded = true); - /// Helper to create and return a content node with the given \p hasRC - /// flag. \p addrNode will gain a points-to edge to the new content node. - CGNode *createContentNode(CGNode *addrNode, bool hasRC); + // Helper for getValueContent to create and return a content node with the + // given \p isInterior and \p hasReferenceOnly flags. \p addrNode + // will gain a points-to edge to the new content node. + CGNode *createContentNode(CGNode *addrNode, bool isInterior, + bool hasReferenceOnly); + + CGNode *getOrCreateContentNode(CGNode *addrNode, bool isInterior, + bool hasReferenceOnly); /// Create a new content node based on an existing content node to support /// graph merging. @@ -683,18 +748,33 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// state will be initialized based on the \p srcContent node. CGNode *createMergedContent(CGNode *destAddrNode, CGNode *srcContent); - /// Get a node represnting the field data within the given RC node. - CGNode *getFieldContent(CGNode *rcNode); + // Helper for getValueContent to get the content node for an address, which + // may be variable content or field content. + CGNode *getOrCreateAddressContent(SILValue addrVal, CGNode *addrNode); + + // Helper for getValueContent to get the content representing a referenced + // object. \p refVal's type may or may not have reference semantics. The + // caller must knows based on the operand using \p refVal that it contains a + // reference. + CGNode *getOrCreateReferenceContent(SILValue refVal, CGNode *refNode); + + // Helper for getValueContent to get the content node for an unknown pointer + // value. This is useful to determine whether multiple nodes are in the same + // defer web, but is otherwise conservative. + CGNode *getOrCreateUnknownContent(CGNode *addrNode); + + /// Get a node representing the field data within the given object node. + /// If objNode was a recognized reference-only value, then it's content node + /// will already be initialized according to the reference type. Otherwise, + /// return null. + CGNode *getFieldContent(CGNode *objNode) { + if (!objNode->isInterior()) + return nullptr; + return objNode->getContentNodeOrNull(); + } /// Get or creates a pseudo node for the function return value. - CGNode *getReturnNode() { - if (!ReturnNode) { - ReturnNode = allocNode(nullptr, NodeType::Return); - if (!isSummaryGraph) - ReturnNode->mergeEscapeState(EscapeState::Return); - } - return ReturnNode; - } + CGNode *getReturnNode(); /// Returns the node for the function return value if present. CGNode *getReturnNodeOrNull() const { @@ -731,6 +811,21 @@ class EscapeAnalysis : public BottomUpIPAnalysis { assert(UsePoints.size() == UsePointTable.size()); Node->setUsePointBit(Idx); return Idx; + /// If \p pointer is a pointer, set it's content to global escaping. + /// + /// Only mark the content node as escaping. Marking a pointer node as + /// escaping would generally be meaningless because it may have aliases or + /// defer edges. Marking the pointer node as escaping would also be too + /// conservative because, when the pointer is mapped to a content node, it + /// would behave as if the memory containing the pointer also escaped. + /// + /// If the pointer is mapped to a node, then calling setEscapesGlobal must + /// ensure that it points to a content node. Even if we could mark the + /// pointer node as escaping, it would be insufficient because only content + /// nodes are merged into the caller graph. + void setEscapesGlobal(SILValue pointer) { + if (CGNode *content = getValueContent(pointer)) + content->markEscaping(); } void escapeContentsOf(CGNode *Node) { @@ -806,6 +901,14 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Returns null, if V is not a "pointer". CGNode *getNodeOrNull(ValueBase *V) { return getNode(V, false); } + /// Get the content node pointed to by \p ptrVal. + /// + /// If \p ptrVal cannot be mapped to a node, return nullptr. + /// + /// If \p ptrVal is mapped to a node, return a non-null node representing + /// the content that \p ptrVal points to. + CGNode *getValueContent(SILValue ptrVal); + /// Returns the number of use-points of a node. int getNumUsePoints(CGNode *Node) { assert(!Node->escapes() && @@ -901,6 +1004,8 @@ class EscapeAnalysis : public BottomUpIPAnalysis { MaxGraphMerges = 4 }; + using PointerKindCache = llvm::DenseMap; + /// The connection graphs for all functions (does not include external /// functions). llvm::DenseMap Function2Info; @@ -909,7 +1014,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { llvm::SpecificBumpPtrAllocator Allocator; /// Cache for isPointer(). - llvm::DenseMap isPointerCache; + PointerKindCache pointerKindCache; SILModule *M; @@ -919,10 +1024,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Callee analysis, used for determining the callees at call sites. BasicCalleeAnalysis *BCA; - /// Returns true if \p V may encapsulate a "pointer" value. - /// See EscapeAnalysis::NodeType::Value. - bool isPointer(ValueBase *V) const; - /// If EscapeAnalysis should consider the given value to be a derived address /// or pointer based on one of its address or pointer operands, then return /// that operand value. Otherwise, return an invalid value. @@ -933,23 +1034,14 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// value. SILValue getPointerRoot(SILValue value) const; - /// If \p pointer is a pointer, set it to global escaping. - void setEscapesGlobal(ConnectionGraph *conGraph, ValueBase *pointer) { - CGNode *Node = conGraph->getNode(pointer); - if (!Node) - return; + PointerKind findRecursivePointerKind(SILType Ty, const SILFunction &F) const; - if (Node->isContent()) { - Node->markEscaping(); - return; - } - Node->mergeEscapeState(EscapeState::Global); + PointerKind findCachedPointerKind(SILType Ty, const SILFunction &F) const; - // Make sure to have a content node. Otherwise we may end up not merging - // the global-escape state into a caller graph (only content nodes are - // merged). Either the node itself is a content node or we let the node - // point to one. - conGraph->escapeContentsOf(Node); + // Returns true if the type \p Ty must be a reference or must transitively + // contain a reference and no other pointer or address type. + bool hasReferenceOnly(SILType Ty, const SILFunction &F) const { + return findCachedPointerKind(Ty, F) <= ReferenceOnly; } /// Gets or creates FunctionEffects for \p F. @@ -960,15 +1052,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return FInfo; } - /// Get or create the node representing the memory pointed to by \p - /// addrVal. If \p addrVal is an address, then return the content node for the - /// variable's memory. Otherwise, \p addrVal may contain a reference, so - /// return the content node for the referenced heap object. - /// - /// Note that \p addrVal cannot be an address within a heap object, such as - /// an address from ref_element_addr or project_box. - CGNode *getValueContent(ConnectionGraph *conGraph, SILValue addrVal); - /// Build a connection graph for reach callee from the callee list. bool buildConnectionGraphForCallees(SILInstruction *Caller, CalleeList Callees, @@ -990,6 +1073,9 @@ class EscapeAnalysis : public BottomUpIPAnalysis { void buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth); + bool createArrayUninitializedSubgraph(FullApplySite apply, + ConnectionGraph *conGraph); + /// Updates the graph by analyzing instruction \p I. /// Visited callees are added to \p BottomUpOrder until \p RecursionDepth /// reaches MaxRecursionDepth. @@ -1046,6 +1132,19 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return &FInfo->Graph; } + /// Return \p value's PointerKind. + PointerKind getPointerKind(ValueBase *value) const { + auto *f = value->getFunction(); + // The function can be null, e.g. if V is an undef. + if (!f) + return NoPointer; + + return findCachedPointerKind(value->getType(), *f); + } + /// Returns true if \p V may encapsulate a "pointer" value. + /// See EscapeAnalysis::NodeType::Value. + bool isPointer(ValueBase *V) const { return getPointerKind(V) > NoPointer; } + /// Returns true if the value \p V can escape to the function call \p FAS. /// This means that the called function may access the value \p V. /// If \p V has reference semantics, this function returns false if only the @@ -1063,14 +1162,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// to \p V. bool canEscapeToValue(SILValue V, SILValue To); - /// Returns true if the parameter with index \p ParamIdx can escape in the - /// called function of apply site \p FAS. - /// If it is an indirect parameter and \p checkContentOfIndirectParam is true - /// then the escape status is not checked for the address itself but for the - /// referenced pointer (if the referenced type is a pointer). - bool canParameterEscape(FullApplySite FAS, int ParamIdx, - bool checkContentOfIndirectParam); - /// Returns true if the pointers \p V1 and \p V2 can possibly point to the /// same memory. /// diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 99e3fae1dbf49..742d9e2172670 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -30,120 +30,91 @@ static llvm::cl::opt EnableInternalVerify( llvm::cl::desc("Enable internal verification of escape analysis"), llvm::cl::init(false)); -// Returns true if \p Ty recursively contains a reference. If \p mustBeRef is -// true, only return true if the type is guaranteed to hold a reference. If \p -// mustBeRef is false, only return false if the type is guaranteed not to hold a -// reference. -// -// If \p Ty is itself an address, return false. -static bool findRecursiveRefType(SILType Ty, const SILFunction &F, - bool mustBeRef) { - if (mustBeRef) { - // An address *may* be converted into a reference via something like - // raw_pointer_to_ref. However, addresses don't normally refer to the head - // of a reference counted object. - // - // The check for trivial types catches types that have AST "reference - // semantics", but are determined by type lowering to be trivial, such as - // noescape function types. - if (Ty.isAddress() || Ty.isTrivial(F)) - return false; - } +// Returns the kind of pointer that \p Ty recursively contains. +EscapeAnalysis::PointerKind +EscapeAnalysis::findRecursivePointerKind(SILType Ty, + const SILFunction &F) const { + // An address may be converted into a reference via something like + // raw_pointer_to_ref, but in general we don't know what kind of pointer it + // is. + if (Ty.isAddress()) + return EscapeAnalysis::AnyPointer; - if (!mustBeRef) { - // Opaque types may contain a reference. Speculatively track them too. - // - // 1. It may be possible to optimize opaque values based on known mutation - // points. - // - // 2. A specialized function may call a generic function passing a concrete - // reference type via incomplete specialization. - // - // 3. A generic function may call a specialized function taking a concrete - // reference type via devirtualization. - if (Ty.isAddressOnly(F)) - return true; + // Opaque types may contain a reference. Speculatively track them too. + // + // 1. It may be possible to optimize opaque values based on known mutation + // points. + // + // 2. A specialized function may call a generic function passing a concrete + // reference type via incomplete specialization. + // + // 3. A generic function may call a specialized function taking a concrete + // reference type via devirtualization. + if (Ty.isAddressOnly(F)) + return EscapeAnalysis::AnyPointer; - if (Ty.getASTType() == F.getModule().getASTContext().TheRawPointerType) - return true; - } + // A raw pointer definitely does not have a reference, but could point + // anywhere. We do track these because critical stdlib data structures often + // use raw pointers under the hood. + if (Ty.getASTType() == F.getModule().getASTContext().TheRawPointerType) + return EscapeAnalysis::AnyPointer; if (Ty.hasReferenceSemantics()) - return true; + return EscapeAnalysis::ReferenceOnly; - auto &Mod = F.getModule(); + auto &M = F.getModule(); + // Start with the most precise pointer kind + PointerKind aggregateKind = NoPointer; + auto meetAggregateKind = [&](PointerKind otherKind) { + if (otherKind > aggregateKind) + aggregateKind = otherKind; + }; if (auto *Str = Ty.getStructOrBoundGenericStruct()) { for (auto *Field : Str->getStoredProperties()) { - if (findRecursiveRefType( - Ty.getFieldType(Field, Mod, F.getTypeExpansionContext()), F, - mustBeRef)) - return true; + SILType fieldTy = Ty.getFieldType(Field, M, F.getTypeExpansionContext()); + meetAggregateKind(findCachedPointerKind(fieldTy, F)); } - return false; + return aggregateKind; } if (auto TT = Ty.getAs()) { for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) { - if (findRecursiveRefType(Ty.getTupleElementType(i), F, mustBeRef)) - return true; + meetAggregateKind(findCachedPointerKind(Ty.getTupleElementType(i), F)); } - return false; + return aggregateKind; } if (auto En = Ty.getEnumOrBoundGenericEnum()) { for (auto *ElemDecl : En->getAllElements()) { - if (ElemDecl->hasAssociatedValues() && - findRecursiveRefType( - Ty.getEnumElementType(ElemDecl, Mod, F.getTypeExpansionContext()), - F, mustBeRef)) - return true; + if (!ElemDecl->hasAssociatedValues()) + continue; + SILType eltTy = + Ty.getEnumElementType(ElemDecl, M, F.getTypeExpansionContext()); + meetAggregateKind(findCachedPointerKind(eltTy, F)); } - return false; + return aggregateKind; } - // FIXME: without a covered switch, this is not robust for mayContainReference - // in the event that new reference-holding AST types are invented. - return false; + // FIXME: without a covered switch, this is not robust in the event that new + // reference-holding AST types are invented. + return NoPointer; } -// Returns true if the type \p Ty is a reference or may transitively contain -// a reference. If \p Ty is itself an address, return false. -// -// An address may contain a reference because addresses can be cast into -// reference types. -static bool mayContainReference(SILType Ty, const SILFunction &F) { - if (Ty.isAddress()) - return true; - return findRecursiveRefType(Ty, F, false); -} +// Returns the kind of pointer that \p Ty recursively contains. +EscapeAnalysis::PointerKind +EscapeAnalysis::findCachedPointerKind(SILType Ty, const SILFunction &F) const { + auto iter = pointerKindCache.find(Ty); + if (iter != pointerKindCache.end()) + return iter->second; -// Returns true if the type \p Ty must be a reference or must transitively -// contain a reference. If \p Ty is itself an address, return false. -static bool mustContainReference(SILType Ty, const SILFunction &F) { - return findRecursiveRefType(Ty, F, true); -} - -bool EscapeAnalysis::isPointer(ValueBase *V) const { - auto *F = V->getFunction(); - - // The function can be null, e.g. if V is an undef. - if (!F) - return false; - - SILType Ty = V->getType(); - auto Iter = isPointerCache.find(Ty); - if (Iter != isPointerCache.end()) - return Iter->second; - - bool IP = mayContainReference(Ty, *F); - const_cast(this)->isPointerCache[Ty] = IP; - return IP; + PointerKind pointerKind = findRecursivePointerKind(Ty, F); + const_cast(this)->pointerKindCache[Ty] = pointerKind; + return pointerKind; } static bool isExtractOfArrayUninitializedPointer(TupleExtractInst *TEI) { - if (TEI->getFieldNo() == 1) { - if (auto apply = dyn_cast(TEI->getOperand())) - if (ArraySemanticsCall(apply, "array.uninitialized", false)) - return true; - } + if (auto apply = dyn_cast(TEI->getOperand())) + if (ArraySemanticsCall(apply, "array.uninitialized", false)) + return true; + return false; } @@ -362,15 +333,26 @@ EscapeAnalysis::CGNode::RepValue EscapeAnalysis::CGNode::getRepValue() const { depth}; } +void EscapeAnalysis::CGNode::mergeFlags(bool isInterior, + bool hasReferenceOnly) { + // isInterior is conservatively preserved from either node unless two content + // nodes are being merged and one is the interior node's content. + isInteriorFlag |= isInterior; + + // hasReferenceOnly is always conservatively merged. + hasReferenceOnlyFlag &= hasReferenceOnly; +} + void EscapeAnalysis::CGNode::mergeProperties(CGNode *fromNode) { - // TODO: Optimistically merge hasRC. 'this' node can only be merged with - // `fromNode` if their pointer values are compatible. If `fromNode->hasRC` is - // true, then it is guaranteed to represent the head of a heap object. Thus, - // it can only be merged with 'this' when the pointer values that access - // 'this' are also references. - // - // For now, this is pessimistic until we understand performance implications. - hasRC &= fromNode->hasRC; + // isInterior is conservatively preserved from either node unless the other + // node is the interior node's content. + bool isInterior = fromNode->isInteriorFlag; + if (fromNode == pointsTo) + this->isInteriorFlag = isInterior; + else if (this == fromNode->pointsTo) + isInterior = this->isInteriorFlag; + + mergeFlags(isInterior, fromNode->hasReferenceOnlyFlag); } template @@ -413,38 +395,68 @@ void EscapeAnalysis::ConnectionGraph::clear() { } EscapeAnalysis::CGNode * -EscapeAnalysis::ConnectionGraph::getNode(ValueBase *V, bool createIfNeeded) { +EscapeAnalysis::ConnectionGraph::getOrCreateNode(ValueBase *V, + PointerKind pointerKind) { + assert(pointerKind != EscapeAnalysis::NoPointer); + if (isa(V) || isa(V) || isa(V)) return nullptr; - // In the case of a struct or tuple extract, 'V' may not be a pointer - // even if it's pointer root is a pointer. Bail first because we only expect - // graph nodes for pointer values. - if (!EA->isPointer(V)) - return nullptr; - - // Look past address projections, pointer casts, and the like within the same - // object. Does not look past a dereference such as ref_element_addr, or - // project_box. - V = EA->getPointerRoot(V); - - if (!createIfNeeded) - return lookupNode(V); - CGNode * &Node = Values2Nodes[V]; + // Nodes mapped to values must have an indirect pointsTo. Nodes that don't + // have an indirect pointsTo are imaginary nodes that don't directly represnt + // a SIL value. + bool hasReferenceOnly = canOnlyContainReferences(pointerKind); if (!Node) { if (isa(V)) { - Node = allocNode(V, NodeType::Argument); + Node = allocNode(V, NodeType::Argument, false, hasReferenceOnly); if (!isSummaryGraph) Node->mergeEscapeState(EscapeState::Arguments); } else { - Node = allocNode(V, NodeType::Value); + Node = allocNode(V, NodeType::Value, false, hasReferenceOnly); } } return Node->getMergeTarget(); } +EscapeAnalysis::CGNode * +EscapeAnalysis::ConnectionGraph::getNode(ValueBase *V, bool createIfNeeded) { + PointerKind pointerKind = EA->getPointerKind(V); + if (pointerKind == EscapeAnalysis::NoPointer) + return nullptr; + + // Look past address projections, pointer casts, and the like within the same + // object. Does not look past a dereference such as ref_element_addr, or + // project_box. + V = EA->getPointerRoot(V); + + if (!createIfNeeded) + return lookupNode(V); + + return getOrCreateNode(V, pointerKind); +} + +/// Adds an argument/instruction in which the node's memory is released. +int EscapeAnalysis::ConnectionGraph::addUsePoint(CGNode *Node, + SILInstruction *User) { + // Use points are never consulted for escaping nodes, but still need to + // propagate to other nodes in a defer web. Even if this node is escaping, + // some defer predecessors may not be escaping. Only checking if this node has + // defer predecessors is insufficient because a defer successor of this node + // may have defer predecessors. + if (Node->getEscapeState() >= EscapeState::Global) + return -1; + + int Idx = (int)UsePoints.size(); + assert(UsePoints.count(User) == 0 && "value is already a use-point"); + UsePoints[User] = Idx; + UsePointTable.push_back(User); + assert(UsePoints.size() == UsePointTable.size()); + Node->setUsePointBit(Idx); + return Idx; +} + CGNode *EscapeAnalysis::ConnectionGraph::defer(CGNode *From, CGNode *To, bool &Changed) { if (!From->canAddDeferred(To)) @@ -864,15 +876,30 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { } while (Changed); } -CGNode *EscapeAnalysis::ConnectionGraph::createContentNode(CGNode *addrNode, - bool hasRC) { - CGNode *newContent = allocNode(nullptr, NodeType::Content, hasRC); +CGNode *EscapeAnalysis::ConnectionGraph::createContentNode( + CGNode *addrNode, bool isInterior, bool hasReferenceOnly) { + CGNode *newContent = + allocNode(nullptr, NodeType::Content, isInterior, hasReferenceOnly); initializePointsToEdge(addrNode, newContent); assert(ToMerge.empty() && "Initially setting pointsTo should not require any node merges"); return newContent; } +CGNode *EscapeAnalysis::ConnectionGraph::getOrCreateContentNode( + CGNode *addrNode, bool isInterior, bool hasReferenceOnly) { + if (CGNode *content = addrNode->getContentNodeOrNull()) { + content->mergeFlags(isInterior, hasReferenceOnly); + return content; + } + CGNode *content = createContentNode(addrNode, isInterior, hasReferenceOnly); + // getValueContent may be called after the graph is built and escape states + // are propagated. Keep the escape state and use points consistent here. + content->mergeEscapeState(addrNode->State); + content->mergeUsePoints(addrNode); + return content; +} + // Create a content node for merging based on an address node in the destination // graph and a content node in the source graph. CGNode * @@ -881,19 +908,103 @@ EscapeAnalysis::ConnectionGraph::createMergedContent(CGNode *destAddrNode, // destAddrNode may itself be a content node, so its value may be null. Since // we don't have the original pointer value, build a new content node based // on the source content. - return createContentNode(destAddrNode, srcContent->hasRC); + CGNode *mergedContent = createContentNode( + destAddrNode, srcContent->isInterior(), srcContent->hasReferenceOnly()); + return mergedContent; } -// Get a node representing the field data within the given reference-counted -// node. The caller has already determined that rcNode represents the head of a -// heap object rather than field content or the address of a local variable or -// argument. -CGNode *EscapeAnalysis::ConnectionGraph::getFieldContent(CGNode *rcNode) { - assert(rcNode->isContent()); - if (rcNode->pointsTo) - return rcNode->pointsTo; +CGNode * +EscapeAnalysis::ConnectionGraph::getOrCreateAddressContent(SILValue addrVal, + CGNode *addrNode) { + assert(addrVal->getType().isAddress()); - return createContentNode(rcNode, /*hasRC=*/false); + bool contentHasReferenceOnly = + EA->hasReferenceOnly(addrVal->getType().getObjectType(), *F); + // Address content always has an indirect pointsTo (only reference content can + // have a non-indirect pointsTo). + return getOrCreateContentNode(addrNode, false, contentHasReferenceOnly); +} + +// refVal is allowed to be invalid so we can model escaping content for +// secondary deinitializers of released objects. +CGNode * +EscapeAnalysis::ConnectionGraph::getOrCreateReferenceContent(SILValue refVal, + CGNode *refNode) { + // The object node points to internal fields. It neither has indirect pointsTo + // nor reference-only pointsTo. + CGNode *objNode = getOrCreateContentNode(refNode, true, false); + if (!objNode->isInterior()) + return objNode; + + bool contentHasReferenceOnly = false; + if (refVal) { + SILType refType = refVal->getType(); + if (auto *C = refType.getClassOrBoundGenericClass()) { + PointerKind aggregateKind = NoPointer; + for (auto *field : C->getStoredProperties()) { + SILType fieldType = refType.getFieldType(field, F->getModule(), + F->getTypeExpansionContext()); + PointerKind fieldKind = EA->findCachedPointerKind(fieldType, *F); + if (fieldKind > aggregateKind) + aggregateKind = fieldKind; + } + contentHasReferenceOnly = canOnlyContainReferences(aggregateKind); + } + } + getOrCreateContentNode(objNode, false, contentHasReferenceOnly); + return objNode; +} + +CGNode * +EscapeAnalysis::ConnectionGraph::getOrCreateUnknownContent(CGNode *addrNode) { + // We don't know if addrVal has been cast from a reference or raw + // pointer. More importantly, we don't know what memory contents it may + // point to. There's no need to consider it an "interior" node initially. If + // it's ever merged with another interior node (from ref_element_addr), then + // it will conservatively take on the interior flag at that time. + return getOrCreateContentNode(addrNode, false, false); +} + +// If ptrVal is itself mapped to a node, then this must return a non-null +// contentnode. Otherwise, setEscapesGlobal won't be able to represent escaping +// memory. +// +// This may be called after the graph is built and all escape states and use +// points are propagate. If a new content node is created, update its state +// on-the-fly. +EscapeAnalysis::CGNode * +EscapeAnalysis::ConnectionGraph::getValueContent(SILValue ptrVal) { + // Look past address projections, pointer casts, and the like within the same + // object. Does not look past a dereference such as ref_element_addr, or + // project_box. + SILValue ptrBase = EA->getPointerRoot(ptrVal); + + PointerKind pointerKind = EA->getPointerKind(ptrBase); + if (pointerKind == EscapeAnalysis::NoPointer) + return nullptr; + + CGNode *addrNode = getOrCreateNode(ptrBase, pointerKind); + if (!addrNode) + return nullptr; + + if (ptrBase->getType().isAddress()) + return getOrCreateAddressContent(ptrBase, addrNode); + + if (canOnlyContainReferences(pointerKind)) + return getOrCreateReferenceContent(ptrBase, addrNode); + + // The pointer value may contain raw pointers. + return getOrCreateUnknownContent(addrNode); +} + +CGNode *EscapeAnalysis::ConnectionGraph::getReturnNode() { + if (!ReturnNode) { + SILType resultTy = + F->mapTypeIntoContext(F->getConventions().getSILResultType()); + bool hasReferenceOnly = EA->hasReferenceOnly(resultTy, *F); + ReturnNode = allocNode(nullptr, NodeType::Return, false, hasReferenceOnly); + } + return ReturnNode; } bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, @@ -918,7 +1029,9 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, // global escaping state set. // Just set global escaping in the caller node and that's it. Changed |= DestNd->mergeEscapeState(EscapeState::Global); - continue; + // If DestNd is an interior node, its content still needs to be created. + if (!DestNd->isInterior()) + continue; } CGNode *SourcePT = SourceNd->pointsTo; @@ -1208,7 +1321,7 @@ std::string CGForDotView::getNodeAttributes(const Node *Node) const { switch (Orig->Type) { case EscapeAnalysis::NodeType::Content: attr = "style=\"rounded"; - if (Orig->hasRefCount()) { + if (Orig->isInterior()) { attr += ",filled"; } attr += "\""; @@ -1330,8 +1443,10 @@ void EscapeAnalysis::ConnectionGraph::dumpCG() const { void EscapeAnalysis::CGNode::dump() const { llvm::errs() << getTypeStr(); - if (hasRefCount()) - llvm::errs() << " [rc]"; + if (isInterior()) + llvm::errs() << " [int]"; + if (hasReferenceOnly()) + llvm::errs() << " [ref]"; auto rep = getRepValue(); if (rep.depth > 0) @@ -1391,16 +1506,6 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { }); }; - auto nodeStr = [&](CGNode *Nd) -> std::string { - std::string Str; - llvm::raw_string_ostream OS(Str); - if (Nd->hasRefCount()) - OS << "[rc] "; - Nd->getRepValue().print(OS, InstToIDMap); - OS.flush(); - return Str; - }; - llvm::SmallVector SortedNodes; for (CGNode *Nd : Nodes) { if (!Nd->isMerged) @@ -1409,7 +1514,13 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { sortNodes(SortedNodes); for (CGNode *Nd : SortedNodes) { - OS << " " << Nd->getTypeStr() << ' ' << nodeStr(Nd) << " Esc: "; + OS << " " << Nd->getTypeStr() << ' '; + if (Nd->isInterior()) + OS << "[int] "; + if (Nd->hasReferenceOnly()) + OS << "[ref] "; + Nd->getRepValue().print(OS, InstToIDMap); + OS << " Esc: "; switch (Nd->getEscapeState()) { case EscapeState::None: { const char *Separator = ""; @@ -1434,13 +1545,16 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { OS << ", Succ: "; const char *Separator = ""; if (CGNode *PT = Nd->getPointsToEdge()) { - OS << '(' << nodeStr(PT) << ')'; + OS << '('; + PT->getRepValue().print(OS, InstToIDMap); + OS << ')'; Separator = ", "; } llvm::SmallVector SortedDefers = Nd->defersTo; sortNodes(SortedDefers); for (CGNode *Def : SortedDefers) { - OS << Separator << nodeStr(Def); + OS << Separator; + Def->getRepValue().print(OS, InstToIDMap); Separator = ", "; } OS << '\n'; @@ -1476,11 +1590,14 @@ void EscapeAnalysis::ConnectionGraph::verify(bool allowMerge) const { // which consist of only defer-edges and a single trailing points-to edge // must lead to the same assert(Nd->matchPointToOfDefers(allowMerge)); - if (Nd->hasRefCount()) { - SILValue v = Nd->getRepValue().getValue(); - (void)v; - assert(!v || mayContainReference(v->getType(), *F)); + if (Nd->mappedValue && !(allowMerge && Nd->isMerged)) { + assert(Nd == Values2Nodes.lookup(Nd->mappedValue)); + assert(EA->isPointer(Nd->mappedValue)); + // Nodes must always be mapped from the pointer root value. + assert(Nd->mappedValue == EA->getPointerRoot(Nd->mappedValue)); } + if (Nd->isInterior() && !Nd->isMerged) + assert(Nd->pointsTo && "Interior content node requires a pointsTo node"); } #endif } @@ -1488,9 +1605,6 @@ void EscapeAnalysis::ConnectionGraph::verify(bool allowMerge) const { void EscapeAnalysis::ConnectionGraph::verifyStructure(bool allowMerge) const { #ifndef NDEBUG for (CGNode *Nd : Nodes) { - if (Nd->mappedValue && !(allowMerge && Nd->mergeTo)) - assert(Nd == Values2Nodes.lookup(Nd->mappedValue)); - if (Nd->isMerged) { assert(Nd->mergeTo); assert(!Nd->pointsTo); @@ -1548,45 +1662,6 @@ static bool linkBBArgs(SILBasicBlock *BB) { return true; } -EscapeAnalysis::CGNode * -EscapeAnalysis::getValueContent(ConnectionGraph *conGraph, SILValue addrVal) { - CGNode *addrNode = conGraph->getNode(addrVal); - if (!addrNode) - return nullptr; - - if (CGNode *content = addrNode->getPointsToEdge()) - return content; - -#ifndef NDEBUG - if (!addrNode->isContent()) { - if (SILValue addrNodeValue = addrNode->getRepValue().getValue()) { - assert(isPointer(addrNodeValue)); - assert(addrNodeValue == getPointerRoot(addrVal)); - } - } -#endif - SILValue baseAddr = getPointerRoot(addrVal); - auto *F = addrVal->getFunction(); - auto hasRC = [&](){ - return mustContainReference(baseAddr->getType(), *F) - || mustContainReference(addrVal->getType(), *F); - }; - // Have we already merged a content node for this address? - if (CGNode *content = addrNode->getContentNodeOrNull()) { - // TODO: Optimistically merge hasRC content. The original content might not - // have an RC if one of the values pointing to this content was cast to an - // unknown type. If any of the types must contain a reference, then the - // content should contain a reference. - // - // For now, conservatively merge the RC flag instead. - if (content->hasRefCount() && !hasRC()) - content->setRefCount(false); - - return content; - } - return conGraph->createContentNode(addrNode, hasRC()); -} - void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth) { @@ -1632,7 +1707,7 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, if (!BBArg->getSingleTerminatorOperands(Incoming)) { // We don't know where the block argument comes from -> treat it // conservatively. - setEscapesGlobal(ConGraph, BBArg); + ConGraph->setEscapesGlobal(BBArg); continue; } CGNode *ArgNode = ConGraph->getNode(BBArg); @@ -1644,7 +1719,7 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, if (SrcArg) { ArgNode = ConGraph->defer(ArgNode, SrcArg); } else { - setEscapesGlobal(ConGraph, BBArg); + ConGraph->setEscapesGlobal(BBArg); break; } } @@ -1654,13 +1729,26 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, << FInfo->Graph.F->getName() << '\n'); } -/// Returns true if all uses of \p I are tuple_extract instructions. -static bool onlyUsedInTupleExtract(SILValue V) { +/// Returns the tuple extract for the first two fields if all uses of \p I are +/// tuple_extract instructions. +static std::pair +onlyUsedInTupleExtract(SILValue V) { + TupleExtractInst *field0 = nullptr; + TupleExtractInst *field1 = nullptr; for (Operand *Use : getNonDebugUses(V)) { - if (!isa(Use->getUser())) - return false; + if (auto *TEI = dyn_cast(Use->getUser())) { + if (TEI->getFieldNo() == 0) { + field0 = TEI; + continue; + } + if (TEI->getFieldNo() == 1) { + field1 = TEI; + continue; + } + } + return std::make_pair(nullptr, nullptr); } - return true; + return std::make_pair(field0, field1); } bool EscapeAnalysis::buildConnectionGraphForCallees( @@ -1719,6 +1807,40 @@ bool EscapeAnalysis::buildConnectionGraphForDestructor( RecursionDepth); } +// Handle array.uninitialized +bool EscapeAnalysis::createArrayUninitializedSubgraph( + FullApplySite apply, ConnectionGraph *conGraph) { + + // Check if the result is used in the usual way: extracting the + // array and the element pointer with tuple_extract. + TupleExtractInst *arrayStruct; + TupleExtractInst *arrayElementPtr; + std::tie(arrayStruct, arrayElementPtr) = + onlyUsedInTupleExtract(cast(apply.getInstruction())); + if (!arrayStruct || !arrayElementPtr) + return false; + + // array.uninitialized may have a first argument which is the + // allocated array buffer. The call is like a struct(buffer) + // instruction. + CGNode *arrayRefNode = conGraph->getNode(apply.getArgument(0)); + if (!arrayRefNode) + return false; + + CGNode *arrayStructNode = conGraph->getNode(arrayStruct); + assert(arrayStructNode && "Array struct must have a node"); + + CGNode *arrayObjNode = conGraph->getValueContent(apply.getArgument(0)); + + // The reference argument is effectively stored inside the returned + // array struct. + conGraph->defer(arrayStructNode, arrayRefNode); + + // Map the returned element pointer to the array object's field pointer. + conGraph->setNode(arrayElementPtr, arrayObjNode); + return true; +} + void EscapeAnalysis::analyzeInstruction(SILInstruction *I, FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, @@ -1739,74 +1861,71 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // These array semantics calls do not capture anything. return; case ArrayCallKind::kArrayUninitialized: - // Check if the result is used in the usual way: extracting the - // array and the element pointer with tuple_extract. - if (onlyUsedInTupleExtract(ASC.getCallResult())) { - // array.uninitialized may have a first argument which is the - // allocated array buffer. The call is like a struct(buffer) - // instruction. - if (CGNode *BufferNode = ConGraph->getNode(FAS.getArgument(0))) { - SILValue ArrayBase = ASC.getCallResult(); - CGNode *ArrayContent = getValueContent(ConGraph, ArrayBase); - assert(ArrayContent && "Array base must have a node"); - ConGraph->defer(ArrayContent, BufferNode); - } + if (createArrayUninitializedSubgraph(FAS, ConGraph)) return; - } break; case ArrayCallKind::kGetElement: - if (CGNode *ArrayRefNode = getValueContent(ConGraph, ASC.getSelf())) { + if (CGNode *ArrayObjNode = ConGraph->getValueContent(ASC.getSelf())) { CGNode *LoadedElement = nullptr; // This is like a load from a ref_element_addr. if (ASC.hasGetElementDirectResult()) { LoadedElement = ConGraph->getNode(ASC.getCallResult()); } else { // The content of the destination address. - LoadedElement = getValueContent(ConGraph, FAS.getArgument(0)); + LoadedElement = ConGraph->getValueContent(FAS.getArgument(0)); assert(LoadedElement && "indirect result must have node"); } if (LoadedElement) { - CGNode *ArrayElementStorage = - ConGraph->getFieldContent(ArrayRefNode); - ConGraph->defer(LoadedElement, ArrayElementStorage); - return; + if (CGNode *arrayElementStorage = + ConGraph->getFieldContent(ArrayObjNode)) { + ConGraph->defer(LoadedElement, arrayElementStorage); + return; + } } } break; case ArrayCallKind::kGetElementAddress: - // This is like a ref_element_addr. - if (CGNode *ArrayRefNode = getValueContent(ConGraph, ASC.getSelf())) { - ConGraph->defer(ConGraph->getNode(ASC.getCallResult()), ArrayRefNode); + // This is like a ref_element_addr. Both the object node and the + // returned address point to the same element storage. + if (CGNode *ArrayObjNode = ConGraph->getValueContent(ASC.getSelf())) { + CGNode *arrayElementAddress = ConGraph->getNode(ASC.getCallResult()); + ConGraph->defer(arrayElementAddress, ArrayObjNode); + return; } - return; + break; case ArrayCallKind::kWithUnsafeMutableBufferPointer: // Model this like an escape of the elements of the array and a capture // of anything captured by the closure. // Self is passed inout. - if (CGNode *ArrayStructValue = - getValueContent(ConGraph, ASC.getSelf())) { + if (CGNode *ArrayStructNode = + ConGraph->getValueContent(ASC.getSelf())) { + // The first non indirect result is the closure. + auto Args = FAS.getArgumentsWithoutIndirectResults(); + ConGraph->setEscapesGlobal(Args[0]); // One content node for going from the array buffer pointer to // the element address (like ref_element_addr). - CGNode *ArrayRefNode = ArrayStructValue->getContentNodeOrNull(); - // TODO: If ArrayRefNode already exists, optimistically do - // ArrayRefNode->setRefCount(true). - if (!ArrayRefNode) { - ArrayRefNode = ConGraph->createContentNode( - ArrayStructValue, /*hasRC=*/true); + CGNode *ArrayObjNode = + ConGraph->getOrCreateContentNode(ArrayStructNode, + /*isInterior*/ true, + /*hasRefOnly*/ false); + // If ArrayObjNode was already potentially merged with its pointsTo, + // then conservatively mark the whole thing as escaping. + if (!ArrayObjNode->isInterior()) { + ArrayObjNode->markEscaping(); + return; } - // Another content node for the element storage. - CGNode *ArrayElementStorage = ConGraph->getFieldContent(ArrayRefNode); + // Otherwise, create the content node for the element storage. + CGNode *ArrayElementStorage = ConGraph->getOrCreateContentNode( + ArrayObjNode, /*isInterior*/ false, + /*hasRefOnly*/ true); ArrayElementStorage->markEscaping(); - // The first non indirect result is the closure. - auto Args = FAS.getArgumentsWithoutIndirectResults(); - setEscapesGlobal(ConGraph, Args[0]); return; } break; default: break; - } + } if (FAS.getReferencedFunctionOrNull() && FAS.getReferencedFunctionOrNull()->hasSemanticsAttr( @@ -1819,7 +1938,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // from the pointer. auto Args = FAS.getArgumentsWithoutIndirectResults(); // The first not indirect result argument is the closure. - setEscapesGlobal(ConGraph, Args[0]); + ConGraph->setEscapesGlobal(Args[0]); return; } @@ -1834,7 +1953,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // from the pointer. auto Args = FAS.getArgumentsWithoutIndirectResults(); // The second not indirect result argument is the closure. - setEscapesGlobal(ConGraph, Args[1]); + ConGraph->setEscapesGlobal(Args[1]); return; } @@ -1916,86 +2035,117 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::StrongReleaseInst: case SILInstructionKind::ReleaseValueInst: { // A release instruction may deallocate the pointer operand. This may - // capture anything pointed to by the released object, but not the pointer - // to the object itself (because it will be a dangling pointer after - // deallocation). + // capture anything pointed to by the released object, but not the object + // itself (because it will be a dangling pointer after deallocation). SILValue OpV = I->getOperand(0); - CGNode *rcContent = getValueContent(ConGraph, OpV); - if (!rcContent) + CGNode *objNode = ConGraph->getValueContent(OpV); + if (!objNode) return; - // rcContent->hasRefCount() may or may not be true depending on whether - // the type could be analyzed. Either way, treat it structurally like a - // refcounted object. - CGNode *fieldContent = ConGraph->getFieldContent(rcContent); + CGNode *fieldNode = ConGraph->getFieldContent(objNode); + if (!fieldNode) { + // In the unexpected case that the object has no field content, create + // escaping unknown content. + ConGraph->getOrCreateUnknownContent(objNode)->markEscaping(); + return; + } if (!deinitIsKnownToNotCapture(OpV)) { - fieldContent->markEscaping(); + ConGraph->getOrCreateUnknownContent(fieldNode)->markEscaping(); return; } - // This deinit is known to not directly capture it's own field content, - // however, indirect deinitializers could still capture anything pointed - // to by those fields. - ConGraph->escapeContentsOf(fieldContent); + // This deinit is known to not directly capture it's own field content; + // however, other secondary deinitializers could still capture anything + // pointed to by references within those fields. Since secondary + // deinitializers only apply to reference-type fields, not pointer-type + // fields, the "field" content can initially be considered an indirect + // reference. Unfortunately, we can't know all possible reference types + // that may eventually be associated with 'fieldContent', so we must + // assume here that 'fieldContent2' could hold raw pointers. This is + // implied by passing in invalid SILValue. + CGNode *objNode2 = + ConGraph->getOrCreateReferenceContent(SILValue(), fieldNode); + CGNode *fieldNode2 = objNode2->getContentNodeOrNull(); + ConGraph->getOrCreateUnknownContent(fieldNode2)->markEscaping(); + return; + } + case SILInstructionKind::DestroyAddrInst: { + SILValue addressVal = I->getOperand(0); + CGNode *valueNode = ConGraph->getValueContent(addressVal); + if (!valueNode) + return; + + // The value's destructor may escape anything the value points to. + // This could be an object referenced by the value or the contents of an + // existential box. + if (CGNode *fieldNode = ConGraph->getFieldContent(valueNode)) { + ConGraph->getOrCreateUnknownContent(fieldNode)->markEscaping(); + return; + } + ConGraph->getOrCreateUnknownContent(valueNode)->markEscaping(); return; } #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Load##Name##Inst: #include "swift/AST/ReferenceStorage.def" - case SILInstructionKind::LoadInst: + case SILInstructionKind::LoadInst: { assert(!cast(I)->getType().isAddress()); - LLVM_FALLTHROUGH; - case SILInstructionKind::RefElementAddrInst: - case SILInstructionKind::RefTailAddrInst: - case SILInstructionKind::ProjectBoxInst: - case SILInstructionKind::InitExistentialAddrInst: - case SILInstructionKind::OpenExistentialAddrInst: { - // Loads and projections into RC objects have a similar pattern: - // - // For RC object projections, get the non-address reference operand and - // return an RC content node that the reference directly points to. It is - // as-if the RC content node holds the pointer to the object fields. - // // For loads, get the address-type operand and return the content node // that the address directly points to. The load's address may itself come // from a ref_element_addr, project_box or open_existential, in which - // case, the loaded content will be the field content, not the RC content. + // case, the loaded content will be the field content, not the RC + // content. auto SVI = cast(I); if (!isPointer(SVI)) return; - SILValue pointerVal = SVI->getOperand(0); - if (CGNode *PointsTo = getValueContent(ConGraph, pointerVal)) { + if (CGNode *PointsTo = ConGraph->getValueContent(SVI->getOperand(0))) { + ConGraph->setNode(SVI, PointsTo); + return; + } + // A load from an address we don't handle -> be conservative. + ConGraph->setEscapesGlobal(SVI); + break; + } + case SILInstructionKind::RefElementAddrInst: + case SILInstructionKind::RefTailAddrInst: + case SILInstructionKind::ProjectBoxInst: + case SILInstructionKind::InitExistentialAddrInst: + case SILInstructionKind::OpenExistentialAddrInst: { + // For projections into objects, get the non-address reference operand and + // return an interior content node that the reference points to. + auto SVI = cast(I); + if (CGNode *PointsTo = ConGraph->getValueContent(SVI->getOperand(0))) { ConGraph->setNode(SVI, PointsTo); return; } // A load or projection from an address we don't handle -> be // conservative. - setEscapesGlobal(ConGraph, SVI); + ConGraph->setEscapesGlobal(SVI); return; } case SILInstructionKind::CopyAddrInst: { // Be conservative if the dest may be the final release. if (!cast(I)->isInitializationOfDest()) { setAllEscaping(I, ConGraph); - break; + return; } // A copy_addr is like a 'store (load src) to dest'. SILValue srcAddr = I->getOperand(CopyAddrInst::Src); - CGNode *loadedContent = getValueContent(ConGraph, srcAddr); + CGNode *loadedContent = ConGraph->getValueContent(srcAddr); if (!loadedContent) { setAllEscaping(I, ConGraph); break; } SILValue destAddr = I->getOperand(CopyAddrInst::Dest); // Create a defer-edge from the store location to the loaded content. - if (CGNode *destContent = getValueContent(ConGraph, destAddr)) { + if (CGNode *destContent = ConGraph->getValueContent(destAddr)) { ConGraph->defer(destContent, loadedContent); return; } // A store to an address we don't handle -> be conservative. - setEscapesGlobal(ConGraph, srcAddr); + ConGraph->setEscapesGlobal(srcAddr); return; } @@ -2016,13 +2166,13 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // (via ref_element_addr, project_box, or open_existential_addr) where the // stored field content is chained one level below the RC content. SILValue destAddr = I->getOperand(StoreInst::Dest); - if (CGNode *pointsTo = getValueContent(ConGraph, destAddr)) { + if (CGNode *pointsTo = ConGraph->getValueContent(destAddr)) { // Create a defer-edge from the content to the stored value. ConGraph->defer(pointsTo, valueNode); return; } // A store to an address we don't handle -> be conservative. - setEscapesGlobal(ConGraph, srcVal); + ConGraph->setEscapesGlobal(srcVal); return; } case SILInstructionKind::PartialApplyInst: { @@ -2062,15 +2212,14 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::TupleExtractInst: { // This is a tuple_extract which extracts the second result of an // array.uninitialized call (otherwise getPointerBase should have already - // looked through it). The first result is the array itself. - // The second result (which is a pointer to the array elements) must be - // the content node of the first result. It's just like a ref_element_addr - // instruction. + // looked through it). The first result is the array itself. The second + // result (which is a pointer to the array elements) must be the content + // node of the first result. It's just like a ref_element_addr + // instruction. It is mapped to a node when processing + // array.uninitialized. auto *TEI = cast(I); assert(isExtractOfArrayUninitializedPointer(TEI) && "tuple_extract should be handled as projection"); - if (CGNode *ArrayElements = getValueContent(ConGraph, TEI->getOperand())) - ConGraph->setNode(TEI, ArrayElements); return; } case SILInstructionKind::UncheckedRefCastAddrInst: { @@ -2081,12 +2230,15 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, ConGraph->defer(DestNode, SrcNode); return; } - case SILInstructionKind::ReturnInst: - if (CGNode *ValueNd = - ConGraph->getNode(cast(I)->getOperand())) { + case SILInstructionKind::ReturnInst: { + SILValue returnVal = cast(I)->getOperand(); + if (CGNode *ValueNd = ConGraph->getNode(returnVal)) { ConGraph->defer(ConGraph->getReturnNode(), ValueNd); + ConGraph->getValueContent(returnVal)->mergeEscapeState( + EscapeState::Return); } return; + } default: // We handle all other instructions conservatively. setAllEscaping(I, ConGraph); @@ -2147,8 +2299,8 @@ bool EscapeAnalysis::deinitIsKnownToNotCapture(SILValue V) { void EscapeAnalysis::setAllEscaping(SILInstruction *I, ConnectionGraph *ConGraph) { if (auto *TAI = dyn_cast(I)) { - setEscapesGlobal(ConGraph, TAI->getNormalBB()->getArgument(0)); - setEscapesGlobal(ConGraph, TAI->getErrorBB()->getArgument(0)); + ConGraph->setEscapesGlobal(TAI->getNormalBB()->getArgument(0)); + ConGraph->setEscapesGlobal(TAI->getErrorBB()->getArgument(0)); } // Even if the instruction does not write memory we conservatively set all // operands to escaping, because they may "escape" to the result value in @@ -2157,12 +2309,12 @@ void EscapeAnalysis::setAllEscaping(SILInstruction *I, for (const Operand &Op : I->getAllOperands()) { SILValue OpVal = Op.get(); if (!isNonWritableMemoryAddress(OpVal)) - setEscapesGlobal(ConGraph, OpVal); + ConGraph->setEscapesGlobal(OpVal); } // Even if the instruction does not write memory it could e.g. return the // address of global memory. Therefore we have to define it as escaping. for (auto result : I->getResults()) - setEscapesGlobal(ConGraph, result); + ConGraph->setEscapesGlobal(result); } void EscapeAnalysis::recompute(FunctionInfo *Initial) { @@ -2483,35 +2635,6 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { return true; } -bool EscapeAnalysis::canParameterEscape(FullApplySite FAS, int ParamIdx, - bool checkContentOfIndirectParam) { - CalleeList Callees = BCA->getCalleeList(FAS); - if (!Callees.allCalleesVisible()) - return true; - - // Derive the connection graph of the apply from the known callees. - for (SILFunction *Callee : Callees) { - FunctionInfo *FInfo = getFunctionInfo(Callee); - if (!FInfo->isValid()) - recompute(FInfo); - - CGNode *Node = - FInfo->SummaryGraph.getNodeOrNull(Callee->getArgument(ParamIdx)); - if (!Node) - return true; - - if (checkContentOfIndirectParam) { - Node = Node->getContentNodeOrNull(); - if (!Node) - continue; - } - - if (Node->escapes()) - return true; - } - return false; -} - void EscapeAnalysis::invalidate() { Function2Info.clear(); Allocator.DestroyAll(); From 5a27e5d80256c59a979579a12011fa736fb5dd48 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 16 Dec 2019 16:10:57 -0800 Subject: [PATCH 055/478] EscapeAnalysis: Make EscapeState and UsePoints a property of the content node only. For alias analysis query to be generally correct, we need to effectively merge the escape state and use points for everything in a defer web. It was unclear from the current design whether the "escaping" property applied to the pointer value or its content. The implementation is inconsistent in how it was treated. It appears that some bugs have been worked around by propagating forward through defer edges, some have been worked around by querying the content instead of the pointer, and others have been worked around be creating fake use points at block arguments. If we always simply query the content for escape state and use points, then we never need to propagate along defer edges. The current code that propagates escape state along defer edges in one direction is simply incorrect from the perspective of alias analysis. One very attractive solution is to merge nodes eagerly without creating any defer edges, but that would be a much more radical change even than what I've done here. It would also pose some new issues: how to resolve the current "node types" when merging and how to deal with missing content nodes. This solution of applying escape state to content nodes solves all these problems without too radical of a change at the expense of eagerly creating content nodes. (The potential graph memory usage is not really an issue because it's possible to drastically shrink the size of the graph anyway in a future commit--I've been able to fit a node within one cache line). This solution nicely preserves graph structure which makes it easy to debug and relate to the IR. Eagerly creating content nodes also solves the missing content node problem. For example, when querying canEscapeTo, we need to know whether to look at the escape state for just the pointer value itself, or also for its content. It may be possible the its content node is actually part of the same object at the IR level. If the content node is missing, then we don't know if the object's interior address is not recognizable/representable or whether we simply never saw an access to the interior address. We can't simply look at whether the current IR value happens to be a reference, because that doesn't tell us whether the graph node may have been merged with a non-reference node or even with it's own content node. To be correct in general, this query would need to be extremely conservative. However, if content nodes are always created for references, then we only need to query the escape state of a pointer's content node. The content node's flag tells us if it's an interior node, in which case it will always point to another content node which also needs to be queried. --- .../SILOptimizer/Analysis/EscapeAnalysis.h | 39 ++++------- lib/SILOptimizer/Analysis/AliasAnalysis.cpp | 4 +- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 64 +++++++++---------- .../Transforms/StackPromotion.cpp | 20 ++---- .../UtilityPasses/EscapeAnalysisDumper.cpp | 44 +++++++++++++ 5 files changed, 93 insertions(+), 78 deletions(-) diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index 0c68b94f9984e..d6ee88c0a36a8 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -641,10 +641,10 @@ class EscapeAnalysis : public BottomUpIPAnalysis { CGNode *ReturnNode = nullptr; /// The list of use points. - llvm::SmallVector UsePointTable; + llvm::SmallVector UsePointTable; /// Mapping of use points to bit indices in CGNode::UsePoints. - llvm::DenseMap UsePoints; + llvm::DenseMap UsePoints; /// The allocator for nodes. llvm::SpecificBumpPtrAllocator NodeAllocator; @@ -798,19 +798,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { Node->mappedValue = V; } - /// Adds an argument/instruction in which the node's value is used. - int addUsePoint(CGNode *Node, SILNode *User) { - if (Node->getEscapeState() >= EscapeState::Global) - return -1; - - User = User->getRepresentativeSILNodeInObject(); - int Idx = (int)UsePoints.size(); - assert(UsePoints.count(User) == 0 && "value is already a use-point"); - UsePoints[User] = Idx; - UsePointTable.push_back(User); - assert(UsePoints.size() == UsePointTable.size()); - Node->setUsePointBit(Idx); - return Idx; /// If \p pointer is a pointer, set it's content to global escaping. /// /// Only mark the content node as escaping. Marking a pointer node as @@ -828,13 +815,8 @@ class EscapeAnalysis : public BottomUpIPAnalysis { content->markEscaping(); } - void escapeContentsOf(CGNode *Node) { - CGNode *escapedContent = Node->getContentNodeOrNull(); - if (!escapedContent) { - escapedContent = createContentNode(Node, /*hasRC=*/false); - } - escapedContent->markEscaping(); - } + /// Adds an argument/instruction in which the node's value is used. + int addUsePoint(CGNode *Node, SILInstruction *User); /// Creates a defer-edge between \p From and \p To. /// This may trigger node merges to keep the graph invariance 4). @@ -920,11 +902,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// (indirectly) somehow refer to the Node's value. /// Use-points are only values which are relevant for lifeness computation, /// e.g. release or apply instructions. - bool isUsePoint(SILNode *UsePoint, CGNode *Node); + bool isUsePoint(SILInstruction *UsePoint, CGNode *Node); /// Returns all use points of \p Node in \p UsePoints. void getUsePoints(CGNode *Node, - llvm::SmallVectorImpl &UsePoints); + llvm::SmallVectorImpl &UsePoints); /// Computes the use point information. void computeUsePoints(); @@ -1108,10 +1090,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis { bool mergeSummaryGraph(ConnectionGraph *SummaryGraph, ConnectionGraph *Graph); - /// Returns true if the value \p V can escape to the \p UsePoint, where - /// \p UsePoint is either a release-instruction or a function call. - bool canEscapeToUsePoint(SILValue V, SILNode *UsePoint, - ConnectionGraph *ConGraph); + /// Returns true if the value \p value or any address within that value can + /// escape to the \p usePoint, where \p usePoint is either a + /// release-instruction or a function call. + bool canEscapeToUsePoint(SILValue value, SILInstruction *usePoint, + ConnectionGraph *conGraph); friend struct ::CGForDotView; diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index 90e304d0a5084..a6ab4c4ea36a5 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -737,8 +737,8 @@ bool AliasAnalysis::mayValueReleaseInterfereWithInstruction(SILInstruction *User // The most important check: does the object escape the current function? auto LO = getUnderlyingObject(V); auto *ConGraph = EA->getConnectionGraph(User->getFunction()); - auto *Node = ConGraph->getNodeOrNull(LO); - if (Node && !Node->escapes()) + auto *Content = ConGraph->getValueContent(LO); + if (Content && !Content->escapes()) return false; // This is either a non-local allocation or a scoped allocation that escapes. diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 742d9e2172670..aaeb89430311e 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -804,10 +804,14 @@ void EscapeAnalysis::ConnectionGraph::propagateEscapeStates() { Changed = false; for (CGNode *Node : Nodes) { - // Propagate the state to all successor nodes. + // Propagate the state to all pointsTo nodes. It would be sufficient to + // only follow proper pointsTo edges, since this loop also follows defer + // edges, but this may converge faster. if (Node->pointsTo) { Changed |= Node->pointsTo->mergeEscapeState(Node->State); } + // Note: Propagating along defer edges may be interesting from an SSA + // standpoint, but it is entirely irrelevant alias analysis. for (CGNode *Def : Node->defersTo) { Changed |= Def->mergeEscapeState(Node->State); } @@ -845,14 +849,13 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { /// liferange. And that must be a releasing instruction. int ValueIdx = -1; for (const Operand &Op : I.getAllOperands()) { - ValueBase *OpV = Op.get(); - if (CGNode *OpNd = lookupNode(EA->getPointerRoot(OpV))) { - if (ValueIdx < 0) { - ValueIdx = addUsePoint(OpNd, &I); - } else { - OpNd->setUsePointBit(ValueIdx); - } - } + CGNode *content = getValueContent(Op.get()); + if (!content) + continue; + if (ValueIdx < 0) + ValueIdx = addUsePoint(content, &I); + else + content->setUsePointBit(ValueIdx); } break; } @@ -867,11 +870,10 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { do { Changed = false; for (CGNode *Node : Nodes) { - // Propagate the bits to all successor nodes. - Node->visitSuccessors([&Changed, Node](CGNode *succ) { - Changed |= succ->mergeUsePoints(Node); - return true; - }); + // Propagate the bits to pointsTo. A release of a node may also release + // any content pointed to be the node. + if (Node->pointsTo) + Changed |= Node->pointsTo->mergeUsePoints(Node); } } while (Changed); } @@ -1097,11 +1099,10 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, /// somehow refer to the Node's value. /// Use-points are only values which are relevant for lifeness computation, /// e.g. release or apply instructions. -bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILNode *UsePoint, +bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILInstruction *UsePoint, CGNode *Node) { assert(Node->getEscapeState() < EscapeState::Global && "Use points are only valid for non-escaping nodes"); - UsePoint = UsePoint->getRepresentativeSILNodeInObject(); auto Iter = UsePoints.find(UsePoint); if (Iter == UsePoints.end()) return false; @@ -1111,8 +1112,8 @@ bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILNode *UsePoint, return Node->UsePoints.test(Idx); } -void EscapeAnalysis::ConnectionGraph:: -getUsePoints(CGNode *Node, llvm::SmallVectorImpl &UsePoints) { +void EscapeAnalysis::ConnectionGraph::getUsePoints( + CGNode *Node, llvm::SmallVectorImpl &UsePoints) { assert(Node->getEscapeState() < EscapeState::Global && "Use points are only valid for non-escaping nodes"); for (int Idx = Node->UsePoints.find_first(); Idx >= 0; @@ -2571,13 +2572,13 @@ bool EscapeAnalysis::canEscapeToValue(SILValue V, SILValue To) { return true; auto *ConGraph = getConnectionGraph(F); - CGNode *Node = ConGraph->getNodeOrNull(V); - if (!Node) + CGNode *valueContent = ConGraph->getValueContent(V); + if (!valueContent) return true; - CGNode *ToNode = ConGraph->getNodeOrNull(To); - if (!ToNode) + CGNode *userContent = ConGraph->getValueContent(To); + if (!userContent) return true; - return ConGraph->mayReach(ToNode, Node); + return ConGraph->mayReach(userContent, valueContent); } bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { @@ -2592,27 +2593,26 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { return true; auto *ConGraph = getConnectionGraph(F); - CGNode *Node1 = ConGraph->getNodeOrNull(V1); - if (!Node1) + CGNode *Content1 = ConGraph->getValueContent(V1); + if (!Content1) return true; - CGNode *Node2 = ConGraph->getNodeOrNull(V2); - if (!Node2) + + CGNode *Content2 = ConGraph->getValueContent(V2); + if (!Content2) return true; // Finish the check for one value being a non-escaping local object. - if (isUniq1 && Node1->valueEscapesInsideFunction(V1)) + if (isUniq1 && Content1->valueEscapesInsideFunction(V1)) isUniq1 = false; - if (isUniq2 && Node2->valueEscapesInsideFunction(V2)) + if (isUniq2 && Content2->valueEscapesInsideFunction(V2)) isUniq2 = false; if (!isUniq1 && !isUniq2) return true; // Check if both nodes may point to the same content. - CGNode *Content1 = getValueContent(ConGraph, V1); - CGNode *Content2 = getValueContent(ConGraph, V2); - + // FIXME!!!: This will be rewritten to use node flags in the next commit. SILType T1 = V1->getType(); SILType T2 = V2->getType(); if (T1.isAddress() && T2.isAddress()) { diff --git a/lib/SILOptimizer/Transforms/StackPromotion.cpp b/lib/SILOptimizer/Transforms/StackPromotion.cpp index 9c769f5197da9..a7579edc39094 100644 --- a/lib/SILOptimizer/Transforms/StackPromotion.cpp +++ b/lib/SILOptimizer/Transforms/StackPromotion.cpp @@ -108,32 +108,20 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, return false; auto *ConGraph = EA->getConnectionGraph(ARI->getFunction()); - auto *Node = ConGraph->getNodeOrNull(ARI); - if (!Node) + auto *contentNode = ConGraph->getValueContent(ARI); + if (!contentNode) return false; // The most important check: does the object escape the current function? - if (Node->escapes()) + if (contentNode->escapes()) return false; LLVM_DEBUG(llvm::dbgs() << "Promote " << *ARI); // Collect all use-points of the allocation. These are refcount instructions // and apply instructions. - llvm::SmallVector BaseUsePoints; llvm::SmallVector UsePoints; - ConGraph->getUsePoints(Node, BaseUsePoints); - for (SILNode *UsePoint : BaseUsePoints) { - if (SILInstruction *I = dyn_cast(UsePoint)) { - UsePoints.push_back(I); - } else { - // Also block arguments can be use points. - SILBasicBlock *UseBB = cast(UsePoint)->getParent(); - // For simplicity we just add the first instruction of the block as use - // point. - UsePoints.push_back(&UseBB->front()); - } - } + ConGraph->getUsePoints(contentNode, UsePoints); ValueLifetimeAnalysis VLA(ARI, UsePoints); // Check if there is a use point before the allocation (this can happen e.g. diff --git a/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp b/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp index 3d1cf9331b697..da470ec37d705 100644 --- a/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp @@ -25,6 +25,23 @@ llvm::cl::opt EnableGraphWriter( namespace { +static bool gatherValues(EscapeAnalysis *EA, SILFunction &Fn, + std::vector &Values) { + for (auto &BB : Fn) { + for (auto *Arg : BB.getArguments()) { + if (EA->isPointer(Arg)) + Values.push_back(SILValue(Arg)); + } + for (auto &II : BB) { + for (auto result : II.getResults()) { + if (EA->isPointer(result)) + Values.push_back(result); + } + } + } + return Values.size() > 1; +} + /// Dumps the escape information of all functions in the module. /// Only dumps if the compiler is built with assertions. /// For details see EscapeAnalysis. @@ -43,6 +60,33 @@ class EscapeAnalysisDumper : public SILModuleTransform { ConnectionGraph->print(llvm::outs()); if (EnableGraphWriter) ConnectionGraph->dumpCG(); + + // Gather up all Values in Fn. + std::vector Values; + if (!gatherValues(EA, F, Values)) + continue; + + // Emit the N^2 escape analysis evaluation of the values. + for (auto &bb : F) { + for (auto &ii : bb) { + if (auto fas = FullApplySite::isa(&ii)) { + for (unsigned i = 0, e = Values.size(); i != e; ++i) { + SILValue val = Values[i]; + bool escape = EA->canEscapeTo(val, fas); + llvm::outs() << (escape ? "May" : "No") << "Escape: " << val + << " to " << ii; + } + } + if (RefCountingInst *rci = dyn_cast(&ii)) { + for (unsigned i = 0, e = Values.size(); i != e; ++i) { + SILValue val = Values[i]; + bool escape = EA->canEscapeTo(val, rci); + llvm::outs() << (escape ? "May" : "No") << "Escape: " << val + << " to " << ii; + } + } + } + } } } #endif From cec925c5499a24f7a86cf63bdff7ddd3cfefd787 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 16 Dec 2019 16:12:17 -0800 Subject: [PATCH 056/478] EscapeAnalysis: Do not create defer edges for block arguemnts. That appears to have been a partial workaround for the real problem that usepoints need to be propagated across the entire defer web. This is now solved by considering use points on the reference node's content, not the reference node itself. --- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index aaeb89430311e..c03da48549324 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -826,15 +826,6 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { #endif // First scan the whole function and add relevant instructions as use-points. for (auto &BB : *F) { - for (SILArgument *BBArg : BB.getArguments()) { - /// In addition to releasing instructions (see below) we also add block - /// arguments as use points. In case of loops, block arguments can - /// "extend" the liferange of a reference in upward direction. - if (CGNode *ArgNode = lookupNode(BBArg)) { - addUsePoint(ArgNode, BBArg); - } - } - for (auto &I : BB) { switch (I.getKind()) { #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ From c2c36d0769cab4371e37786e550268cc23b50afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Mon, 16 Dec 2019 15:55:05 -0800 Subject: [PATCH 057/478] [Serialization] Fix reporting a dependency cycle with a missing clang module rdar://problem/57364033 --- lib/Serialization/SerializedModuleLoader.cpp | 3 +- .../Recovery/missing-clang-module.swift | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 test/Serialization/Recovery/missing-clang-module.swift diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 903391055dd75..373f17f2b83f1 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -747,7 +747,8 @@ void swift::serialization::diagnoseSerializedASTLoadFailure( auto circularDependencyIter = llvm::find_if(loadedModuleFile->getDependencies(), [](const ModuleFile::Dependency &next) { - return !next.Import.second->hasResolvedImports(); + return next.isLoaded() && + !next.Import.second->hasResolvedImports(); }); assert(circularDependencyIter != loadedModuleFile->getDependencies().end() && diff --git a/test/Serialization/Recovery/missing-clang-module.swift b/test/Serialization/Recovery/missing-clang-module.swift new file mode 100644 index 0000000000000..248bb8922c471 --- /dev/null +++ b/test/Serialization/Recovery/missing-clang-module.swift @@ -0,0 +1,32 @@ +//// Report dependency cycles involving missing clang modules without crashing +//// rdar://problem/57364033 + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/custom-modules %t/ + +// RUN: %target-swift-frontend -emit-module -DLIB_A %s -module-name A -emit-module-path %t/A.swiftmodule +// RUN: %target-swift-frontend -emit-module -DLIB_B %s -module-name B -emit-module-path %t/B.swiftmodule -I %t -I %t/custom-modules +// RUN: %target-swift-frontend -emit-module -DLIB_C %s -module-name C -emit-module-path %t/C.swiftmodule -I %t + +//// Delete the clang module +// RUN: rm -r %t/custom-modules/ + +// RUN: not %target-swift-frontend -emit-module -DLIB_D %s -module-name A -emit-module-path %t/D.swiftmodule -I %t 2>&1 | %FileCheck %s + +#if LIB_A + +#elseif LIB_B + +import IndirectImport // From custom-modules +import A + +#elseif LIB_C + +import B + +#elseif LIB_D + +import C +// CHECK: :0: error: circular dependency between modules 'A' and 'B' + +#endif From 66c2429cba2f91b040692d63d6e9901a1cfe8089 Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Tue, 17 Dec 2019 01:07:43 +0000 Subject: [PATCH 058/478] [Typechecker] Fix a few regressions with @autoclosure (#28677) * [Typechecker] Introduce a new request to check the structure of @autoclosure * Revert "[Typechecker] Introduce a new request to check the structure of @autoclosure" This reverts commit 3b1d4308bd3444230bfc7d031f7ccfa3b366a038. * [Typechecker] Fix a few regressions related to use of @autoclosure --- lib/Sema/TypeCheckType.cpp | 46 +++++++++++++++----------------- test/attr/attr_autoclosure.swift | 11 ++++++++ 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index d1e911e0aa66a..2beeb5bd3e7e8 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2241,33 +2241,9 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, rep = FunctionType::Representation::Swift; } else { rep = *parsedRep; - - if (attrs.has(TAK_autoclosure)) { - // @convention(c) and @convention(block) are not allowed with an @autoclosure type. - if (rep == FunctionType::Representation::CFunctionPointer || - rep == FunctionType::Representation::Block) { - diagnose(attrs.getLoc(TAK_convention), - diag::invalid_autoclosure_and_convention_attributes, - attrs.getConventionName()); - attrs.clearAttribute(TAK_convention); - } - } } } - // @autoclosure is only valid on parameters. - if (!isParam && attrs.has(TAK_autoclosure)) { - bool isVariadicFunctionParam = - options.is(TypeResolverContext::VariadicFunctionInput) && - !options.hasBase(TypeResolverContext::EnumElementDecl); - - diagnose(attrs.getLoc(TAK_autoclosure), - isVariadicFunctionParam ? diag::attr_not_on_variadic_parameters - : diag::attr_only_on_parameters, - "@autoclosure"); - attrs.clearAttribute(TAK_autoclosure); - } - if (attrs.has(TAK_differentiable) && !Context.LangOpts.EnableExperimentalDifferentiableProgramming) { diagnose(attrs.getLoc(TAK_differentiable), @@ -2287,6 +2263,28 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, } } + // Validate use of @autoclosure + if (attrs.has(TAK_autoclosure)) { + if (attrs.hasConvention()) { + if (attrs.getConventionName() == "c" || + attrs.getConventionName() == "block") { + diagnose(attrs.getLoc(TAK_convention), + diag::invalid_autoclosure_and_convention_attributes, + attrs.getConventionName()); + attrs.clearAttribute(TAK_convention); + } + } else if (options.is(TypeResolverContext::VariadicFunctionInput) && + !options.hasBase(TypeResolverContext::EnumElementDecl)) { + diagnose(attrs.getLoc(TAK_autoclosure), + diag::attr_not_on_variadic_parameters, "@autoclosure"); + attrs.clearAttribute(TAK_autoclosure); + } else if (!options.is(TypeResolverContext::FunctionInput)) { + diagnose(attrs.getLoc(TAK_autoclosure), diag::attr_only_on_parameters, + "@autoclosure"); + attrs.clearAttribute(TAK_autoclosure); + } + } + auto instanceOptions = options; instanceOptions.setContext(None); diff --git a/test/attr/attr_autoclosure.swift b/test/attr/attr_autoclosure.swift index 4c136c330414e..608a4c69f720e 100644 --- a/test/attr/attr_autoclosure.swift +++ b/test/attr/attr_autoclosure.swift @@ -281,3 +281,14 @@ func test_autoclosure_with_generic_argument_mismatch() { foo(S()) // expected-error {{cannot convert value of type 'S' to expected argument type 'S'}} } + +// SR-11934 +func sr_11934(_ x: @autoclosure String...) {} // expected-error {{'@autoclosure' must not be used on variadic parameters}} + +// SR-11938 +let sr_11938_1: Array<@autoclosure String> = [] // expected-error {{'@autoclosure' may only be used on parameters}} +func sr_11938_2() -> @autoclosure String { "" } // expected-error {{'@autoclosure' may only be used on parameters}} +func sr_11938_3(_ x: [@autoclosure String]) {} // expected-error {{'@autoclosure' may only be used on parameters}} + +protocol SR_11938_P {} +struct SR_11938_S : @autoclosure SR_11938_P {} // expected-error {{'@autoclosure' may only be used on parameters}} From ca693eeca34fda98bdb682938c54ccc772830d10 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Mon, 16 Dec 2019 17:24:30 -0800 Subject: [PATCH 059/478] Revert "SR-5289: Teach Mirror how to handle unowned/unmanaged references (#28368)" This reverts commit 9c638ae60dcac3b7bfcf7b3ecc1ea384ccd7ea97. --- include/swift/ABI/Metadata.h | 4 - include/swift/ABI/MetadataValues.h | 49 ++++ include/swift/Runtime/ExistentialContainer.h | 2 - stdlib/public/runtime/Metadata.cpp | 7 - stdlib/public/runtime/Private.h | 12 +- stdlib/public/runtime/ReflectionMirror.mm | 169 +++++++------- test/stdlib/Mirror.swift | 234 +------------------ 7 files changed, 142 insertions(+), 335 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 6bcdaf8d87a3a..34b0c0f1affc8 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -160,7 +160,6 @@ using TargetRelativeIndirectablePointer struct HeapObject; class WeakReference; -struct UnownedReference; template struct TargetMetadata; using Metadata = TargetMetadata; @@ -646,9 +645,6 @@ struct TargetMetadata { // NOTE: This *is* a box for copy-on-write existentials. OpaqueValue *allocateBoxForExistentialIn(ValueBuffer *Buffer) const; - // Deallocate an out-of-line buffer box if one is present. - void deallocateBoxForExistentialIn(ValueBuffer *Buffer) const; - /// Get the nominal type descriptor if this metadata describes a nominal type, /// or return null if it does not. ConstTargetMetadataPointer diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index e6c869c1ad97a..cc15eed09bb8c 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -913,6 +913,55 @@ class TargetTupleTypeFlags { }; using TupleTypeFlags = TargetTupleTypeFlags; +/// Field types and flags as represented in a nominal type's field/case type +/// vector. +class FieldType { + typedef uintptr_t int_type; + // Type metadata is always at least pointer-aligned, so we get at least two + // low bits to stash flags. We could use three low bits on 64-bit, and maybe + // some high bits as well. + enum : int_type { + Indirect = 1, + Weak = 2, + + TypeMask = ((uintptr_t)-1) & ~(alignof(void*) - 1), + }; + int_type Data; + + constexpr FieldType(int_type Data) : Data(Data) {} +public: + constexpr FieldType() : Data(0) {} + FieldType withType(const Metadata *T) const { + return FieldType((Data & ~TypeMask) | (uintptr_t)T); + } + + constexpr FieldType withIndirect(bool indirect) const { + return FieldType((Data & ~Indirect) + | (indirect ? Indirect : 0)); + } + + constexpr FieldType withWeak(bool weak) const { + return FieldType((Data & ~Weak) + | (weak ? Weak : 0)); + } + + bool isIndirect() const { + return bool(Data & Indirect); + } + + bool isWeak() const { + return bool(Data & Weak); + } + + const Metadata *getType() const { + return (const Metadata *)(Data & TypeMask); + } + + int_type getIntValue() const { + return Data; + } +}; + /// Flags for exclusivity-checking operations. enum class ExclusivityFlags : uintptr_t { Read = 0x0, diff --git a/include/swift/Runtime/ExistentialContainer.h b/include/swift/Runtime/ExistentialContainer.h index bf7e63c7cb825..7e1d733395790 100644 --- a/include/swift/Runtime/ExistentialContainer.h +++ b/include/swift/Runtime/ExistentialContainer.h @@ -95,8 +95,6 @@ struct ClassExistentialContainerImpl { using ClassExistentialContainer = ClassExistentialContainerImpl; using WeakClassExistentialContainer = ClassExistentialContainerImpl; -using UnownedClassExistentialContainer = - ClassExistentialContainerImpl; } // end swift namespace diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 1b1a0f5a1c101..66f1dc053d182 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -3862,13 +3862,6 @@ template <> OpaqueValue *Metadata::allocateBoxForExistentialIn(ValueBuffer *buff return refAndValueAddr.buffer; } -template <> void Metadata::deallocateBoxForExistentialIn(ValueBuffer *buffer) const { - auto *vwt = getValueWitnesses(); - if (vwt->isValueInline()) - return; - swift_deallocBox(reinterpret_cast(buffer->PrivateData[0])); -} - template <> OpaqueValue *Metadata::allocateBufferIn(ValueBuffer *buffer) const { auto *vwt = getValueWitnesses(); if (vwt->isValueInline()) diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index 11b82d09c3578..9bd4a808876fa 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -50,10 +50,8 @@ class TypeReferenceOwnership { #define REF_STORAGE(Name, ...) \ void set##Name() { Data |= Name; } \ - bool is##Name() const { return Data == Name; } + bool is##Name() const { return Data & Name; } #include "swift/AST/ReferenceStorage.def" - - bool isStrong() const { return Data == 0; } }; /// Type information consists of metadata and its ownership info, @@ -78,11 +76,9 @@ class TypeInfo { const Metadata *getMetadata() const { return Response.Value; } MetadataResponse getResponse() const { return Response; } -#define REF_STORAGE(Name, ...) \ - bool is##Name() const { return ReferenceOwnership.is##Name(); } -#include "swift/AST/ReferenceStorage.def" - - bool isStrong() const { return ReferenceOwnership.isStrong(); } + bool isWeak() const { return ReferenceOwnership.isWeak(); } + bool isUnowned() const { return ReferenceOwnership.isUnowned(); } + bool isUnmanaged() const { return ReferenceOwnership.isUnmanaged(); } TypeReferenceOwnership getReferenceOwnership() const { return ReferenceOwnership; diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.mm index 32dd7e7398cab..310bd403d3b29 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.mm @@ -89,29 +89,6 @@ - (id)debugQuickLookObject; namespace { -class FieldType { - const Metadata *type; - bool indirect; - TypeReferenceOwnership referenceOwnership; -public: - - constexpr FieldType() : type(nullptr), indirect(false), referenceOwnership() { } - constexpr FieldType(const Metadata *T) : type(T), indirect(false), referenceOwnership() { } - - static constexpr FieldType untypedEnumCase(bool indirect) { - FieldType type{}; - type.indirect = indirect; - return type; - } - const Metadata *getType() const { return type; } - const TypeReferenceOwnership getReferenceOwnership() const { return referenceOwnership; } - bool isIndirect() const { return indirect; } - void setIndirect(bool value) { indirect = value; } - void setReferenceOwnership(TypeReferenceOwnership newOwnership) { - referenceOwnership = newOwnership; - } -}; - /// The layout of Any. using Any = OpaqueExistentialContainer; @@ -146,63 +123,58 @@ void setReferenceOwnership(TypeReferenceOwnership newOwnership) { return std::make_tuple(T, Value); } -static void copyWeakFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { - assert(type->getKind() == MetadataKind::Optional); - auto *srcContainer = reinterpret_cast(fieldData); - auto *destClassContainer = reinterpret_cast(destContainer); - destClassContainer->Value = swift_unknownObjectWeakLoadStrong(&srcContainer->Value); - auto witnessTablesSize = type->vw_size() - sizeof(WeakClassExistentialContainer); - memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize); -} - -static void copyUnownedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { - auto *srcContainer = reinterpret_cast(fieldData); - auto *destClassContainer = reinterpret_cast(destContainer); - destClassContainer->Value = swift_unknownObjectUnownedLoadStrong(&srcContainer->Value); - auto witnessTablesSize = type->vw_size() - sizeof(UnownedClassExistentialContainer); - memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize); -} - -static void copyUnmanagedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { - // Also known as "unowned(unsafe)". - // This is simpler than the unowned/weak cases because unmanaged - // references are fundamentally the same as strong ones, so we - // can use the regular strong reference support that already - // knows how to handle existentials and Obj-C references. - type->vw_initializeWithCopy(destContainer, fieldData); -} - -static AnyReturn copyFieldContents(OpaqueValue *fieldData, - const FieldType fieldType) { - Any outValue; - auto *type = fieldType.getType(); - outValue.Type = type; - auto ownership = fieldType.getReferenceOwnership(); - auto *destContainer = type->allocateBoxForExistentialIn(&outValue.Buffer); - - if (ownership.isStrong()) { - type->vw_initializeWithCopy(destContainer, fieldData); - } +static bool loadSpecialReferenceStorage(OpaqueValue *fieldData, + const FieldType fieldType, + Any *outValue) { + // isWeak() implies a reference type via Sema. + if (!fieldType.isWeak()) + return false; - // Generate a conditional clause for every known ownership type. - // If this causes errors, it's because someone added a new ownership type - // to ReferenceStorage.def and missed some related updates. -#define REF_STORAGE(Name, ...) \ - else if (ownership.is##Name()) { \ - copy##Name##FieldContents(destContainer, type, fieldData); \ - } -#include "swift/AST/ReferenceStorage.def" - - else { - // The field was declared with a reference type we don't understand. - warning(0, "Value with unrecognized reference type is reflected as ()"); - // Clean up the buffer allocated above - type->deallocateBoxForExistentialIn(&outValue.Buffer); - // Return an existential containing Void - outValue.Type = &METADATA_SYM(EMPTY_TUPLE_MANGLING); - } + auto type = fieldType.getType(); + assert(type->getKind() == MetadataKind::Optional); - return AnyReturn(outValue); + auto *weakField = reinterpret_cast(fieldData); + auto *strongValue = swift_unknownObjectWeakLoadStrong(weakField); + + // Now that we have a strong reference, we need to create a temporary buffer + // from which to copy the whole value, which might be a native class-bound + // existential, which means we also need to copy n witness tables, for + // however many protocols are in the protocol composition. For example, if we + // are copying a: + // weak var myWeakProperty : (Protocol1 & Protocol2)? + // then we need to copy three values: + // - the instance + // - the witness table for Protocol1 + // - the witness table for Protocol2 + + auto *weakContainer = + reinterpret_cast(fieldData); + + // Create a temporary existential where we can put the strong reference. + // The allocateBuffer value witness requires a ValueBuffer to own the + // allocated storage. + ValueBuffer temporaryBuffer; + + auto *temporaryValue = reinterpret_cast( + type->allocateBufferIn(&temporaryBuffer)); + + // Now copy the entire value out of the parent, which will include the + // witness tables. + temporaryValue->Value = strongValue; + auto valueWitnessesSize = type->getValueWitnesses()->getSize() - + sizeof(WeakClassExistentialContainer); + memcpy(temporaryValue->getWitnessTables(), weakContainer->getWitnessTables(), + valueWitnessesSize); + + outValue->Type = type; + auto *opaqueValueAddr = type->allocateBoxForExistentialIn(&outValue->Buffer); + type->vw_initializeWithCopy(opaqueValueAddr, + reinterpret_cast(temporaryValue)); + + type->deallocateBufferIn(&temporaryBuffer); + swift_unknownObjectRelease(strongValue); + + return true; } @@ -338,7 +310,11 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { "type '%*s' that claims to be reflectable. Its fields will show up as " "'unknown' in Mirrors\n", (int)typeName.length, typeName.data); - return {"unknown", FieldType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))}; + return {"unknown", + FieldType() + .withType(&METADATA_SYM(EMPTY_TUPLE_MANGLING)) + .withIndirect(false) + .withWeak(false)}; }; auto *baseDesc = base->getTypeContextDescriptor(); @@ -349,13 +325,14 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { if (!fields) return failedToFindMetadata(); - auto &field = fields->getFields()[index]; + const FieldDescriptor &descriptor = *fields; + auto &field = descriptor.getFields()[index]; // Bounds are always valid as the offset is constant. auto name = field.getFieldName(); // Enum cases don't always have types. if (!field.hasMangledTypeName()) - return {name, FieldType::untypedEnumCase(field.isIndirectCase())}; + return {name, FieldType().withIndirect(field.isIndirectCase())}; auto typeName = field.getMangledTypeName(); @@ -383,10 +360,10 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { (int)typeName.size(), typeName.data()); } - auto fieldType = FieldType(typeInfo.getMetadata()); - fieldType.setIndirect(field.isIndirectCase()); - fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership()); - return {name, fieldType}; + return {name, FieldType() + .withType(typeInfo.getMetadata()) + .withIndirect(field.isIndirectCase()) + .withWeak(typeInfo.isWeak())}; } // Implementation for structs. @@ -420,6 +397,7 @@ AnyReturn subscript(intptr_t i, const char **outName, // Load the offset from its respective vector. auto fieldOffset = Struct->getFieldOffsets()[i]; + Any result; StringRef name; FieldType fieldInfo; std::tie(name, fieldInfo) = getFieldAt(type, i); @@ -431,7 +409,15 @@ AnyReturn subscript(intptr_t i, const char **outName, auto *bytes = reinterpret_cast(value); auto *fieldData = reinterpret_cast(bytes + fieldOffset); - return copyFieldContents(fieldData, fieldInfo); + bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result); + if (!didLoad) { + result.Type = fieldInfo.getType(); + auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer); + result.Type->vw_initializeWithCopy(opaqueValueAddr, + const_cast(fieldData)); + } + + return AnyReturn(result); } }; @@ -573,6 +559,7 @@ AnyReturn subscript(intptr_t i, const char **outName, #endif } + Any result; StringRef name; FieldType fieldInfo; std::tie(name, fieldInfo) = getFieldAt(type, i); @@ -584,7 +571,15 @@ AnyReturn subscript(intptr_t i, const char **outName, *outName = name.data(); *outFreeFunc = nullptr; - return copyFieldContents(fieldData, fieldInfo); + bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result); + if (!didLoad) { + result.Type = fieldInfo.getType(); + auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer); + result.Type->vw_initializeWithCopy(opaqueValueAddr, + const_cast(fieldData)); + } + + return AnyReturn(result); } #if SWIFT_OBJC_INTEROP diff --git a/test/stdlib/Mirror.swift b/test/stdlib/Mirror.swift index 108f5f4c73f83..b2b09fe97a1f2 100644 --- a/test/stdlib/Mirror.swift +++ b/test/stdlib/Mirror.swift @@ -509,226 +509,6 @@ mirrors.test("struct/WrapNSArray") { #endif // _runtime(_ObjC) -//===--- Weak and Unowned References --------------------------------------===// - -// Check that Mirror correctly reflects weak/unowned refs to both -// Swift and ObjC objects from Swift structs and classes. - -protocol WeakUnownedTestsP1: class { - func f1() -> Int -} - -protocol WeakUnownedTestsP2 { - func f2() -> String -} - -class WeakUnownedSwiftClass: WeakUnownedTestsP1, WeakUnownedTestsP2 { - let tracker = LifetimeTracked(0) - func f1() -> Int { return 2 } - func f2() -> String { return "b" } -} - -#if _runtime(_ObjC) -@objc class WeakUnownedObjCClass: NSObject, WeakUnownedTestsP1, WeakUnownedTestsP2 { - let tracker = LifetimeTracked(0) - func f1() -> Int { return 2 } - func f2() -> String { return "b" } -} -#endif - -// The four tests below populate objects with different types -// but identical overall structure. -// This function is used by all four to verify that the resulting -// Mirror objects have the expected entries. -func verifyWeakUnownedReflection - - (_ m: Mirror, expectedClass: ExpectedClass.Type ) -{ - let i = m.children.makeIterator() - - func verifyClassField(child: (label: String?, value: Any), name: String) { - expectEqual(child.label, name) - let v = child.value as? ExpectedClass - expectNotNil(v) - expectEqual(v!.f1(), 2) - } - - func verifyExistentialField(child: (label: String?, value: Any), name: String) { - expectEqual(child.label, name) - expectNotNil(child.value) - - // FIXME: These casts are currently broken (Dec 2019) - // Once they are fixed, enable additional checks: - //let vp1 = child.value as? WeakUnownedTestsP1 - //expectNotNil(vp1) - //expectEqual(vp1!.f1(), 2) - //let vp2 = child.value as? WeakUnownedTestsP2 - //expectNotNil(vp2) - //expectEqual(vp2!.f2(), "b") - - let v = child.value as? ExpectedClass - expectNotNil(v) - expectEqual(v!.f1(), 2) - let m = Mirror(reflecting: v!) - expectEqual(m.displayStyle, .`class`) - // TODO: Find a way to verify that the existential wrapper carries - // the expected protocol witnesses. The current Swift runtime does - // a very good job of hiding this from users. - } - - verifyClassField(child: i.next()!, name: "strong_class") - verifyExistentialField(child: i.next()!, name: "strong_existential") - verifyClassField(child: i.next()!, name: "weak_class") - verifyExistentialField(child: i.next()!, name: "weak_existential") - verifyClassField(child: i.next()!, name: "unowned_safe_class") - verifyExistentialField(child: i.next()!, name: "unowned_safe_existential") - - verifyClassField(child: i.next()!, name: "unowned_unsafe_class") - verifyExistentialField(child: i.next()!, name: "unowned_unsafe_existential") - expectNil(i.next()) - - // The original bug report from SR-5289 crashed when the print() code - // attempted to reflect the contents of an unowned field. - // The tests above _should_ suffice to check this, but let's print everything - // anyway just to be sure. - for c in m.children { - print(c.label ?? "?", c.value) - } -} - -#if _runtime(_ObjC) -// Related: SR-5289 reported a crash when using Mirror to inspect Swift -// class objects containing unowned pointers to Obj-C class objects. -mirrors.test("Weak and Unowned Obj-C refs in class (SR-5289)") { - class SwiftClassWithWeakAndUnowned { - var strong_class: WeakUnownedObjCClass - var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 - weak var weak_class: WeakUnownedObjCClass? - weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? - unowned(safe) let unowned_safe_class: WeakUnownedObjCClass - unowned(safe) let unowned_safe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 - unowned(unsafe) let unowned_unsafe_class: WeakUnownedObjCClass - unowned(unsafe) let unowned_unsafe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 - - init(_ objc: WeakUnownedObjCClass) { - self.strong_class = objc - self.strong_existential = objc - self.weak_class = objc - self.weak_existential = objc - self.unowned_safe_class = objc - self.unowned_safe_existential = objc - self.unowned_unsafe_class = objc - self.unowned_unsafe_existential = objc - } - } - - let objc = WeakUnownedObjCClass() - let classWithReferences = SwiftClassWithWeakAndUnowned(objc) - let m = Mirror(reflecting: classWithReferences) - expectEqual(m.displayStyle, .`class`) - expectEqual(m.description, "Mirror for SwiftClassWithWeakAndUnowned") - expectEqual(m.subjectType, SwiftClassWithWeakAndUnowned.self) - verifyWeakUnownedReflection(m, expectedClass: WeakUnownedObjCClass.self) -} - -mirrors.test("Weak and Unowned Obj-C refs in struct") { - struct SwiftStructWithWeakAndUnowned { - var strong_class: WeakUnownedObjCClass - var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 - weak var weak_class: WeakUnownedObjCClass? - weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? - unowned(safe) let unowned_safe_class: WeakUnownedObjCClass - unowned(safe) let unowned_safe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 - unowned(unsafe) let unowned_unsafe_class: WeakUnownedObjCClass - unowned(unsafe) let unowned_unsafe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 - - init(_ objc: WeakUnownedObjCClass) { - self.strong_class = objc - self.strong_existential = objc - self.weak_class = objc - self.weak_existential = objc - self.unowned_safe_class = objc - self.unowned_safe_existential = objc - self.unowned_unsafe_class = objc - self.unowned_unsafe_existential = objc - } - } - - let objc = WeakUnownedObjCClass() - let structWithReferences = SwiftStructWithWeakAndUnowned(objc) - let m = Mirror(reflecting: structWithReferences) - expectEqual(m.displayStyle, .`struct`) - expectEqual(m.description, "Mirror for SwiftStructWithWeakAndUnowned") - expectEqual(m.subjectType, SwiftStructWithWeakAndUnowned.self) - verifyWeakUnownedReflection(m, expectedClass: WeakUnownedObjCClass.self) -} - -#endif - -mirrors.test("Weak and Unowned Swift refs in class") { - class SwiftClassWithWeakAndUnowned { - var strong_class: WeakUnownedSwiftClass - var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 - weak var weak_class: WeakUnownedSwiftClass? - weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? - unowned(safe) let unowned_safe_class: WeakUnownedSwiftClass - unowned(safe) let unowned_safe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) - unowned(unsafe) let unowned_unsafe_class: WeakUnownedSwiftClass - unowned(unsafe) let unowned_unsafe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) - - init(_ swift: WeakUnownedSwiftClass) { - self.strong_class = swift - self.strong_existential = swift - self.weak_class = swift - self.weak_existential = swift - self.unowned_safe_class = swift - self.unowned_safe_existential = swift - self.unowned_unsafe_class = swift - self.unowned_unsafe_existential = swift - } - } - - let swift = WeakUnownedSwiftClass() - let classWithReferences = SwiftClassWithWeakAndUnowned(swift) - let m = Mirror(reflecting: classWithReferences) - expectEqual(m.displayStyle, .`class`) - expectEqual(m.description, "Mirror for SwiftClassWithWeakAndUnowned") - expectEqual(m.subjectType, SwiftClassWithWeakAndUnowned.self) - verifyWeakUnownedReflection(m, expectedClass: WeakUnownedSwiftClass.self) -} - -mirrors.test("Weak and Unowned Swift refs in struct") { - struct SwiftStructWithWeakAndUnowned { - var strong_class: WeakUnownedSwiftClass - var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 - weak var weak_class: WeakUnownedSwiftClass? - weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? - unowned(safe) let unowned_safe_class: WeakUnownedSwiftClass - unowned(safe) let unowned_safe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) - unowned(unsafe) let unowned_unsafe_class: WeakUnownedSwiftClass - unowned(unsafe) let unowned_unsafe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) - - init(_ swift: WeakUnownedSwiftClass) { - self.strong_class = swift - self.strong_existential = swift - self.weak_class = swift - self.weak_existential = swift - self.unowned_safe_class = swift - self.unowned_safe_existential = swift - self.unowned_unsafe_class = swift - self.unowned_unsafe_existential = swift - } - } - - let swift = WeakUnownedSwiftClass() - let structWithReferences = SwiftStructWithWeakAndUnowned(swift) - let m = Mirror(reflecting: structWithReferences) - expectEqual(m.displayStyle, .`struct`) - expectEqual(m.description, "Mirror for SwiftStructWithWeakAndUnowned") - expectEqual(m.subjectType, SwiftStructWithWeakAndUnowned.self) - verifyWeakUnownedReflection(m, expectedClass: WeakUnownedSwiftClass.self) -} - //===--- Suppressed Superclass Mirrors ------------------------------------===// mirrors.test("Class/Root/NoSuperclassMirror") { class B : CustomReflectable { @@ -1074,7 +854,7 @@ struct GenericStructWithDefaultMirror { mirrors.test("Struct/Generic/DefaultMirror") { do { - let value = GenericStructWithDefaultMirror( + var value = GenericStructWithDefaultMirror( first: 123, second: ["abc", 456, 789.25]) var output = "" @@ -1836,7 +1616,7 @@ mirrors.test("Float") { } do { - let input: Float = 42.125 + var input: Float = 42.125 var output = "" dump(input, to: &output) @@ -1869,7 +1649,7 @@ mirrors.test("Double") { } do { - let input: Double = 42.125 + var input: Double = 42.125 var output = "" dump(input, to: &output) @@ -1965,9 +1745,9 @@ mirrors.test("FieldNamesBug") { } mirrors.test("MirrorMirror") { - let object = 1 - let mirror = Mirror(reflecting: object) - let mirrorMirror = Mirror(reflecting: mirror) + var object = 1 + var mirror = Mirror(reflecting: object) + var mirrorMirror = Mirror(reflecting: mirror) expectEqual(0, mirrorMirror.children.count) } @@ -1975,7 +1755,7 @@ mirrors.test("MirrorMirror") { mirrors.test("OpaquePointer/null") { // Don't crash on null pointers. rdar://problem/19708338 let pointer: OpaquePointer? = nil - let mirror = Mirror(reflecting: pointer as Any) + let mirror = Mirror(reflecting: pointer) expectEqual(0, mirror.children.count) } From 1ebc78b22b03b723bdbfe937268b85cd4ef7289f Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Mon, 16 Dec 2019 17:57:14 -0800 Subject: [PATCH 060/478] [RemoteAST] Using module names specified by @_originallyDefinedIn for top-level decls marked as so This should allow runtime de-mangling of those moved symbols. --- lib/AST/Decl.cpp | 8 ++++++-- lib/IRGen/GenDecl.cpp | 11 +++++++++++ lib/IRGen/GenMeta.cpp | 7 +++++++ lib/IRGen/IRGenModule.h | 2 ++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index e0a1bc258e696..594a14915ab27 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -577,8 +577,12 @@ Result->X = SM.getLocFromExternalSource(Locs->SourceFilePath, Locs->X.Line, \ } StringRef Decl::getAlternateModuleName() const { - if (auto *OD = Attrs.getAttribute(DeclAttrKind::DAK_OriginallyDefinedIn)) { - return static_cast(OD)->OriginalModuleName; + for (auto *Att: Attrs) { + if (auto *OD = dyn_cast(Att)) { + if (OD->isActivePlatform(getASTContext())) { + return OD->OriginalModuleName; + } + } } for (auto *DC = getDeclContext(); DC; DC = DC->getParent()) { if (auto decl = DC->getAsDecl()) { diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 5051f0e788418..cc5e490b601ca 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -788,6 +788,17 @@ IRGenModule::getAddrOfContextDescriptorForParent(DeclContext *parent, LLVM_FALLTHROUGH; case DeclContextKind::Module: + if (auto *D = ofChild->getAsDecl()) { + // If the top-level decl has been marked as moved from another module, + // using @_originallyDefinedIn, we should emit the original module as + // the context because all run-time names of this decl are based on the + // original module name. + auto OriginalModule = D->getAlternateModuleName(); + if (!OriginalModule.empty()) { + return {getAddrOfOriginalModuleContextDescriptor(OriginalModule), + ConstantReference::Direct}; + } + } return {getAddrOfModuleContextDescriptor(cast(parent)), ConstantReference::Direct}; } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 1844cf3fc0134..a89d65828eccf 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -1908,6 +1908,13 @@ IRGenModule::getAddrOfAnonymousContextDescriptor( [&]{ AnonymousContextDescriptorBuilder(*this, DC).emit(); }); } +llvm::Constant * +IRGenModule::getAddrOfOriginalModuleContextDescriptor(StringRef Name) { + return getAddrOfModuleContextDescriptor(OriginalModules.insert({Name, + ModuleDecl::create(Context.getIdentifier(Name), Context)}) + .first->getValue()); +} + static void emitInitializeFieldOffsetVector(IRGenFunction &IGF, SILType T, llvm::Value *metadata, diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 7bdcb858b3bb1..b460ccb9860dc 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -510,6 +510,7 @@ class IRGenModule { ModuleDecl *ClangImporterModule = nullptr; SourceFile *CurSourceFile = nullptr; + llvm::StringMap OriginalModules; llvm::SmallString<128> OutputFilename; llvm::SmallString<128> MainInputFilenameForDebugInfo; @@ -1340,6 +1341,7 @@ private: \ ConstantInit definition = ConstantInit()); llvm::Constant *getAddrOfObjCModuleContextDescriptor(); llvm::Constant *getAddrOfClangImporterModuleContextDescriptor(); + llvm::Constant *getAddrOfOriginalModuleContextDescriptor(StringRef Name); ConstantReference getAddrOfParentContextDescriptor(DeclContext *from, bool fromAnonymousContext); ConstantReference getAddrOfContextDescriptorForParent(DeclContext *parent, From f8dace593c7ea5af0ef1ba2b662b060b6adf56a2 Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Tue, 17 Dec 2019 03:24:09 +0000 Subject: [PATCH 061/478] [Typechecker] Fix a crash related to use of invalid @autoclosure parameter --- lib/Sema/TypeCheckType.cpp | 8 ++++++++ test/attr/attr_autoclosure.swift | 2 +- validation-test/compiler_crashers_2_fixed/sr11939.swift | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 validation-test/compiler_crashers_2_fixed/sr11939.swift diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 2beeb5bd3e7e8..c3616371deeec 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2265,6 +2265,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Validate use of @autoclosure if (attrs.has(TAK_autoclosure)) { + bool didDiagnose = false; if (attrs.hasConvention()) { if (attrs.getConventionName() == "c" || attrs.getConventionName() == "block") { @@ -2272,16 +2273,23 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, diag::invalid_autoclosure_and_convention_attributes, attrs.getConventionName()); attrs.clearAttribute(TAK_convention); + didDiagnose = true; } } else if (options.is(TypeResolverContext::VariadicFunctionInput) && !options.hasBase(TypeResolverContext::EnumElementDecl)) { diagnose(attrs.getLoc(TAK_autoclosure), diag::attr_not_on_variadic_parameters, "@autoclosure"); attrs.clearAttribute(TAK_autoclosure); + didDiagnose = true; } else if (!options.is(TypeResolverContext::FunctionInput)) { diagnose(attrs.getLoc(TAK_autoclosure), diag::attr_only_on_parameters, "@autoclosure"); attrs.clearAttribute(TAK_autoclosure); + didDiagnose = true; + } + + if (didDiagnose) { + ty = ErrorType::get(Context); } } diff --git a/test/attr/attr_autoclosure.swift b/test/attr/attr_autoclosure.swift index 608a4c69f720e..6b6d689dd08ee 100644 --- a/test/attr/attr_autoclosure.swift +++ b/test/attr/attr_autoclosure.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift -swift-version 5 // Simple case. -var fn : @autoclosure () -> Int = 4 // expected-error {{'@autoclosure' may only be used on parameters}} expected-error {{cannot convert value of type 'Int' to specified type '() -> Int'}} +var fn : @autoclosure () -> Int = 4 // expected-error {{'@autoclosure' may only be used on parameters}} @autoclosure func func1() {} // expected-error {{attribute can only be applied to types, not declarations}} diff --git a/validation-test/compiler_crashers_2_fixed/sr11939.swift b/validation-test/compiler_crashers_2_fixed/sr11939.swift new file mode 100644 index 0000000000000..1d097021efc2e --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr11939.swift @@ -0,0 +1,4 @@ +// RUN: %target-swift-frontend %s -typecheck -verify + +func sr_11939(_ closure: @autoclosure () -> String...) {} // expected-error {{'@autoclosure' must not be used on variadic parameters}} +sr_11939("A") // No crash From 90a33fa32cf8ca083d986c0f492e3088db580b37 Mon Sep 17 00:00:00 2001 From: YOCKOW Date: Mon, 16 Dec 2019 16:46:34 +0900 Subject: [PATCH 062/478] [run-test] Change default path for "lit.py" executable. Since https://github.com/apple/swift/commit/1b4b7f9e17576bdae479c2bb721b08895bee0609 has been committed, `update-checkout` clones "llvm-project" repo but does not clone "llvm" repo itself alone by default. That may cause `run-test` failure because "lit.py" cannot be found. This commit fixes the issue. --- utils/run-test | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/utils/run-test b/utils/run-test index e4bce782f9f41..c7db643dd5645 100755 --- a/utils/run-test +++ b/utils/run-test @@ -47,8 +47,19 @@ SWIFT_SOURCE_DIR = os.path.join(SWIFT_SOURCE_ROOT, 'swift') TEST_SOURCE_DIR = os.path.join(SWIFT_SOURCE_DIR, 'test') VALIDATION_TEST_SOURCE_DIR = os.path.join(SWIFT_SOURCE_DIR, 'validation-test') -LIT_BIN_DEFAULT = os.path.join(SWIFT_SOURCE_ROOT, 'llvm', + +def _get_default_llvm_source_dir(): + legacy_llvm_dir_path = os.path.join(SWIFT_SOURCE_ROOT, 'llvm') + if os.path.isdir(legacy_llvm_dir_path): + return legacy_llvm_dir_path + return os.path.join(SWIFT_SOURCE_ROOT, 'llvm-project', 'llvm') + + +# Default path for "lit.py" executable. +LIT_BIN_DEFAULT = os.path.join(os.environ.get("LLVM_SOURCE_DIR", + _get_default_llvm_source_dir()), 'utils', 'lit', 'lit.py') + host_target = StdlibDeploymentTarget.host_target().name From 942aecafa5aac77d51596e1a6d560e3ab7e7946c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 16 Dec 2019 22:45:08 -0800 Subject: [PATCH 063/478] [Type check] Consider dynamic casts to NSError and NSObject. --- lib/Sema/TypeCheckConstraints.cpp | 21 ++++++++++++++++++++- test/Constraints/ErrorBridging.swift | 21 +++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index e2d10d5fade33..8567666f5a354 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -4453,11 +4453,13 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, // type. This is handled in the runtime, so it doesn't need a special cast // kind. if (Context.LangOpts.EnableObjCInterop) { + auto nsObject = Context.getNSObjectType(); + auto nsErrorTy = Context.getNSErrorType(); + if (auto errorTypeProto = Context.getProtocol(KnownProtocolKind::Error)) { if (!conformsToProtocol(toType, errorTypeProto, dc, ConformanceCheckFlags::InExpression) .isInvalid()) { - auto nsErrorTy = Context.getNSErrorType(); if (nsErrorTy) { if (isSubtypeOf(fromType, nsErrorTy, dc) // Don't mask "always true" warnings if NSError is cast to @@ -4466,6 +4468,23 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, return CheckedCastKind::ValueCast; } } + + if (!conformsToProtocol(fromType, errorTypeProto, dc, + ConformanceCheckFlags::InExpression) + .isInvalid()) { + // Cast of an error-conforming type to NSError or NSObject. + if ((nsObject && toType->isEqual(nsObject)) || + (nsErrorTy && toType->isEqual(nsErrorTy))) + return CheckedCastKind::BridgingCoercion; + } + } + + // Any class-like type could be dynamically cast to NSObject or NSError + // via an Error conformance. + if (fromType->mayHaveSuperclass() && + ((nsObject && toType->isEqual(nsObject)) || + (nsErrorTy && toType->isEqual(nsErrorTy)))) { + return CheckedCastKind::ValueCast; } } diff --git a/test/Constraints/ErrorBridging.swift b/test/Constraints/ErrorBridging.swift index d80afc5d06d4e..81a8a7675fc60 100644 --- a/test/Constraints/ErrorBridging.swift +++ b/test/Constraints/ErrorBridging.swift @@ -73,3 +73,24 @@ extension Error { func throwErrorCode() throws { throw FictionalServerError.meltedDown // expected-error{{thrown error code type 'FictionalServerError.Code' does not conform to 'Error'; construct an 'FictionalServerError' instance}}{{29-29=(}}{{40-40=)}} } + +class MyErrorClass { } +extension MyErrorClass: Error { } + +class MyClass { } + +func testUnknownErrorBridge(cond: Bool, mc: MyClass) -> NSObject? { + if cond { + return mc as? NSError // okay + } + + return mc as? NSObject // okay +} + +func testAlwaysErrorBridge(cond: Bool, mec: MyErrorClass) -> NSObject? { + if cond { + return mec as? NSError // expected-warning{{conditional cast from 'MyErrorClass}}' to 'NSError' always succeeds + } + + return mec as? NSObject // expected-warning{{conditional cast from 'MyErrorClass}}' to 'NSObject' always succeeds +} From f67ccf5c9dda486dd43e074ffab8c7d554393b3d Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Tue, 17 Dec 2019 07:43:58 -0800 Subject: [PATCH 064/478] Unset IFS after its use in set_build_options_for_host (#28811) If IFS remains set, it may cause compilation errors due to unanticipated replacement of characters in some parameters. Addresses rdar://problem/57927748 --- utils/build-script-impl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index fce8102a7f453..ba3e769cd8b4b 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -542,10 +542,14 @@ function set_build_options_for_host() { esac if [[ "${DARWIN_SDK_DEPLOYMENT_TARGETS}" != "" ]]; then - local IFS=";"; DARWIN_SDK_DEPLOYMENT_TARGETS=($DARWIN_SDK_DEPLOYMENT_TARGETS) + # IFS is immediately unset after its use to avoid unwanted + # replacement of characters in subsequent lines. + local IFS=";"; DARWIN_SDK_DEPLOYMENT_TARGETS=($DARWIN_SDK_DEPLOYMENT_TARGETS); unset IFS for target in "${DARWIN_SDK_DEPLOYMENT_TARGETS[@]}"; do - local IFS="-"; triple=($target) + # IFS is immediately unset after its use to avoid unwanted + # replacement of characters in subsequent lines. + local IFS="-"; triple=($target); unset IFS sdk_target=$(toupper ${triple[0]}_${triple[1]}) swift_cmake_options+=( "-DSWIFTLIB_DEPLOYMENT_VERSION_${sdk_target}=${triple[2]}" From fdb19264210680e724782567b43c25e170e55bb0 Mon Sep 17 00:00:00 2001 From: tbkka Date: Tue, 17 Dec 2019 09:42:52 -0800 Subject: [PATCH 065/478] [SR-5289] Teach Mirror how to handle unowned/unmanaged references (#28823) SR-5289: Teach Mirror how to inspect weak, unowned, and unmanaged refs Correctly reflect weak, unowned, and unmanaged references to both Swift and Obj-C types (including existential references to such types) that occur in both Swift class objects and in Swift structs. This includes the specific reported case (unowned reference to an Obj-C object) and several related ones. Related changes in this PR: * Tweak internal bitmap used for tracking ownership modifiers to reject unsupported combinations. * Move FieldType into ReflectionMirror.mm FieldType is really just an internal implementation detail of this one source file, so it does not belong in an ABI header. * Use TypeReferenceOwnership directly to track field ownership This avoids bitwise copying of properties and localizes some of the knowledge about reference ownership * Generate a top-level "copyFieldContents" from ReferenceStorage.def Adding new ownership types to ReferenceStorage.def will now automatically produce calls to `copy*FieldContents` - failure to provide a suitable implementation will fail the build. * Add `deallocateBoxForExistentialIn` to match `allocateBoxForExistentialIn` Caveat: The unit tests are not as strict as I'd like. Attempting to make them so ran afoul of otherwise-unrelated bugs in dynamic casting. --- include/swift/ABI/Metadata.h | 4 + include/swift/ABI/MetadataValues.h | 49 ---- include/swift/Runtime/ExistentialContainer.h | 2 + stdlib/public/runtime/Metadata.cpp | 7 + stdlib/public/runtime/Private.h | 12 +- stdlib/public/runtime/ReflectionMirror.mm | 169 ++++++------- test/stdlib/Mirror.swift | 242 ++++++++++++++++++- 7 files changed, 343 insertions(+), 142 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 34b0c0f1affc8..6bcdaf8d87a3a 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -160,6 +160,7 @@ using TargetRelativeIndirectablePointer struct HeapObject; class WeakReference; +struct UnownedReference; template struct TargetMetadata; using Metadata = TargetMetadata; @@ -645,6 +646,9 @@ struct TargetMetadata { // NOTE: This *is* a box for copy-on-write existentials. OpaqueValue *allocateBoxForExistentialIn(ValueBuffer *Buffer) const; + // Deallocate an out-of-line buffer box if one is present. + void deallocateBoxForExistentialIn(ValueBuffer *Buffer) const; + /// Get the nominal type descriptor if this metadata describes a nominal type, /// or return null if it does not. ConstTargetMetadataPointer diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index cc15eed09bb8c..e6c869c1ad97a 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -913,55 +913,6 @@ class TargetTupleTypeFlags { }; using TupleTypeFlags = TargetTupleTypeFlags; -/// Field types and flags as represented in a nominal type's field/case type -/// vector. -class FieldType { - typedef uintptr_t int_type; - // Type metadata is always at least pointer-aligned, so we get at least two - // low bits to stash flags. We could use three low bits on 64-bit, and maybe - // some high bits as well. - enum : int_type { - Indirect = 1, - Weak = 2, - - TypeMask = ((uintptr_t)-1) & ~(alignof(void*) - 1), - }; - int_type Data; - - constexpr FieldType(int_type Data) : Data(Data) {} -public: - constexpr FieldType() : Data(0) {} - FieldType withType(const Metadata *T) const { - return FieldType((Data & ~TypeMask) | (uintptr_t)T); - } - - constexpr FieldType withIndirect(bool indirect) const { - return FieldType((Data & ~Indirect) - | (indirect ? Indirect : 0)); - } - - constexpr FieldType withWeak(bool weak) const { - return FieldType((Data & ~Weak) - | (weak ? Weak : 0)); - } - - bool isIndirect() const { - return bool(Data & Indirect); - } - - bool isWeak() const { - return bool(Data & Weak); - } - - const Metadata *getType() const { - return (const Metadata *)(Data & TypeMask); - } - - int_type getIntValue() const { - return Data; - } -}; - /// Flags for exclusivity-checking operations. enum class ExclusivityFlags : uintptr_t { Read = 0x0, diff --git a/include/swift/Runtime/ExistentialContainer.h b/include/swift/Runtime/ExistentialContainer.h index 7e1d733395790..bf7e63c7cb825 100644 --- a/include/swift/Runtime/ExistentialContainer.h +++ b/include/swift/Runtime/ExistentialContainer.h @@ -95,6 +95,8 @@ struct ClassExistentialContainerImpl { using ClassExistentialContainer = ClassExistentialContainerImpl; using WeakClassExistentialContainer = ClassExistentialContainerImpl; +using UnownedClassExistentialContainer = + ClassExistentialContainerImpl; } // end swift namespace diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 66f1dc053d182..1b1a0f5a1c101 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -3862,6 +3862,13 @@ template <> OpaqueValue *Metadata::allocateBoxForExistentialIn(ValueBuffer *buff return refAndValueAddr.buffer; } +template <> void Metadata::deallocateBoxForExistentialIn(ValueBuffer *buffer) const { + auto *vwt = getValueWitnesses(); + if (vwt->isValueInline()) + return; + swift_deallocBox(reinterpret_cast(buffer->PrivateData[0])); +} + template <> OpaqueValue *Metadata::allocateBufferIn(ValueBuffer *buffer) const { auto *vwt = getValueWitnesses(); if (vwt->isValueInline()) diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index 9bd4a808876fa..11b82d09c3578 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -50,8 +50,10 @@ class TypeReferenceOwnership { #define REF_STORAGE(Name, ...) \ void set##Name() { Data |= Name; } \ - bool is##Name() const { return Data & Name; } + bool is##Name() const { return Data == Name; } #include "swift/AST/ReferenceStorage.def" + + bool isStrong() const { return Data == 0; } }; /// Type information consists of metadata and its ownership info, @@ -76,9 +78,11 @@ class TypeInfo { const Metadata *getMetadata() const { return Response.Value; } MetadataResponse getResponse() const { return Response; } - bool isWeak() const { return ReferenceOwnership.isWeak(); } - bool isUnowned() const { return ReferenceOwnership.isUnowned(); } - bool isUnmanaged() const { return ReferenceOwnership.isUnmanaged(); } +#define REF_STORAGE(Name, ...) \ + bool is##Name() const { return ReferenceOwnership.is##Name(); } +#include "swift/AST/ReferenceStorage.def" + + bool isStrong() const { return ReferenceOwnership.isStrong(); } TypeReferenceOwnership getReferenceOwnership() const { return ReferenceOwnership; diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.mm index 310bd403d3b29..32dd7e7398cab 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.mm @@ -89,6 +89,29 @@ - (id)debugQuickLookObject; namespace { +class FieldType { + const Metadata *type; + bool indirect; + TypeReferenceOwnership referenceOwnership; +public: + + constexpr FieldType() : type(nullptr), indirect(false), referenceOwnership() { } + constexpr FieldType(const Metadata *T) : type(T), indirect(false), referenceOwnership() { } + + static constexpr FieldType untypedEnumCase(bool indirect) { + FieldType type{}; + type.indirect = indirect; + return type; + } + const Metadata *getType() const { return type; } + const TypeReferenceOwnership getReferenceOwnership() const { return referenceOwnership; } + bool isIndirect() const { return indirect; } + void setIndirect(bool value) { indirect = value; } + void setReferenceOwnership(TypeReferenceOwnership newOwnership) { + referenceOwnership = newOwnership; + } +}; + /// The layout of Any. using Any = OpaqueExistentialContainer; @@ -123,58 +146,63 @@ - (id)debugQuickLookObject; return std::make_tuple(T, Value); } -static bool loadSpecialReferenceStorage(OpaqueValue *fieldData, - const FieldType fieldType, - Any *outValue) { - // isWeak() implies a reference type via Sema. - if (!fieldType.isWeak()) - return false; - - auto type = fieldType.getType(); +static void copyWeakFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { assert(type->getKind() == MetadataKind::Optional); + auto *srcContainer = reinterpret_cast(fieldData); + auto *destClassContainer = reinterpret_cast(destContainer); + destClassContainer->Value = swift_unknownObjectWeakLoadStrong(&srcContainer->Value); + auto witnessTablesSize = type->vw_size() - sizeof(WeakClassExistentialContainer); + memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize); +} - auto *weakField = reinterpret_cast(fieldData); - auto *strongValue = swift_unknownObjectWeakLoadStrong(weakField); - - // Now that we have a strong reference, we need to create a temporary buffer - // from which to copy the whole value, which might be a native class-bound - // existential, which means we also need to copy n witness tables, for - // however many protocols are in the protocol composition. For example, if we - // are copying a: - // weak var myWeakProperty : (Protocol1 & Protocol2)? - // then we need to copy three values: - // - the instance - // - the witness table for Protocol1 - // - the witness table for Protocol2 - - auto *weakContainer = - reinterpret_cast(fieldData); - - // Create a temporary existential where we can put the strong reference. - // The allocateBuffer value witness requires a ValueBuffer to own the - // allocated storage. - ValueBuffer temporaryBuffer; - - auto *temporaryValue = reinterpret_cast( - type->allocateBufferIn(&temporaryBuffer)); - - // Now copy the entire value out of the parent, which will include the - // witness tables. - temporaryValue->Value = strongValue; - auto valueWitnessesSize = type->getValueWitnesses()->getSize() - - sizeof(WeakClassExistentialContainer); - memcpy(temporaryValue->getWitnessTables(), weakContainer->getWitnessTables(), - valueWitnessesSize); - - outValue->Type = type; - auto *opaqueValueAddr = type->allocateBoxForExistentialIn(&outValue->Buffer); - type->vw_initializeWithCopy(opaqueValueAddr, - reinterpret_cast(temporaryValue)); - - type->deallocateBufferIn(&temporaryBuffer); - swift_unknownObjectRelease(strongValue); - - return true; +static void copyUnownedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { + auto *srcContainer = reinterpret_cast(fieldData); + auto *destClassContainer = reinterpret_cast(destContainer); + destClassContainer->Value = swift_unknownObjectUnownedLoadStrong(&srcContainer->Value); + auto witnessTablesSize = type->vw_size() - sizeof(UnownedClassExistentialContainer); + memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize); +} + +static void copyUnmanagedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { + // Also known as "unowned(unsafe)". + // This is simpler than the unowned/weak cases because unmanaged + // references are fundamentally the same as strong ones, so we + // can use the regular strong reference support that already + // knows how to handle existentials and Obj-C references. + type->vw_initializeWithCopy(destContainer, fieldData); +} + +static AnyReturn copyFieldContents(OpaqueValue *fieldData, + const FieldType fieldType) { + Any outValue; + auto *type = fieldType.getType(); + outValue.Type = type; + auto ownership = fieldType.getReferenceOwnership(); + auto *destContainer = type->allocateBoxForExistentialIn(&outValue.Buffer); + + if (ownership.isStrong()) { + type->vw_initializeWithCopy(destContainer, fieldData); + } + + // Generate a conditional clause for every known ownership type. + // If this causes errors, it's because someone added a new ownership type + // to ReferenceStorage.def and missed some related updates. +#define REF_STORAGE(Name, ...) \ + else if (ownership.is##Name()) { \ + copy##Name##FieldContents(destContainer, type, fieldData); \ + } +#include "swift/AST/ReferenceStorage.def" + + else { + // The field was declared with a reference type we don't understand. + warning(0, "Value with unrecognized reference type is reflected as ()"); + // Clean up the buffer allocated above + type->deallocateBoxForExistentialIn(&outValue.Buffer); + // Return an existential containing Void + outValue.Type = &METADATA_SYM(EMPTY_TUPLE_MANGLING); + } + + return AnyReturn(outValue); } @@ -310,11 +338,7 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { "type '%*s' that claims to be reflectable. Its fields will show up as " "'unknown' in Mirrors\n", (int)typeName.length, typeName.data); - return {"unknown", - FieldType() - .withType(&METADATA_SYM(EMPTY_TUPLE_MANGLING)) - .withIndirect(false) - .withWeak(false)}; + return {"unknown", FieldType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))}; }; auto *baseDesc = base->getTypeContextDescriptor(); @@ -325,14 +349,13 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { if (!fields) return failedToFindMetadata(); - const FieldDescriptor &descriptor = *fields; - auto &field = descriptor.getFields()[index]; + auto &field = fields->getFields()[index]; // Bounds are always valid as the offset is constant. auto name = field.getFieldName(); // Enum cases don't always have types. if (!field.hasMangledTypeName()) - return {name, FieldType().withIndirect(field.isIndirectCase())}; + return {name, FieldType::untypedEnumCase(field.isIndirectCase())}; auto typeName = field.getMangledTypeName(); @@ -360,10 +383,10 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { (int)typeName.size(), typeName.data()); } - return {name, FieldType() - .withType(typeInfo.getMetadata()) - .withIndirect(field.isIndirectCase()) - .withWeak(typeInfo.isWeak())}; + auto fieldType = FieldType(typeInfo.getMetadata()); + fieldType.setIndirect(field.isIndirectCase()); + fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership()); + return {name, fieldType}; } // Implementation for structs. @@ -397,7 +420,6 @@ AnyReturn subscript(intptr_t i, const char **outName, // Load the offset from its respective vector. auto fieldOffset = Struct->getFieldOffsets()[i]; - Any result; StringRef name; FieldType fieldInfo; std::tie(name, fieldInfo) = getFieldAt(type, i); @@ -409,15 +431,7 @@ AnyReturn subscript(intptr_t i, const char **outName, auto *bytes = reinterpret_cast(value); auto *fieldData = reinterpret_cast(bytes + fieldOffset); - bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result); - if (!didLoad) { - result.Type = fieldInfo.getType(); - auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer); - result.Type->vw_initializeWithCopy(opaqueValueAddr, - const_cast(fieldData)); - } - - return AnyReturn(result); + return copyFieldContents(fieldData, fieldInfo); } }; @@ -559,7 +573,6 @@ AnyReturn subscript(intptr_t i, const char **outName, #endif } - Any result; StringRef name; FieldType fieldInfo; std::tie(name, fieldInfo) = getFieldAt(type, i); @@ -571,15 +584,7 @@ AnyReturn subscript(intptr_t i, const char **outName, *outName = name.data(); *outFreeFunc = nullptr; - bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result); - if (!didLoad) { - result.Type = fieldInfo.getType(); - auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer); - result.Type->vw_initializeWithCopy(opaqueValueAddr, - const_cast(fieldData)); - } - - return AnyReturn(result); + return copyFieldContents(fieldData, fieldInfo); } #if SWIFT_OBJC_INTEROP diff --git a/test/stdlib/Mirror.swift b/test/stdlib/Mirror.swift index b2b09fe97a1f2..d3075ca6be337 100644 --- a/test/stdlib/Mirror.swift +++ b/test/stdlib/Mirror.swift @@ -509,6 +509,234 @@ mirrors.test("struct/WrapNSArray") { #endif // _runtime(_ObjC) +//===--- Weak and Unowned References --------------------------------------===// + +// Check that Mirror correctly reflects weak/unowned refs to both +// Swift and ObjC objects from Swift structs and classes. + +protocol WeakUnownedTestsP1: class { + func f1() -> Int +} + +protocol WeakUnownedTestsP2 { + func f2() -> String +} + +class WeakUnownedSwiftClass: WeakUnownedTestsP1, WeakUnownedTestsP2 { + let tracker = LifetimeTracked(0) + func f1() -> Int { return 2 } + func f2() -> String { return "b" } +} + +#if _runtime(_ObjC) +@objc class WeakUnownedObjCClass: NSObject, WeakUnownedTestsP1, WeakUnownedTestsP2 { + let tracker = LifetimeTracked(0) + func f1() -> Int { return 2 } + func f2() -> String { return "b" } +} +#endif + +// The four tests below populate objects with different types +// but identical overall structure. +// This function is used by all four to verify that the resulting +// Mirror objects have the expected entries. +func verifyWeakUnownedReflection + + (_ m: Mirror, expectedClass: ExpectedClass.Type ) +{ + let i = m.children.makeIterator() + + func verifyClassField(child: (label: String?, value: Any), name: String) { + expectEqual(child.label, name) + let v = child.value as? ExpectedClass + expectNotNil(v) + expectEqual(v!.f1(), 2) + } + + func verifyExistentialField(child: (label: String?, value: Any), name: String) { + expectEqual(child.label, name) + expectNotNil(child.value) + + // FIXME: These casts are currently broken (Dec 2019) + // Once they are fixed, enable additional checks: + //let vp1 = child.value as? WeakUnownedTestsP1 + //expectNotNil(vp1) + //expectEqual(vp1!.f1(), 2) + //let vp2 = child.value as? WeakUnownedTestsP2 + //expectNotNil(vp2) + //expectEqual(vp2!.f2(), "b") + + let v = child.value as? ExpectedClass + expectNotNil(v) + expectEqual(v!.f1(), 2) + let m = Mirror(reflecting: v!) + expectEqual(m.displayStyle, .`class`) + // TODO: Find a way to verify that the existential wrapper carries + // the expected protocol witnesses. The current Swift runtime does + // a very good job of hiding this from users. + } + + verifyClassField(child: i.next()!, name: "strong_class") + verifyExistentialField(child: i.next()!, name: "strong_existential") + verifyClassField(child: i.next()!, name: "weak_class") + verifyExistentialField(child: i.next()!, name: "weak_existential") + verifyClassField(child: i.next()!, name: "unowned_safe_class") + verifyExistentialField(child: i.next()!, name: "unowned_safe_existential") + + verifyClassField(child: i.next()!, name: "unowned_unsafe_class") + verifyExistentialField(child: i.next()!, name: "unowned_unsafe_existential") + expectNil(i.next()) + + // The original bug report from SR-5289 crashed when the print() code + // attempted to reflect the contents of an unowned field. + // The tests above _should_ suffice to check this, but let's print everything + // anyway just to be sure. + for c in m.children { + print(c.label ?? "?", c.value) + } +} + +#if _runtime(_ObjC) +// Related: SR-5289 reported a crash when using Mirror to inspect Swift +// class objects containing unowned pointers to Obj-C class objects. +mirrors.test("Weak and Unowned Obj-C refs in class (SR-5289)") { + class SwiftClassWithWeakAndUnowned { + var strong_class: WeakUnownedObjCClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedObjCClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedObjCClass + unowned(safe) let unowned_safe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + unowned(unsafe) let unowned_unsafe_class: WeakUnownedObjCClass + unowned(unsafe) let unowned_unsafe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + + init(_ objc: WeakUnownedObjCClass) { + self.strong_class = objc + self.strong_existential = objc + self.weak_class = objc + self.weak_existential = objc + self.unowned_safe_class = objc + self.unowned_safe_existential = objc + self.unowned_unsafe_class = objc + self.unowned_unsafe_existential = objc + } + } + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + let objc = WeakUnownedObjCClass() + let classWithReferences = SwiftClassWithWeakAndUnowned(objc) + let m = Mirror(reflecting: classWithReferences) + expectEqual(m.displayStyle, .`class`) + expectEqual(m.description, "Mirror for SwiftClassWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftClassWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedObjCClass.self) + } +} + +mirrors.test("Weak and Unowned Obj-C refs in struct") { + struct SwiftStructWithWeakAndUnowned { + var strong_class: WeakUnownedObjCClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedObjCClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedObjCClass + unowned(safe) let unowned_safe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + unowned(unsafe) let unowned_unsafe_class: WeakUnownedObjCClass + unowned(unsafe) let unowned_unsafe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + + init(_ objc: WeakUnownedObjCClass) { + self.strong_class = objc + self.strong_existential = objc + self.weak_class = objc + self.weak_existential = objc + self.unowned_safe_class = objc + self.unowned_safe_existential = objc + self.unowned_unsafe_class = objc + self.unowned_unsafe_existential = objc + } + } + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + let objc = WeakUnownedObjCClass() + let structWithReferences = SwiftStructWithWeakAndUnowned(objc) + let m = Mirror(reflecting: structWithReferences) + expectEqual(m.displayStyle, .`struct`) + expectEqual(m.description, "Mirror for SwiftStructWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftStructWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedObjCClass.self) + } +} + +#endif + +mirrors.test("Weak and Unowned Swift refs in class") { + class SwiftClassWithWeakAndUnowned { + var strong_class: WeakUnownedSwiftClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedSwiftClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedSwiftClass + unowned(safe) let unowned_safe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + unowned(unsafe) let unowned_unsafe_class: WeakUnownedSwiftClass + unowned(unsafe) let unowned_unsafe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + + init(_ swift: WeakUnownedSwiftClass) { + self.strong_class = swift + self.strong_existential = swift + self.weak_class = swift + self.weak_existential = swift + self.unowned_safe_class = swift + self.unowned_safe_existential = swift + self.unowned_unsafe_class = swift + self.unowned_unsafe_existential = swift + } + } + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + let swift = WeakUnownedSwiftClass() + let classWithReferences = SwiftClassWithWeakAndUnowned(swift) + let m = Mirror(reflecting: classWithReferences) + expectEqual(m.displayStyle, .`class`) + expectEqual(m.description, "Mirror for SwiftClassWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftClassWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedSwiftClass.self) + } +} + +mirrors.test("Weak and Unowned Swift refs in struct") { + struct SwiftStructWithWeakAndUnowned { + var strong_class: WeakUnownedSwiftClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedSwiftClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedSwiftClass + unowned(safe) let unowned_safe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + unowned(unsafe) let unowned_unsafe_class: WeakUnownedSwiftClass + unowned(unsafe) let unowned_unsafe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + + init(_ swift: WeakUnownedSwiftClass) { + self.strong_class = swift + self.strong_existential = swift + self.weak_class = swift + self.weak_existential = swift + self.unowned_safe_class = swift + self.unowned_safe_existential = swift + self.unowned_unsafe_class = swift + self.unowned_unsafe_existential = swift + } + } + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + let swift = WeakUnownedSwiftClass() + let structWithReferences = SwiftStructWithWeakAndUnowned(swift) + let m = Mirror(reflecting: structWithReferences) + expectEqual(m.displayStyle, .`struct`) + expectEqual(m.description, "Mirror for SwiftStructWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftStructWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedSwiftClass.self) + } +} + //===--- Suppressed Superclass Mirrors ------------------------------------===// mirrors.test("Class/Root/NoSuperclassMirror") { class B : CustomReflectable { @@ -854,7 +1082,7 @@ struct GenericStructWithDefaultMirror { mirrors.test("Struct/Generic/DefaultMirror") { do { - var value = GenericStructWithDefaultMirror( + let value = GenericStructWithDefaultMirror( first: 123, second: ["abc", 456, 789.25]) var output = "" @@ -1616,7 +1844,7 @@ mirrors.test("Float") { } do { - var input: Float = 42.125 + let input: Float = 42.125 var output = "" dump(input, to: &output) @@ -1649,7 +1877,7 @@ mirrors.test("Double") { } do { - var input: Double = 42.125 + let input: Double = 42.125 var output = "" dump(input, to: &output) @@ -1745,9 +1973,9 @@ mirrors.test("FieldNamesBug") { } mirrors.test("MirrorMirror") { - var object = 1 - var mirror = Mirror(reflecting: object) - var mirrorMirror = Mirror(reflecting: mirror) + let object = 1 + let mirror = Mirror(reflecting: object) + let mirrorMirror = Mirror(reflecting: mirror) expectEqual(0, mirrorMirror.children.count) } @@ -1755,7 +1983,7 @@ mirrors.test("MirrorMirror") { mirrors.test("OpaquePointer/null") { // Don't crash on null pointers. rdar://problem/19708338 let pointer: OpaquePointer? = nil - let mirror = Mirror(reflecting: pointer) + let mirror = Mirror(reflecting: pointer as Any) expectEqual(0, mirror.children.count) } From 729609b1e905ac6f66d6022b626e9c478fdf73c5 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Tue, 17 Dec 2019 10:21:37 -0800 Subject: [PATCH 066/478] IRGen: Replace local utility function by existing function on IRGenModule NFC --- lib/IRGen/GenType.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 447df5d61fbd7..b8be36434f023 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -198,15 +198,11 @@ void LoadableTypeInfo::addScalarToAggLowering(IRGenModule &IGM, offset.asCharUnits() + storageSize.asCharUnits()); } -static llvm::Constant *asSizeConstant(IRGenModule &IGM, Size size) { - return llvm::ConstantInt::get(IGM.SizeTy, size.getValue()); -} - llvm::Value *FixedTypeInfo::getSize(IRGenFunction &IGF, SILType T) const { return FixedTypeInfo::getStaticSize(IGF.IGM); } llvm::Constant *FixedTypeInfo::getStaticSize(IRGenModule &IGM) const { - return asSizeConstant(IGM, getFixedSize()); + return IGM.getSize(getFixedSize()); } llvm::Value *FixedTypeInfo::getAlignmentMask(IRGenFunction &IGF, @@ -214,7 +210,7 @@ llvm::Value *FixedTypeInfo::getAlignmentMask(IRGenFunction &IGF, return FixedTypeInfo::getStaticAlignmentMask(IGF.IGM); } llvm::Constant *FixedTypeInfo::getStaticAlignmentMask(IRGenModule &IGM) const { - return asSizeConstant(IGM, Size(getFixedAlignment().getValue() - 1)); + return IGM.getSize(Size(getFixedAlignment().getValue() - 1)); } llvm::Value *FixedTypeInfo::getStride(IRGenFunction &IGF, SILType T) const { @@ -229,7 +225,7 @@ llvm::Value *FixedTypeInfo::getIsBitwiseTakable(IRGenFunction &IGF, SILType T) c isBitwiseTakable(ResilienceExpansion::Maximal) == IsBitwiseTakable); } llvm::Constant *FixedTypeInfo::getStaticStride(IRGenModule &IGM) const { - return asSizeConstant(IGM, getFixedStride()); + return IGM.getSize(getFixedStride()); } llvm::Value *FixedTypeInfo::isDynamicallyPackedInline(IRGenFunction &IGF, @@ -399,7 +395,7 @@ static llvm::Value *computeExtraTagBytes(IRGenFunction &IGF, IRBuilder &Builder, } auto *entryBB = Builder.GetInsertBlock(); - llvm::Value *size = asSizeConstant(IGM, fixedSize); + llvm::Value *size = IGM.getSize(fixedSize); auto *returnBB = llvm::BasicBlock::Create(Ctx); size = Builder.CreateZExtOrTrunc(size, int32Ty); // We know size < 4. From 0ea3bb1ff582f357522615b8c5a3fef22ea93c09 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 16 Dec 2019 16:12:58 -0800 Subject: [PATCH 067/478] EscapeAnalysis: rewrite canEscapeToUsePoint. Correctness: do not make any unenforced assumptions about how the connection graph is built (I don't think the previous assumption about the structure of the graph node mapped to a reference-type value would always hold if content nodes can be arbitrarily merged). Only make one assumption about the client code: the access being checked must be to some address within the provided value, not another object indirectly reachable from that value. Optimization: Allow escape analysis to prove that an addressable object does not escape even when one of its reference-type fields escapes. --- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 62 ++++---- .../escape_analysis_dead_store.sil | 138 ++++++++++++++++++ 2 files changed, 169 insertions(+), 31 deletions(-) create mode 100644 test/SILOptimizer/escape_analysis_dead_store.sil diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index c03da48549324..6ff7660b1a28b 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -2479,44 +2479,44 @@ bool EscapeAnalysis::mergeSummaryGraph(ConnectionGraph *SummaryGraph, return SummaryGraph->mergeFrom(Graph, Mapping); } -bool EscapeAnalysis::canEscapeToUsePoint(SILValue V, SILNode *UsePoint, - ConnectionGraph *ConGraph) { - - assert((FullApplySite::isa(UsePoint) || isa(UsePoint)) && - "use points are only created for calls and refcount instructions"); +// Return true if any content within the logical object pointed to by \p value +// escapes. +// +// Get the value's content node and check the escaping flag on all nodes within +// that object. An interior CG node points to content within the same object. +bool EscapeAnalysis::canEscapeToUsePoint(SILValue value, + SILInstruction *usePoint, + ConnectionGraph *conGraph) { - CGNode *Node = ConGraph->getNodeOrNull(V); - if (!Node) - return true; + assert((FullApplySite::isa(usePoint) || isa(usePoint)) + && "use points are only created for calls and refcount instructions"); - // First check if there are escape paths which we don't explicitly see - // in the graph. - if (Node->valueEscapesInsideFunction(V)) + CGNode *node = conGraph->getValueContent(value); + if (!node) return true; - // No hidden escapes: check if the Node is reachable from the UsePoint. - // Check if the object itself can escape to the called function. - if (ConGraph->isUsePoint(UsePoint, Node)) - return true; + // Follow points-to edges and return true if the current 'node' may escape at + // 'usePoint'. + while (node) { + // First check if 'node' may escape in a way not represented by the + // connection graph, assuming that it may represent part of the object + // pointed to by 'value'. If 'node' happens to represent another object + // indirectly reachabe from 'value', then it cannot actually escape to this + // usePoint, so passing the original value is still conservatively correct. + if (node->valueEscapesInsideFunction(value)) + return true; - assert(isPointer(V) && "should not have a node for a non-pointer"); - - // Check if the object "content" can escape to the called function. - // This will catch cases where V is a reference and a pointer to a stored - // property escapes. - // It's also important in case of a pointer assignment, e.g. - // V = V1 - // apply(V1) - // In this case the apply is only a use-point for V1 and V1's content node. - // As V1's content node is the same as V's content node, we also make the - // check for the content node. - CGNode *ContentNode = getValueContent(ConGraph, V); - if (ContentNode->valueEscapesInsideFunction(V)) - return true; + // No hidden escapes; check if 'usePoint' may access memory at 'node'. + if (conGraph->isUsePoint(usePoint, node)) + return true; - if (ConGraph->isUsePoint(UsePoint, ContentNode)) - return true; + if (!node->isInterior()) + break; + // Continue to check for escaping content whenever 'content' may point to + // the same object as 'node'. + node = node->getContentNodeOrNull(); + } return false; } diff --git a/test/SILOptimizer/escape_analysis_dead_store.sil b/test/SILOptimizer/escape_analysis_dead_store.sil new file mode 100644 index 0000000000000..9a649975407cb --- /dev/null +++ b/test/SILOptimizer/escape_analysis_dead_store.sil @@ -0,0 +1,138 @@ +// RUN: %target-sil-opt %s -dead-store-elim -enable-sil-verify-all | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +final class X { + init() +} + +public struct S { + @_hasStorage var x: X { get set } + @_hasStorage var i: Int { get set } + init(x: X, i: Int) +} + +@_hasStorage @_hasInitialValue var gg: X { get set } + +@inline(never) func takex(_ x: X) + +sil [noinline] @takeX : $@convention(thin) (@guaranteed X) -> () + +// Test that escape analysis does not consider an inout argument to +// escape at a call site even though it's reference-type field does +// escape. Dead store elimination asks MemoryBehaviorVisitor whether +// the apply may read from the inout argument. This call into +// canEscapeToUsePoint, which should return false because the inout +// structure itself is not exposed to the call, only it's +// reference-type field is. +// +// CHECK-LABEL: sil @testInoutNoEscape +// CHECK-NOT: store +// CHECK: apply +// CHECK: store +// CHECK-NOT: store +// CHECK: } // end sil function 'testInoutNoEscape' +sil @testInoutNoEscape : $@convention(thin) (@inout S, @guaranteed S) -> () { +bb0(%0 : $*S, %1 : $S): + %4 = struct_extract %1 : $S, #S.x + %5 = struct_element_addr %0 : $*S, #S.x + %6 = load %5 : $*X + strong_retain %4 : $X + strong_release %6 : $X + store %1 to %0 : $*S + %10 = function_ref @takeX : $@convention(thin) (@guaranteed X) -> () + strong_retain %4 : $X + %12 = apply %10(%4) : $@convention(thin) (@guaranteed X) -> () + release_value %1 : $S + store %1 to %0 : $*S + %15 = tuple () + return %15 : $() +} + +// ============================================================================= +// Test that a store writing back into a container is not eliminated +// when the container's interior pointer later escapes into a function +// that reads from the pointer. + +final internal class TestArrayContainer { + @_hasStorage @_hasInitialValue internal final var pointer: UnsafeMutablePointer { get set } + @_hasStorage @_hasInitialValue internal final var storage: ContiguousArray { get set } + @_optimize(none) @inline(never) internal final func append(_ arg: Int32) + internal final func va_list() -> UnsafeMutableRawPointer + init() +} + +sil @UnsafeMutablePointer_load_Int64 : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> // user: %5 +// ContiguousArray.append(_:) +sil @$ss15ContiguousArrayV6appendyyxnF : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () + + +// Helper that reads from a raw pointer. +sil hidden [noinline] @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool { +bb0(%0 : $UnsafeMutableRawPointer): + %1 = integer_literal $Builtin.Int64, 0 + %2 = struct $Int64 (%1 : $Builtin.Int64) + %3 = function_ref @UnsafeMutablePointer_load_Int64 : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> + %4 = apply %3(%2, %0) : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> // users: %18, %6 + %5 = integer_literal $Builtin.Int1, -1 + %6 = struct $Bool (%5 : $Builtin.Int1) + return %6 : $Bool +} + +// TestArrayContainer.append(_:) +// Helper that produces a nonempty array. +sil hidden [noinline] [Onone] @TestArrayContainer_append : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () { +bb0(%0 : $Int32, %1 : $TestArrayContainer): + %2 = alloc_stack $Int32 + store %0 to %2 : $*Int32 + %4 = ref_element_addr %1 : $TestArrayContainer, #TestArrayContainer.storage + %5 = function_ref @$ss15ContiguousArrayV6appendyyxnF : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () + %6 = apply %5(%2, %4) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () + dealloc_stack %2 : $*Int32 + %8 = tuple () + return %8 : $() +} + +// CHECK-LABEL: sil [noinline] @testContainerPointer : $@convention(thin) () -> Bool { +// CHECK: [[ALLOC:%.*]] = alloc_ref [stack] $TestArrayContainer +// CHECK: [[PTR:%.*]] = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.pointer +// CHECK: [[LOAD:%.*]] = load %{{.*}} : $*__ContiguousArrayStorageBase +// CHECK: [[ELTS:%.*]] = ref_tail_addr [[LOAD]] : $__ContiguousArrayStorageBase, $Int32 +// CHECK: [[ELTPTR:%.*]] = address_to_pointer [[ELTS]] : $*Int32 to $Builtin.RawPointer +// CHECK: [[UMP:%.*]] = struct $UnsafeMutablePointer ([[ELTPTR]] : $Builtin.RawPointer) +// CHECK: store [[UMP]] to [[PTR]] : $*UnsafeMutablePointer +// CHECK: [[F:%.*]] = function_ref @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool +// CHECK: apply [[F]](%{{.*}}) : $@convention(thin) (UnsafeMutableRawPointer) -> Bool +// CHECK: fix_lifetime %0 : $TestArrayContainer +// CHECK-LABEL: } // end sil function 'testContainerPointer' +sil [noinline] @testContainerPointer : $@convention(thin) () -> Bool { +bb0: + %0 = alloc_ref [stack] $TestArrayContainer + %1 = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.pointer + %2 = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.storage + %3 = integer_literal $Builtin.Int32, 42 + %4 = struct $Int32 (%3 : $Builtin.Int32) + %5 = function_ref @TestArrayContainer_append : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () + %6 = apply %5(%4, %0) : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () + %7 = struct_element_addr %2 : $*ContiguousArray, #ContiguousArray._buffer + %8 = struct_element_addr %7 : $*_ContiguousArrayBuffer, #_ContiguousArrayBuffer._storage + %9 = load %8 : $*__ContiguousArrayStorageBase + %10 = ref_tail_addr %9 : $__ContiguousArrayStorageBase, $Int32 + %11 = address_to_pointer %10 : $*Int32 to $Builtin.RawPointer + %12 = struct $UnsafeMutablePointer (%11 : $Builtin.RawPointer) + store %12 to %1 : $*UnsafeMutablePointer + %14 = address_to_pointer %1 : $*UnsafeMutablePointer to $Builtin.RawPointer + %15 = struct $UnsafeMutableRawPointer (%14 : $Builtin.RawPointer) + %16 = function_ref @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool + %17 = apply %16(%15) : $@convention(thin) (UnsafeMutableRawPointer) -> Bool + fix_lifetime %0 : $TestArrayContainer + set_deallocating %0 : $TestArrayContainer + strong_release %9 : $__ContiguousArrayStorageBase + dealloc_ref %0 : $TestArrayContainer + dealloc_ref [stack] %0 : $TestArrayContainer + return %17 : $Bool +} From fa47f19b4c9374fef47f64b07373d0a1db169719 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 16 Dec 2019 16:15:03 -0800 Subject: [PATCH 068/478] EscapeAnalysis: Add and update tests. Fix tests for changes to EscapeAnalysis output - New node flags - Only content nodes are marked escaping - Content nodes are eagerly created - No defer edges for block args --- test/SILOptimizer/escape_analysis.sil | 723 +++++++++++------- .../sil_combine_concrete_existential.swift | 2 +- 2 files changed, 432 insertions(+), 293 deletions(-) diff --git a/test/SILOptimizer/escape_analysis.sil b/test/SILOptimizer/escape_analysis.sil index 25085c9609fbc..e1bf8c7f06b79 100644 --- a/test/SILOptimizer/escape_analysis.sil +++ b/test/SILOptimizer/escape_analysis.sil @@ -77,8 +77,8 @@ struct FourFields { sil @take_indirect_tuple : $@convention(method) (@in (Int, ())) -> () // CHECK-LABEL: CG of handle_undef -// CHECK: Val %2 Esc: G, Succ: (%2.1) -// CHECK: Con %2.1 Esc: G, Succ: +// CHECK: Val %2 Esc: , Succ: (%2.1) +// CHECK: Con [ref] %2.1 Esc: G, Succ: // CHECK: End sil @handle_undef : $@convention(thin) (Int) -> () { bb0(%0 : $Int): @@ -96,11 +96,12 @@ bb0(%0 : $Int): // CHECK-LABEL: CG of test_simple // CHECK-NEXT: Arg %0 Esc: A, Succ: (%5) -// CHECK-NEXT: Arg %1 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: %1 -// CHECK-NEXT: Con %5 Esc: A, Succ: %2 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Con %3.1 Esc: A, Succ: %1 +// CHECK-NEXT: Con [ref] %5 Esc: A, Succ: %2 // CHECK-NEXT: End sil @test_simple : $@convention(thin) (@inout Y, @owned X) -> () { bb0(%0 : $*Y, %1 : $X): @@ -117,12 +118,12 @@ bb0(%0 : $*Y, %1 : $X): // Test if a deferring edge is created for a block argument. // CHECK-LABEL: CG of deferringEdge -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %4) -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: %3, Succ: ([rc] %4), %0 -// CHECK-NEXT: Con [rc] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%4) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%4), %0 +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) // CHECK-NEXT: Con %4.1 Esc: A, Succ: %1 -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @deferringEdge : $@convention(thin) (@owned LinkedNode, @owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode, %1 : $LinkedNode): @@ -137,8 +138,10 @@ bb1(%3 : $LinkedNode): // Test a local object just escaping via return. // CHECK-LABEL: CG of escapes_via_return -// CHECK-NEXT: Val %0 Esc: R, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: R, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: R, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @escapes_via_return : $@convention(thin) () -> @owned X { bb0: @@ -149,14 +152,14 @@ bb0: // A linear chain of assignments is collapsed to a single node. // CHECK-LABEL: CG of test_linked_list -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Val %1 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%13) -// CHECK-NEXT: Val %4 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Val %7 Esc: %11, Succ: ([rc] %2) -// CHECK-NEXT: Val %11 Esc: %11, Succ: ([rc] %2), %7, %13 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Val [ref] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%13) +// CHECK-NEXT: Val [ref] %4 Esc: A, Succ: (%2) +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%2) +// CHECK-NEXT: Val [ref] %11 Esc: , Succ: (%2), %7, %13 // CHECK-NEXT: Con %13 Esc: A, Succ: %0, %1, %4 -// CHECK-NEXT: Ret return Esc: R, Succ: %13 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %13 // CHECK-NEXT: End sil @test_linked_list : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -184,14 +187,14 @@ bb2: // The same example as above but distributed over two functions. // CHECK-LABEL: CG of create_chain -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %8) -// CHECK-NEXT: Val %1 Esc: A, Succ: ([rc] %8) -// CHECK-NEXT: Val %4 Esc: A, Succ: ([rc] %8) -// CHECK-NEXT: Val %7 Esc: %11, Succ: ([rc] %8) -// CHECK-NEXT: Con [rc] %8 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%8) +// CHECK-NEXT: Val [ref] %1 Esc: A, Succ: (%8) +// CHECK-NEXT: Val [ref] %4 Esc: A, Succ: (%8) +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%8) +// CHECK-NEXT: Con [int] %8 Esc: A, Succ: (%8.1) // CHECK-NEXT: Con %8.1 Esc: A, Succ: %0, %1, %4 -// CHECK-NEXT: Val %11 Esc: R, Succ: ([rc] %8), %8.1 -// CHECK-NEXT: Ret return Esc: R, Succ: %11 +// CHECK-NEXT: Val [ref] %11 Esc: , Succ: (%8), %8.1 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %11 // CHECK-NEXT: End sil @create_chain : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -211,11 +214,11 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of loadNext -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Val %2 Esc: %2, Succ: ([rc] %3), %0, %4 -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %4 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%3) +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%3), %0, %4 +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con %4 Esc: A, Succ: (%3) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @loadNext : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -233,15 +236,17 @@ bb2: // Content nodes in the callee are duplicated in the caller. // CHECK-LABEL: CG of call_load_next3 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: ([rc] %0.3) -// CHECK-NEXT: Con [rc] %0.3 Esc: A, Succ: (%0.4) -// CHECK-NEXT: Con %0.4 Esc: A, Succ: ([rc] %0.5) -// CHECK-NEXT: Con [rc] %0.5 Esc: A, Succ: (%0.6) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con [int] %0.3 Esc: A, Succ: (%0.4) +// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%0.5) +// CHECK-NEXT: Con [int] %0.5 Esc: A, Succ: (%0.6) // CHECK-NEXT: Con %0.6 Esc: A, Succ: -// CHECK-NEXT: Val %2 Esc: R, Succ: %0.6 -// CHECK-NEXT: Ret return Esc: R, Succ: %2 +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1), %0.6 +// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: (%2.2) +// CHECK-NEXT: Con %2.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %2 // CHECK-NEXT: End sil @call_load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -251,14 +256,16 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of load_next3 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %4 Esc: A, Succ: ([rc] %5) -// CHECK-NEXT: Con [rc] %5 Esc: A, Succ: (%6) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con %4 Esc: A, Succ: (%5) +// CHECK-NEXT: Con [int] %5 Esc: A, Succ: (%6) // CHECK-NEXT: Con %6 Esc: A, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %6 +// CHECK-NEXT: Con [int] %6.1 Esc: A, Succ: (%6.2) +// CHECK-NEXT: Con %6.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -277,10 +284,12 @@ sil_global @global_x : $X // The argument escapes because it is stored to a global variable in the callee. // CHECK-LABEL: CG of call_store_pointer -// CHECK-NEXT: Arg %0 Esc: G, Succ: ([rc] %5) -// CHECK-NEXT: Con [rc] %5 Esc: G, Succ: (%6) +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: (%5) +// CHECK-NEXT: Con [int] %5 Esc: G, Succ: (%6) // CHECK-NEXT: Con %6 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %6 +// CHECK-NEXT: Con [int] %6.1 Esc: G, Succ: (%6.2) +// CHECK-NEXT: Con [ref] %6.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @call_store_pointer : $@convention(thin) (@owned Pointer) -> @owned X { bb0(%0 : $Pointer): @@ -294,9 +303,9 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of store_pointer -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @store_pointer : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -310,10 +319,10 @@ bb0(%0 : $Pointer): // global variable in the callee. // CHECK-LABEL: CG of store_content -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %4 -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%3) +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %4 +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) // CHECK-NEXT: Con %4 Esc: G, Succ: // CHECK-NEXT: End sil @store_content : $@convention(thin) (@owned Pointer) -> () { @@ -328,10 +337,12 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of call_store_content -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %4) -// CHECK-NEXT: Con [rc] %4 Esc: A, Succ: (%5) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%5) // CHECK-NEXT: Con %5 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %5 +// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2) +// CHECK-NEXT: Con [ref] %5.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @call_store_content : $@convention(thin) (@owned Pointer) -> @owned X { bb0(%0 : $Pointer): @@ -345,9 +356,9 @@ bb0(%0 : $Pointer): // CHECK-LABEL: CG of copy_addr_content // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1 +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.1 // CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %1.1 Esc: A, Succ: // CHECK-NEXT: End sil @copy_addr_content : $@convention(thin) (@in_guaranteed Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -358,9 +369,9 @@ bb0(%0: $*Int32, %1: $*Int32): // CHECK-LABEL: CG of copy_addr_take_content // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1 +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.1 // CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %1.1 Esc: A, Succ: // CHECK-NEXT: End sil @copy_addr_take_content : $@convention(thin) (@in Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -370,10 +381,10 @@ bb0(%0: $*Int32, %1: $*Int32): } // CHECK-LABEL: CG of copy_addr_noinit_content -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: +// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: // CHECK-NEXT: End sil @copy_addr_noinit_content : $@convention(thin) (@in Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -383,10 +394,10 @@ bb0(%0: $*Int32, %1: $*Int32): } // CHECK-LABEL: CG of call_copy_addr_content -// CHECK-NEXT: Val %0 Esc: %3, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: %3, Succ: %1.1 -// CHECK-NEXT: Val %1 Esc: %3, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: %3, Succ: +// CHECK-NEXT: Val %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: %3, Succ: %1.1 +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: %3, Succ: // CHECK-NEXT: End sil @call_copy_addr_content : $@convention(thin) () -> () { %0 = alloc_stack $Int32 @@ -404,14 +415,17 @@ sil @call_copy_addr_content : $@convention(thin) () -> () { // of Y's box _could_ capture Y.x in Y's deinit. // CHECK-LABEL: CG of test_partial_apply -// CHECK-NEXT: Arg %1 Esc: G, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: %14,%15,%17, Succ: ([rc] %7) -// CHECK-NEXT: Val %6 Esc: %14,%15,%16, Succ: ([rc] %7) -// CHECK-NEXT: Con [rc] %7 Esc: %14,%15,%16,%17, Succ: (%7.1) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %1.2 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: +// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%1.1), %1 +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%7) +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: (%7) +// CHECK-NEXT: Con [int] %7 Esc: %14,%15,%16,%17, Succ: (%7.1) // CHECK-NEXT: Con %7.1 Esc: %14,%15,%16,%17, Succ: %2 -// CHECK-NEXT: Val %12 Esc: %14,%15, Succ: %3, %6 +// CHECK-NEXT: Val [ref] %12 Esc: , Succ: %3, %6 // CHECK-NEXT: End sil @test_partial_apply : $@convention(thin) (Int64, @owned X, @owned Y) -> Int64 { bb0(%0 : $Int64, %1 : $X, %2 : $Y): @@ -434,16 +448,21 @@ bb0(%0 : $Int64, %1 : $X, %2 : $Y): } // CHECK-LABEL: CG of closure1 -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Arg %2 Esc: A, Succ: ([rc] %4) -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%3) +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%3.1) // CHECK-NEXT: Con %3.1 Esc: A, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: G, Succ: -// CHECK-NEXT: Con [rc] %4 Esc: A, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: A, Succ: ([rc] %4.2) -// CHECK-NEXT: Con [rc] %4.2 Esc: G, Succ: -// CHECK-NEXT: Val %7 Esc: %8, Succ: %2 +// CHECK-NEXT: Con [int] %3.2 Esc: A, Succ: (%3.3) +// CHECK-NEXT: Con %3.3 Esc: A, Succ: (%3.4) +// CHECK-NEXT: Con %3.4 Esc: G, Succ: +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con %4.1 Esc: A, Succ: (%4.2) +// CHECK-NEXT: Con [int] %4.2 Esc: A, Succ: +// CHECK-NEXT: Con %4.3 Esc: A, Succ: (%0.1), %0 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: %2 // CHECK-NEXT: End sil @closure1 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } , @owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } , %2 : $<τ_0_0> { var τ_0_0 } ): @@ -458,12 +477,13 @@ bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } , %2 : $<τ_0_0> { var τ_0_0 } // CHECK-LABEL: CG of closure2 -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: A, Succ: ([rc] %4) -// CHECK-NEXT: Con [rc] %4 Esc: G, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con %0.1 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con %4.1 Esc: A, Succ: %0 // CHECK-NEXT: End sil @closure2 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } ) -> () { bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } ): @@ -479,11 +499,13 @@ bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } ): // Test partial_apply. The box escapes in the callee. // CHECK-LABEL: CG of test_escaped_box -// CHECK-NEXT: Val %1 Esc: G, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: G, Succ: (%2.1) +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: G, Succ: (%2.1) // CHECK-NEXT: Con %2.1 Esc: G, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: G, Succ: -// CHECK-NEXT: Val %6 Esc: G, Succ: %1 +// CHECK-NEXT: Con [int] %2.2 Esc: G, Succ: +// CHECK-NEXT: Con %2.3 Esc: G, Succ: +// CHECK-NEXT: Con %2.4 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1 // CHECK-NEXT: End sil @test_escaped_box : $@convention(thin) (Int64) -> Int64 { bb0(%0 : $Int64): @@ -502,10 +524,12 @@ bb0(%0 : $Int64): } // CHECK-LABEL: CG of let_box_escape -// CHECK-NEXT: Arg %0 Esc: G, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: G, Succ: (%1.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: G, Succ: (%1.1) // CHECK-NEXT: Con %1.1 Esc: G, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Con [int] %1.2 Esc: G, Succ: (%1.3) +// CHECK-NEXT: Con %1.3 Esc: G, Succ: (%1.4) +// CHECK-NEXT: Con %1.4 Esc: G, Succ: // CHECK-NEXT: End sil @let_box_escape : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $<τ_0_0> { var τ_0_0 } ): @@ -524,9 +548,10 @@ sil @takebox : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> ( // The partial_apply itself escapes and therefore also the box escapes. // CHECK-LABEL: CG of test_escaped_partial_apply -// CHECK-NEXT: Val %1 Esc: G, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: G, Succ: -// CHECK-NEXT: Val %6 Esc: G, Succ: %1 +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: G, Succ: +// CHECK-NEXT: Con %2.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1 // CHECK-NEXT: End sil @test_escaped_partial_apply : $@convention(thin) (Int64) -> () { bb0(%0 : $Int64): @@ -544,10 +569,12 @@ bb0(%0 : $Int64): } // CHECK-LABEL: CG of closure3 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%1.1) // CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Con [int] %1.2 Esc: A, Succ: +// CHECK-NEXT: Con %1.3 Esc: A, Succ: (%1.4) +// CHECK-NEXT: Con %1.4 Esc: G, Succ: // CHECK-NEXT: End sil @closure3 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $<τ_0_0> { var τ_0_0 } ): @@ -564,11 +591,13 @@ sil @take_partial_apply : $@convention(thin) (@owned @callee_owned () -> Int64) sil_global @global_ln : $LinkedNode // CHECK-LABEL: CG of load_next_recursive -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) // CHECK-NEXT: Con %2 Esc: G, Succ: -// CHECK-NEXT: Val %4 Esc: G, Succ: %2 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1), %2 +// CHECK-NEXT: Con [int] %4.1 Esc: G, Succ: (%4.2) +// CHECK-NEXT: Con %4.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @load_next_recursive : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -580,12 +609,13 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of let_escape -// CHECK-NEXT: Arg %0 Esc: G, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0 -// CHECK-NEXT: Val %4 Esc: G, Succ: %0 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %0 +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%0.1), %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @let_escape : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -597,12 +627,12 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of return_same -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %3.1) -// CHECK-NEXT: Val %3 Esc: G, Succ: ([rc] %3.1), %3.2 -// CHECK-NEXT: Con [rc] %3.1 Esc: G, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: G, Succ: ([rc] %3.1) -// CHECK-NEXT: Val %5 Esc: R, Succ: %0, %3 -// CHECK-NEXT: Ret return Esc: R, Succ: %5 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%5.1) +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%5.1), %5.2 +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%5.1), %0, %3 +// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2) +// CHECK-NEXT: Con %5.2 Esc: G, Succ: (%5.1) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @return_same : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -620,15 +650,15 @@ bb2(%5 : $LinkedNode): // Another recursion test. // CHECK-LABEL: CG of loadNext2 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: A, Succ: ([rc] %2.1) -// CHECK-NEXT: Con [rc] %2.1 Esc: A, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: A, Succ: ([rc] %2.3) -// CHECK-NEXT: Con [rc] %2.3 Esc: A, Succ: (%2.4) -// CHECK-NEXT: Con %2.4 Esc: A, Succ: ([rc] %2.3) -// CHECK-NEXT: Val %4 Esc: R, Succ: %2.2, %2.4 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: (%2.2) +// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1), %2.2, %4.2 +// CHECK-NEXT: Con [int] %4.1 Esc: A, Succ: (%4.2) +// CHECK-NEXT: Con %4.2 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @loadNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -640,14 +670,14 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of returnNext2 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %5) -// CHECK-NEXT: Val %3 Esc: R, Succ: ([rc] %3.1), %3.2 -// CHECK-NEXT: Con [rc] %3.1 Esc: A, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: A, Succ: ([rc] %3.1) -// CHECK-NEXT: Con [rc] %5 Esc: A, Succ: (%6) -// CHECK-NEXT: Con %6 Esc: A, Succ: ([rc] %3.1) -// CHECK-NEXT: Val %8 Esc: R, Succ: %3, %6 -// CHECK-NEXT: Ret return Esc: R, Succ: %8 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%5) +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%8.1), %8.2 +// CHECK-NEXT: Con [int] %5 Esc: A, Succ: (%6) +// CHECK-NEXT: Con %6 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Val [ref] %8 Esc: , Succ: (%8.1), %3, %6 +// CHECK-NEXT: Con [int] %8.1 Esc: A, Succ: (%8.2) +// CHECK-NEXT: Con %8.2 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %8 // CHECK-NEXT: End sil @returnNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -670,12 +700,12 @@ bb3(%8 : $LinkedNode): // A single-cycle recursion test. // CHECK-LABEL: CG of single_cycle_recursion -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Val %5 Esc: R, Succ: ([rc] %2), %3 -// CHECK-NEXT: Val %7 Esc: R, Succ: %0, %5 -// CHECK-NEXT: Ret return Esc: R, Succ: %7 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: A, Succ: (%2) +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%2), %3 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%2), %0, %5 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %7 // CHECK-NEXT: End sil @single_cycle_recursion : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -695,9 +725,11 @@ bb2(%5 : $LinkedNode): // Test if a try_apply is represented correctly in the connection graph. // CHECK-LABEL: CG of call_throwing_func -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: R, Succ: %0 -// CHECK-NEXT: Ret return Esc: R, Succ: %3 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%0.1), %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @call_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -712,10 +744,12 @@ bb2(%5 : $Error): } // CHECK-LABEL: CG of throwing_func -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -735,11 +769,13 @@ bb2: // Test if a try_apply to an unknown function is handled correctly. // CHECK-LABEL: CG of call_unknown_throwing_func -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %3 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [int] %3.1 Esc: G, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @call_unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -758,12 +794,14 @@ sil @unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error // Test that the deinit of a box itself does not capture anything. // CHECK-LABEL: CG of test_release_of_partial_apply_with_box -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: %6, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: %6, Succ: (%2.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con %0.3 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: %6, Succ: (%2.1) // CHECK-NEXT: Con %2.1 Esc: %6, Succ: %0 -// CHECK-NEXT: Val %5 Esc: %6, Succ: %1 +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: %1 // CHECK-NEXT: End sil @test_release_of_partial_apply_with_box : $@convention(thin) (@owned Y) -> () { bb0(%0 : $Y): @@ -782,9 +820,9 @@ sil @take_y_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () // Test is an unknown value is merged correctly into the caller graph. // CHECK-LABEL: CG of store_to_unknown_reference -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: ([rc] %3) -// CHECK-NEXT: Con [rc] %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: G, Succ: (%3.1) // CHECK-NEXT: Con %3.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @store_to_unknown_reference : $@convention(thin) (@owned X) -> () { @@ -798,9 +836,10 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of get_reference -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %1 +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: G, Succ: + // CHECK-NEXT: Ret [ref] return Esc: , Succ: %1 // CHECK-NEXT: End sil @get_reference : $@convention(thin) () -> @owned Y { bb0: @@ -816,9 +855,11 @@ sil @unknown_get_reference : $@convention(thin) () -> @owned Y sil @unknown_set_y : $@convention(thin) () -> @out Y // CHECK-LABEL: CG of get_y -// CHECK-NEXT: Val %0 Esc: G, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %3 +// CHECK-NEXT: Val %0 Esc: , Succ: (%3) +// CHECK-NEXT: Con [ref] %3 Esc: G, Succ: +// CHECK-NEXT: Con [int] %3.1 Esc: G, Succ: (%3.2) +// CHECK-NEXT: Con %3.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @get_y : $@convention(thin) () -> @owned Y { bb0: @@ -831,9 +872,9 @@ bb0: } // CHECK-LABEL: CG of create_and_store_x -// CHECK-NEXT: Val %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: ([rc] %3) -// CHECK-NEXT: Con [rc] %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Val [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: G, Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: G, Succ: (%3.1) // CHECK-NEXT: Con %3.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @create_and_store_x : $@convention(thin) () -> () { @@ -850,11 +891,13 @@ bb0: // Test types which are considered as pointers. // CHECK-LABEL: CG of pointer_types -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: R, Succ: %0, %1 -// CHECK-NEXT: Val %7 Esc: R, Succ: %4 -// CHECK-NEXT: Ret return Esc: R, Succ: %7 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %0, %1 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%7.1), %4 +// CHECK-NEXT: Con [int] %7.1 Esc: A, Succ: (%7.2) +// CHECK-NEXT: Con %7.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %7 // CHECK-NEXT: End sil @pointer_types : $@convention(thin) (@owned Y, @owned Y) -> @owned Y { bb0(%0 : $Y, %1 : $Y): @@ -873,9 +916,9 @@ bb1(%7 : $(Pointer, Pointer)): // CHECK-LABEL: CG of defer_edge_cycle // CHECK-NEXT: Arg %0 Esc: A, Succ: (%2) // CHECK-NEXT: Arg %1 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %2 Esc: A, Succ: ([rc] %6), %4 -// CHECK-NEXT: Con %4 Esc: A, Succ: %2 -// CHECK-NEXT: Con [rc] %6 Esc: A, Succ: (%7) +// CHECK-NEXT: Con [ref] %2 Esc: A, Succ: (%6), %4 +// CHECK-NEXT: Con [ref] %4 Esc: A, Succ: %2 +// CHECK-NEXT: Con [int] %6 Esc: A, Succ: (%7) // CHECK-NEXT: Con %7 Esc: A, Succ: // CHECK-NEXT: End sil @defer_edge_cycle : $@convention(thin) (@inout Y, @inout Y) -> () { @@ -891,9 +934,10 @@ entry(%0 : $*Y, %1 : $*Y): } // CHECK-LABEL: CG of take_c_func -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @take_c_func : $@convention(thin) (@convention(c) () -> ()) -> @convention(c) () -> () { bb0(%0 : $@convention(c) () -> ()): @@ -910,8 +954,9 @@ bb0: } // CHECK-LABEL: CG of pass_c_func -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: +// CHECK-NEXT: Con %2.2 Esc: G, Succ: // CHECK-NEXT: End sil @pass_c_func : $@convention(thin) () -> () { bb0: @@ -924,12 +969,14 @@ bb0: // CHECK-LABEL: CG of test_select_enum -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: -// CHECK-NEXT: Arg %3 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: R, Succ: %1, %2, %3 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: +// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %1, %2, %3 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @test_select_enum : $@convention(thin) (PointerEnum2, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $PointerEnum2, %1 : $X, %2 : $X, %3 : $X): @@ -939,11 +986,13 @@ bb0(%0 : $PointerEnum2, %1 : $X, %2 : $X, %3 : $X): // CHECK-LABEL: CG of test_select_enum_addr // CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: -// CHECK-NEXT: Arg %3 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: R, Succ: %1, %2, %3 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %1, %2, %3 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @test_select_enum_addr : $@convention(thin) (@in PointerEnum2, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $*PointerEnum2, %1 : $X, %2 : $X, %3 : $X): @@ -952,11 +1001,13 @@ bb0(%0 : $*PointerEnum2, %1 : $X, %2 : $X, %3 : $X): } // CHECK-LABEL: CG of test_select_value -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: -// CHECK-NEXT: Arg %3 Esc: A, Succ: -// CHECK-NEXT: Val %6 Esc: R, Succ: %1, %2, %3 -// CHECK-NEXT: Ret return Esc: R, Succ: %6 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: +// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1, %2, %3 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @test_select_value : $@convention(thin) (Builtin.Int64, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X): @@ -967,10 +1018,10 @@ bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X): } // CHECK-LABEL: CG of test_existential_addr -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: Val %1 Esc: , Succ: (%2) // CHECK-NEXT: Con %2 Esc: , Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: , Succ: %0 +// CHECK-NEXT: Con [ref] %2.1 Esc: , Succ: %0 // CHECK-NEXT: End sil @test_existential_addr : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -985,7 +1036,7 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of test_existential_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @test_existential_ref : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1000,9 +1051,9 @@ bb0(%0 : $X): // Check that we don't crash on this. // CHECK-LABEL: CG of test_unknown_store -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [ref] %2.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @test_unknown_store : $@convention(thin) (@owned ErrorClass) -> () { bb0(%0 : $ErrorClass): @@ -1014,8 +1065,10 @@ bb0(%0 : $ErrorClass): } // CHECK-LABEL: CG of test_raw_pointer_to_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @test_raw_pointer_to_ref : $@convention(thin) (@owned X) -> @owned X { bb0(%0 : $X): @@ -1025,8 +1078,10 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of test_bridge_object_to_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @test_bridge_object_to_ref : $@convention(thin) (@owned X, Builtin.Word) -> @owned X { bb0(%0 : $X, %1 : $Builtin.Word): @@ -1037,8 +1092,8 @@ bb0(%0 : $X, %1 : $Builtin.Word): // CHECK-LABEL: CG of test_address_to_pointer // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1 -// CHECK-NEXT: Arg %1 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: // CHECK-NEXT: End sil @test_address_to_pointer : $@convention(thin) (@owned X) -> @out X { bb0(%0 : $*X, %1 : $X): @@ -1050,8 +1105,10 @@ bb0(%0 : $*X, %1 : $X): } // CHECK-LABEL: CG of test_casts -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @test_casts : $@convention(thin) (@owned AnyObject) -> @owned X { bb0(%0 : $AnyObject): @@ -1074,10 +1131,10 @@ bb0(%0 : $*U, %1 : $*T, %2 : $@thick U.Type): sil_global @global_y : $SomeData // CHECK-LABEL: CG of test_node_merge_during_struct_inst -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%8) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%8) // CHECK-NEXT: Val %1 Esc: G, Succ: (%8) // CHECK-NEXT: Val %4 Esc: , Succ: (%8) -// CHECK-NEXT: Con %8 Esc: G, Succ: (%8), %1 +// CHECK-NEXT: Con [ref] %8 Esc: G, Succ: (%8), %1 // CHECK-NEXT: Val %10 Esc: , Succ: %0, %4, %8 // CHECK-NEXT: End sil @test_node_merge_during_struct_inst : $@convention(thin) (Y) -> () { @@ -1104,7 +1161,9 @@ bb0(%0 : $Y): } // CHECK-LABEL: CG of arraysemantics_is_native_no_typecheck -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_is_native_no_typecheck : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1116,7 +1175,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_check_subscript -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_check_subscript : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1133,7 +1194,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_check_index -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_check_index : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1149,9 +1212,9 @@ bb0(%0 : $Array): // CHECK-LABEL: CG of arraysemantics_get_element // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.2 -// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %1.1) -// CHECK-NEXT: Con [rc] %1.1 Esc: A, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.2 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: A, Succ: (%1.2) // CHECK-NEXT: Con %1.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_element : $@convention(thin) (Array) -> @out X { @@ -1170,6 +1233,7 @@ bb0(%io : $*X, %1 : $Array): // CHECK-LABEL: CG of arraysemantics_make_mutable // CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_make_mutable : $@convention(thin) (@inout Array) -> () { bb0(%0 : $*Array): @@ -1181,9 +1245,10 @@ bb0(%0 : $*Array): } // CHECK-LABEL: CG of arraysemantics_get_element_address -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: , Succ: [rc] %0.1 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: +// CHECK-NEXT: Con %0.2 Esc: A, Succ: +// CHECK-NEXT: Val %4 Esc: , Succ: %0.1 // CHECK-NEXT: End sil @arraysemantics_get_element_address : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1198,7 +1263,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_get_count -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_count : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1211,7 +1278,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_get_capacity -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_capacity : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1225,12 +1294,14 @@ bb0(%0 : $Array): // CHECK-LABEL: CG of arraysemantics_withUnsafeMutableBufferPointer // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: ([rc] %0.2) -// CHECK-NEXT: Con [rc] %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: %4, Succ: +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [int] %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con [ref] %0.3 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: %4, Succ: // CHECK-NEXT: End sil @arraysemantics_withUnsafeMutableBufferPointer : $@convention(thin) (@inout Array, @owned @callee_owned (@inout X) -> (@out (), @error Error)) -> () { bb(%0 : $*Array, %1 : $@callee_owned (@inout X) -> (@out (), @error Error)): @@ -1245,12 +1316,12 @@ bb(%0 : $*Array, %1 : $@callee_owned (@inout X) -> (@out (), @error Error)): } // CHECK-LABEL: CG of arraysemantics_createUninitialized -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %2 Esc: R, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: R, Succ: %0 -// CHECK-NEXT: Val %4 Esc: R, Succ: ([rc] %6) -// CHECK-NEXT: Con [rc] %6 Esc: R, Succ: %2 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%6) +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: %2 +// CHECK-NEXT: Con [int] %6 Esc: R, Succ: (%6.1) +// CHECK-NEXT: Con %6.1 Esc: R, Succ: %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @arraysemantics_createUninitialized : $@convention(thin) (@owned X) -> @owned Array { bb0(%0 : $X): @@ -1284,11 +1355,17 @@ sil [_semantics "array.uninitialized"] @createUninitialized : $@convention(metho sil @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject // CHECK-LABEL: CG of semantics_pair_no_escaping_closure -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: -// CHECK-NEXT: Val %4 Esc: %5, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Con [int] %1.1 Esc: A, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %1.2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: (%2.2) +// CHECK-NEXT: Con %2.2 Esc: G, Succ: +// CHECK-NEXT: Val %4 Esc: , Succ: (%4.1) +// CHECK-NEXT: Con [ref] %4.1 Esc: %5, Succ: // CHECK-NEXT: End sil @semantics_pair_no_escaping_closure : $@convention(thin) (@owned X, @guaranteed X, @owned @callee_owned (X, X) -> (@out X, @error Error)) -> () { bb(%0 : $X, %1 : $X, %2: $@callee_owned (X, X) -> (@out X, @error Error)): @@ -1301,10 +1378,14 @@ bb(%0 : $X, %1 : $X, %2: $@callee_owned (X, X) -> (@out X, @error Error)): } // CHECK-LABEL: CG of semantics_self_no_escaping_closure -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: %4, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: +// CHECK-NEXT: Con [ref] %3.1 Esc: %4, Succ: // CHECK-NEXT: End sil @semantics_self_no_escaping_closure : $@convention(thin) (@guaranteed X, @owned @callee_owned (X, X) -> (@out X, @error Error)) -> () { bb(%0 : $X, %1: $@callee_owned (X, X) -> (@out X, @error Error)): @@ -1317,7 +1398,7 @@ bb(%0 : $X, %1: $@callee_owned (X, X) -> (@out X, @error Error)): } // CHECK-LABEL: CG of check_dealloc_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @check_dealloc_ref : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1327,7 +1408,7 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of check_set_deallocating -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @check_set_deallocating : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1340,7 +1421,9 @@ bb0(%0 : $X): // happen due to strong_release and figure out that the object will not // globally escape. // CHECK-LABEL: CG of check_non_escaping_implicit_destructor_invocation_via_strong_release -// CHECK-NEXT: Val %0 Esc: %1, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: %1, Succ: // CHECK-NEXT: End sil @check_non_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () { bb0: @@ -1355,7 +1438,9 @@ bb0: // happen due to strong_release and figure out that the object will not // globally escape. // CHECK-LABEL: CG of check_non_escaping_implicit_destructor_invocation_via_release_value -// CHECK-NEXT: Val %0 Esc: %1, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: %1, Succ: // CHECK-NEXT: End sil @check_non_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () { bb0: @@ -1370,8 +1455,8 @@ bb0: // happen due to strong_release and figure out that the object will // globally escape. // CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_strong_release -// CHECK-NEXT: Val %0 Esc: %1, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) // CHECK-NEXT: Con %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @check_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () { @@ -1387,8 +1472,8 @@ bb0: // happen due to strong_release and figure out that the object will // globally escape. // CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_release_value -// CHECK-NEXT: Val %0 Esc: %1, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) // CHECK-NEXT: Con %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @check_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () { @@ -1406,9 +1491,11 @@ bb0: // invoked by strong_release it would also determine that these objects // do not escape from the function. // CHECK-LABEL: CG of check_is_local_object_through_bb_args -// CHECK-LABEL: Val %2 Esc: %6,%7, Succ: -// CHECK-LABEL: Val %4 Esc: %6,%7, Succ: -// CHECK-LABEL: Val %6 Esc: %6,%7, Succ: %2, %4 +// CHECK-LABEL: Val [ref] %2 Esc: , Succ: (%2.1) +// CHECK-LABEL: Con [int] %2.1 Esc: %7, Succ: (%2.2) +// CHECK-LABEL: Con [ref] %2.2 Esc: %7, Succ: +// CHECK-LABEL: Val [ref] %4 Esc: , Succ: (%2.1) +// CHECK-LABEL: Val [ref] %6 Esc: , Succ: %2, %4 // CHECK-LABEL: End sil @check_is_local_object_through_bb_args: $@convention(thin) (Builtin.Int1) -> () { bb0(%0 : $Builtin.Int1): @@ -1429,7 +1516,10 @@ bb3(%6: $X): } // CHECK-LABEL: CG of check_look_through_thin_to_thick -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: %2, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: %2, Succ: // CHECK-NEXT: End sil @check_look_through_thin_to_thick: $(@convention(thin) () -> ()) -> () { bb0(%0 : $@convention(thin) () -> ()): @@ -1441,7 +1531,7 @@ bb0(%0 : $@convention(thin) () -> ()): // X.deinit // CHECK-LABEL: CG of $s4main1XCfD -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @$s4main1XCfD: $@convention(method) (@owned X) -> () { bb0(%0 : $X): @@ -1452,11 +1542,11 @@ bb0(%0 : $X): // Z.deinit // CHECK-LABEL: CG of $s4main1ZCfD -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) // CHECK-NEXT: Con %2 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: %2 +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: %2 // CHECK-NEXT: End sil @$s4main1ZCfD: $@convention(method) (@owned Z) -> () { bb0(%0 : $Z): @@ -1485,8 +1575,9 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of call_public_external_func -// CHECK-NEXT: Val %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @call_public_external_func : $@convention(thin) () -> () { bb0: @@ -1515,12 +1606,13 @@ bb2: // begin_apply result) is just marked as escaping. // CHECK-LABEL: CG of call_coroutine -// CHECK-NEXT: Val %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) // CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: %4 -// CHECK-NEXT: Val %4 Esc: G, Succ: +// CHECK-NEXT: Con %0.3 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [ref] %2.1 Esc: G, Succ: %4 +// CHECK-NEXT: Val [ref] %4 Esc: G, Succ: // CHECK-NEXT: End sil @call_coroutine : $@convention(thin) () -> () { bb0: @@ -1537,14 +1629,14 @@ bb0: // Test the absence of redundant pointsTo edges // CHECK-LABEL: CG of testInitializePointsToLeaf -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: ([rc] %13) -// CHECK-NEXT: Val %2 Esc: %4, Succ: %0.2 -// CHECK-NEXT: Val %4 Esc: %4, Succ: %2 -// CHECK-NEXT: Val %7 Esc: %12, Succ: ([rc] %13), %0.2 -// CHECK-NEXT: Val %12 Esc: %12, Succ: ([rc] %13), %7 -// CHECK-NEXT: Con [rc] %13 Esc: A, Succ: (%14) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%13) +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%13), %0.2 +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %2 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%13), %0.2 +// CHECK-NEXT: Val [ref] %12 Esc: , Succ: (%13), %7 +// CHECK-NEXT: Con [int] %13 Esc: A, Succ: (%14) // CHECK-NEXT: Con %14 Esc: A, Succ: // CHECK-LABEL: End class C { @@ -1593,14 +1685,14 @@ bb10(%arg2 : $LinkedNode): // a defer edge from a node with uninitialized pointsTo to a node with // already-initialized pointsTo. // CHECK-LABEL: CG of testInitializePointsToRedundant -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) // CHECK-NEXT: Con %3 Esc: A, Succ: -// CHECK-NEXT: Val %7 Esc: %7,%18, Succ: %0 -// CHECK-NEXT: Val %12 Esc: %12,%14,%18, Succ: %1 -// CHECK-NEXT: Val %14 Esc: %18, Succ: ([rc] %2), %1, %12 -// CHECK-NEXT: Val %18 Esc: %18, Succ: %7, %14 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: %0 +// CHECK-NEXT: Val [ref] %12 Esc: , Succ: %1 +// CHECK-NEXT: Val [ref] %14 Esc: , Succ: (%2), %1, %12 +// CHECK-NEXT: Val [ref] %18 Esc: , Succ: %7, %14 // CHECK-LABEL: End sil @testInitializePointsToMerge : $@convention(method) (@guaranteed C, @guaranteed C) -> C { bb0(%0: $C, %1 : $C): @@ -1655,3 +1747,50 @@ bb10(%arg3 : $C): %66 = tuple () return %66 : $() } + +// Test canEscapeToUsePoint with defer edges between non-reference-type nodes. +// +// Unfortunately, the only way I can think of to create defer edges +// between non-reference nodes is with address-type block arguments. +// This will be banned soon, so the test will need to be disabled, but +// this is still an important corner case in the EscapeAnalysis +// logic. To keep the test working, and be able to test many more +// important corner cases, we should introduce testing modes that +// introduce spurious but predictable defer edges and node merges. +// +// Here, canEscapeTo is called on %bbarg (%5) whose node is not +// marked escaping. But it does have a defer edge to the local address +// (%0) which does cause the address to escape via the call site. +// +// CHECK-LABEL: CG of testDeferPointer +// CHECK-NEXT: Val %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%0.1) +// CHECK-NEXT: Val %5 Esc: , Succ: %0, %1 +// CHECK-LABEL: End +// CHECK: MayEscape: %5 = argument of bb3 : $*Builtin.Int32 +// CHECK-NEXT: to %{{.*}} = apply %{{.*}}(%0) : $@convention(thin) (@inout Builtin.Int32) -> () +sil @takeAdr : $@convention(thin) (@inout Builtin.Int32) -> () + +sil hidden @testDeferPointer : $@convention(thin) () -> () { +bb0: + %iadr1 = alloc_stack $Builtin.Int32 + %iadr2 = alloc_stack $Builtin.Int32 + cond_br undef, bb1, bb2 + +bb1: + br bb3(%iadr1: $*Builtin.Int32) + +bb2: + br bb3(%iadr2: $*Builtin.Int32) + +bb3(%bbarg: $*Builtin.Int32): + %val = integer_literal $Builtin.Int32, 1 + store %val to %bbarg : $*Builtin.Int32 + %ftake = function_ref @takeAdr : $@convention(thin) (@inout Builtin.Int32) -> () + %call = apply %ftake(%iadr1) : $@convention(thin) (@inout Builtin.Int32) -> () + dealloc_stack %iadr2 : $*Builtin.Int32 + dealloc_stack %iadr1 : $*Builtin.Int32 + %z = tuple () + return %z : $() +} diff --git a/test/SILOptimizer/sil_combine_concrete_existential.swift b/test/SILOptimizer/sil_combine_concrete_existential.swift index f7e49003c9333..b202d5fbd0f63 100644 --- a/test/SILOptimizer/sil_combine_concrete_existential.swift +++ b/test/SILOptimizer/sil_combine_concrete_existential.swift @@ -101,7 +101,7 @@ struct SS: PPP { // CHECK-LABEL: } // end sil function '$s32sil_combine_concrete_existential37testWitnessReturnOptionalIndirectSelfyyF' public func testWitnessReturnOptionalIndirectSelf() { let p: PPP = S() - p.returnsOptionalIndirect()?.returnsOptionalIndirect() + _ = p.returnsOptionalIndirect()?.returnsOptionalIndirect() } //===----------------------------------------------------------------------===// From 12d148b40ba680e56fea59290d5a03a4a1ebe872 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Tue, 17 Dec 2019 11:13:37 -0800 Subject: [PATCH 069/478] test: add a test for demangle moved type names during runtime --- test/Runtime/demangleToMetadata.swift | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/Runtime/demangleToMetadata.swift b/test/Runtime/demangleToMetadata.swift index 2b36ff5090f46..0dbe1f1507476 100644 --- a/test/Runtime/demangleToMetadata.swift +++ b/test/Runtime/demangleToMetadata.swift @@ -423,5 +423,33 @@ DemangleToMetadataTests.test("Nested types in same-type-constrained extensions") // V !: P3 in InnerTEqualsConformsToP1 } +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "foo", OSX 10.13) +struct MovedS { + struct Nested { } +} + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "foo", OSX 10.13) +enum MovedE { case e } + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "bar", OSX 10.13) +class MovedC {} + +DemangleToMetadataTests.test("Moved Symbols") { + // Simple Struct + expectEqual(type(of: MovedS()), _typeByName("3foo6MovedSV")!) + + // Simple Enum + expectEqual(type(of: MovedE.e), _typeByName("3foo6MovedEO")!) + + // Nested struct + expectEqual(type(of: MovedS.Nested()), _typeByName("3foo6MovedSV6NestedV")!) + + // Simple Class + expectEqual(type(of: MovedC()), _typeByName("3bar6MovedCC")!) +} + runAllTests() From cf79163764c51b91dabb981c1904ddbac204abdf Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Tue, 17 Dec 2019 11:29:56 -0800 Subject: [PATCH 070/478] test: update test to combine @available with @_originallyDefinedIn --- test/ModuleInterface/originally-defined-attr.swift | 2 ++ test/Parse/original_defined_in_attr.swift | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/ModuleInterface/originally-defined-attr.swift b/test/ModuleInterface/originally-defined-attr.swift index 79bf27b7cc27c..0940414f57e32 100644 --- a/test/ModuleInterface/originally-defined-attr.swift +++ b/test/ModuleInterface/originally-defined-attr.swift @@ -9,6 +9,7 @@ // RUN: %FileCheck %s < %t/printed-module.txt // CHECK: @_originallyDefinedIn(module: "another", OSX 13.13) +@available(OSX 10.8, *) @_originallyDefinedIn(module: "another", OSX 13.13) public protocol SimpleProto { } @@ -16,6 +17,7 @@ public protocol SimpleProto { } // CHECK: @_originallyDefinedIn(module: "another_original", OSX 2.0) // CHECK: @_originallyDefinedIn(module: "another_original", iOS 3.0) // CHECK: @_originallyDefinedIn(module: "another_original", watchOS 4.0) +@available(tvOS 0.7, OSX 1.1, iOS 2.1, watchOS 3.2, *) @_originallyDefinedIn(module: "original", tvOS 1.0) @_originallyDefinedIn(module: "another_original", OSX 2.0, iOS 3.0, watchOS 4.0) public struct SimpleStruct {} diff --git a/test/Parse/original_defined_in_attr.swift b/test/Parse/original_defined_in_attr.swift index 634132b7ac933..148fb4ba84550 100644 --- a/test/Parse/original_defined_in_attr.swift +++ b/test/Parse/original_defined_in_attr.swift @@ -1,12 +1,12 @@ // RUN: %target-typecheck-verify-swift -@_originallyDefinedIn(module: "foo", OSX 13.13) +@_originallyDefinedIn(module: "foo", OSX 13.13) // expected-error {{need @available attribute for @_originallyDefinedIn}} func foo() {} @_originallyDefinedIn(modulename: "foo", OSX 13.13) // expected-error {{expected 'module: "original"' in the first argument to @_originallyDefinedIn}} func foo1() {} -@_originallyDefinedIn(module: "foo", OSX 13.13.3) // expected-warning {{@_originallyDefinedIn only uses major and minor version number}} +@_originallyDefinedIn(module: "foo", OSX 13.13.3) // expected-warning {{@_originallyDefinedIn only uses major and minor version number}} expected-error {{need @available attribute for @_originallyDefinedIn}} class ToplevelClass {} @_originallyDefinedIn(module: "foo") // expected-error {{expected at least one platform version in @_originallyDefinedIn}} From b94c097de935fd9a93b358aaf2d17f52c5e0b748 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Tue, 17 Dec 2019 11:47:49 -0800 Subject: [PATCH 071/478] Clarify `@derivative(of:)` and `@transpose(of:)` attribute names. --- include/swift/AST/Attr.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 234f3611f9e7b..a65b14acea7dc 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1765,19 +1765,19 @@ class DifferentiableAttr final } }; -/// The `@derivative` attribute registers a function as a derivative of another -/// function-like declaration: a 'func', 'init', 'subscript', or 'var' computed -/// property declaration. +/// The `@derivative(of:)` attribute registers a function as a derivative of +/// another function-like declaration: a 'func', 'init', 'subscript', or 'var' +/// computed property declaration. /// -/// The `@derivative` attribute also has an optional `wrt:` clause specifying -/// the parameters that are differentiated "with respect to", i.e. the -/// differentiation parameters. The differentiation parameters must conform to -/// the `Differentiable` protocol. +/// The `@derivative(of:)` attribute also has an optional `wrt:` clause +/// specifying the parameters that are differentiated "with respect to", i.e. +/// the differentiation parameters. The differentiation parameters must conform +/// to the `Differentiable` protocol. /// /// If the `wrt:` clause is unspecified, the differentiation parameters are /// inferred to be all parameters that conform to `Differentiable`. /// -/// `@derivative` attribute type-checking verifies that the type of the +/// `@derivative(of:)` attribute type-checking verifies that the type of the /// derivative function declaration is consistent with the type of the /// referenced original declaration and the differentiation parameters. /// @@ -1858,17 +1858,17 @@ class DerivativeAttr final } }; -/// The `@transpose` attribute registers a function as a transpose of another -/// function-like declaration: a 'func', 'init', 'subscript', or 'var' computed -/// property declaration. +/// The `@transpose(of:)` attribute registers a function as a transpose of +/// another function-like declaration: a 'func', 'init', 'subscript', or 'var' +/// computed property declaration. /// -/// The `@transpose` attribute also has a `wrt:` clause specifying the +/// The `@transpose(of:)` attribute also has a `wrt:` clause specifying the /// parameters that are transposed "with respect to", i.e. the transposed /// parameters. /// /// Examples: /// @transpose(of: foo) -/// @transpose(of: +, wrt: (lhs, rhs)) +/// @transpose(of: +, wrt: (0, 1)) class TransposeAttr final : public DeclAttribute, private llvm::TrailingObjects { From aecf9a171a115999dee8c8bee67898462a686f49 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Tue, 17 Dec 2019 12:07:48 -0800 Subject: [PATCH 072/478] Fix qualified name parsing syntax verification errors. --- lib/Parse/ParseDecl.cpp | 17 +++++++++-------- lib/Parse/ParseType.cpp | 9 +++++---- .../round_trip_parse_gen.swift.withkinds | 16 ++++++++++++++++ test/AutoDiff/Syntax/round_trip_parse_gen.swift | 16 ++++++++++++++++ 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index a22b8a9137794..87cadda1723c2 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1093,22 +1093,23 @@ bool Parser::parseDifferentiableAttributeArguments( static bool parseBaseTypeForQualifiedDeclName(Parser &P, TypeRepr *&baseType) { baseType = nullptr; + // If base type cannot be parsed, return false (no error). if (!P.canParseBaseTypeForQualifiedDeclName()) return false; auto result = P.parseTypeIdentifier(/*isParsingQualifiedDeclName*/ true); + // If base type should be parseable but the actual base type result is null, + // return true (error). if (result.isNull()) return true; - // Consume a leading period. This is relevant only for qualified operators. - // TODO(TF-1065): Consider disallowing qualified operator names. There is no - // precedent for qualified operator syntax elsewhere in Swift. - if (P.startsWithSymbol(P.Tok, '.')) { - assert(P.Tok.isAnyOperator() && - "Only operators should have leading period here"); - P.consumeStartingCharacterOfCurrentToken(tok::period); - } + // Consume the leading period before the final declaration name component. + // `parseTypeIdentifier(/*isParsingQualifiedDeclName*/ true)` leaves the + // leading period unparsed to avoid syntax verification errors. + assert(P.startsWithSymbol(P.Tok, '.') && "false"); + P.consumeStartingCharacterOfCurrentToken(tok::period); + // Set base type and return false (no error). baseType = result.getPtrOrNull(); return false; } diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 1a560a8fa74d4..d49a83b26a17e 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -734,18 +734,19 @@ Parser::parseTypeIdentifier(bool isParsingQualifiedDeclBaseType) { } if (!peekToken().isContextualKeyword("Type") && !peekToken().isContextualKeyword("Protocol")) { - // Consume the period. - consumeToken(); // If parsing a qualified declaration name, break before parsing the - // final declaration name component. + // period before the final declaration name component. if (isParsingQualifiedDeclBaseType) { - BacktrackingScope backtrack(*this); // If qualified name base type cannot be parsed from the current // point (i.e. the next type identifier is not followed by a '.'), // then the next identifier is the final declaration name component. + BacktrackingScope backtrack(*this); + consumeStartingCharacterOfCurrentToken(tok::period); if (!canParseBaseTypeForQualifiedDeclName()) break; } + // Consume the period. + consumeToken(); continue; } } else if (Tok.is(tok::code_complete)) { diff --git a/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds index 87c1cab0cd250..ea9e5722954a2 100644 --- a/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds +++ b/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds @@ -50,6 +50,12 @@ func bazDerivative(_ x: < return (x, { v in v }) } +@derivative(of: A.B.C.foo(label:_:), wrt: (x)) +func qualifiedDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + @transpose(of: +) func addTranspose(_ v: Float) -> (Float, Float) { return (v, v) @@ -58,4 +64,14 @@ func addTranspose(_ v: of: -, wrt: (0, 1)) func subtractTranspose(_ v: Float) -> (Float, Float) { return (v, -v) +} + +@transpose(of: Float.-, wrt: (0, 1)) +func subtractTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} + +@transpose(of: A.B.C.foo(label:_:), wrt: (0)) +func qualifiedTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) } diff --git a/test/AutoDiff/Syntax/round_trip_parse_gen.swift b/test/AutoDiff/Syntax/round_trip_parse_gen.swift index 0ebe54b7de2cc..2603a994a2e34 100644 --- a/test/AutoDiff/Syntax/round_trip_parse_gen.swift +++ b/test/AutoDiff/Syntax/round_trip_parse_gen.swift @@ -50,6 +50,12 @@ func bazDerivative(_ x: Float, y: Float) return (x, { v in v }) } +@derivative(of: A.B.C.foo(label:_:), wrt: (x)) +func qualifiedDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + @transpose(of: +) func addTranspose(_ v: Float) -> (Float, Float) { return (v, v) @@ -59,3 +65,13 @@ func addTranspose(_ v: Float) -> (Float, Float) { func subtractTranspose(_ v: Float) -> (Float, Float) { return (v, -v) } + +@transpose(of: Float.-, wrt: (0, 1)) +func subtractTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} + +@transpose(of: A.B.C.foo(label:_:), wrt: (0)) +func qualifiedTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} From 624c2fc4a773adbb06f74117e056042bd856e0c2 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Tue, 17 Dec 2019 12:21:55 -0800 Subject: [PATCH 073/478] test: move demangle metadata for moved symbols to a separate file --- test/IRGen/original-defined-attr.swift | 1 + test/Parse/original_defined_in_attr.swift | 1 + test/Runtime/demangleToMetadata.swift | 28 ------------ .../demangleToMetadataMovedSymbols.swift | 45 +++++++++++++++++++ 4 files changed, 47 insertions(+), 28 deletions(-) create mode 100644 test/Runtime/demangleToMetadataMovedSymbols.swift diff --git a/test/IRGen/original-defined-attr.swift b/test/IRGen/original-defined-attr.swift index c50a97e558056..65ce1ce378152 100644 --- a/test/IRGen/original-defined-attr.swift +++ b/test/IRGen/original-defined-attr.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name CurrentModule -D CURRENT_MODULE | %FileCheck %s --check-prefix=CHECK-COMMON --check-prefix=CHECK-CURRENT --check-prefix=CHECK-CURRENT-%target-ptrsize // RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name OriginalModule | %FileCheck %s --check-prefix=CHECK-COMMON --check-prefix=CHECK-ORIGINAL --check-prefix=CHECK-ORIGINAL-%target-ptrsize +// REQUIRES: OS=macosx #if CURRENT_MODULE diff --git a/test/Parse/original_defined_in_attr.swift b/test/Parse/original_defined_in_attr.swift index 148fb4ba84550..03a43dbb3bce1 100644 --- a/test/Parse/original_defined_in_attr.swift +++ b/test/Parse/original_defined_in_attr.swift @@ -1,4 +1,5 @@ // RUN: %target-typecheck-verify-swift +// REQUIRES: OS=macosx @_originallyDefinedIn(module: "foo", OSX 13.13) // expected-error {{need @available attribute for @_originallyDefinedIn}} func foo() {} diff --git a/test/Runtime/demangleToMetadata.swift b/test/Runtime/demangleToMetadata.swift index 0dbe1f1507476..2b36ff5090f46 100644 --- a/test/Runtime/demangleToMetadata.swift +++ b/test/Runtime/demangleToMetadata.swift @@ -423,33 +423,5 @@ DemangleToMetadataTests.test("Nested types in same-type-constrained extensions") // V !: P3 in InnerTEqualsConformsToP1 } -@available(OSX 10.9, *) -@_originallyDefinedIn(module: "foo", OSX 10.13) -struct MovedS { - struct Nested { } -} - -@available(OSX 10.9, *) -@_originallyDefinedIn(module: "foo", OSX 10.13) -enum MovedE { case e } - -@available(OSX 10.9, *) -@_originallyDefinedIn(module: "bar", OSX 10.13) -class MovedC {} - -DemangleToMetadataTests.test("Moved Symbols") { - // Simple Struct - expectEqual(type(of: MovedS()), _typeByName("3foo6MovedSV")!) - - // Simple Enum - expectEqual(type(of: MovedE.e), _typeByName("3foo6MovedEO")!) - - // Nested struct - expectEqual(type(of: MovedS.Nested()), _typeByName("3foo6MovedSV6NestedV")!) - - // Simple Class - expectEqual(type(of: MovedC()), _typeByName("3bar6MovedCC")!) -} - runAllTests() diff --git a/test/Runtime/demangleToMetadataMovedSymbols.swift b/test/Runtime/demangleToMetadataMovedSymbols.swift new file mode 100644 index 0000000000000..397d51701d736 --- /dev/null +++ b/test/Runtime/demangleToMetadataMovedSymbols.swift @@ -0,0 +1,45 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -parse-stdlib %s -module-name main -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out +// REQUIRES: executable_test +// REQUIRES: OS=macosx + +import Swift +import StdlibUnittest + +let DemangleToMetadataMovedSymbolsTests = TestSuite("DemangleToMetadataMovedSymbols") + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "foo", OSX 10.13) +struct MovedS { + struct Nested { } +} + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "foo", OSX 10.13) +enum MovedE { case e } + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "bar", OSX 10.13) +class MovedC {} + +DemangleToMetadataMovedSymbolsTests.test("Moved Nominals") { + // Simple Struct + expectEqual(type(of: MovedS()), _typeByName("3foo6MovedSV")!) + expectNil(_typeByName("4main6MovedSV")) + + // Simple Enum + expectEqual(type(of: MovedE.e), _typeByName("3foo6MovedEO")!) + expectNil(_typeByName("4main6MovedEO")) + + // Nested struct + expectEqual(type(of: MovedS.Nested()), _typeByName("3foo6MovedSV6NestedV")!) + expectNil(_typeByName("4main6MovedSV6NestedV")) + + // Simple Class + expectEqual(type(of: MovedC()), _typeByName("3bar6MovedCC")!) + expectNil(_typeByName("4main6MovedCC")) +} + +runAllTests() From fbcef9a9ecc457e5372a0b96a885c62406bf0cbc Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Tue, 17 Dec 2019 16:41:47 -0500 Subject: [PATCH 074/478] [Runtime] Use the new NonMetaClass ObjC field to have generic metaclasses point to their classes. Remove metaclass handling from the lazy class namer since it's not necessary. rdar://problem/57674583 --- stdlib/public/runtime/Metadata.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 7bfe15dd5207f..cc631048bc78d 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -2172,7 +2172,10 @@ namespace { #if __POINTER_WIDTH__ == 64 uint32_t Reserved; #endif - const uint8_t *IvarLayout; + union { + const uint8_t *IvarLayout; + ClassMetadata *NonMetaClass; + }; const char *Name; const void *MethodList; const void *ProtocolList; @@ -2249,12 +2252,7 @@ static bool installLazyClassNameHandler() { return false; _objc_setLazyClassNamer([](Class theClass) { - ClassMetadata *metadata; - if (class_isMetaClass(theClass)) { - metadata = (ClassMetadata *)class_getIvarLayout(theClass); - } else { - metadata = (ClassMetadata *)theClass; - } + ClassMetadata *metadata = (ClassMetadata *)theClass; return copyGenericClassObjCName(metadata); }); return true; @@ -2266,7 +2264,7 @@ static void setUpGenericClassObjCName(ClassMetadata *theClass) { getROData(theClass)->Name = nullptr; auto theMetaclass = (ClassMetadata *)object_getClass((id)theClass); getROData(theMetaclass)->Name = nullptr; - getROData(theMetaclass)->IvarLayout = (const uint8_t *)theClass; + getROData(theMetaclass)->NonMetaClass = theClass; } else { initGenericClassObjCName(theClass); } From c874aa0fe74ce494e16b57eb61c1edda0cf5ede1 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 17 Dec 2019 13:45:14 -0800 Subject: [PATCH 075/478] build: enable cross-compilation on Windows When cross-compiling the toolchain for Windows, we would try to run the foreign target compiler on the build which does not run. Fallback to the path to find a `clang-cl` that we can use. --- CMakeLists.txt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7490e1df821d0..7f64e8cbb2066 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -953,10 +953,15 @@ if(SWIFT_BUILD_SYNTAXPARSERLIB OR SWIFT_BUILD_SOURCEKIT) set(SWIFT_LIBDISPATCH_CXX_COMPILER ${CMAKE_CXX_COMPILER}) elseif(${CMAKE_SYSTEM_NAME} STREQUAL ${CMAKE_HOST_SYSTEM_NAME}) if(CMAKE_SYSTEM_NAME STREQUAL Windows) - set(SWIFT_LIBDISPATCH_C_COMPILER - $/clang-cl${CMAKE_EXECUTABLE_SUFFIX}) - set(SWIFT_LIBDISPATCH_CXX_COMPILER - $/clang-cl${CMAKE_EXECUTABLE_SUFFIX}) + if(TARGET clang) + set(SWIFT_LIBDISPATCH_C_COMPILER + $/clang-cl${CMAKE_EXECUTABLE_SUFFIX}) + set(SWIFT_LIBDISPATCH_CXX_COMPILER + $/clang-cl${CMAKE_EXECUTABLE_SUFFIX}) + else() + set(SWIFT_LIBDISPATCH_C_COMPILER clang-cl${CMAKE_EXECUTABLE_SUFFIX}) + set(SWIFT_LIBDISPATCH_CXX_COMPILER clang-cl${CMAKE_EXECUTABLE_SUFFIX}) + endif() else() set(SWIFT_LIBDISPATCH_C_COMPILER $/clang) set(SWIFT_LIBDISPATCH_CXX_COMPILER $/clang++) From 6b072ecb2ad6723ad6829f2c85db4e9f0f2afdc7 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Tue, 17 Dec 2019 22:12:08 +0000 Subject: [PATCH 076/478] [AutoDiff] NFC: gardening. Add accidentally deleted newline. --- lib/Serialization/Serialization.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 78fbb5d7762b5..377149dc7e2b5 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2003,7 +2003,8 @@ static uint8_t getRawStableAutoDiffDerivativeFunctionKind( swift::AutoDiffDerivativeFunctionKind kind) { switch (kind) { case swift::AutoDiffDerivativeFunctionKind::JVP: - return uint8_t(serialization::AutoDiffDerivativeFunctionKind::JVP); case swift::AutoDiffDerivativeFunctionKind::VJP: + return uint8_t(serialization::AutoDiffDerivativeFunctionKind::JVP); + case swift::AutoDiffDerivativeFunctionKind::VJP: return uint8_t(serialization::AutoDiffDerivativeFunctionKind::VJP); } llvm_unreachable("bad derivative function kind"); From 2b5ada11cb23efd42d23aca3e39bd2f0b7ec541a Mon Sep 17 00:00:00 2001 From: tbkka Date: Tue, 17 Dec 2019 14:52:35 -0800 Subject: [PATCH 077/478] Correctly calculate extra inhabitants for no-payload enums (#28830) In particular, this fixes the size calculation for nested enums, specifically enums within Optionals. Without this, the reflection library computes `v` below as requiring two bytes instead of one. ``` enum E { case a case b } let v = Optional ``` This also adds a number of test cases for enums alone and wrapped in optionals, including: * Zero-case enums are allocated zero size and have zero extra inhabitants * Zero-case enums in optionals also get zero size * One-case no-payload enums are allocated zero size and have zero extra inhabitants * One-case no-payload enums in optionals get one byte allocated and have zero extra inhabitants * 254-case enums have only two extra inhabitants, so putting them in thrice-nested optionals requires an extra byte * Various cases where each nested optional gets an extra byte Resolves rdar://31154770 --- stdlib/public/Reflection/TypeLowering.cpp | 20 +- test/Reflection/typeref_lowering.swift | 4 +- .../reflect_Enum_254CaseNoPayloads.swift | 364 ++++++++++++++++++ .../Reflection/reflect_Enum_NoCase.swift | 125 ++++++ .../reflect_Enum_SingleCaseNoPayload.swift | 93 +++++ ...eflect_Enum_SingleCasePointerPayload.swift | 81 ++++ .../reflect_Enum_TwoCaseNoPayload.swift | 97 +++++ .../reflect_Enum_TwoCaseOnePayload.swift | 168 ++++++++ .../reflect_Enum_TwoCaseTwoPayloads.swift | 265 +++++++++++++ 9 files changed, 1211 insertions(+), 6 deletions(-) create mode 100644 validation-test/Reflection/reflect_Enum_254CaseNoPayloads.swift create mode 100644 validation-test/Reflection/reflect_Enum_NoCase.swift create mode 100644 validation-test/Reflection/reflect_Enum_SingleCaseNoPayload.swift create mode 100644 validation-test/Reflection/reflect_Enum_SingleCasePointerPayload.swift create mode 100644 validation-test/Reflection/reflect_Enum_TwoCaseNoPayload.swift create mode 100644 validation-test/Reflection/reflect_Enum_TwoCaseOnePayload.swift create mode 100644 validation-test/Reflection/reflect_Enum_TwoCaseTwoPayloads.swift diff --git a/stdlib/public/Reflection/TypeLowering.cpp b/stdlib/public/Reflection/TypeLowering.cpp index aae8ea9452de0..c93afa1d25067 100644 --- a/stdlib/public/Reflection/TypeLowering.cpp +++ b/stdlib/public/Reflection/TypeLowering.cpp @@ -1007,10 +1007,22 @@ class EnumTypeInfoBuilder { // NoPayloadEnumImplStrategy if (PayloadCases.empty()) { Kind = RecordKind::NoPayloadEnum; - Size += getEnumTagCounts(/*size=*/0, - NoPayloadCases, - /*payloadCases=*/0).numTagBytes; - + switch (NoPayloadCases) { + case 0: + case 1: // Zero or one tag has size = 0, extra_inhab = 0 + NumExtraInhabitants = 0; + break; + default: { // 2 or more tags + auto tagCounts = getEnumTagCounts(/*size=*/0, + NoPayloadCases, + /*payloadCases=*/0); + Size += tagCounts.numTagBytes; + NumExtraInhabitants = + (1 << (tagCounts.numTagBytes * 8)) - tagCounts.numTags; + NumExtraInhabitants = std::min(NumExtraInhabitants, + unsigned(ValueWitnessFlags::MaxNumExtraInhabitants)); + } + } // SinglePayloadEnumImplStrategy } else if (PayloadCases.size() == 1) { auto *CaseTR = getCaseTypeRef(PayloadCases[0]); diff --git a/test/Reflection/typeref_lowering.swift b/test/Reflection/typeref_lowering.swift index 4f076bf71d145..750b2af38eeb1 100644 --- a/test/Reflection/typeref_lowering.swift +++ b/test/Reflection/typeref_lowering.swift @@ -1039,9 +1039,9 @@ // CHECK-64-NEXT: (field name=empty offset=0 // CHECK-64-NEXT: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) // CHECK-64-NEXT: (field name=noPayload offset=0 -// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=252 bitwise_takable=1)) // CHECK-64-NEXT: (field name=sillyNoPayload offset=1 -// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=252 bitwise_takable=1)) // CHECK-64-NEXT: (field name=singleton offset=8 // CHECK-64-NEXT: (reference kind=strong refcounting=native)) // CHECK-64-NEXT: (field name=singlePayload offset=16 diff --git a/validation-test/Reflection/reflect_Enum_254CaseNoPayloads.swift b/validation-test/Reflection/reflect_Enum_254CaseNoPayloads.swift new file mode 100644 index 0000000000000..03e18c29c2c7b --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_254CaseNoPayloads.swift @@ -0,0 +1,364 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_254CaseNoPayloads +// RUN: %target-codesign %t/reflect_Enum_254CaseNoPayloads + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_254CaseNoPayloads | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +struct Marker { + let value = 1 + let extra = 2 +} + +enum E254CaseNoPayloadsEnum { +case option0 +case option1 +case option2 +case option3 +case option4 +case option5 +case option6 +case option7 +case option8 +case option9 +case option10 +case option11 +case option12 +case option13 +case option14 +case option15 +case option16 +case option17 +case option18 +case option19 +case option20 +case option21 +case option22 +case option23 +case option24 +case option25 +case option26 +case option27 +case option28 +case option29 +case option30 +case option31 +case option32 +case option33 +case option34 +case option35 +case option36 +case option37 +case option38 +case option39 +case option40 +case option41 +case option42 +case option43 +case option44 +case option45 +case option46 +case option47 +case option48 +case option49 +case option50 +case option51 +case option52 +case option53 +case option54 +case option55 +case option56 +case option57 +case option58 +case option59 +case option60 +case option61 +case option62 +case option63 +case option64 +case option65 +case option66 +case option67 +case option68 +case option69 +case option70 +case option71 +case option72 +case option73 +case option74 +case option75 +case option76 +case option77 +case option78 +case option79 +case option80 +case option81 +case option82 +case option83 +case option84 +case option85 +case option86 +case option87 +case option88 +case option89 +case option90 +case option91 +case option92 +case option93 +case option94 +case option95 +case option96 +case option97 +case option98 +case option99 +case option100 +case option101 +case option102 +case option103 +case option104 +case option105 +case option106 +case option107 +case option108 +case option109 +case option110 +case option111 +case option112 +case option113 +case option114 +case option115 +case option116 +case option117 +case option118 +case option119 +case option120 +case option121 +case option122 +case option123 +case option124 +case option125 +case option126 +case option127 +case option128 +case option129 +case option130 +case option131 +case option132 +case option133 +case option134 +case option135 +case option136 +case option137 +case option138 +case option139 +case option140 +case option141 +case option142 +case option143 +case option144 +case option145 +case option146 +case option147 +case option148 +case option149 +case option150 +case option151 +case option152 +case option153 +case option154 +case option155 +case option156 +case option157 +case option158 +case option159 +case option160 +case option161 +case option162 +case option163 +case option164 +case option165 +case option166 +case option167 +case option168 +case option169 +case option170 +case option171 +case option172 +case option173 +case option174 +case option175 +case option176 +case option177 +case option178 +case option179 +case option180 +case option181 +case option182 +case option183 +case option184 +case option185 +case option186 +case option187 +case option188 +case option189 +case option190 +case option191 +case option192 +case option193 +case option194 +case option195 +case option196 +case option197 +case option198 +case option199 +case option200 +case option201 +case option202 +case option203 +case option204 +case option205 +case option206 +case option207 +case option208 +case option209 +case option210 +case option211 +case option212 +case option213 +case option214 +case option215 +case option216 +case option217 +case option218 +case option219 +case option220 +case option221 +case option222 +case option223 +case option224 +case option225 +case option226 +case option227 +case option228 +case option229 +case option230 +case option231 +case option232 +case option233 +case option234 +case option235 +case option236 +case option237 +case option238 +case option239 +case option240 +case option241 +case option242 +case option243 +case option244 +case option245 +case option246 +case option247 +case option248 +case option249 +case option250 +case option251 +case option252 +case option253 +} + +class ClassWith254CaseEnum { + var e1: E254CaseNoPayloadsEnum = .option0 + var e2: E254CaseNoPayloadsEnum? + var e3: E254CaseNoPayloadsEnum?? + var e4: E254CaseNoPayloadsEnum??? + var e5: E254CaseNoPayloadsEnum???? +} + +reflect(object: ClassWith254CaseEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_254CaseNoPayloads.ClassWith254CaseEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=24 alignment=1 stride=24 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)) +// CHECK-64: (field name=e2 offset=17 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)))) +// CHECK-64: (field name=e3 offset=18 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)))))) +// CHECK-64: (field name=e4 offset=19 +// CHECK-64: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)))))))) +// CHECK-64: (field name=e5 offset=21 +// CHECK-64: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1))))))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_254CaseNoPayloads.ClassWith254CaseEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=16 alignment=1 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)) +// CHECK-32: (field name=e2 offset=9 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)))) +// CHECK-32: (field name=e3 offset=10 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)))))) +// CHECK-32: (field name=e4 offset=11 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)))))))) +// CHECK-32: (field name=e5 offset=13 +// CHECK-32: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1))))))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_Enum_NoCase.swift b/validation-test/Reflection/reflect_Enum_NoCase.swift new file mode 100644 index 0000000000000..537750d6308d3 --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_NoCase.swift @@ -0,0 +1,125 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_NoCase +// RUN: %target-codesign %t/reflect_Enum_NoCase + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_NoCase | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +enum NoCaseEnum { +} + +class ClassWithNoCaseEnum { + var e1: NoCaseEnum? + var e2: NoCaseEnum?? + var e3: NoCaseEnum??? + var e4: NoCaseEnum???? + var e5: NoCaseEnum????? +} + +reflect(object: ClassWithNoCaseEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_NoCase.ClassWithNoCaseEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=31 alignment=1 stride=31 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=e2 offset=17 +// CHECK-64: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=e3 offset=19 +// CHECK-64: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-64: (field name=e4 offset=22 +// CHECK-64: (single_payload_enum size=4 alignment=1 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-64: (field name=e5 offset=26 +// CHECK-64: (single_payload_enum size=5 alignment=1 stride=5 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=4 alignment=1 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))))))))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_NoCase.ClassWithNoCaseEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=23 alignment=1 stride=23 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=e2 offset=9 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=e3 offset=11 +// CHECK-32: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-32: (field name=e4 offset=14 +// CHECK-32: (single_payload_enum size=4 alignment=1 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-32: (field name=e5 offset=18 +// CHECK-32: (single_payload_enum size=5 alignment=1 stride=5 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=4 alignment=1 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))))))))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_Enum_SingleCaseNoPayload.swift b/validation-test/Reflection/reflect_Enum_SingleCaseNoPayload.swift new file mode 100644 index 0000000000000..98a61263b3b69 --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_SingleCaseNoPayload.swift @@ -0,0 +1,93 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_SingleCaseNoPayload +// RUN: %target-codesign %t/reflect_Enum_SingleCaseNoPayload + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_SingleCaseNoPayload | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +struct Marker { + let value = 1 +} + +enum SingleCaseNoPayloadEnum { +case `default` +} + +class ClassWithSingleCaseNoPayloadEnum { + var e1: SingleCaseNoPayloadEnum? + var e2: SingleCaseNoPayloadEnum = .`default` + var e3: SingleCaseNoPayloadEnum? = .`default` + var e4: SingleCaseNoPayloadEnum?? + let marker = Marker() + +} + +reflect(object: ClassWithSingleCaseNoPayloadEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_SingleCaseNoPayload.ClassWithSingleCaseNoPayloadEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=32 alignment=8 stride=32 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=e2 offset=17 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64: (field name=e3 offset=17 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=e4 offset=18 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=marker offset=24 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_SingleCaseNoPayload.ClassWithSingleCaseNoPayloadEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=16 alignment=4 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=e2 offset=9 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-32: (field name=e3 offset=9 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=e4 offset=10 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=marker offset=12 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_Enum_SingleCasePointerPayload.swift b/validation-test/Reflection/reflect_Enum_SingleCasePointerPayload.swift new file mode 100644 index 0000000000000..5cac117d30283 --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_SingleCasePointerPayload.swift @@ -0,0 +1,81 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_SingleCasePointerPayload +// RUN: %target-codesign %t/reflect_Enum_SingleCasePointerPayload + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_SingleCasePointerPayload | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +class Marker { + let value = 1 +} + +enum SingleCasePointerPayloadEnum { +case only(Marker) +} + +class ClassWithSingleCasePointerPayloadEnum { + var e1: SingleCasePointerPayloadEnum? + var e2: SingleCasePointerPayloadEnum = .only(Marker()) + var e3: SingleCasePointerPayloadEnum? = .some(.only(Marker())) + var e4: SingleCasePointerPayloadEnum?? +} + +reflect(object: ClassWithSingleCasePointerPayloadEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_SingleCasePointerPayload.ClassWithSingleCasePointerPayloadEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=48 alignment=8 stride=48 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (single_payload_enum size=8 alignment=8 stride=8 num_extra_inhabitants=2147483646 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (reference kind=strong refcounting=native)))) +// CHECK-64: (field name=e2 offset=24 +// CHECK-64: (reference kind=strong refcounting=native)) +// CHECK-64: (field name=e3 offset=32 +// CHECK-64: (single_payload_enum size=8 alignment=8 stride=8 num_extra_inhabitants=2147483646 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (reference kind=strong refcounting=native)))) +// CHECK-64: (field name=e4 offset=40 +// CHECK-64: (single_payload_enum size=8 alignment=8 stride=8 num_extra_inhabitants=2147483645 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=8 alignment=8 stride=8 num_extra_inhabitants=2147483646 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (reference kind=strong refcounting=native))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_SingleCasePointerPayload.ClassWithSingleCasePointerPayloadEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=24 alignment=4 stride=24 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (single_payload_enum size=4 alignment=4 stride=4 num_extra_inhabitants=4095 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (reference kind=strong refcounting=native)))) +// CHECK-32: (field name=e2 offset=12 +// CHECK-32: (reference kind=strong refcounting=native)) +// CHECK-32: (field name=e3 offset=16 +// CHECK-32: (single_payload_enum size=4 alignment=4 stride=4 num_extra_inhabitants=4095 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (reference kind=strong refcounting=native)))) +// CHECK-32: (field name=e4 offset=20 +// CHECK-32: (single_payload_enum size=4 alignment=4 stride=4 num_extra_inhabitants=4094 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=4 alignment=4 stride=4 num_extra_inhabitants=4095 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (reference kind=strong refcounting=native))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_Enum_TwoCaseNoPayload.swift b/validation-test/Reflection/reflect_Enum_TwoCaseNoPayload.swift new file mode 100644 index 0000000000000..a35d2fb3ed0d9 --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_TwoCaseNoPayload.swift @@ -0,0 +1,97 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_TwoCaseNoPayload +// RUN: %target-codesign %t/reflect_Enum_TwoCaseNoPayload + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_TwoCaseNoPayload | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +struct Marker { + let value = 1 +} + +enum TwoCaseNoPayloadEnum { +case preferred +case other +} + +class ClassWithTwoCaseNoPayloadEnum { + var e1: TwoCaseNoPayloadEnum? + var e2: TwoCaseNoPayloadEnum = .preferred + var e3: TwoCaseNoPayloadEnum = .other + var e4: TwoCaseNoPayloadEnum? = .preferred + var e5: TwoCaseNoPayloadEnum? = .other + let marker = Marker() + +} + +reflect(object: ClassWithTwoCaseNoPayloadEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_TwoCaseNoPayload.ClassWithTwoCaseNoPayloadEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=32 alignment=8 stride=32 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)))) +// CHECK-64: (field name=e2 offset=17 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)) +// CHECK-64: (field name=e3 offset=18 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)) +// CHECK-64: (field name=e4 offset=19 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)))) +// CHECK-64: (field name=e5 offset=20 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)))) +// CHECK-64: (field name=marker offset=24 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_TwoCaseNoPayload.ClassWithTwoCaseNoPayloadEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=20 alignment=4 stride=20 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)))) +// CHECK-32: (field name=e2 offset=9 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)) +// CHECK-32: (field name=e3 offset=10 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)) +// CHECK-32: (field name=e4 offset=11 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)))) +// CHECK-32: (field name=e5 offset=12 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)))) +// CHECK-32: (field name=marker offset=16 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_Enum_TwoCaseOnePayload.swift b/validation-test/Reflection/reflect_Enum_TwoCaseOnePayload.swift new file mode 100644 index 0000000000000..e3b83de75c057 --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_TwoCaseOnePayload.swift @@ -0,0 +1,168 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_TwoCaseOnePayload +// RUN: %target-codesign %t/reflect_Enum_TwoCaseOnePayload + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_TwoCaseOnePayload | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +struct Marker { + let value = 1 +} + +enum TwoCaseOnePayloadEnum { +case valid(Marker) +case invalid +} + +class ClassWithTwoCaseOnePayloadEnum { + var e1: TwoCaseOnePayloadEnum? + var e2: TwoCaseOnePayloadEnum = .valid(Marker()) + var e3: TwoCaseOnePayloadEnum = .invalid + var e4: TwoCaseOnePayloadEnum? = .valid(Marker()) + var e5: TwoCaseOnePayloadEnum? = .invalid + var e6: TwoCaseOnePayloadEnum?? +} + +reflect(object: ClassWithTwoCaseOnePayloadEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_TwoCaseOnePayload.ClassWithTwoCaseOnePayloadEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=107 alignment=8 stride=112 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (single_payload_enum size=10 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-64: (field name=e2 offset=32 +// CHECK-64: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-64: (field name=e3 offset=48 +// CHECK-64: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-64: (field name=e4 offset=64 +// CHECK-64: (single_payload_enum size=10 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-64: (field name=e5 offset=80 +// CHECK-64: (single_payload_enum size=10 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-64: (field name=e6 offset=96 +// CHECK-64: (single_payload_enum size=11 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=10 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_TwoCaseOnePayload.ClassWithTwoCaseOnePayloadEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=55 alignment=4 stride=56 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (single_payload_enum size=6 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-32: (field name=e2 offset=16 +// CHECK-32: (single_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-32: (field name=e3 offset=24 +// CHECK-32: (single_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-32: (field name=e4 offset=32 +// CHECK-32: (single_payload_enum size=6 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-32: (field name=e5 offset=40 +// CHECK-32: (single_payload_enum size=6 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-32: (field name=e6 offset=48 +// CHECK-32: (single_payload_enum size=7 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=6 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))))))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_Enum_TwoCaseTwoPayloads.swift b/validation-test/Reflection/reflect_Enum_TwoCaseTwoPayloads.swift new file mode 100644 index 0000000000000..dd640eb450c1b --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_TwoCaseTwoPayloads.swift @@ -0,0 +1,265 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_TwoCaseTwoPayloads +// RUN: %target-codesign %t/reflect_Enum_TwoCaseTwoPayloads + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_TwoCaseTwoPayloads | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +struct Marker { + let value = 1 + let extra = 2 +} + +enum TwoCaseTwoPayloadsEnum { +case valid(Marker) +case invalid(Int) +} + +class ClassWithTwoCaseTwoPayloadsEnum { + var e1: TwoCaseTwoPayloadsEnum? + var e2: TwoCaseTwoPayloadsEnum = .valid(Marker()) + var e3: TwoCaseTwoPayloadsEnum = .invalid(7) + var e4: TwoCaseTwoPayloadsEnum? = .valid(Marker()) + var e5: TwoCaseTwoPayloadsEnum? = .invalid(7) + var e6: TwoCaseTwoPayloadsEnum?? +} + +reflect(object: ClassWithTwoCaseTwoPayloadsEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_TwoCaseTwoPayloads.ClassWithTwoCaseTwoPayloadsEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=153 alignment=8 stride=160 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (single_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (multi_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=extra offset=8 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=invalid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-64: (field name=e2 offset=40 +// CHECK-64: (multi_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=extra offset=8 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=invalid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=e3 offset=64 +// CHECK-64: (multi_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=extra offset=8 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=invalid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=e4 offset=88 +// CHECK-64: (single_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (multi_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=extra offset=8 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=invalid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-64: (field name=e5 offset=112 +// CHECK-64: (single_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (multi_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=extra offset=8 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=invalid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-64: (field name=e6 offset=136 +// CHECK-64: (single_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=252 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (multi_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=extra offset=8 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=invalid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_TwoCaseTwoPayloads.ClassWithTwoCaseTwoPayloadsEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=77 alignment=4 stride=80 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (single_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (multi_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=extra offset=4 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=invalid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-32: (field name=e2 offset=20 +// CHECK-32: (multi_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=extra offset=4 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=invalid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=e3 offset=32 +// CHECK-32: (multi_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=extra offset=4 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=invalid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=e4 offset=44 +// CHECK-32: (single_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (multi_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=extra offset=4 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=invalid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-32: (field name=e5 offset=56 +// CHECK-32: (single_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (multi_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=extra offset=4 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=invalid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-32: (field name=e6 offset=68 +// CHECK-32: (single_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=252 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (multi_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=extra offset=4 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=invalid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. From c736938911f0d2c4b5fd08968c365bc58e1c0539 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Tue, 17 Dec 2019 16:21:28 -0800 Subject: [PATCH 078/478] Address parsing/syntax review feedback. --- include/swift/Parse/Parser.h | 1 + lib/Parse/ParseType.cpp | 42 ++++++++----------- .../AutoDiff/Parse/transpose_attr_parse.swift | 4 ++ utils/gyb_syntax_support/AttributeNodes.py | 20 ++++----- 4 files changed, 30 insertions(+), 37 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 9681679a2648e..335907a66c7b5 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1388,6 +1388,7 @@ class Parser { /// Returns true if a base type for a qualified declaration name can be /// parsed. + /// /// Examples: /// 'Foo.f' -> true /// 'Foo.Bar.f' -> true diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 8e70a23c88bd7..cde85091c04cf 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -633,30 +633,6 @@ ParserStatus Parser::parseGenericArguments(SmallVectorImpl &Args, return makeParserSuccess(); } -/// Returns true if a base type for a qualified declaration name can be -/// parsed. -/// -/// Examples: -/// 'Foo.f' -> true -/// 'Foo.Bar.f' -> true -/// 'f' -> false, no base type -bool Parser::canParseBaseTypeForQualifiedDeclName() { - BacktrackingScope backtrack(*this); - - // First, parse a single type identifier component. - if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any)) - return false; - consumeToken(); - if (startsWithLess(Tok)) { - if (!canParseGenericArguments()) - return false; - } - - // If the next token is a period or starts with a period, then this can be - // parsed as a type qualifier. - return startsWithSymbol(Tok, '.'); -} - /// parseTypeIdentifier /// /// type-identifier: @@ -1595,7 +1571,7 @@ bool Parser::canParseTypeIdentifier() { } // Treat 'Foo.' as an attempt to write a dotted type - // unless is 'Type'. + // unless is 'Type' or 'Protocol'. if ((Tok.is(tok::period) || Tok.is(tok::period_prefix)) && !peekToken().isContextualKeyword("Type") && !peekToken().isContextualKeyword("Protocol")) { @@ -1606,6 +1582,22 @@ bool Parser::canParseTypeIdentifier() { } } +bool Parser::canParseBaseTypeForQualifiedDeclName() { + BacktrackingScope backtrack(*this); + + // First, parse a single type identifier component. + if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any)) + return false; + consumeToken(); + if (startsWithLess(Tok)) { + if (!canParseGenericArguments()) + return false; + } + + // Qualified name base types must be followed by a period. + // If the next token starts with a period, return true. + return startsWithSymbol(Tok, '.'); +} bool Parser::canParseOldStyleProtocolComposition() { consumeToken(tok::kw_protocol); diff --git a/test/AutoDiff/Parse/transpose_attr_parse.swift b/test/AutoDiff/Parse/transpose_attr_parse.swift index 6f84e962ead3b..09f9685283896 100644 --- a/test/AutoDiff/Parse/transpose_attr_parse.swift +++ b/test/AutoDiff/Parse/transpose_attr_parse.swift @@ -24,6 +24,10 @@ func transpose(v: Float) -> (Float, Float, Float, Float) @transpose(of: A.B.C.foo(x:y:_:z:)) func transpose(v: Float) -> Float +// Qualified declaration with specialized generic type. +@transpose(of: A.B.C.foo(x:y:_:z:)) +func transpose(v: Float) -> Float + // Qualified operator. // TODO(TF-1065): Consider disallowing qualified operators. @transpose(of: Swift.Float.+) diff --git a/utils/gyb_syntax_support/AttributeNodes.py b/utils/gyb_syntax_support/AttributeNodes.py index ee789ea363b2f..633feca51070a 100644 --- a/utils/gyb_syntax_support/AttributeNodes.py +++ b/utils/gyb_syntax_support/AttributeNodes.py @@ -338,24 +338,20 @@ children=[ Child('BaseType', kind='Type', description=''' The base type of the qualified name, optionally specified. - ''', - node_choices=[ - Child('MemberType', kind='MemberTypeIdentifier'), - Child('SimpleType', kind='SimpleTypeIdentifier'), - ], is_optional=True), + ''', is_optional=True), Child('Dot', kind='Token', token_choices=[ 'PeriodToken', 'PrefixPeriodToken' ], is_optional=True), - Child('Name', kind='Syntax', description=''' + Child('Name', kind='Token', description=''' The base name of the referenced function. ''', - node_choices=[ - Child('Identifier', kind='IdentifierToken'), - Child('PrefixOperator', kind='PrefixOperatorToken'), - Child('PostfixOperator', kind='PostfixOperatorToken'), - Child('SpacedBinaryOperator', - kind='SpacedBinaryOperatorToken'), + token_choices=[ + 'IdentifierToken', + 'UnspacedBinaryOperatorToken', + 'SpacedBinaryOperatorToken', + 'PrefixOperatorToken', + 'PostfixOperatorToken', ]), Child('Arguments', kind='DeclNameArguments', is_optional=True, description=''' From 62078c79dde1a67115adfbd80a6899a0777bdfd7 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 8 Nov 2019 16:31:46 -0800 Subject: [PATCH 079/478] [ASTPrinter/CodeCompletion] Stop printing base type when possible rdar://problem/57033931 --- lib/AST/ASTPrinter.cpp | 31 +++++- lib/IDE/CodeCompletion.cpp | 52 +++++----- lib/IDE/CodeCompletionResultBuilder.h | 14 ++- test/IDE/complete_after_self.swift | 12 +-- test/IDE/complete_assignment.swift | 62 ++++++------ test/IDE/complete_constructor.swift | 2 +- test/IDE/complete_crashes.swift | 2 +- ...ember_decls_from_parent_decl_context.swift | 96 +++++++++---------- test/IDE/complete_opaque_result.swift | 3 +- test/IDE/complete_override.swift | 6 +- test/IDE/complete_override_typealias.swift | 8 +- .../complete_single_expression_return.swift | 8 +- test/IDE/complete_type.swift | 66 ++++++++++--- test/IDE/complete_type_subscript.swift | 6 +- test/IDE/complete_unresolved_members.swift | 5 +- 15 files changed, 220 insertions(+), 153 deletions(-) diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 8ae9c430aae4b..e0212538f1261 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -56,6 +56,8 @@ using namespace swift; void PrintOptions::setBaseType(Type T) { + if (T->is()) + return; TransformContext = TypeTransformContext(T); } @@ -670,6 +672,7 @@ class PrintAST : public ASTVisitor { FreshOptions.ExcludeAttrList = options.ExcludeAttrList; FreshOptions.ExclusiveAttrList = options.ExclusiveAttrList; FreshOptions.PrintOptionalAsImplicitlyUnwrapped = options.PrintOptionalAsImplicitlyUnwrapped; + FreshOptions.TransformContext = options.TransformContext; T.print(Printer, FreshOptions); return; } @@ -700,6 +703,8 @@ class PrintAST : public ASTVisitor { } T = T.subst(subMap, SubstFlags::DesugarMemberTypes); + + options.TransformContext = TypeTransformContext(CurrentType); } printTypeWithOptions(T, options); @@ -3591,7 +3596,6 @@ class TypePrinter : public TypeVisitor { // know we're printing a type member so it escapes `Type` and `Protocol`. if (auto parent = Ty->getParent()) { visitParentType(parent); - Printer << "."; NameContext = PrintNameContext::TypeMember; } else if (shouldPrintFullyQualified(Ty)) { printModuleContext(Ty); @@ -3731,13 +3735,32 @@ class TypePrinter : public TypeVisitor { } void visitParentType(Type T) { + /// Don't print the parent type if it's being printed in that type context. + if (Options.TransformContext) { + if (auto currentType = Options.TransformContext->getBaseType()) { + auto printingType = T; + if (currentType->hasArchetype()) + currentType = currentType->mapTypeOutOfContext(); + + if (auto errorTy = printingType->getAs()) + if (auto origTy = errorTy->getOriginalType()) + printingType = origTy; + + if (printingType->hasArchetype()) + printingType = printingType->mapTypeOutOfContext(); + + if (currentType->isEqual(printingType)) + return; + } + } PrintOptions innerOptions = Options; innerOptions.SynthesizeSugarOnTypes = false; if (auto sugarType = dyn_cast(T.getPointer())) T = sugarType->getImplementationType(); - TypePrinter(Printer, innerOptions).visit(T); + TypePrinter(Printer, innerOptions).printWithParensIfNotSimple(T); + Printer << "."; } void visitEnumType(EnumType *T) { @@ -4247,8 +4270,7 @@ class TypePrinter : public TypeVisitor { } void visitNestedArchetypeType(NestedArchetypeType *T) { - printWithParensIfNotSimple(T->getParent()); - Printer << "."; + visitParentType(T->getParent()); printArchetypeCommon(T); } @@ -4339,7 +4361,6 @@ class TypePrinter : public TypeVisitor { void visitDependentMemberType(DependentMemberType *T) { visitParentType(T->getBase()); - Printer << "."; Printer.printName(T->getName()); } diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index f5b9b2e658429..0c0ec66fb7fb8 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1923,6 +1923,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { PrintOptions PO; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + PO.setBaseType(typeContext->getDeclaredTypeInContext()); Builder.addTypeAnnotation(T.getString(PO)); } } @@ -1944,6 +1946,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { PO.PrintOptionalAsImplicitlyUnwrapped = true; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + PO.setBaseType(typeContext->getDeclaredTypeInContext()); Builder.addTypeAnnotation(T.getString(PO) + suffix); } @@ -2288,9 +2292,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (NeedComma) Builder.addComma(); + Type contextTy; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + contextTy = typeContext->getDeclaredTypeInContext(); - Builder.addCallParameter(argName, bodyName, paramTy, isVariadic, isInOut, - isIUO, isAutoclosure); + Builder.addCallParameter(argName, bodyName, paramTy, contextTy, + isVariadic, isInOut, isIUO, isAutoclosure); modifiedBuilder = true; NeedComma = true; @@ -2636,6 +2643,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; PO.PrintOptionalAsImplicitlyUnwrapped = IsIUO; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + PO.setBaseType(typeContext->getDeclaredTypeInContext()); ResultType.print(OS, PO); } } @@ -3472,9 +3481,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { builder.addEqual(); builder.addWhitespace(" "); assert(RHSType && resultType); - builder.addCallParameter(Identifier(), Identifier(), RHSType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + Type contextTy; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + contextTy = typeContext->getDeclaredTypeInContext(); + builder.addCallParameter(Identifier(), RHSType, contextTy); addTypeAnnotation(builder, resultType); } @@ -3495,10 +3505,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { builder.addWhitespace(" "); builder.addTextChunk(op->getName().str()); builder.addWhitespace(" "); - if (RHSType) - builder.addCallParameter(Identifier(), Identifier(), RHSType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + if (RHSType) { + Type contextTy; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + contextTy = typeContext->getDeclaredTypeInContext(); + builder.addCallParameter(Identifier(), RHSType, contextTy); + } if (resultType) addTypeAnnotation(builder, resultType); } @@ -3722,21 +3734,13 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { addFromProto(LK::ColorLiteral, "", [&](Builder &builder) { builder.addTextChunk("#colorLiteral"); builder.addLeftParen(); - builder.addCallParameter(context.getIdentifier("red"), floatType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + builder.addCallParameter(context.getIdentifier("red"), floatType); builder.addComma(); - builder.addCallParameter(context.getIdentifier("green"), floatType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + builder.addCallParameter(context.getIdentifier("green"), floatType); builder.addComma(); - builder.addCallParameter(context.getIdentifier("blue"), floatType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + builder.addCallParameter(context.getIdentifier("blue"), floatType); builder.addComma(); - builder.addCallParameter(context.getIdentifier("alpha"), floatType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + builder.addCallParameter(context.getIdentifier("alpha"), floatType); builder.addRightParen(); }); @@ -3745,9 +3749,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { builder.addTextChunk("#imageLiteral"); builder.addLeftParen(); builder.addCallParameter(context.getIdentifier("resourceName"), - stringType, /*IsVarArg*/ false, - /*IsInOut*/ false, /*isIUO*/ false, - /*isAutoClosure*/ false); + stringType); builder.addRightParen(); }); @@ -4391,6 +4393,8 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { { llvm::raw_svector_ostream OS(DeclStr); PrintOptions Options; + if (auto transformType = CurrDeclContext->getDeclaredTypeInContext()) + Options.setBaseType(transformType); Options.PrintImplicitAttrs = false; Options.SkipAttributes = true; CD->print(OS, Options); diff --git a/lib/IDE/CodeCompletionResultBuilder.h b/lib/IDE/CodeCompletionResultBuilder.h index 6498cc80ad6b2..a56e8eee1dada 100644 --- a/lib/IDE/CodeCompletionResultBuilder.h +++ b/lib/IDE/CodeCompletionResultBuilder.h @@ -344,7 +344,7 @@ class CodeCompletionResultBuilder { } void addCallParameter(Identifier Name, Identifier LocalName, Type Ty, - bool IsVarArg, bool IsInOut, bool IsIUO, + Type ContextTy, bool IsVarArg, bool IsInOut, bool IsIUO, bool isAutoClosure) { CurrentNestingLevel++; @@ -388,6 +388,8 @@ class CodeCompletionResultBuilder { PO.PrintOptionalAsImplicitlyUnwrapped = IsIUO; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; + if (ContextTy) + PO.setBaseType(ContextTy); std::string TypeName = Ty->getString(PO); addChunkWithText(CodeCompletionString::Chunk::ChunkKind::CallParameterType, TypeName); @@ -402,6 +404,8 @@ class CodeCompletionResultBuilder { PO.SkipAttributes = true; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; + if (ContextTy) + PO.setBaseType(ContextTy); addChunkWithText( CodeCompletionString::Chunk::ChunkKind::CallParameterClosureType, AFT->getString(PO)); @@ -412,10 +416,10 @@ class CodeCompletionResultBuilder { CurrentNestingLevel--; } - void addCallParameter(Identifier Name, Type Ty, bool IsVarArg, bool IsInOut, - bool IsIUO, bool isAutoClosure) { - addCallParameter(Name, Identifier(), Ty, IsVarArg, IsInOut, IsIUO, - isAutoClosure); + void addCallParameter(Identifier Name, Type Ty, Type ContextTy = Type()) { + addCallParameter(Name, Identifier(), Ty, ContextTy, + /*IsVarArg=*/false, /*IsInOut=*/false, /*isIUO=*/false, + /*isAutoClosure=*/false); } void addGenericParameter(StringRef Name) { diff --git a/test/IDE/complete_after_self.swift b/test/IDE/complete_after_self.swift index 913f8ecf2f851..0f616400ab595 100644 --- a/test/IDE/complete_after_self.swift +++ b/test/IDE/complete_after_self.swift @@ -240,9 +240,9 @@ class ThisDerived1 : ThisBase1 { // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: .staticTest2()[#Void#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: .derivedExtInstanceFunc0({#(self): ThisDerived1#})[#() -> Void#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: .derivedExtStaticFunc0()[#Void#] -// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Struct]/CurrNominal: .DerivedExtNestedStruct[#ThisDerived1.DerivedExtNestedStruct#] -// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Class]/CurrNominal: .DerivedExtNestedClass[#ThisDerived1.DerivedExtNestedClass#] -// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Enum]/CurrNominal: .DerivedExtNestedEnum[#ThisDerived1.DerivedExtNestedEnum#] +// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Struct]/CurrNominal: .DerivedExtNestedStruct[#DerivedExtNestedStruct#] +// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Class]/CurrNominal: .DerivedExtNestedClass[#DerivedExtNestedClass#] +// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Enum]/CurrNominal: .DerivedExtNestedEnum[#DerivedExtNestedEnum#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[TypeAlias]/CurrNominal: .DerivedExtNestedTypealias[#Int#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Constructor]/CurrNominal: .init({#someExtensionArg: Int#})[#ThisDerived1#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[InstanceMethod]/Super: .baseFunc0({#(self): ThisBase1#})[#() -> Void#] @@ -275,9 +275,9 @@ class ThisDerived1 : ThisBase1 { // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: staticTest2()[#Void#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: derivedExtInstanceFunc0({#(self): ThisDerived1#})[#() -> Void#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: derivedExtStaticFunc0()[#Void#] -// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Struct]/CurrNominal: DerivedExtNestedStruct[#ThisDerived1.DerivedExtNestedStruct#] -// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Class]/CurrNominal: DerivedExtNestedClass[#ThisDerived1.DerivedExtNestedClass#] -// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Enum]/CurrNominal: DerivedExtNestedEnum[#ThisDerived1.DerivedExtNestedEnum#] +// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Struct]/CurrNominal: DerivedExtNestedStruct[#DerivedExtNestedStruct#] +// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Class]/CurrNominal: DerivedExtNestedClass[#DerivedExtNestedClass#] +// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Enum]/CurrNominal: DerivedExtNestedEnum[#DerivedExtNestedEnum#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[TypeAlias]/CurrNominal: DerivedExtNestedTypealias[#Int#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Constructor]/CurrNominal: init({#someExtensionArg: Int#})[#ThisDerived1#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[InstanceMethod]/Super: baseFunc0({#(self): ThisBase1#})[#() -> Void#] diff --git a/test/IDE/complete_assignment.swift b/test/IDE/complete_assignment.swift index 80c5c93b13556..50c58a4dbef6b 100644 --- a/test/IDE/complete_assignment.swift +++ b/test/IDE/complete_assignment.swift @@ -128,16 +128,16 @@ func f2() { } // ASSIGN_5: Begin completions, 2 items -// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case2[#C1.D1#]; name=case2 -// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case1[#C1.D1#]; name=case1 +// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case2[#D1#]; name=case2 +// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case1[#D1#]; name=case1 func f6() { var d : D2 d = .#^ASSIGN_6^# } // ASSIGN_6: Begin completions, 2 items -// ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific: case3[#C1.D2#]; name=case3 -// ASSIGN_6-DAG:Decl[EnumElement]/ExprSpecific: case4[#C1.D2#]; name=case4 +// ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific: case3[#D2#]; name=case3 +// ASSIGN_6-DAG:Decl[EnumElement]/ExprSpecific: case4[#D2#]; name=case4 func f7 (C : C2) { var i : Int @@ -147,10 +147,10 @@ func f2() { // ASSIGN_7: Begin completions // ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntGen()[#Int#] // ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_7-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_7-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f8 (C : C2) { var i : Int? @@ -160,10 +160,10 @@ func f2() { // ASSIGN_8: Begin completions // ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: IntGen()[#Int#] // ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntOpGen()[#Int?#] -// ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_8-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_8-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f9 (C : C2) { var d : D1 @@ -173,10 +173,10 @@ func f2() { // ASSIGN_9: Begin completions // ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#C1.D1#] -// ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#D1#] +// ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_9-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_9-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f10 (C : C2) { var d : D1 @@ -186,10 +186,10 @@ func f2() { // ASSIGN_10: Begin completions // ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#C1.D1#] -// ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#D1#] +// ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_10-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_10-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f11(C: C2) { d = C.#^ASSIGN_11^# @@ -198,10 +198,10 @@ func f2() { // ASSIGN_11: Begin completions // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: VoidGen()[#Void#] -// ASSIGN_11-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_11-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f12() { var i : Int @@ -211,10 +211,10 @@ func f2() { // ASSIGN_12: Begin completions // ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntGen()[#Int#] // ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_12-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_12-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f13() { var i : Int = #^ASSIGN_13^# @@ -250,10 +250,10 @@ func f2() { // ASSIGN_16: Begin completions // ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#C1.D1#] -// ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#D1#] +// ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_16-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_16-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f17() { var i : Int = C2Gen().#^ASSIGN_17^# @@ -262,10 +262,10 @@ func f2() { // ASSIGN_17: Begin completions // ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntGen()[#Int#] // ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_17-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_17-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f18 (C : C2) { var d : D1 = C.#^ASSIGN_18^# @@ -274,10 +274,10 @@ func f2() { // ASSIGN_18: Begin completions // ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#C1.D1#] -// ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#D1#] +// ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_18-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_18-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] } diff --git a/test/IDE/complete_constructor.swift b/test/IDE/complete_constructor.swift index 65d4457e21c4c..6d75f7b6afcc9 100644 --- a/test/IDE/complete_constructor.swift +++ b/test/IDE/complete_constructor.swift @@ -338,7 +338,7 @@ struct ClosureInInit1 { var prop1: S = { return S(#^CLOSURE_IN_INIT_1^# } -// CLOSURE_IN_INIT_1: Decl[Constructor]/CurrNominal{{(/TypeRelation\[Identical\])?}}: ['(']{#Int#}[')'][#ClosureInInit1.S#]; +// CLOSURE_IN_INIT_1: Decl[Constructor]/CurrNominal{{(/TypeRelation\[Identical\])?}}: ['(']{#Int#}[')'][#S#]; var prop2: S = { return S(#^CLOSURE_IN_INIT_2^# }() diff --git a/test/IDE/complete_crashes.swift b/test/IDE/complete_crashes.swift index a3d260645c36b..76d93abec633c 100644 --- a/test/IDE/complete_crashes.swift +++ b/test/IDE/complete_crashes.swift @@ -69,7 +69,7 @@ struct CustomGenericCollection : ExpressibleByDictionaryLiteral { // GENERIC_PARAM_AND_ASSOC_TYPE: Begin completions // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended/TypeRelation[Identical]: count[#Int#]; name=count // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[GenericTypeParam]/Local: Key[#Key#]; name=Key - // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[TypeAlias]/CurrNominal: Value[#CustomGenericCollection.Value#]; name=Value + // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[TypeAlias]/CurrNominal: Value[#Value#]; name=Value // GENERIC_PARAM_AND_ASSOC_TYPE: End completions var count: Int { #^GENERIC_PARAM_AND_ASSOC_TYPE^# } diff --git a/test/IDE/complete_member_decls_from_parent_decl_context.swift b/test/IDE/complete_member_decls_from_parent_decl_context.swift index ca15843815cdb..55a28528f30e2 100644 --- a/test/IDE/complete_member_decls_from_parent_decl_context.swift +++ b/test/IDE/complete_member_decls_from_parent_decl_context.swift @@ -83,9 +83,9 @@ class CodeCompletionInClassMethods1 { // IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInClassMethods1.NestedStruct#]{{; name=.+$}} -// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInClassMethods1.NestedClass#]{{; name=.+$}} -// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInClassMethods1.NestedEnum#]{{; name=.+$}} +// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_CLASS_INSTANCE_METHOD_1: End completions } @@ -100,9 +100,9 @@ class CodeCompletionInClassMethods1 { // IN_CLASS_STATIC_METHOD_1-DAG: Decl[LocalVar]/Local: self[#CodeCompletionInClassMethods1.Type#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0({#(self): CodeCompletionInClassMethods1#})[#() -> Void#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(self): CodeCompletionInClassMethods1#})[#(Int) -> Void#]{{; name=.+$}} -// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInClassMethods1.NestedStruct#]{{; name=.+$}} -// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInClassMethods1.NestedClass#]{{; name=.+$}} -// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInClassMethods1.NestedEnum#]{{; name=.+$}} +// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[StaticMethod]/CurrNominal: staticFunc0()[#Void#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[StaticMethod]/CurrNominal: staticFunc1({#(a): Int#})[#Void#]{{; name=.+$}} @@ -116,9 +116,9 @@ class CodeCompletionInClassMethods1 { // IN_CLASS_CONSTRUCTOR_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_CLASS_CONSTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_CLASS_CONSTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInClassMethods1.NestedStruct#]{{; name=.+$}} -// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInClassMethods1.NestedClass#]{{; name=.+$}} -// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInClassMethods1.NestedEnum#]{{; name=.+$}} +// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_CLASS_CONSTRUCTOR_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_CLASS_CONSTRUCTOR_1: End completions } @@ -130,9 +130,9 @@ class CodeCompletionInClassMethods1 { // IN_CLASS_DESTRUCTOR_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_CLASS_DESTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_CLASS_DESTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInClassMethods1.NestedStruct#]{{; name=.+$}} -// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInClassMethods1.NestedClass#]{{; name=.+$}} -// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInClassMethods1.NestedEnum#]{{; name=.+$}} +// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_CLASS_DESTRUCTOR_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_CLASS_DESTRUCTOR_1: End completions } @@ -184,9 +184,9 @@ struct CodeCompletionInStructMethods1 { // IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInStructMethods1.NestedStruct#]{{; name=.+$}} -// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInStructMethods1.NestedClass#]{{; name=.+$}} -// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInStructMethods1.NestedEnum#]{{; name=.+$}} +// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_STRUCT_INSTANCE_METHOD_1: End completions } @@ -201,9 +201,9 @@ struct CodeCompletionInStructMethods1 { // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[LocalVar]/Local: self[#CodeCompletionInStructMethods1.Type#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0({#(self): &CodeCompletionInStructMethods1#})[#() -> Void#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(self): &CodeCompletionInStructMethods1#})[#(Int) -> Void#]{{; name=.+$}} -// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInStructMethods1.NestedStruct#]{{; name=.+$}} -// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInStructMethods1.NestedClass#]{{; name=.+$}} -// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInStructMethods1.NestedEnum#]{{; name=.+$}} +// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[StaticMethod]/CurrNominal: staticFunc0()[#Void#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[StaticMethod]/CurrNominal: staticFunc1({#(a): Int#})[#Void#]{{; name=.+$}} @@ -217,9 +217,9 @@ struct CodeCompletionInStructMethods1 { // IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInStructMethods1.NestedStruct#]{{; name=.+$}} -// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInStructMethods1.NestedClass#]{{; name=.+$}} -// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInStructMethods1.NestedEnum#]{{; name=.+$}} +// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_STRUCT_CONSTRUCTOR_1: End completions } @@ -241,9 +241,9 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_A_1-DAG: Decl[InstanceMethod]/CurrNominal: aTestInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_1-DAG: Decl[InstanceVar]/CurrNominal: aInstanceVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_1-DAG: Decl[InstanceMethod]/CurrNominal: aInstanceFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerA.NestedInnerAStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerA.NestedInnerAClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerA.NestedInnerAEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerAStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerAClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerAEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_1-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerATypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_A_1-DAG: Decl[Struct]/Local: NestedInnerA[#NestedInnerA#]{{; name=.+$}} @@ -264,9 +264,9 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_A_2-DAG: Decl[InstanceMethod]/CurrNominal: aInstanceFunc({#(self): &NestedInnerA#})[#() -> Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_2-DAG: Decl[StaticVar]/CurrNominal: aStaticVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_2-DAG: Decl[StaticMethod]/CurrNominal: aStaticFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerA.NestedInnerAStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerA.NestedInnerAClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerA.NestedInnerAEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerAStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerAClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerAEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_2-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerATypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_A_2-DAG: Decl[Struct]/Local: NestedInnerA[#NestedInnerA#]{{; name=.+$}} @@ -281,9 +281,9 @@ struct NestedOuter1 { typealias ATestTypealias = #^NESTED_NOMINAL_DECL_A_3^# // NESTED_NOMINAL_DECL_A_3: Begin completions -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Struct]/OutNominal: NestedInnerAStruct[#NestedInnerA.NestedInnerAStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Class]/OutNominal: NestedInnerAClass[#NestedInnerA.NestedInnerAClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Enum]/OutNominal: NestedInnerAEnum[#NestedInnerA.NestedInnerAEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Struct]/OutNominal: NestedInnerAStruct[#NestedInnerAStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Class]/OutNominal: NestedInnerAClass[#NestedInnerAClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Enum]/OutNominal: NestedInnerAEnum[#NestedInnerAEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_3-DAG: Decl[TypeAlias]/OutNominal: NestedInnerATypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_A_3-DAG: Decl[Struct]/Local: NestedInnerA[#NestedInnerA#]{{; name=.+$}} @@ -343,9 +343,9 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_B_1-DAG: Decl[InstanceMethod]/CurrNominal: bTestInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_1-DAG: Decl[InstanceVar]/CurrNominal: bInstanceVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_1-DAG: Decl[InstanceMethod]/CurrNominal: bInstanceFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerB.NestedInnerBStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerB.NestedInnerBClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerB.NestedInnerBEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerBStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerBClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerBEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_1-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerBTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_B_1-DAG: Decl[Struct]/Local: NestedInnerB[#NestedInnerB#]{{; name=.+$}} @@ -369,9 +369,9 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_B_2-DAG: Decl[InstanceMethod]/CurrNominal: bInstanceFunc({#(self): &NestedInnerB#})[#() -> Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_2-DAG: Decl[StaticVar]/CurrNominal: bStaticVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_2-DAG: Decl[StaticMethod]/CurrNominal: bStaticFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerB.NestedInnerBStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerB.NestedInnerBClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerB.NestedInnerBEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerBStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerBClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerBEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_2-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerBTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_B_2-DAG: Decl[Struct]/Local: NestedInnerB[#NestedInnerB#]{{; name=.+$}} @@ -389,9 +389,9 @@ struct NestedOuter1 { typealias BTestTypealias = #^NESTED_NOMINAL_DECL_B_3^# // NESTED_NOMINAL_DECL_B_3: Begin completions -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Struct]/OutNominal: NestedInnerBStruct[#NestedInnerB.NestedInnerBStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Class]/OutNominal: NestedInnerBClass[#NestedInnerB.NestedInnerBClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Enum]/OutNominal: NestedInnerBEnum[#NestedInnerB.NestedInnerBEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Struct]/OutNominal: NestedInnerBStruct[#NestedInnerBStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Class]/OutNominal: NestedInnerBClass[#NestedInnerBClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Enum]/OutNominal: NestedInnerBEnum[#NestedInnerBEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_3-DAG: Decl[TypeAlias]/OutNominal: NestedInnerBTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_B_3-DAG: Decl[Struct]/Local: NestedInnerB[#NestedInnerB#]{{; name=.+$}} @@ -461,9 +461,9 @@ func testOuterC() { // NESTED_NOMINAL_DECL_C_1-DAG: Decl[InstanceMethod]/CurrNominal: cTestInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_1-DAG: Decl[InstanceVar]/CurrNominal: cInstanceVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_1-DAG: Decl[InstanceMethod]/CurrNominal: cInstanceFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerC.NestedInnerCStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerC.NestedInnerCClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerC.NestedInnerCEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerCStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerCClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerCEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_1-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerCTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_C_1-DAG: Decl[Struct]/Local: NestedInnerC[#NestedInnerC#]{{; name=.+$}} @@ -479,9 +479,9 @@ func testOuterC() { // NESTED_NOMINAL_DECL_C_2-DAG: Decl[InstanceMethod]/CurrNominal: cInstanceFunc({#(self): &NestedInnerC#})[#() -> Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_2-DAG: Decl[StaticVar]/CurrNominal: cStaticVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_2-DAG: Decl[StaticMethod]/CurrNominal: cStaticFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerC.NestedInnerCStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerC.NestedInnerCClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerC.NestedInnerCEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerCStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerCClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerCEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_2-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerCTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_C_2-DAG: Decl[Struct]/Local: NestedInnerC[#NestedInnerC#]{{; name=.+$}} @@ -491,9 +491,9 @@ func testOuterC() { typealias CTestTypealias = #^NESTED_NOMINAL_DECL_C_3^# // NESTED_NOMINAL_DECL_C_3: Begin completions -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Struct]/OutNominal: NestedInnerCStruct[#NestedInnerC.NestedInnerCStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Class]/OutNominal: NestedInnerCClass[#NestedInnerC.NestedInnerCClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Enum]/OutNominal: NestedInnerCEnum[#NestedInnerC.NestedInnerCEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Struct]/OutNominal: NestedInnerCStruct[#NestedInnerCStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Class]/OutNominal: NestedInnerCClass[#NestedInnerCClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Enum]/OutNominal: NestedInnerCEnum[#NestedInnerCEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_3-DAG: Decl[TypeAlias]/OutNominal: NestedInnerCTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_C_3-DAG: Decl[Struct]/Local: NestedInnerC[#NestedInnerC#]{{; name=.+$}} diff --git a/test/IDE/complete_opaque_result.swift b/test/IDE/complete_opaque_result.swift index 1d376c8710a67..c1e221593d2f8 100644 --- a/test/IDE/complete_opaque_result.swift +++ b/test/IDE/complete_opaque_result.swift @@ -111,9 +111,8 @@ class TestClass : HasAssocWithDefault, HasAssocWithConstraintAndDefault { #^OVERRIDE_TestClass^# -// OVERRIDE: found code completion token OVERRIDE_[[BASETYPE:[A-Za-z0-9]+]] at // OVERRIDE: Begin completions -// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocPlain() -> [[BASETYPE]].AssocPlain {|}; +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocPlain() -> AssocPlain {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConformanceConstraint(fn: (Int) -> Int) -> some MyProtocol {|}; // OVERRIDE-DAG: Decl[InstanceVar]/Super: var valAssocWithSuperClassConstraint: some MyClass; // OVERRIDE-DAG: Decl[Subscript]/Super: subscript(idx: T) -> some MyClass & MyProtocol where T : Comparable {|}; diff --git a/test/IDE/complete_override.swift b/test/IDE/complete_override.swift index 27110af69ab8e..4d85e3a36a40d 100644 --- a/test/IDE/complete_override.swift +++ b/test/IDE/complete_override.swift @@ -857,7 +857,7 @@ struct MissingAssoc: AssocAndMethod { func #^MISSING_ASSOC_1^# } // MISSING_ASSOC_1: Begin completions -// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f1(_: MissingAssoc.T) {|}; -// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f2(_: MissingAssoc.U) {|}; -// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f3(_: MissingAssoc.V) {|}; +// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f1(_: T) {|}; +// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f2(_: U) {|}; +// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f3(_: V) {|}; // MISSING_ASSOC_1: End completions diff --git a/test/IDE/complete_override_typealias.swift b/test/IDE/complete_override_typealias.swift index 0485e4a279c70..b9be0cb2dc28a 100644 --- a/test/IDE/complete_override_typealias.swift +++ b/test/IDE/complete_override_typealias.swift @@ -30,16 +30,16 @@ struct MyStruct3 : MyProtocol { } // OVERRIDE_1: Begin completions, 2 items -// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: foo(arg1: MyStruct1.Arg1, arg2: MyStruct1.Arg2, arg3: MyStruct1.Arg2, arg4: [MyStruct1.Arg1], arg5: MyStruct1.Arg2?, arg6: Generic>>>) -> MyStruct1.Result {|}; -// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: bar() -> MyStruct1.Result {|}; +// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2?, arg6: Generic>>>) -> Result {|}; +// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: bar() -> Result {|}; // OVERRIDE_1: End completions // OVERRIDE_2: Begin completions, 2 items -// OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: foo(arg1: MyStruct2.Arg1, arg2: MyStruct2.Arg2, arg3: MyStruct2.Arg2, arg4: [MyStruct2.Arg1], arg5: MyStruct2.Arg2?, arg6: Generic>>>) -> String {|}; +// OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2?, arg6: Generic>>>) -> String {|}; // OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: bar() -> String {|}; // OVERRIDE_2: End completions // OVERRIDE_3-DAG: Begin completions, 1 items -// OVERRIDE_3-DAG: Decl[InstanceMethod]/Super: foo(arg1: MyStruct3.Arg1, arg2: MyStruct3.Arg2, arg3: MyStruct3.Arg2, arg4: [MyStruct3.Arg1], arg5: MyStruct3.Arg2?, arg6: Generic>>>) -> String {|}; +// OVERRIDE_3-DAG: Decl[InstanceMethod]/Super: foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2?, arg6: Generic>>>) -> String {|}; // OVERRIDE_3-DAG: End completions diff --git a/test/IDE/complete_single_expression_return.swift b/test/IDE/complete_single_expression_return.swift index 8573a1ff1a3d8..1df478228eb2d 100644 --- a/test/IDE/complete_single_expression_return.swift +++ b/test/IDE/complete_single_expression_return.swift @@ -98,7 +98,7 @@ struct TestSingleExprClosureRetUnresolved { // TestSingleExprClosureRetUnresolved: Begin completions // TestSingleExprClosureRetUnresolved-NOT: notMine -// TestSingleExprClosureRetUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExprClosure{{(Ret)?}}Unresolved.MyEnum#]; +// TestSingleExprClosureRetUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; // TestSingleExprClosureRetUnresolved-NOT: notMine // TestSingleExprClosureRetUnresolved: End completions } @@ -263,7 +263,7 @@ struct TestSingleExprFuncUnresolved { // TestSingleExprFuncUnresolved: Begin completions // TestSingleExprFuncUnresolved-NOT: notMine -// TestSingleExprFuncUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExpr{{(Local)?}}FuncUnresolved.MyEnum#]; +// TestSingleExprFuncUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; // TestSingleExprFuncUnresolved-NOT: notMine // TestSingleExprFuncUnresolved: End completions } @@ -373,7 +373,7 @@ struct TestSingleExprAccessorUnresolved { // TestSingleExprAccessorUnresolved: Begin completions // TestSingleExprAccessorUnresolved-NOT: notMine -// TestSingleExprAccessorUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExpr{{(Local)?}}Accessor{{(Get)?}}Unresolved.MyEnum#]; +// TestSingleExprAccessorUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; // TestSingleExprAccessorUnresolved-NOT: notMine // TestSingleExprAccessorUnresolved: End completions } @@ -525,7 +525,7 @@ struct TestSingleExprSubscriptUnresolved { // TestSingleExprSubscriptUnresolved: Begin completions // TestSingleExprSubscriptUnresolved-NOT: notMine -// TestSingleExprSubscriptUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExpr{{(Local)?}}Subscript{{(Get)?}}Unresolved.MyEnum#]; +// TestSingleExprSubscriptUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; // TestSingleExprSubscriptUnresolved-NOT: notMine // TestSingleExprSubscriptUnresolved: End completions } diff --git a/test/IDE/complete_type.swift b/test/IDE/complete_type.swift index 93334e7f0e124..c3ffb661adffd 100644 --- a/test/IDE/complete_type.swift +++ b/test/IDE/complete_type.swift @@ -247,64 +247,64 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_FUNC_PARAM_NESTED_TYPES_1 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_FUNC_PARAM_NESTED_TYPES_2 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_FUNC_PARAM_NESTED_TYPES_3 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_FUNC_PARAM_NESTED_TYPES_4 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_INSTANCE_VAR_1 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_INSTANCE_VAR_2 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_INSTANCE_VAR_3 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_INSTANCE_VAR_4 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_1 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_2 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_3 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_4 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt @@ -671,9 +671,9 @@ class TestTypeInLocalVarInMemberFunc1 { } } // TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1: Begin completions -// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#TestTypeInLocalVarInMemberFunc1.NestedStruct#]{{; name=.+$}} -// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Class]/CurrNominal: NestedClass[#TestTypeInLocalVarInMemberFunc1.NestedClass#]{{; name=.+$}} -// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#TestTypeInLocalVarInMemberFunc1.NestedEnum#]{{; name=.+$}} +// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1: End completions @@ -911,6 +911,19 @@ extension VarBase1 { // VAR_BASE_1_TYPES-DAG: Decl[TypeAlias]/CurrNominal: BaseExtNestedTypealias[#Int#]{{; name=.+$}} // VAR_BASE_1_TYPES: End completions +// VAR_BASE_1_TYPES_INCONTEXT: Begin completions +// From VarBase1 +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Struct]/CurrNominal: BaseNestedStruct[#BaseNestedStruct#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Class]/CurrNominal: BaseNestedClass[#BaseNestedClass#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Enum]/CurrNominal: BaseNestedEnum[#BaseNestedEnum#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/CurrNominal: BaseNestedTypealias[#Int#]{{; name=.+$}} +// From VarBase1 extension +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Struct]/CurrNominal: BaseExtNestedStruct[#BaseExtNestedStruct#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Class]/CurrNominal: BaseExtNestedClass[#BaseExtNestedClass#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Enum]/CurrNominal: BaseExtNestedEnum[#BaseExtNestedEnum#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/CurrNominal: BaseExtNestedTypealias[#Int#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT: End completions + // VAR_BASE_1_NO_DOT_TYPES: Begin completions // From VarBase1 // VAR_BASE_1_NO_DOT_TYPES-DAG: Decl[Struct]/CurrNominal: .BaseNestedStruct[#VarBase1.BaseNestedStruct#]{{; name=.+$}} @@ -987,6 +1000,29 @@ extension VarDerived1 { // VAR_DERIVED_1_TYPES-DAG: Decl[TypeAlias]/CurrNominal: DerivedExtNestedTypealias[#Int#]{{; name=.+$}} // VAR_DERIVED_1_TYPES: End completions +// VAR_DERIVED_1_TYPES_INCONTEXT: Begin completions +// From VarBase1 +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Struct]/Super: BaseNestedStruct[#VarBase1.BaseNestedStruct#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Class]/Super: BaseNestedClass[#VarBase1.BaseNestedClass#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Enum]/Super: BaseNestedEnum[#VarBase1.BaseNestedEnum#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/Super: BaseNestedTypealias[#Int#]{{; name=.+$}} +// From VarBase1 extension +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Struct]/Super: BaseExtNestedStruct[#VarBase1.BaseExtNestedStruct#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Class]/Super: BaseExtNestedClass[#VarBase1.BaseExtNestedClass#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Enum]/Super: BaseExtNestedEnum[#VarBase1.BaseExtNestedEnum#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/Super: BaseExtNestedTypealias[#Int#]{{; name=.+$}} +// From VarDerived1 +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Struct]/CurrNominal: DerivedNestedStruct[#DerivedNestedStruct#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Class]/CurrNominal: DerivedNestedClass[#DerivedNestedClass#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Enum]/CurrNominal: DerivedNestedEnum[#DerivedNestedEnum#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/CurrNominal: DerivedNestedTypealias[#Int#]{{; name=.+$}} +// From VarDerived1 extension +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Struct]/CurrNominal: DerivedExtNestedStruct[#DerivedExtNestedStruct#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Class]/CurrNominal: DerivedExtNestedClass[#DerivedExtNestedClass#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Enum]/CurrNominal: DerivedExtNestedEnum[#DerivedExtNestedEnum#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/CurrNominal: DerivedExtNestedTypealias[#Int#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT: End completions + //===--- //===--- Test that we can complete based on user-provided type-identifier. //===--- diff --git a/test/IDE/complete_type_subscript.swift b/test/IDE/complete_type_subscript.swift index e93fcf59a96e6..5411068a60b6c 100644 --- a/test/IDE/complete_type_subscript.swift +++ b/test/IDE/complete_type_subscript.swift @@ -20,7 +20,7 @@ struct S1 { subscript(x: MyStruct#^PARAM_1^#) -> Int { return 0 } subscript(x: MyStruct) -> MyStruct#^RETURN_1^# { } } -// MYSTRUCT_0: Keyword/None: .Type[#S1.MyStruct.Type#]; +// MYSTRUCT_0: Keyword/None: .Type[#MyStruct.Type#]; // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PARAM_2 | %FileCheck %s -check-prefix=MYSTRUCT_1 @@ -30,8 +30,8 @@ struct S2 { subscript(x: MyStruct.#^PARAM_2^#) -> Int { return 0 } subscript(x: MyStruct) -> MyStruct.#^RETURN_2^# { } } -// MYSTRUCT_1: Keyword/None: Type[#S2.MyStruct.Type#]; -// MYSTRUCT_1-NOT: Keyword/CurrNominal: self[#S2.MyStruct#]; +// MYSTRUCT_1: Keyword/None: Type[#MyStruct.Type#]; +// MYSTRUCT_1-NOT: Keyword/CurrNominal: self[#MyStruct#]; // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_PARAM_0 | %FileCheck %s -check-prefix=GEN_TOP_LEVEL_0 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_RETURN_0 | %FileCheck %s -check-prefix=GEN_TOP_LEVEL_0 diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index c19557f524fff..bbe71f44ea5ec 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -53,7 +53,7 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=WITH_LITERAL_1 | %FileCheck %s -check-prefix=WITH_LITERAL_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=WITH_LITERAL_2 | %FileCheck %s -check-prefix=WITH_LITERAL_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=WITH_LITERAL_3 | %FileCheck %s -check-prefix=WITH_LITERAL_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=WITH_LITERAL_3 | %FileCheck %s -check-prefix=WITH_LITERAL_3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INVALID_1 @@ -443,6 +443,9 @@ func testWithLiteral3() { func takeEnum(thing: MyEnum, other: Double) {} func test(s: S) { _ = s.takeEnum(thing: .#^WITH_LITERAL_3^#, other: 1.0) +// WITH_LITERAL_3: Begin completions, 1 items +// WITH_LITERAL_3-NEXT: Decl[EnumElement]/ExprSpecific: myCase[#MyEnum#]; +// WITH_LITERAL_3-NEXT: End completions } } } From 6da2ca7afae4ad62f7f47fc726190e43d1741c75 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 17 Dec 2019 15:19:26 -0800 Subject: [PATCH 080/478] [ASTPrinter] Test case for moduleinterface with member typealiases --- test/ModuleInterface/member-typealias.swift | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/ModuleInterface/member-typealias.swift diff --git a/test/ModuleInterface/member-typealias.swift b/test/ModuleInterface/member-typealias.swift new file mode 100644 index 0000000000000..6d8f2182c2cd6 --- /dev/null +++ b/test/ModuleInterface/member-typealias.swift @@ -0,0 +1,21 @@ +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path - %s -enable-library-evolution -module-name MyModule | %FileCheck %s --check-prefix CHECK + +public struct MyStruct { +// CHECK-LABEL: public struct MyStruct { + public typealias AliasT = T + public typealias AliasInt = Int + + public func foo(x: AliasInt) -> AliasT { fatalError() } +// CHECK: public func foo(x: MyModule.MyStruct.AliasInt) -> MyModule.MyStruct.AliasT +} + +public class MyBase { + public typealias AliasU = U + public typealias AliasInt = Int +} + +public class MyDerived: MyBase { +// CHECK-LABEL: public class MyDerived : MyModule.MyBase { + public func bar(x: AliasU) -> AliasInt { fatalError() } +// CHECK: public func bar(x: MyModule.MyDerived.AliasU) -> MyModule.MyDerived.AliasInt +} From fa31c75aac4ac56c3b15e59b42bdc0c941cb2498 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Tue, 17 Dec 2019 16:35:01 -0800 Subject: [PATCH 081/478] Create shared utility `Parser::canParseSimpleTypeIdentifier`. Use it in both `Parser::canParseTypeIdentifier` and `Parser::canParseBaseTypeForQualifiedDeclName`. --- include/swift/Parse/Parser.h | 18 ++++++++++++------ lib/Parse/ParseType.cpp | 31 +++++++++++++++++-------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 335907a66c7b5..202780a8f1485 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1377,6 +1377,14 @@ class Parser { bool canParseAsGenericArgumentList(); bool canParseType(); + + /// Returns true if a simple type identifier can be parsed. + /// + /// \verbatim + /// simple-type-identifier: identifier generic-argument-list? + /// \endverbatim + bool canParseSimpleTypeIdentifier(); + bool canParseTypeIdentifier(); bool canParseTypeIdentifierOrTypeComposition(); bool canParseOldStyleProtocolComposition(); @@ -1386,13 +1394,11 @@ class Parser { bool canParseTypedPattern(); - /// Returns true if a base type for a qualified declaration name can be - /// parsed. + /// Returns true if a qualified declaration name base type can be parsed. /// - /// Examples: - /// 'Foo.f' -> true - /// 'Foo.Bar.f' -> true - /// 'f' -> false + /// \verbatim + /// qualified-decl-name-base-type: simple-type-identifier '.' + /// \endverbatim bool canParseBaseTypeForQualifiedDeclName(); //===--------------------------------------------------------------------===// diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index cde85091c04cf..58149f6e8b014 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -1559,16 +1559,24 @@ bool Parser::canParseTypeIdentifierOrTypeComposition() { } } +bool Parser::canParseSimpleTypeIdentifier() { + // Parse an identifier. + if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any)) + return false; + consumeToken(); + + // Parse an optional generic argument list. + if (startsWithLess(Tok)) + if (!canParseGenericArguments()) + return false; + + return true; +} + bool Parser::canParseTypeIdentifier() { while (true) { - if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any)) + if (!canParseSimpleTypeIdentifier()) return false; - consumeToken(); - - if (startsWithLess(Tok)) { - if (!canParseGenericArguments()) - return false; - } // Treat 'Foo.' as an attempt to write a dotted type // unless is 'Type' or 'Protocol'. @@ -1585,14 +1593,9 @@ bool Parser::canParseTypeIdentifier() { bool Parser::canParseBaseTypeForQualifiedDeclName() { BacktrackingScope backtrack(*this); - // First, parse a single type identifier component. - if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any)) + // Parse a simple type identifier. + if (!canParseSimpleTypeIdentifier()) return false; - consumeToken(); - if (startsWithLess(Tok)) { - if (!canParseGenericArguments()) - return false; - } // Qualified name base types must be followed by a period. // If the next token starts with a period, return true. From 80dbf7e54965ae5ea1d64612ecb40052d2ef2129 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 17 Dec 2019 16:42:47 -0800 Subject: [PATCH 082/478] Add test case for https://bugs.swift.org/browse/SR-9868 rdar://problem/47871647 --- test/IDE/complete_override_typealias.swift | 11 ++++++++--- test/decl/protocol/conforms/fixit_stub_editor.swift | 9 +++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/test/IDE/complete_override_typealias.swift b/test/IDE/complete_override_typealias.swift index b9be0cb2dc28a..dd0e77cc53474 100644 --- a/test/IDE/complete_override_typealias.swift +++ b/test/IDE/complete_override_typealias.swift @@ -8,8 +8,10 @@ protocol MyProtocol { associatedtype Result typealias Arg1 = Generic typealias Arg2 = Generic + typealias Func = () -> Int func foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2? arg6: Generic>>>) -> Result func bar() -> Result + func baz(arg: @escaping Func) } struct MyStruct1 : MyProtocol { @@ -29,17 +31,20 @@ struct MyStruct3 : MyProtocol { func #^OVERRIDE_3^# } -// OVERRIDE_1: Begin completions, 2 items +// OVERRIDE_1: Begin completions, 3 items // OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2?, arg6: Generic>>>) -> Result {|}; // OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: bar() -> Result {|}; +// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: baz(arg: @escaping Func) {|}; // OVERRIDE_1: End completions -// OVERRIDE_2: Begin completions, 2 items +// OVERRIDE_2: Begin completions, 3 items // OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2?, arg6: Generic>>>) -> String {|}; // OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: bar() -> String {|}; +// OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: baz(arg: @escaping Func) {|}; // OVERRIDE_2: End completions -// OVERRIDE_3-DAG: Begin completions, 1 items +// OVERRIDE_3-DAG: Begin completions, 2 items // OVERRIDE_3-DAG: Decl[InstanceMethod]/Super: foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2?, arg6: Generic>>>) -> String {|}; +// OVERRIDE_3-DAG: Decl[InstanceMethod]/Super: baz(arg: @escaping Func) {|}; // OVERRIDE_3-DAG: End completions diff --git a/test/decl/protocol/conforms/fixit_stub_editor.swift b/test/decl/protocol/conforms/fixit_stub_editor.swift index 8b4cb5232405c..30c1aeb2aa1b5 100644 --- a/test/decl/protocol/conforms/fixit_stub_editor.swift +++ b/test/decl/protocol/conforms/fixit_stub_editor.swift @@ -72,3 +72,12 @@ struct Struct3: PropertyMutabilityProto { // expected-error{{type 'Struct3' does class Class4 {} extension Class4: PropertyMutabilityProto { // expected-error{{type 'Class4' does not conform to protocol 'PropertyMutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{44-44=\n var computed: Int {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n\n var stored: Int {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n}} } + +// https://bugs.swift.org/browse/SR-9868 +protocol FooProto { + typealias CompletionType = (Int) -> Void + func doSomething(then completion: @escaping CompletionType) +} + +struct FooType : FooProto { // expected-error {{type 'FooType' does not conform to protocol 'FooProto'}} expected-note {{do you want to add protocol stubs?}} {{28-28=\n func doSomething(then completion: @escaping CompletionType) {\n <#code#>\n \}\n}} +} From 6eb6bbefd2805e2c6112c27e5813671a6f7963b8 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Tue, 17 Dec 2019 18:25:13 -0800 Subject: [PATCH 083/478] Revert "[ClangImporter] Hash content for validation when using clang modules" This reverts commit c362e925af7ff948be704c55cb4406a05f5d13b7. This might be causing hard to reproduce issues. Taking it out until we have further investigation. rdar://problem/57964637 --- lib/ClangImporter/ClangImporter.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index d3ffc06ccb67f..125c39f0b27a9 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -1076,10 +1076,6 @@ ClangImporter::create(ASTContext &ctx, const ClangImporterOptions &importerOpts, // read them later. instance.getLangOpts().NeededByPCHOrCompilationUsesPCH = true; - // Make sure to not trigger extra rebuilds on identical files with mismatching - // timestamps. - instance.getHeaderSearchOpts().ValidateASTInputFilesContent = true; - if (importerOpts.Mode == ClangImporterOptions::Modes::PrecompiledModule) return importer; From 208ac454383374488825af639a13079301ff8cfe Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 17 Dec 2019 21:55:04 -0500 Subject: [PATCH 084/478] TypeNodes.def -> TypeNodes.inc because of a random open-source request. --- lib/ClangImporter/ImportType.cpp | 2 +- lib/IRGen/GenCall.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 8e112ab39efce..2deb1a8045edd 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -185,7 +185,7 @@ namespace { llvm_unreachable("Dependent types cannot be converted"); \ } #define TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // Given a loaded type like CInt, look through the type alias sugar that the // stdlib uses to show the underlying type. We want to import the signature diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 96665c5bd7db4..8dbfb7847f052 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -692,7 +692,7 @@ namespace { case clang::Type::Class: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) \ case clang::Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("canonical or dependent type in ABI lowering"); // These shouldn't occur in expandable struct types. From 80c45b40af1e8af3860bcc4daa67dfa4bc9c931e Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 17 Dec 2019 20:25:31 -0800 Subject: [PATCH 085/478] build: add explicit architecture check When doing a unified cross-compile, the target will always exist, but the architecture will not be the same. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f64e8cbb2066..cc4a74ae93336 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -953,7 +953,8 @@ if(SWIFT_BUILD_SYNTAXPARSERLIB OR SWIFT_BUILD_SOURCEKIT) set(SWIFT_LIBDISPATCH_CXX_COMPILER ${CMAKE_CXX_COMPILER}) elseif(${CMAKE_SYSTEM_NAME} STREQUAL ${CMAKE_HOST_SYSTEM_NAME}) if(CMAKE_SYSTEM_NAME STREQUAL Windows) - if(TARGET clang) + if(CMAKE_SYSTEM_PROCESSOR STREQUAL CMAKE_HOST_SYSTEM_PROCESSOR AND + TARGET clang) set(SWIFT_LIBDISPATCH_C_COMPILER $/clang-cl${CMAKE_EXECUTABLE_SUFFIX}) set(SWIFT_LIBDISPATCH_CXX_COMPILER From bb067f4d6827f9be36df8779552cfc6c595e1c00 Mon Sep 17 00:00:00 2001 From: eeckstein Date: Wed, 18 Dec 2019 16:17:12 +0100 Subject: [PATCH 086/478] Revert "EscapeAnalysis: add node flags and change the meaning of "escaping"" --- .../SILOptimizer/Analysis/EscapeAnalysis.h | 358 +++---- lib/SILOptimizer/Analysis/AliasAnalysis.cpp | 4 +- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 890 ++++++++---------- .../Transforms/StackPromotion.cpp | 20 +- .../UtilityPasses/EscapeAnalysisDumper.cpp | 44 - test/SILOptimizer/escape_analysis.sil | 723 ++++++-------- .../escape_analysis_dead_store.sil | 138 --- .../sil_combine_concrete_existential.swift | 2 +- 8 files changed, 841 insertions(+), 1338 deletions(-) delete mode 100644 test/SILOptimizer/escape_analysis_dead_store.sil diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index d6ee88c0a36a8..9c4b364383d11 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -43,20 +43,18 @@ /// return a /// /// Generates the following connection graph, where 'a' is in the SILValue %0: -/// Val [ref] %1 Esc: R, Succ: (%1) // Reference 'a'; points to 'a's object -/// Con [int] %2 Esc: R, Succ: (%2.1) // Object pointed to by 'a' -/// Con %2.1 Esc: G, Succ: // Fields in the object -/// Ret Esc: R, Succ: %0 // The returned value, aliased with 'a' +/// Val %0 Esc: R, Succ: (%0.1) // Represents 'a', and points to 'a's content +/// Con %0.1 Esc: G, Succ: // Represents the content of 'a' +/// Ret Esc: R, Succ: %0 // The returned value, aliased with 'a' /// /// Each node has an escaping state: None, (R)eturn, (A)rguments, or (G)lobal. /// These states form a lattice in which None is the most refined, or top, state /// and Global is the least refined, or bottom, state. Merging nodes performs a -/// meet operation on their escaping states, moving the state down in the -/// lattice. At a call site, the callee graph is merged with the caller graph by -/// merging the respective call argument nodes. A node has a "Return" escaping -/// state if it only escapes by being returned from the current function. A node -/// has an "Argument" escaping state if only escapes by being passed as an -/// incoming argument to this function. +/// meet operation on their escaping states. At a call site, the callee graph is +/// merged with the callee graph by merging the respective call argument +/// nodes. A node has a "Return" escaping state if it only escapes by being +/// returned from the current function. A node has an "Argument" escaping state +/// if only escapes by being passed as an incoming argument to this function. /// /// A directed edge between two connection graph nodes indicates that the memory /// represented by the destination node memory is reachable via an address @@ -83,37 +81,30 @@ /// /// Generates the following connection graph, where the alloc_stack for variable /// 'a' is in the SILValue %0 and class allocation returns SILValue %3. -/// -/// Val %0 Esc: , Succ: (%0.1) // Stack address of 'a' -/// Con [ref] %0.1 Esc: , Succ: %3 // Local reference 'a', aliased with %3 -/// Val [ref] %3 Esc: , Succ: (%4) // Local instance 'a', stored in %0.1 -/// Con [int] %4 Esc: G, Succ: (%4.1) // Object, escapes -/// Con %4.1 Esc: G, Succ: // Fields, escapes -/// Ret Esc: , Succ: (%4), %3 +/// Val %0 Esc: G, Succ: (%0.1) +/// Con %0.1 Esc: G, Succ: %3 +/// Val %3 Esc: G, Succ: (%3.1) +/// Con %3.1 Esc: G, Succ: +/// Ret Esc: R, Succ: %3 /// /// The value node for variable 'a' now points to local variable storage /// (%0.1). That local variable storage contains a reference. Assignment into /// that reference creates a defer edge to the allocated reference (%3). The -/// allocated reference in turn points to the object storage (%4). +/// allocated reference in turn points to the object storage (%3.1). /// -/// Note that a variable holding a single class reference and a variable holding -/// a non-trivial struct will have the same graph representation. A '[ref]' flag -/// on a node indicates that the all pointer-type fields that may be stored -/// inside the memory represented by that node are references. This allows alias -/// analysis to assume the object the node points to will not be released when -/// the node's memory is released as long as there are subsequent accesses to -/// the object accessed via a different path in the connection graph. +/// Note that a variable holding a single class reference and a variable +/// holding a non-trivial struct has the same graph representation. The +/// variable's content node only represents the value of the references, not the +/// memory pointed-to by the reference. /// /// A pointsTo edge does not necessarily indicate pointer indirection. It may -/// simply represent a derived address within the same object. A node that -/// points to the same logical object must be flagged as an interior node -/// ('[int]'). Interior nodes always have pointsTo content representing the rest -/// of the object. This allows escape analysis to view an object's memory in -/// layers, each with separate escaping properties. For example, a class -/// object's first-level content node represents the object header including the -/// metadata pointer and reference count. An object's second level content node -/// only represents the reference-holding fields within that object. Consider -/// the connection graph for a class with properties: +/// simply represent a derived address within the same object. This allows +/// escape analysis to view an object's memory in layers, each with separate +/// escaping properties. For example, a class object's first-level content node +/// represents the object header including the metadata pointer and reference +/// count. An object's second level content node only represents the +/// reference-holding fields within that object. Consider the connection graph +/// for a class with properties: /// /// class HasObj { /// var obj: AnyObject @@ -123,37 +114,35 @@ /// } /// /// Which generates this graph where the argument 'h' is %0, and 'o' is %1: +/// Arg %0 Esc: A, Succ: (%0.1) +/// Con %0.1 Esc: A, Succ: (%0.2) +/// Con %0.2 Esc: A, Succ: %1 +/// Arg %1 Esc: A, Succ: (%1.1) +/// Con %1.1 Esc: A, Succ: (%1.2) +/// Con %1.2 Esc: G, Succ: /// -/// Arg [ref] %0 Esc: A, Succ: (%6) // 'h' -/// Arg [ref] %1 Esc: A, Succ: (%1.1) // 'o' -/// Con [int] %1.1 Esc: A, Succ: (%1.2) // 'o' object -/// Con %1.2 Esc: A, Succ: (%1.3) // 'o' fields -/// Con %1.3 Esc: G, Succ: // memory 'h.obj' may point to -/// Con [int] %6 Esc: A, Succ: (%7) // 'h' object -/// Con %7 Esc: A, Succ: %1 // 'h.obj' -/// -/// Node %1.1 represents the header of 'o', including reference count and -/// metadata pointer. This node points to %1.2 which represents the 'obj' -/// property. '%1.3' represents any potential nontrivial properties in 'o' which -/// may have escaped globally when 'o' was released. '%6' is a ref_element_addr -/// accessing 'h.obj'. '%7' is a load of 'h.obj'. The assignment 'h.obj = o' -/// creates a defer edge from '%7' to '%1'. +/// Node %0.1 represents the header of 'h', including reference count and +/// metadata pointer. This node points to %0.2 which represents the 'obj' +/// property. The assignment 'h.obj = o' creates a defer edge from %0.2 to +/// %1. Similarly, %1.1 represents the header of 'o', and %1.2 represents any +/// potential nontrivial properties in 'o' which may have escaped globally when +/// 'o' was released. /// /// The connection graph is constructed by summarizing all memory operations in /// a flow-insensitive way. Hint: ConGraph->viewCG() displays the Dot-formatted /// connection graph. /// -/// In addition to the connection graph, EscapeAnalysis caches information about -/// "use points". Each release operation in which the released reference can be -/// identified is a considered a use point. The use point instructions are -/// recorded in a table and given an ID. Each connection graph content node -/// stores a bitset indicating the use points that may release references that -/// point to by that content node. Correctness relies on an invariant: for each -/// reference-type value in the function, all points in the function which may -/// release the reference must be identified as use points of the node that the -/// value points to. If the reference-type value may be released any other -/// way, then its content node must be marked escaping. -/// ===---------------------------------------------------------------------===// +/// In addition to the connection graph, EscapeAnalysis stores information about +/// "use points". Each release operation is a use points. These instructions are +/// recorded in a table and given an ID. Each connection graph node stores a +/// bitset indicating the use points reachable via the CFG by that node. This +/// provides some flow-sensitive information on top of the otherwise flow +/// insensitive connection graph. +/// +/// Note: storing bitsets in each node may be unnecessary overhead since the +/// same information can be obtained with a graph traversal, typically of only +/// 1-3 hops. +// ===---------------------------------------------------------------------===// #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ESCAPEANALYSIS_H_ #define SWIFT_SILOPTIMIZER_ANALYSIS_ESCAPEANALYSIS_H_ @@ -245,13 +234,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { Global }; - // Must be ordered from most precise to least precise. A meet across memory - // locations (such as aggregate fields) always moves down. - enum PointerKind { NoPointer, ReferenceOnly, AnyPointer }; - static bool canOnlyContainReferences(PointerKind pointerKind) { - return pointerKind <= ReferenceOnly; - } - public: class CGNode; class ConnectionGraph; @@ -338,52 +320,25 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// is completely unlinked from the graph, bool isMerged = false; - /// True if this node's pointsTo content is part of the same object with - /// respect to SIL memory access. When this is true, then this node must - /// have a content node. - /// - /// When this flag is false, it provides a "separate access guarantee" for - /// stronger analysis. If the base object for a SIL memory access is mapped - /// to this node, then the accessed memory must have the same escaping - /// properties as the base object. In contrast, when this is true, the - /// connection graph may model the escaping properties of the base object - /// separately from the accessed memory. - bool isInteriorFlag; - - /// True if this node can only point to other nodes via a reference, not an - /// address or raw pointer. - /// - /// When this flag is true, it provides a "separate lifetime guarantee". Any - /// reference modeled by this node keeps alive all pointsTo content until - /// it is released. e.g. Given two nodes that both pointTo the same - /// content, releasing the value associated one node cannot free that - /// content as long as the released node is reference-only and the value - /// associated with the other node is accessed later. - /// - /// Note that an object field may contain a raw pointer which could point - /// anywhere, even back into the same object. In that case - /// hasReferenceOnlyFlag must be false. - /// - /// Typically, when this is true, the node represents at least one - /// reference, however, a return node may be created even when the return - /// type is NoPointer. - bool hasReferenceOnlyFlag; + /// True if this is a content node that owns a reference count. Such a + /// content node necessarilly keeps alive all content it points to until it + /// is released. This can be conservatively false. + bool hasRC = false; /// The type of the node (mainly distinguishes between content and value /// nodes). NodeType Type; /// The constructor. - CGNode(ValueBase *V, NodeType Type, bool isInterior, bool hasReferenceOnly) - : mappedValue(V), UsePoints(0), isInteriorFlag(isInterior), - hasReferenceOnlyFlag(hasReferenceOnly), Type(Type) { + CGNode(ValueBase *V, NodeType Type, bool hasRC) + : mappedValue(V), UsePoints(0), hasRC(hasRC), Type(Type) { switch (Type) { case NodeType::Argument: case NodeType::Value: - assert(V && !isInteriorFlag); + assert(V); break; case NodeType::Return: - assert(!V && !isInteriorFlag); + assert(!V); break; case NodeType::Content: // A content node representing the returned value has no associated @@ -405,8 +360,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return Changed; } - void mergeFlags(bool isInterior, bool hasReferenceOnly); - // Merge the properties of \p fromNode into this node. void mergeProperties(CGNode *fromNode); @@ -501,18 +454,10 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Return true if this node represents content. bool isContent() const { return Type == NodeType::Content; } - /// Return true if this node pointsTo the same object. - bool isInterior() const { return isInteriorFlag; } - - void setInterior(bool isInterior) { isInteriorFlag = isInterior; } + /// Return true if this node represents an entire reference counted object. + bool hasRefCount() const { return hasRC; } - /// Return true if this node can only point to another node via a reference, - /// as opposed to an address or raw pointer. - bool hasReferenceOnly() const { return hasReferenceOnlyFlag; } - - void setHasReferenceOnly(bool hasReferenceOnly) { - hasReferenceOnlyFlag = hasReferenceOnly; - } + void setRefCount(bool rc) { hasRC = rc; } /// Returns the escape state. EscapeState getEscapeState() const { return State; } @@ -641,10 +586,10 @@ class EscapeAnalysis : public BottomUpIPAnalysis { CGNode *ReturnNode = nullptr; /// The list of use points. - llvm::SmallVector UsePointTable; + llvm::SmallVector UsePointTable; /// Mapping of use points to bit indices in CGNode::UsePoints. - llvm::DenseMap UsePoints; + llvm::DenseMap UsePoints; /// The allocator for nodes. llvm::SpecificBumpPtrAllocator NodeAllocator; @@ -669,13 +614,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Allocates a node of a given type. /// - /// isInterior is always false for non-content nodes and is set for content - /// nodes based on the type and origin of the pointer. - CGNode *allocNode(ValueBase *V, NodeType Type, bool isInterior, - bool isReference) { - assert((Type == NodeType::Content) || !isInterior); - CGNode *Node = new (NodeAllocator.Allocate()) - CGNode(V, Type, isInterior, isReference); + /// hasRC is set for Content nodes based on the type and origin of + /// the pointer. + CGNode *allocNode(ValueBase *V, NodeType Type, bool hasRC = false) { + assert(Type == NodeType::Content || !hasRC); + CGNode *Node = new (NodeAllocator.Allocate()) CGNode(V, Type, hasRC); Nodes.push_back(Node); return Node; } @@ -722,9 +665,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { } } - // Helper for getNode and getValueContent. - CGNode *getOrCreateNode(ValueBase *V, PointerKind pointerKind); - /// Gets or creates a node for a value \p V. /// If V is a projection(-path) then the base of the projection(-path) is /// taken. This means the node is always created for the "outermost" value @@ -732,14 +672,9 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Returns null, if V is not a "pointer". CGNode *getNode(ValueBase *V, bool createIfNeeded = true); - // Helper for getValueContent to create and return a content node with the - // given \p isInterior and \p hasReferenceOnly flags. \p addrNode - // will gain a points-to edge to the new content node. - CGNode *createContentNode(CGNode *addrNode, bool isInterior, - bool hasReferenceOnly); - - CGNode *getOrCreateContentNode(CGNode *addrNode, bool isInterior, - bool hasReferenceOnly); + /// Helper to create and return a content node with the given \p hasRC + /// flag. \p addrNode will gain a points-to edge to the new content node. + CGNode *createContentNode(CGNode *addrNode, bool hasRC); /// Create a new content node based on an existing content node to support /// graph merging. @@ -748,33 +683,18 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// state will be initialized based on the \p srcContent node. CGNode *createMergedContent(CGNode *destAddrNode, CGNode *srcContent); - // Helper for getValueContent to get the content node for an address, which - // may be variable content or field content. - CGNode *getOrCreateAddressContent(SILValue addrVal, CGNode *addrNode); - - // Helper for getValueContent to get the content representing a referenced - // object. \p refVal's type may or may not have reference semantics. The - // caller must knows based on the operand using \p refVal that it contains a - // reference. - CGNode *getOrCreateReferenceContent(SILValue refVal, CGNode *refNode); - - // Helper for getValueContent to get the content node for an unknown pointer - // value. This is useful to determine whether multiple nodes are in the same - // defer web, but is otherwise conservative. - CGNode *getOrCreateUnknownContent(CGNode *addrNode); - - /// Get a node representing the field data within the given object node. - /// If objNode was a recognized reference-only value, then it's content node - /// will already be initialized according to the reference type. Otherwise, - /// return null. - CGNode *getFieldContent(CGNode *objNode) { - if (!objNode->isInterior()) - return nullptr; - return objNode->getContentNodeOrNull(); - } + /// Get a node represnting the field data within the given RC node. + CGNode *getFieldContent(CGNode *rcNode); /// Get or creates a pseudo node for the function return value. - CGNode *getReturnNode(); + CGNode *getReturnNode() { + if (!ReturnNode) { + ReturnNode = allocNode(nullptr, NodeType::Return); + if (!isSummaryGraph) + ReturnNode->mergeEscapeState(EscapeState::Return); + } + return ReturnNode; + } /// Returns the node for the function return value if present. CGNode *getReturnNodeOrNull() const { @@ -798,25 +718,28 @@ class EscapeAnalysis : public BottomUpIPAnalysis { Node->mappedValue = V; } - /// If \p pointer is a pointer, set it's content to global escaping. - /// - /// Only mark the content node as escaping. Marking a pointer node as - /// escaping would generally be meaningless because it may have aliases or - /// defer edges. Marking the pointer node as escaping would also be too - /// conservative because, when the pointer is mapped to a content node, it - /// would behave as if the memory containing the pointer also escaped. - /// - /// If the pointer is mapped to a node, then calling setEscapesGlobal must - /// ensure that it points to a content node. Even if we could mark the - /// pointer node as escaping, it would be insufficient because only content - /// nodes are merged into the caller graph. - void setEscapesGlobal(SILValue pointer) { - if (CGNode *content = getValueContent(pointer)) - content->markEscaping(); + /// Adds an argument/instruction in which the node's value is used. + int addUsePoint(CGNode *Node, SILNode *User) { + if (Node->getEscapeState() >= EscapeState::Global) + return -1; + + User = User->getRepresentativeSILNodeInObject(); + int Idx = (int)UsePoints.size(); + assert(UsePoints.count(User) == 0 && "value is already a use-point"); + UsePoints[User] = Idx; + UsePointTable.push_back(User); + assert(UsePoints.size() == UsePointTable.size()); + Node->setUsePointBit(Idx); + return Idx; } - /// Adds an argument/instruction in which the node's value is used. - int addUsePoint(CGNode *Node, SILInstruction *User); + void escapeContentsOf(CGNode *Node) { + CGNode *escapedContent = Node->getContentNodeOrNull(); + if (!escapedContent) { + escapedContent = createContentNode(Node, /*hasRC=*/false); + } + escapedContent->markEscaping(); + } /// Creates a defer-edge between \p From and \p To. /// This may trigger node merges to keep the graph invariance 4). @@ -883,14 +806,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Returns null, if V is not a "pointer". CGNode *getNodeOrNull(ValueBase *V) { return getNode(V, false); } - /// Get the content node pointed to by \p ptrVal. - /// - /// If \p ptrVal cannot be mapped to a node, return nullptr. - /// - /// If \p ptrVal is mapped to a node, return a non-null node representing - /// the content that \p ptrVal points to. - CGNode *getValueContent(SILValue ptrVal); - /// Returns the number of use-points of a node. int getNumUsePoints(CGNode *Node) { assert(!Node->escapes() && @@ -902,11 +817,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// (indirectly) somehow refer to the Node's value. /// Use-points are only values which are relevant for lifeness computation, /// e.g. release or apply instructions. - bool isUsePoint(SILInstruction *UsePoint, CGNode *Node); + bool isUsePoint(SILNode *UsePoint, CGNode *Node); /// Returns all use points of \p Node in \p UsePoints. void getUsePoints(CGNode *Node, - llvm::SmallVectorImpl &UsePoints); + llvm::SmallVectorImpl &UsePoints); /// Computes the use point information. void computeUsePoints(); @@ -986,8 +901,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { MaxGraphMerges = 4 }; - using PointerKindCache = llvm::DenseMap; - /// The connection graphs for all functions (does not include external /// functions). llvm::DenseMap Function2Info; @@ -996,7 +909,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { llvm::SpecificBumpPtrAllocator Allocator; /// Cache for isPointer(). - PointerKindCache pointerKindCache; + llvm::DenseMap isPointerCache; SILModule *M; @@ -1006,6 +919,10 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Callee analysis, used for determining the callees at call sites. BasicCalleeAnalysis *BCA; + /// Returns true if \p V may encapsulate a "pointer" value. + /// See EscapeAnalysis::NodeType::Value. + bool isPointer(ValueBase *V) const; + /// If EscapeAnalysis should consider the given value to be a derived address /// or pointer based on one of its address or pointer operands, then return /// that operand value. Otherwise, return an invalid value. @@ -1016,14 +933,23 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// value. SILValue getPointerRoot(SILValue value) const; - PointerKind findRecursivePointerKind(SILType Ty, const SILFunction &F) const; + /// If \p pointer is a pointer, set it to global escaping. + void setEscapesGlobal(ConnectionGraph *conGraph, ValueBase *pointer) { + CGNode *Node = conGraph->getNode(pointer); + if (!Node) + return; - PointerKind findCachedPointerKind(SILType Ty, const SILFunction &F) const; + if (Node->isContent()) { + Node->markEscaping(); + return; + } + Node->mergeEscapeState(EscapeState::Global); - // Returns true if the type \p Ty must be a reference or must transitively - // contain a reference and no other pointer or address type. - bool hasReferenceOnly(SILType Ty, const SILFunction &F) const { - return findCachedPointerKind(Ty, F) <= ReferenceOnly; + // Make sure to have a content node. Otherwise we may end up not merging + // the global-escape state into a caller graph (only content nodes are + // merged). Either the node itself is a content node or we let the node + // point to one. + conGraph->escapeContentsOf(Node); } /// Gets or creates FunctionEffects for \p F. @@ -1034,6 +960,15 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return FInfo; } + /// Get or create the node representing the memory pointed to by \p + /// addrVal. If \p addrVal is an address, then return the content node for the + /// variable's memory. Otherwise, \p addrVal may contain a reference, so + /// return the content node for the referenced heap object. + /// + /// Note that \p addrVal cannot be an address within a heap object, such as + /// an address from ref_element_addr or project_box. + CGNode *getValueContent(ConnectionGraph *conGraph, SILValue addrVal); + /// Build a connection graph for reach callee from the callee list. bool buildConnectionGraphForCallees(SILInstruction *Caller, CalleeList Callees, @@ -1055,9 +990,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { void buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth); - bool createArrayUninitializedSubgraph(FullApplySite apply, - ConnectionGraph *conGraph); - /// Updates the graph by analyzing instruction \p I. /// Visited callees are added to \p BottomUpOrder until \p RecursionDepth /// reaches MaxRecursionDepth. @@ -1090,11 +1022,10 @@ class EscapeAnalysis : public BottomUpIPAnalysis { bool mergeSummaryGraph(ConnectionGraph *SummaryGraph, ConnectionGraph *Graph); - /// Returns true if the value \p value or any address within that value can - /// escape to the \p usePoint, where \p usePoint is either a - /// release-instruction or a function call. - bool canEscapeToUsePoint(SILValue value, SILInstruction *usePoint, - ConnectionGraph *conGraph); + /// Returns true if the value \p V can escape to the \p UsePoint, where + /// \p UsePoint is either a release-instruction or a function call. + bool canEscapeToUsePoint(SILValue V, SILNode *UsePoint, + ConnectionGraph *ConGraph); friend struct ::CGForDotView; @@ -1115,19 +1046,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return &FInfo->Graph; } - /// Return \p value's PointerKind. - PointerKind getPointerKind(ValueBase *value) const { - auto *f = value->getFunction(); - // The function can be null, e.g. if V is an undef. - if (!f) - return NoPointer; - - return findCachedPointerKind(value->getType(), *f); - } - /// Returns true if \p V may encapsulate a "pointer" value. - /// See EscapeAnalysis::NodeType::Value. - bool isPointer(ValueBase *V) const { return getPointerKind(V) > NoPointer; } - /// Returns true if the value \p V can escape to the function call \p FAS. /// This means that the called function may access the value \p V. /// If \p V has reference semantics, this function returns false if only the @@ -1145,6 +1063,14 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// to \p V. bool canEscapeToValue(SILValue V, SILValue To); + /// Returns true if the parameter with index \p ParamIdx can escape in the + /// called function of apply site \p FAS. + /// If it is an indirect parameter and \p checkContentOfIndirectParam is true + /// then the escape status is not checked for the address itself but for the + /// referenced pointer (if the referenced type is a pointer). + bool canParameterEscape(FullApplySite FAS, int ParamIdx, + bool checkContentOfIndirectParam); + /// Returns true if the pointers \p V1 and \p V2 can possibly point to the /// same memory. /// diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index a6ab4c4ea36a5..90e304d0a5084 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -737,8 +737,8 @@ bool AliasAnalysis::mayValueReleaseInterfereWithInstruction(SILInstruction *User // The most important check: does the object escape the current function? auto LO = getUnderlyingObject(V); auto *ConGraph = EA->getConnectionGraph(User->getFunction()); - auto *Content = ConGraph->getValueContent(LO); - if (Content && !Content->escapes()) + auto *Node = ConGraph->getNodeOrNull(LO); + if (Node && !Node->escapes()) return false; // This is either a non-local allocation or a scoped allocation that escapes. diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 6ff7660b1a28b..99e3fae1dbf49 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -30,91 +30,120 @@ static llvm::cl::opt EnableInternalVerify( llvm::cl::desc("Enable internal verification of escape analysis"), llvm::cl::init(false)); -// Returns the kind of pointer that \p Ty recursively contains. -EscapeAnalysis::PointerKind -EscapeAnalysis::findRecursivePointerKind(SILType Ty, - const SILFunction &F) const { - // An address may be converted into a reference via something like - // raw_pointer_to_ref, but in general we don't know what kind of pointer it - // is. - if (Ty.isAddress()) - return EscapeAnalysis::AnyPointer; +// Returns true if \p Ty recursively contains a reference. If \p mustBeRef is +// true, only return true if the type is guaranteed to hold a reference. If \p +// mustBeRef is false, only return false if the type is guaranteed not to hold a +// reference. +// +// If \p Ty is itself an address, return false. +static bool findRecursiveRefType(SILType Ty, const SILFunction &F, + bool mustBeRef) { + if (mustBeRef) { + // An address *may* be converted into a reference via something like + // raw_pointer_to_ref. However, addresses don't normally refer to the head + // of a reference counted object. + // + // The check for trivial types catches types that have AST "reference + // semantics", but are determined by type lowering to be trivial, such as + // noescape function types. + if (Ty.isAddress() || Ty.isTrivial(F)) + return false; + } - // Opaque types may contain a reference. Speculatively track them too. - // - // 1. It may be possible to optimize opaque values based on known mutation - // points. - // - // 2. A specialized function may call a generic function passing a concrete - // reference type via incomplete specialization. - // - // 3. A generic function may call a specialized function taking a concrete - // reference type via devirtualization. - if (Ty.isAddressOnly(F)) - return EscapeAnalysis::AnyPointer; + if (!mustBeRef) { + // Opaque types may contain a reference. Speculatively track them too. + // + // 1. It may be possible to optimize opaque values based on known mutation + // points. + // + // 2. A specialized function may call a generic function passing a concrete + // reference type via incomplete specialization. + // + // 3. A generic function may call a specialized function taking a concrete + // reference type via devirtualization. + if (Ty.isAddressOnly(F)) + return true; - // A raw pointer definitely does not have a reference, but could point - // anywhere. We do track these because critical stdlib data structures often - // use raw pointers under the hood. - if (Ty.getASTType() == F.getModule().getASTContext().TheRawPointerType) - return EscapeAnalysis::AnyPointer; + if (Ty.getASTType() == F.getModule().getASTContext().TheRawPointerType) + return true; + } if (Ty.hasReferenceSemantics()) - return EscapeAnalysis::ReferenceOnly; + return true; - auto &M = F.getModule(); + auto &Mod = F.getModule(); - // Start with the most precise pointer kind - PointerKind aggregateKind = NoPointer; - auto meetAggregateKind = [&](PointerKind otherKind) { - if (otherKind > aggregateKind) - aggregateKind = otherKind; - }; if (auto *Str = Ty.getStructOrBoundGenericStruct()) { for (auto *Field : Str->getStoredProperties()) { - SILType fieldTy = Ty.getFieldType(Field, M, F.getTypeExpansionContext()); - meetAggregateKind(findCachedPointerKind(fieldTy, F)); + if (findRecursiveRefType( + Ty.getFieldType(Field, Mod, F.getTypeExpansionContext()), F, + mustBeRef)) + return true; } - return aggregateKind; + return false; } if (auto TT = Ty.getAs()) { for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) { - meetAggregateKind(findCachedPointerKind(Ty.getTupleElementType(i), F)); + if (findRecursiveRefType(Ty.getTupleElementType(i), F, mustBeRef)) + return true; } - return aggregateKind; + return false; } if (auto En = Ty.getEnumOrBoundGenericEnum()) { for (auto *ElemDecl : En->getAllElements()) { - if (!ElemDecl->hasAssociatedValues()) - continue; - SILType eltTy = - Ty.getEnumElementType(ElemDecl, M, F.getTypeExpansionContext()); - meetAggregateKind(findCachedPointerKind(eltTy, F)); + if (ElemDecl->hasAssociatedValues() && + findRecursiveRefType( + Ty.getEnumElementType(ElemDecl, Mod, F.getTypeExpansionContext()), + F, mustBeRef)) + return true; } - return aggregateKind; + return false; } - // FIXME: without a covered switch, this is not robust in the event that new - // reference-holding AST types are invented. - return NoPointer; + // FIXME: without a covered switch, this is not robust for mayContainReference + // in the event that new reference-holding AST types are invented. + return false; +} + +// Returns true if the type \p Ty is a reference or may transitively contain +// a reference. If \p Ty is itself an address, return false. +// +// An address may contain a reference because addresses can be cast into +// reference types. +static bool mayContainReference(SILType Ty, const SILFunction &F) { + if (Ty.isAddress()) + return true; + return findRecursiveRefType(Ty, F, false); +} + +// Returns true if the type \p Ty must be a reference or must transitively +// contain a reference. If \p Ty is itself an address, return false. +static bool mustContainReference(SILType Ty, const SILFunction &F) { + return findRecursiveRefType(Ty, F, true); } -// Returns the kind of pointer that \p Ty recursively contains. -EscapeAnalysis::PointerKind -EscapeAnalysis::findCachedPointerKind(SILType Ty, const SILFunction &F) const { - auto iter = pointerKindCache.find(Ty); - if (iter != pointerKindCache.end()) - return iter->second; +bool EscapeAnalysis::isPointer(ValueBase *V) const { + auto *F = V->getFunction(); + + // The function can be null, e.g. if V is an undef. + if (!F) + return false; + + SILType Ty = V->getType(); + auto Iter = isPointerCache.find(Ty); + if (Iter != isPointerCache.end()) + return Iter->second; - PointerKind pointerKind = findRecursivePointerKind(Ty, F); - const_cast(this)->pointerKindCache[Ty] = pointerKind; - return pointerKind; + bool IP = mayContainReference(Ty, *F); + const_cast(this)->isPointerCache[Ty] = IP; + return IP; } static bool isExtractOfArrayUninitializedPointer(TupleExtractInst *TEI) { - if (auto apply = dyn_cast(TEI->getOperand())) - if (ArraySemanticsCall(apply, "array.uninitialized", false)) - return true; - + if (TEI->getFieldNo() == 1) { + if (auto apply = dyn_cast(TEI->getOperand())) + if (ArraySemanticsCall(apply, "array.uninitialized", false)) + return true; + } return false; } @@ -333,26 +362,15 @@ EscapeAnalysis::CGNode::RepValue EscapeAnalysis::CGNode::getRepValue() const { depth}; } -void EscapeAnalysis::CGNode::mergeFlags(bool isInterior, - bool hasReferenceOnly) { - // isInterior is conservatively preserved from either node unless two content - // nodes are being merged and one is the interior node's content. - isInteriorFlag |= isInterior; - - // hasReferenceOnly is always conservatively merged. - hasReferenceOnlyFlag &= hasReferenceOnly; -} - void EscapeAnalysis::CGNode::mergeProperties(CGNode *fromNode) { - // isInterior is conservatively preserved from either node unless the other - // node is the interior node's content. - bool isInterior = fromNode->isInteriorFlag; - if (fromNode == pointsTo) - this->isInteriorFlag = isInterior; - else if (this == fromNode->pointsTo) - isInterior = this->isInteriorFlag; - - mergeFlags(isInterior, fromNode->hasReferenceOnlyFlag); + // TODO: Optimistically merge hasRC. 'this' node can only be merged with + // `fromNode` if their pointer values are compatible. If `fromNode->hasRC` is + // true, then it is guaranteed to represent the head of a heap object. Thus, + // it can only be merged with 'this' when the pointer values that access + // 'this' are also references. + // + // For now, this is pessimistic until we understand performance implications. + hasRC &= fromNode->hasRC; } template @@ -395,35 +413,15 @@ void EscapeAnalysis::ConnectionGraph::clear() { } EscapeAnalysis::CGNode * -EscapeAnalysis::ConnectionGraph::getOrCreateNode(ValueBase *V, - PointerKind pointerKind) { - assert(pointerKind != EscapeAnalysis::NoPointer); - +EscapeAnalysis::ConnectionGraph::getNode(ValueBase *V, bool createIfNeeded) { if (isa(V) || isa(V) || isa(V)) return nullptr; - CGNode * &Node = Values2Nodes[V]; - // Nodes mapped to values must have an indirect pointsTo. Nodes that don't - // have an indirect pointsTo are imaginary nodes that don't directly represnt - // a SIL value. - bool hasReferenceOnly = canOnlyContainReferences(pointerKind); - if (!Node) { - if (isa(V)) { - Node = allocNode(V, NodeType::Argument, false, hasReferenceOnly); - if (!isSummaryGraph) - Node->mergeEscapeState(EscapeState::Arguments); - } else { - Node = allocNode(V, NodeType::Value, false, hasReferenceOnly); - } - } - return Node->getMergeTarget(); -} - -EscapeAnalysis::CGNode * -EscapeAnalysis::ConnectionGraph::getNode(ValueBase *V, bool createIfNeeded) { - PointerKind pointerKind = EA->getPointerKind(V); - if (pointerKind == EscapeAnalysis::NoPointer) + // In the case of a struct or tuple extract, 'V' may not be a pointer + // even if it's pointer root is a pointer. Bail first because we only expect + // graph nodes for pointer values. + if (!EA->isPointer(V)) return nullptr; // Look past address projections, pointer casts, and the like within the same @@ -433,28 +431,18 @@ EscapeAnalysis::ConnectionGraph::getNode(ValueBase *V, bool createIfNeeded) { if (!createIfNeeded) return lookupNode(V); - - return getOrCreateNode(V, pointerKind); -} - -/// Adds an argument/instruction in which the node's memory is released. -int EscapeAnalysis::ConnectionGraph::addUsePoint(CGNode *Node, - SILInstruction *User) { - // Use points are never consulted for escaping nodes, but still need to - // propagate to other nodes in a defer web. Even if this node is escaping, - // some defer predecessors may not be escaping. Only checking if this node has - // defer predecessors is insufficient because a defer successor of this node - // may have defer predecessors. - if (Node->getEscapeState() >= EscapeState::Global) - return -1; - - int Idx = (int)UsePoints.size(); - assert(UsePoints.count(User) == 0 && "value is already a use-point"); - UsePoints[User] = Idx; - UsePointTable.push_back(User); - assert(UsePoints.size() == UsePointTable.size()); - Node->setUsePointBit(Idx); - return Idx; + + CGNode * &Node = Values2Nodes[V]; + if (!Node) { + if (isa(V)) { + Node = allocNode(V, NodeType::Argument); + if (!isSummaryGraph) + Node->mergeEscapeState(EscapeState::Arguments); + } else { + Node = allocNode(V, NodeType::Value); + } + } + return Node->getMergeTarget(); } CGNode *EscapeAnalysis::ConnectionGraph::defer(CGNode *From, CGNode *To, @@ -804,14 +792,10 @@ void EscapeAnalysis::ConnectionGraph::propagateEscapeStates() { Changed = false; for (CGNode *Node : Nodes) { - // Propagate the state to all pointsTo nodes. It would be sufficient to - // only follow proper pointsTo edges, since this loop also follows defer - // edges, but this may converge faster. + // Propagate the state to all successor nodes. if (Node->pointsTo) { Changed |= Node->pointsTo->mergeEscapeState(Node->State); } - // Note: Propagating along defer edges may be interesting from an SSA - // standpoint, but it is entirely irrelevant alias analysis. for (CGNode *Def : Node->defersTo) { Changed |= Def->mergeEscapeState(Node->State); } @@ -826,6 +810,15 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { #endif // First scan the whole function and add relevant instructions as use-points. for (auto &BB : *F) { + for (SILArgument *BBArg : BB.getArguments()) { + /// In addition to releasing instructions (see below) we also add block + /// arguments as use points. In case of loops, block arguments can + /// "extend" the liferange of a reference in upward direction. + if (CGNode *ArgNode = lookupNode(BBArg)) { + addUsePoint(ArgNode, BBArg); + } + } + for (auto &I : BB) { switch (I.getKind()) { #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ @@ -840,13 +833,14 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { /// liferange. And that must be a releasing instruction. int ValueIdx = -1; for (const Operand &Op : I.getAllOperands()) { - CGNode *content = getValueContent(Op.get()); - if (!content) - continue; - if (ValueIdx < 0) - ValueIdx = addUsePoint(content, &I); - else - content->setUsePointBit(ValueIdx); + ValueBase *OpV = Op.get(); + if (CGNode *OpNd = lookupNode(EA->getPointerRoot(OpV))) { + if (ValueIdx < 0) { + ValueIdx = addUsePoint(OpNd, &I); + } else { + OpNd->setUsePointBit(ValueIdx); + } + } } break; } @@ -861,38 +855,24 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { do { Changed = false; for (CGNode *Node : Nodes) { - // Propagate the bits to pointsTo. A release of a node may also release - // any content pointed to be the node. - if (Node->pointsTo) - Changed |= Node->pointsTo->mergeUsePoints(Node); + // Propagate the bits to all successor nodes. + Node->visitSuccessors([&Changed, Node](CGNode *succ) { + Changed |= succ->mergeUsePoints(Node); + return true; + }); } } while (Changed); } -CGNode *EscapeAnalysis::ConnectionGraph::createContentNode( - CGNode *addrNode, bool isInterior, bool hasReferenceOnly) { - CGNode *newContent = - allocNode(nullptr, NodeType::Content, isInterior, hasReferenceOnly); +CGNode *EscapeAnalysis::ConnectionGraph::createContentNode(CGNode *addrNode, + bool hasRC) { + CGNode *newContent = allocNode(nullptr, NodeType::Content, hasRC); initializePointsToEdge(addrNode, newContent); assert(ToMerge.empty() && "Initially setting pointsTo should not require any node merges"); return newContent; } -CGNode *EscapeAnalysis::ConnectionGraph::getOrCreateContentNode( - CGNode *addrNode, bool isInterior, bool hasReferenceOnly) { - if (CGNode *content = addrNode->getContentNodeOrNull()) { - content->mergeFlags(isInterior, hasReferenceOnly); - return content; - } - CGNode *content = createContentNode(addrNode, isInterior, hasReferenceOnly); - // getValueContent may be called after the graph is built and escape states - // are propagated. Keep the escape state and use points consistent here. - content->mergeEscapeState(addrNode->State); - content->mergeUsePoints(addrNode); - return content; -} - // Create a content node for merging based on an address node in the destination // graph and a content node in the source graph. CGNode * @@ -901,103 +881,19 @@ EscapeAnalysis::ConnectionGraph::createMergedContent(CGNode *destAddrNode, // destAddrNode may itself be a content node, so its value may be null. Since // we don't have the original pointer value, build a new content node based // on the source content. - CGNode *mergedContent = createContentNode( - destAddrNode, srcContent->isInterior(), srcContent->hasReferenceOnly()); - return mergedContent; -} - -CGNode * -EscapeAnalysis::ConnectionGraph::getOrCreateAddressContent(SILValue addrVal, - CGNode *addrNode) { - assert(addrVal->getType().isAddress()); - - bool contentHasReferenceOnly = - EA->hasReferenceOnly(addrVal->getType().getObjectType(), *F); - // Address content always has an indirect pointsTo (only reference content can - // have a non-indirect pointsTo). - return getOrCreateContentNode(addrNode, false, contentHasReferenceOnly); -} - -// refVal is allowed to be invalid so we can model escaping content for -// secondary deinitializers of released objects. -CGNode * -EscapeAnalysis::ConnectionGraph::getOrCreateReferenceContent(SILValue refVal, - CGNode *refNode) { - // The object node points to internal fields. It neither has indirect pointsTo - // nor reference-only pointsTo. - CGNode *objNode = getOrCreateContentNode(refNode, true, false); - if (!objNode->isInterior()) - return objNode; - - bool contentHasReferenceOnly = false; - if (refVal) { - SILType refType = refVal->getType(); - if (auto *C = refType.getClassOrBoundGenericClass()) { - PointerKind aggregateKind = NoPointer; - for (auto *field : C->getStoredProperties()) { - SILType fieldType = refType.getFieldType(field, F->getModule(), - F->getTypeExpansionContext()); - PointerKind fieldKind = EA->findCachedPointerKind(fieldType, *F); - if (fieldKind > aggregateKind) - aggregateKind = fieldKind; - } - contentHasReferenceOnly = canOnlyContainReferences(aggregateKind); - } - } - getOrCreateContentNode(objNode, false, contentHasReferenceOnly); - return objNode; + return createContentNode(destAddrNode, srcContent->hasRC); } -CGNode * -EscapeAnalysis::ConnectionGraph::getOrCreateUnknownContent(CGNode *addrNode) { - // We don't know if addrVal has been cast from a reference or raw - // pointer. More importantly, we don't know what memory contents it may - // point to. There's no need to consider it an "interior" node initially. If - // it's ever merged with another interior node (from ref_element_addr), then - // it will conservatively take on the interior flag at that time. - return getOrCreateContentNode(addrNode, false, false); -} - -// If ptrVal is itself mapped to a node, then this must return a non-null -// contentnode. Otherwise, setEscapesGlobal won't be able to represent escaping -// memory. -// -// This may be called after the graph is built and all escape states and use -// points are propagate. If a new content node is created, update its state -// on-the-fly. -EscapeAnalysis::CGNode * -EscapeAnalysis::ConnectionGraph::getValueContent(SILValue ptrVal) { - // Look past address projections, pointer casts, and the like within the same - // object. Does not look past a dereference such as ref_element_addr, or - // project_box. - SILValue ptrBase = EA->getPointerRoot(ptrVal); - - PointerKind pointerKind = EA->getPointerKind(ptrBase); - if (pointerKind == EscapeAnalysis::NoPointer) - return nullptr; - - CGNode *addrNode = getOrCreateNode(ptrBase, pointerKind); - if (!addrNode) - return nullptr; - - if (ptrBase->getType().isAddress()) - return getOrCreateAddressContent(ptrBase, addrNode); - - if (canOnlyContainReferences(pointerKind)) - return getOrCreateReferenceContent(ptrBase, addrNode); +// Get a node representing the field data within the given reference-counted +// node. The caller has already determined that rcNode represents the head of a +// heap object rather than field content or the address of a local variable or +// argument. +CGNode *EscapeAnalysis::ConnectionGraph::getFieldContent(CGNode *rcNode) { + assert(rcNode->isContent()); + if (rcNode->pointsTo) + return rcNode->pointsTo; - // The pointer value may contain raw pointers. - return getOrCreateUnknownContent(addrNode); -} - -CGNode *EscapeAnalysis::ConnectionGraph::getReturnNode() { - if (!ReturnNode) { - SILType resultTy = - F->mapTypeIntoContext(F->getConventions().getSILResultType()); - bool hasReferenceOnly = EA->hasReferenceOnly(resultTy, *F); - ReturnNode = allocNode(nullptr, NodeType::Return, false, hasReferenceOnly); - } - return ReturnNode; + return createContentNode(rcNode, /*hasRC=*/false); } bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, @@ -1022,9 +918,7 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, // global escaping state set. // Just set global escaping in the caller node and that's it. Changed |= DestNd->mergeEscapeState(EscapeState::Global); - // If DestNd is an interior node, its content still needs to be created. - if (!DestNd->isInterior()) - continue; + continue; } CGNode *SourcePT = SourceNd->pointsTo; @@ -1090,10 +984,11 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, /// somehow refer to the Node's value. /// Use-points are only values which are relevant for lifeness computation, /// e.g. release or apply instructions. -bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILInstruction *UsePoint, +bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILNode *UsePoint, CGNode *Node) { assert(Node->getEscapeState() < EscapeState::Global && "Use points are only valid for non-escaping nodes"); + UsePoint = UsePoint->getRepresentativeSILNodeInObject(); auto Iter = UsePoints.find(UsePoint); if (Iter == UsePoints.end()) return false; @@ -1103,8 +998,8 @@ bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILInstruction *UsePoint, return Node->UsePoints.test(Idx); } -void EscapeAnalysis::ConnectionGraph::getUsePoints( - CGNode *Node, llvm::SmallVectorImpl &UsePoints) { +void EscapeAnalysis::ConnectionGraph:: +getUsePoints(CGNode *Node, llvm::SmallVectorImpl &UsePoints) { assert(Node->getEscapeState() < EscapeState::Global && "Use points are only valid for non-escaping nodes"); for (int Idx = Node->UsePoints.find_first(); Idx >= 0; @@ -1313,7 +1208,7 @@ std::string CGForDotView::getNodeAttributes(const Node *Node) const { switch (Orig->Type) { case EscapeAnalysis::NodeType::Content: attr = "style=\"rounded"; - if (Orig->isInterior()) { + if (Orig->hasRefCount()) { attr += ",filled"; } attr += "\""; @@ -1435,10 +1330,8 @@ void EscapeAnalysis::ConnectionGraph::dumpCG() const { void EscapeAnalysis::CGNode::dump() const { llvm::errs() << getTypeStr(); - if (isInterior()) - llvm::errs() << " [int]"; - if (hasReferenceOnly()) - llvm::errs() << " [ref]"; + if (hasRefCount()) + llvm::errs() << " [rc]"; auto rep = getRepValue(); if (rep.depth > 0) @@ -1498,6 +1391,16 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { }); }; + auto nodeStr = [&](CGNode *Nd) -> std::string { + std::string Str; + llvm::raw_string_ostream OS(Str); + if (Nd->hasRefCount()) + OS << "[rc] "; + Nd->getRepValue().print(OS, InstToIDMap); + OS.flush(); + return Str; + }; + llvm::SmallVector SortedNodes; for (CGNode *Nd : Nodes) { if (!Nd->isMerged) @@ -1506,13 +1409,7 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { sortNodes(SortedNodes); for (CGNode *Nd : SortedNodes) { - OS << " " << Nd->getTypeStr() << ' '; - if (Nd->isInterior()) - OS << "[int] "; - if (Nd->hasReferenceOnly()) - OS << "[ref] "; - Nd->getRepValue().print(OS, InstToIDMap); - OS << " Esc: "; + OS << " " << Nd->getTypeStr() << ' ' << nodeStr(Nd) << " Esc: "; switch (Nd->getEscapeState()) { case EscapeState::None: { const char *Separator = ""; @@ -1537,16 +1434,13 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { OS << ", Succ: "; const char *Separator = ""; if (CGNode *PT = Nd->getPointsToEdge()) { - OS << '('; - PT->getRepValue().print(OS, InstToIDMap); - OS << ')'; + OS << '(' << nodeStr(PT) << ')'; Separator = ", "; } llvm::SmallVector SortedDefers = Nd->defersTo; sortNodes(SortedDefers); for (CGNode *Def : SortedDefers) { - OS << Separator; - Def->getRepValue().print(OS, InstToIDMap); + OS << Separator << nodeStr(Def); Separator = ", "; } OS << '\n'; @@ -1582,14 +1476,11 @@ void EscapeAnalysis::ConnectionGraph::verify(bool allowMerge) const { // which consist of only defer-edges and a single trailing points-to edge // must lead to the same assert(Nd->matchPointToOfDefers(allowMerge)); - if (Nd->mappedValue && !(allowMerge && Nd->isMerged)) { - assert(Nd == Values2Nodes.lookup(Nd->mappedValue)); - assert(EA->isPointer(Nd->mappedValue)); - // Nodes must always be mapped from the pointer root value. - assert(Nd->mappedValue == EA->getPointerRoot(Nd->mappedValue)); + if (Nd->hasRefCount()) { + SILValue v = Nd->getRepValue().getValue(); + (void)v; + assert(!v || mayContainReference(v->getType(), *F)); } - if (Nd->isInterior() && !Nd->isMerged) - assert(Nd->pointsTo && "Interior content node requires a pointsTo node"); } #endif } @@ -1597,6 +1488,9 @@ void EscapeAnalysis::ConnectionGraph::verify(bool allowMerge) const { void EscapeAnalysis::ConnectionGraph::verifyStructure(bool allowMerge) const { #ifndef NDEBUG for (CGNode *Nd : Nodes) { + if (Nd->mappedValue && !(allowMerge && Nd->mergeTo)) + assert(Nd == Values2Nodes.lookup(Nd->mappedValue)); + if (Nd->isMerged) { assert(Nd->mergeTo); assert(!Nd->pointsTo); @@ -1654,6 +1548,45 @@ static bool linkBBArgs(SILBasicBlock *BB) { return true; } +EscapeAnalysis::CGNode * +EscapeAnalysis::getValueContent(ConnectionGraph *conGraph, SILValue addrVal) { + CGNode *addrNode = conGraph->getNode(addrVal); + if (!addrNode) + return nullptr; + + if (CGNode *content = addrNode->getPointsToEdge()) + return content; + +#ifndef NDEBUG + if (!addrNode->isContent()) { + if (SILValue addrNodeValue = addrNode->getRepValue().getValue()) { + assert(isPointer(addrNodeValue)); + assert(addrNodeValue == getPointerRoot(addrVal)); + } + } +#endif + SILValue baseAddr = getPointerRoot(addrVal); + auto *F = addrVal->getFunction(); + auto hasRC = [&](){ + return mustContainReference(baseAddr->getType(), *F) + || mustContainReference(addrVal->getType(), *F); + }; + // Have we already merged a content node for this address? + if (CGNode *content = addrNode->getContentNodeOrNull()) { + // TODO: Optimistically merge hasRC content. The original content might not + // have an RC if one of the values pointing to this content was cast to an + // unknown type. If any of the types must contain a reference, then the + // content should contain a reference. + // + // For now, conservatively merge the RC flag instead. + if (content->hasRefCount() && !hasRC()) + content->setRefCount(false); + + return content; + } + return conGraph->createContentNode(addrNode, hasRC()); +} + void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth) { @@ -1699,7 +1632,7 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, if (!BBArg->getSingleTerminatorOperands(Incoming)) { // We don't know where the block argument comes from -> treat it // conservatively. - ConGraph->setEscapesGlobal(BBArg); + setEscapesGlobal(ConGraph, BBArg); continue; } CGNode *ArgNode = ConGraph->getNode(BBArg); @@ -1711,7 +1644,7 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, if (SrcArg) { ArgNode = ConGraph->defer(ArgNode, SrcArg); } else { - ConGraph->setEscapesGlobal(BBArg); + setEscapesGlobal(ConGraph, BBArg); break; } } @@ -1721,26 +1654,13 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, << FInfo->Graph.F->getName() << '\n'); } -/// Returns the tuple extract for the first two fields if all uses of \p I are -/// tuple_extract instructions. -static std::pair -onlyUsedInTupleExtract(SILValue V) { - TupleExtractInst *field0 = nullptr; - TupleExtractInst *field1 = nullptr; +/// Returns true if all uses of \p I are tuple_extract instructions. +static bool onlyUsedInTupleExtract(SILValue V) { for (Operand *Use : getNonDebugUses(V)) { - if (auto *TEI = dyn_cast(Use->getUser())) { - if (TEI->getFieldNo() == 0) { - field0 = TEI; - continue; - } - if (TEI->getFieldNo() == 1) { - field1 = TEI; - continue; - } - } - return std::make_pair(nullptr, nullptr); + if (!isa(Use->getUser())) + return false; } - return std::make_pair(field0, field1); + return true; } bool EscapeAnalysis::buildConnectionGraphForCallees( @@ -1799,40 +1719,6 @@ bool EscapeAnalysis::buildConnectionGraphForDestructor( RecursionDepth); } -// Handle array.uninitialized -bool EscapeAnalysis::createArrayUninitializedSubgraph( - FullApplySite apply, ConnectionGraph *conGraph) { - - // Check if the result is used in the usual way: extracting the - // array and the element pointer with tuple_extract. - TupleExtractInst *arrayStruct; - TupleExtractInst *arrayElementPtr; - std::tie(arrayStruct, arrayElementPtr) = - onlyUsedInTupleExtract(cast(apply.getInstruction())); - if (!arrayStruct || !arrayElementPtr) - return false; - - // array.uninitialized may have a first argument which is the - // allocated array buffer. The call is like a struct(buffer) - // instruction. - CGNode *arrayRefNode = conGraph->getNode(apply.getArgument(0)); - if (!arrayRefNode) - return false; - - CGNode *arrayStructNode = conGraph->getNode(arrayStruct); - assert(arrayStructNode && "Array struct must have a node"); - - CGNode *arrayObjNode = conGraph->getValueContent(apply.getArgument(0)); - - // The reference argument is effectively stored inside the returned - // array struct. - conGraph->defer(arrayStructNode, arrayRefNode); - - // Map the returned element pointer to the array object's field pointer. - conGraph->setNode(arrayElementPtr, arrayObjNode); - return true; -} - void EscapeAnalysis::analyzeInstruction(SILInstruction *I, FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, @@ -1853,71 +1739,74 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // These array semantics calls do not capture anything. return; case ArrayCallKind::kArrayUninitialized: - if (createArrayUninitializedSubgraph(FAS, ConGraph)) + // Check if the result is used in the usual way: extracting the + // array and the element pointer with tuple_extract. + if (onlyUsedInTupleExtract(ASC.getCallResult())) { + // array.uninitialized may have a first argument which is the + // allocated array buffer. The call is like a struct(buffer) + // instruction. + if (CGNode *BufferNode = ConGraph->getNode(FAS.getArgument(0))) { + SILValue ArrayBase = ASC.getCallResult(); + CGNode *ArrayContent = getValueContent(ConGraph, ArrayBase); + assert(ArrayContent && "Array base must have a node"); + ConGraph->defer(ArrayContent, BufferNode); + } return; + } break; case ArrayCallKind::kGetElement: - if (CGNode *ArrayObjNode = ConGraph->getValueContent(ASC.getSelf())) { + if (CGNode *ArrayRefNode = getValueContent(ConGraph, ASC.getSelf())) { CGNode *LoadedElement = nullptr; // This is like a load from a ref_element_addr. if (ASC.hasGetElementDirectResult()) { LoadedElement = ConGraph->getNode(ASC.getCallResult()); } else { // The content of the destination address. - LoadedElement = ConGraph->getValueContent(FAS.getArgument(0)); + LoadedElement = getValueContent(ConGraph, FAS.getArgument(0)); assert(LoadedElement && "indirect result must have node"); } if (LoadedElement) { - if (CGNode *arrayElementStorage = - ConGraph->getFieldContent(ArrayObjNode)) { - ConGraph->defer(LoadedElement, arrayElementStorage); - return; - } + CGNode *ArrayElementStorage = + ConGraph->getFieldContent(ArrayRefNode); + ConGraph->defer(LoadedElement, ArrayElementStorage); + return; } } break; case ArrayCallKind::kGetElementAddress: - // This is like a ref_element_addr. Both the object node and the - // returned address point to the same element storage. - if (CGNode *ArrayObjNode = ConGraph->getValueContent(ASC.getSelf())) { - CGNode *arrayElementAddress = ConGraph->getNode(ASC.getCallResult()); - ConGraph->defer(arrayElementAddress, ArrayObjNode); - return; + // This is like a ref_element_addr. + if (CGNode *ArrayRefNode = getValueContent(ConGraph, ASC.getSelf())) { + ConGraph->defer(ConGraph->getNode(ASC.getCallResult()), ArrayRefNode); } - break; + return; case ArrayCallKind::kWithUnsafeMutableBufferPointer: // Model this like an escape of the elements of the array and a capture // of anything captured by the closure. // Self is passed inout. - if (CGNode *ArrayStructNode = - ConGraph->getValueContent(ASC.getSelf())) { - // The first non indirect result is the closure. - auto Args = FAS.getArgumentsWithoutIndirectResults(); - ConGraph->setEscapesGlobal(Args[0]); + if (CGNode *ArrayStructValue = + getValueContent(ConGraph, ASC.getSelf())) { // One content node for going from the array buffer pointer to // the element address (like ref_element_addr). - CGNode *ArrayObjNode = - ConGraph->getOrCreateContentNode(ArrayStructNode, - /*isInterior*/ true, - /*hasRefOnly*/ false); - // If ArrayObjNode was already potentially merged with its pointsTo, - // then conservatively mark the whole thing as escaping. - if (!ArrayObjNode->isInterior()) { - ArrayObjNode->markEscaping(); - return; + CGNode *ArrayRefNode = ArrayStructValue->getContentNodeOrNull(); + // TODO: If ArrayRefNode already exists, optimistically do + // ArrayRefNode->setRefCount(true). + if (!ArrayRefNode) { + ArrayRefNode = ConGraph->createContentNode( + ArrayStructValue, /*hasRC=*/true); } - // Otherwise, create the content node for the element storage. - CGNode *ArrayElementStorage = ConGraph->getOrCreateContentNode( - ArrayObjNode, /*isInterior*/ false, - /*hasRefOnly*/ true); + // Another content node for the element storage. + CGNode *ArrayElementStorage = ConGraph->getFieldContent(ArrayRefNode); ArrayElementStorage->markEscaping(); + // The first non indirect result is the closure. + auto Args = FAS.getArgumentsWithoutIndirectResults(); + setEscapesGlobal(ConGraph, Args[0]); return; } break; default: break; - } + } if (FAS.getReferencedFunctionOrNull() && FAS.getReferencedFunctionOrNull()->hasSemanticsAttr( @@ -1930,7 +1819,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // from the pointer. auto Args = FAS.getArgumentsWithoutIndirectResults(); // The first not indirect result argument is the closure. - ConGraph->setEscapesGlobal(Args[0]); + setEscapesGlobal(ConGraph, Args[0]); return; } @@ -1945,7 +1834,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // from the pointer. auto Args = FAS.getArgumentsWithoutIndirectResults(); // The second not indirect result argument is the closure. - ConGraph->setEscapesGlobal(Args[1]); + setEscapesGlobal(ConGraph, Args[1]); return; } @@ -2027,117 +1916,86 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::StrongReleaseInst: case SILInstructionKind::ReleaseValueInst: { // A release instruction may deallocate the pointer operand. This may - // capture anything pointed to by the released object, but not the object - // itself (because it will be a dangling pointer after deallocation). + // capture anything pointed to by the released object, but not the pointer + // to the object itself (because it will be a dangling pointer after + // deallocation). SILValue OpV = I->getOperand(0); - CGNode *objNode = ConGraph->getValueContent(OpV); - if (!objNode) + CGNode *rcContent = getValueContent(ConGraph, OpV); + if (!rcContent) return; - CGNode *fieldNode = ConGraph->getFieldContent(objNode); - if (!fieldNode) { - // In the unexpected case that the object has no field content, create - // escaping unknown content. - ConGraph->getOrCreateUnknownContent(objNode)->markEscaping(); - return; - } + // rcContent->hasRefCount() may or may not be true depending on whether + // the type could be analyzed. Either way, treat it structurally like a + // refcounted object. + CGNode *fieldContent = ConGraph->getFieldContent(rcContent); if (!deinitIsKnownToNotCapture(OpV)) { - ConGraph->getOrCreateUnknownContent(fieldNode)->markEscaping(); - return; - } - // This deinit is known to not directly capture it's own field content; - // however, other secondary deinitializers could still capture anything - // pointed to by references within those fields. Since secondary - // deinitializers only apply to reference-type fields, not pointer-type - // fields, the "field" content can initially be considered an indirect - // reference. Unfortunately, we can't know all possible reference types - // that may eventually be associated with 'fieldContent', so we must - // assume here that 'fieldContent2' could hold raw pointers. This is - // implied by passing in invalid SILValue. - CGNode *objNode2 = - ConGraph->getOrCreateReferenceContent(SILValue(), fieldNode); - CGNode *fieldNode2 = objNode2->getContentNodeOrNull(); - ConGraph->getOrCreateUnknownContent(fieldNode2)->markEscaping(); - return; - } - case SILInstructionKind::DestroyAddrInst: { - SILValue addressVal = I->getOperand(0); - CGNode *valueNode = ConGraph->getValueContent(addressVal); - if (!valueNode) - return; - - // The value's destructor may escape anything the value points to. - // This could be an object referenced by the value or the contents of an - // existential box. - if (CGNode *fieldNode = ConGraph->getFieldContent(valueNode)) { - ConGraph->getOrCreateUnknownContent(fieldNode)->markEscaping(); + fieldContent->markEscaping(); return; } - ConGraph->getOrCreateUnknownContent(valueNode)->markEscaping(); + // This deinit is known to not directly capture it's own field content, + // however, indirect deinitializers could still capture anything pointed + // to by those fields. + ConGraph->escapeContentsOf(fieldContent); return; } #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Load##Name##Inst: #include "swift/AST/ReferenceStorage.def" - case SILInstructionKind::LoadInst: { + case SILInstructionKind::LoadInst: assert(!cast(I)->getType().isAddress()); + LLVM_FALLTHROUGH; + case SILInstructionKind::RefElementAddrInst: + case SILInstructionKind::RefTailAddrInst: + case SILInstructionKind::ProjectBoxInst: + case SILInstructionKind::InitExistentialAddrInst: + case SILInstructionKind::OpenExistentialAddrInst: { + // Loads and projections into RC objects have a similar pattern: + // + // For RC object projections, get the non-address reference operand and + // return an RC content node that the reference directly points to. It is + // as-if the RC content node holds the pointer to the object fields. + // // For loads, get the address-type operand and return the content node // that the address directly points to. The load's address may itself come // from a ref_element_addr, project_box or open_existential, in which - // case, the loaded content will be the field content, not the RC - // content. + // case, the loaded content will be the field content, not the RC content. auto SVI = cast(I); if (!isPointer(SVI)) return; - if (CGNode *PointsTo = ConGraph->getValueContent(SVI->getOperand(0))) { - ConGraph->setNode(SVI, PointsTo); - return; - } - // A load from an address we don't handle -> be conservative. - ConGraph->setEscapesGlobal(SVI); - break; - } - case SILInstructionKind::RefElementAddrInst: - case SILInstructionKind::RefTailAddrInst: - case SILInstructionKind::ProjectBoxInst: - case SILInstructionKind::InitExistentialAddrInst: - case SILInstructionKind::OpenExistentialAddrInst: { - // For projections into objects, get the non-address reference operand and - // return an interior content node that the reference points to. - auto SVI = cast(I); - if (CGNode *PointsTo = ConGraph->getValueContent(SVI->getOperand(0))) { + SILValue pointerVal = SVI->getOperand(0); + if (CGNode *PointsTo = getValueContent(ConGraph, pointerVal)) { ConGraph->setNode(SVI, PointsTo); return; } // A load or projection from an address we don't handle -> be // conservative. - ConGraph->setEscapesGlobal(SVI); + setEscapesGlobal(ConGraph, SVI); return; } case SILInstructionKind::CopyAddrInst: { // Be conservative if the dest may be the final release. if (!cast(I)->isInitializationOfDest()) { setAllEscaping(I, ConGraph); - return; + break; } // A copy_addr is like a 'store (load src) to dest'. SILValue srcAddr = I->getOperand(CopyAddrInst::Src); - CGNode *loadedContent = ConGraph->getValueContent(srcAddr); + CGNode *loadedContent = getValueContent(ConGraph, srcAddr); if (!loadedContent) { setAllEscaping(I, ConGraph); break; } SILValue destAddr = I->getOperand(CopyAddrInst::Dest); // Create a defer-edge from the store location to the loaded content. - if (CGNode *destContent = ConGraph->getValueContent(destAddr)) { + if (CGNode *destContent = getValueContent(ConGraph, destAddr)) { ConGraph->defer(destContent, loadedContent); return; } // A store to an address we don't handle -> be conservative. - ConGraph->setEscapesGlobal(srcAddr); + setEscapesGlobal(ConGraph, srcAddr); return; } @@ -2158,13 +2016,13 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // (via ref_element_addr, project_box, or open_existential_addr) where the // stored field content is chained one level below the RC content. SILValue destAddr = I->getOperand(StoreInst::Dest); - if (CGNode *pointsTo = ConGraph->getValueContent(destAddr)) { + if (CGNode *pointsTo = getValueContent(ConGraph, destAddr)) { // Create a defer-edge from the content to the stored value. ConGraph->defer(pointsTo, valueNode); return; } // A store to an address we don't handle -> be conservative. - ConGraph->setEscapesGlobal(srcVal); + setEscapesGlobal(ConGraph, srcVal); return; } case SILInstructionKind::PartialApplyInst: { @@ -2204,14 +2062,15 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::TupleExtractInst: { // This is a tuple_extract which extracts the second result of an // array.uninitialized call (otherwise getPointerBase should have already - // looked through it). The first result is the array itself. The second - // result (which is a pointer to the array elements) must be the content - // node of the first result. It's just like a ref_element_addr - // instruction. It is mapped to a node when processing - // array.uninitialized. + // looked through it). The first result is the array itself. + // The second result (which is a pointer to the array elements) must be + // the content node of the first result. It's just like a ref_element_addr + // instruction. auto *TEI = cast(I); assert(isExtractOfArrayUninitializedPointer(TEI) && "tuple_extract should be handled as projection"); + if (CGNode *ArrayElements = getValueContent(ConGraph, TEI->getOperand())) + ConGraph->setNode(TEI, ArrayElements); return; } case SILInstructionKind::UncheckedRefCastAddrInst: { @@ -2222,15 +2081,12 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, ConGraph->defer(DestNode, SrcNode); return; } - case SILInstructionKind::ReturnInst: { - SILValue returnVal = cast(I)->getOperand(); - if (CGNode *ValueNd = ConGraph->getNode(returnVal)) { + case SILInstructionKind::ReturnInst: + if (CGNode *ValueNd = + ConGraph->getNode(cast(I)->getOperand())) { ConGraph->defer(ConGraph->getReturnNode(), ValueNd); - ConGraph->getValueContent(returnVal)->mergeEscapeState( - EscapeState::Return); } return; - } default: // We handle all other instructions conservatively. setAllEscaping(I, ConGraph); @@ -2291,8 +2147,8 @@ bool EscapeAnalysis::deinitIsKnownToNotCapture(SILValue V) { void EscapeAnalysis::setAllEscaping(SILInstruction *I, ConnectionGraph *ConGraph) { if (auto *TAI = dyn_cast(I)) { - ConGraph->setEscapesGlobal(TAI->getNormalBB()->getArgument(0)); - ConGraph->setEscapesGlobal(TAI->getErrorBB()->getArgument(0)); + setEscapesGlobal(ConGraph, TAI->getNormalBB()->getArgument(0)); + setEscapesGlobal(ConGraph, TAI->getErrorBB()->getArgument(0)); } // Even if the instruction does not write memory we conservatively set all // operands to escaping, because they may "escape" to the result value in @@ -2301,12 +2157,12 @@ void EscapeAnalysis::setAllEscaping(SILInstruction *I, for (const Operand &Op : I->getAllOperands()) { SILValue OpVal = Op.get(); if (!isNonWritableMemoryAddress(OpVal)) - ConGraph->setEscapesGlobal(OpVal); + setEscapesGlobal(ConGraph, OpVal); } // Even if the instruction does not write memory it could e.g. return the // address of global memory. Therefore we have to define it as escaping. for (auto result : I->getResults()) - ConGraph->setEscapesGlobal(result); + setEscapesGlobal(ConGraph, result); } void EscapeAnalysis::recompute(FunctionInfo *Initial) { @@ -2479,44 +2335,44 @@ bool EscapeAnalysis::mergeSummaryGraph(ConnectionGraph *SummaryGraph, return SummaryGraph->mergeFrom(Graph, Mapping); } -// Return true if any content within the logical object pointed to by \p value -// escapes. -// -// Get the value's content node and check the escaping flag on all nodes within -// that object. An interior CG node points to content within the same object. -bool EscapeAnalysis::canEscapeToUsePoint(SILValue value, - SILInstruction *usePoint, - ConnectionGraph *conGraph) { +bool EscapeAnalysis::canEscapeToUsePoint(SILValue V, SILNode *UsePoint, + ConnectionGraph *ConGraph) { - assert((FullApplySite::isa(usePoint) || isa(usePoint)) - && "use points are only created for calls and refcount instructions"); + assert((FullApplySite::isa(UsePoint) || isa(UsePoint)) && + "use points are only created for calls and refcount instructions"); - CGNode *node = conGraph->getValueContent(value); - if (!node) + CGNode *Node = ConGraph->getNodeOrNull(V); + if (!Node) return true; - // Follow points-to edges and return true if the current 'node' may escape at - // 'usePoint'. - while (node) { - // First check if 'node' may escape in a way not represented by the - // connection graph, assuming that it may represent part of the object - // pointed to by 'value'. If 'node' happens to represent another object - // indirectly reachabe from 'value', then it cannot actually escape to this - // usePoint, so passing the original value is still conservatively correct. - if (node->valueEscapesInsideFunction(value)) - return true; + // First check if there are escape paths which we don't explicitly see + // in the graph. + if (Node->valueEscapesInsideFunction(V)) + return true; - // No hidden escapes; check if 'usePoint' may access memory at 'node'. - if (conGraph->isUsePoint(usePoint, node)) - return true; + // No hidden escapes: check if the Node is reachable from the UsePoint. + // Check if the object itself can escape to the called function. + if (ConGraph->isUsePoint(UsePoint, Node)) + return true; - if (!node->isInterior()) - break; + assert(isPointer(V) && "should not have a node for a non-pointer"); + + // Check if the object "content" can escape to the called function. + // This will catch cases where V is a reference and a pointer to a stored + // property escapes. + // It's also important in case of a pointer assignment, e.g. + // V = V1 + // apply(V1) + // In this case the apply is only a use-point for V1 and V1's content node. + // As V1's content node is the same as V's content node, we also make the + // check for the content node. + CGNode *ContentNode = getValueContent(ConGraph, V); + if (ContentNode->valueEscapesInsideFunction(V)) + return true; + + if (ConGraph->isUsePoint(UsePoint, ContentNode)) + return true; - // Continue to check for escaping content whenever 'content' may point to - // the same object as 'node'. - node = node->getContentNodeOrNull(); - } return false; } @@ -2563,13 +2419,13 @@ bool EscapeAnalysis::canEscapeToValue(SILValue V, SILValue To) { return true; auto *ConGraph = getConnectionGraph(F); - CGNode *valueContent = ConGraph->getValueContent(V); - if (!valueContent) + CGNode *Node = ConGraph->getNodeOrNull(V); + if (!Node) return true; - CGNode *userContent = ConGraph->getValueContent(To); - if (!userContent) + CGNode *ToNode = ConGraph->getNodeOrNull(To); + if (!ToNode) return true; - return ConGraph->mayReach(userContent, valueContent); + return ConGraph->mayReach(ToNode, Node); } bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { @@ -2584,26 +2440,27 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { return true; auto *ConGraph = getConnectionGraph(F); - CGNode *Content1 = ConGraph->getValueContent(V1); - if (!Content1) + CGNode *Node1 = ConGraph->getNodeOrNull(V1); + if (!Node1) return true; - - CGNode *Content2 = ConGraph->getValueContent(V2); - if (!Content2) + CGNode *Node2 = ConGraph->getNodeOrNull(V2); + if (!Node2) return true; // Finish the check for one value being a non-escaping local object. - if (isUniq1 && Content1->valueEscapesInsideFunction(V1)) + if (isUniq1 && Node1->valueEscapesInsideFunction(V1)) isUniq1 = false; - if (isUniq2 && Content2->valueEscapesInsideFunction(V2)) + if (isUniq2 && Node2->valueEscapesInsideFunction(V2)) isUniq2 = false; if (!isUniq1 && !isUniq2) return true; // Check if both nodes may point to the same content. - // FIXME!!!: This will be rewritten to use node flags in the next commit. + CGNode *Content1 = getValueContent(ConGraph, V1); + CGNode *Content2 = getValueContent(ConGraph, V2); + SILType T1 = V1->getType(); SILType T2 = V2->getType(); if (T1.isAddress() && T2.isAddress()) { @@ -2626,6 +2483,35 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { return true; } +bool EscapeAnalysis::canParameterEscape(FullApplySite FAS, int ParamIdx, + bool checkContentOfIndirectParam) { + CalleeList Callees = BCA->getCalleeList(FAS); + if (!Callees.allCalleesVisible()) + return true; + + // Derive the connection graph of the apply from the known callees. + for (SILFunction *Callee : Callees) { + FunctionInfo *FInfo = getFunctionInfo(Callee); + if (!FInfo->isValid()) + recompute(FInfo); + + CGNode *Node = + FInfo->SummaryGraph.getNodeOrNull(Callee->getArgument(ParamIdx)); + if (!Node) + return true; + + if (checkContentOfIndirectParam) { + Node = Node->getContentNodeOrNull(); + if (!Node) + continue; + } + + if (Node->escapes()) + return true; + } + return false; +} + void EscapeAnalysis::invalidate() { Function2Info.clear(); Allocator.DestroyAll(); diff --git a/lib/SILOptimizer/Transforms/StackPromotion.cpp b/lib/SILOptimizer/Transforms/StackPromotion.cpp index a7579edc39094..9c769f5197da9 100644 --- a/lib/SILOptimizer/Transforms/StackPromotion.cpp +++ b/lib/SILOptimizer/Transforms/StackPromotion.cpp @@ -108,20 +108,32 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, return false; auto *ConGraph = EA->getConnectionGraph(ARI->getFunction()); - auto *contentNode = ConGraph->getValueContent(ARI); - if (!contentNode) + auto *Node = ConGraph->getNodeOrNull(ARI); + if (!Node) return false; // The most important check: does the object escape the current function? - if (contentNode->escapes()) + if (Node->escapes()) return false; LLVM_DEBUG(llvm::dbgs() << "Promote " << *ARI); // Collect all use-points of the allocation. These are refcount instructions // and apply instructions. + llvm::SmallVector BaseUsePoints; llvm::SmallVector UsePoints; - ConGraph->getUsePoints(contentNode, UsePoints); + ConGraph->getUsePoints(Node, BaseUsePoints); + for (SILNode *UsePoint : BaseUsePoints) { + if (SILInstruction *I = dyn_cast(UsePoint)) { + UsePoints.push_back(I); + } else { + // Also block arguments can be use points. + SILBasicBlock *UseBB = cast(UsePoint)->getParent(); + // For simplicity we just add the first instruction of the block as use + // point. + UsePoints.push_back(&UseBB->front()); + } + } ValueLifetimeAnalysis VLA(ARI, UsePoints); // Check if there is a use point before the allocation (this can happen e.g. diff --git a/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp b/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp index da470ec37d705..3d1cf9331b697 100644 --- a/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp @@ -25,23 +25,6 @@ llvm::cl::opt EnableGraphWriter( namespace { -static bool gatherValues(EscapeAnalysis *EA, SILFunction &Fn, - std::vector &Values) { - for (auto &BB : Fn) { - for (auto *Arg : BB.getArguments()) { - if (EA->isPointer(Arg)) - Values.push_back(SILValue(Arg)); - } - for (auto &II : BB) { - for (auto result : II.getResults()) { - if (EA->isPointer(result)) - Values.push_back(result); - } - } - } - return Values.size() > 1; -} - /// Dumps the escape information of all functions in the module. /// Only dumps if the compiler is built with assertions. /// For details see EscapeAnalysis. @@ -60,33 +43,6 @@ class EscapeAnalysisDumper : public SILModuleTransform { ConnectionGraph->print(llvm::outs()); if (EnableGraphWriter) ConnectionGraph->dumpCG(); - - // Gather up all Values in Fn. - std::vector Values; - if (!gatherValues(EA, F, Values)) - continue; - - // Emit the N^2 escape analysis evaluation of the values. - for (auto &bb : F) { - for (auto &ii : bb) { - if (auto fas = FullApplySite::isa(&ii)) { - for (unsigned i = 0, e = Values.size(); i != e; ++i) { - SILValue val = Values[i]; - bool escape = EA->canEscapeTo(val, fas); - llvm::outs() << (escape ? "May" : "No") << "Escape: " << val - << " to " << ii; - } - } - if (RefCountingInst *rci = dyn_cast(&ii)) { - for (unsigned i = 0, e = Values.size(); i != e; ++i) { - SILValue val = Values[i]; - bool escape = EA->canEscapeTo(val, rci); - llvm::outs() << (escape ? "May" : "No") << "Escape: " << val - << " to " << ii; - } - } - } - } } } #endif diff --git a/test/SILOptimizer/escape_analysis.sil b/test/SILOptimizer/escape_analysis.sil index e1bf8c7f06b79..25085c9609fbc 100644 --- a/test/SILOptimizer/escape_analysis.sil +++ b/test/SILOptimizer/escape_analysis.sil @@ -77,8 +77,8 @@ struct FourFields { sil @take_indirect_tuple : $@convention(method) (@in (Int, ())) -> () // CHECK-LABEL: CG of handle_undef -// CHECK: Val %2 Esc: , Succ: (%2.1) -// CHECK: Con [ref] %2.1 Esc: G, Succ: +// CHECK: Val %2 Esc: G, Succ: (%2.1) +// CHECK: Con %2.1 Esc: G, Succ: // CHECK: End sil @handle_undef : $@convention(thin) (Int) -> () { bb0(%0 : $Int): @@ -96,12 +96,11 @@ bb0(%0 : $Int): // CHECK-LABEL: CG of test_simple // CHECK-NEXT: Arg %0 Esc: A, Succ: (%5) -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Val [ref] %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: A, Succ: %1 -// CHECK-NEXT: Con [ref] %5 Esc: A, Succ: %2 +// CHECK-NEXT: Arg %1 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: A, Succ: ([rc] %3) +// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Con %3.1 Esc: G, Succ: %1 +// CHECK-NEXT: Con %5 Esc: A, Succ: %2 // CHECK-NEXT: End sil @test_simple : $@convention(thin) (@inout Y, @owned X) -> () { bb0(%0 : $*Y, %1 : $X): @@ -118,12 +117,12 @@ bb0(%0 : $*Y, %1 : $X): // Test if a deferring edge is created for a block argument. // CHECK-LABEL: CG of deferringEdge -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%4) -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: -// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%4), %0 -// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %4) +// CHECK-NEXT: Arg %1 Esc: A, Succ: +// CHECK-NEXT: Val %3 Esc: %3, Succ: ([rc] %4), %0 +// CHECK-NEXT: Con [rc] %4 Esc: A, Succ: (%4.1) // CHECK-NEXT: Con %4.1 Esc: A, Succ: %1 -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 +// CHECK-NEXT: Ret return Esc: R, Succ: %0 // CHECK-NEXT: End sil @deferringEdge : $@convention(thin) (@owned LinkedNode, @owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode, %1 : $LinkedNode): @@ -138,10 +137,8 @@ bb1(%3 : $LinkedNode): // Test a local object just escaping via return. // CHECK-LABEL: CG of escapes_via_return -// CHECK-NEXT: Val [ref] %0 Esc: , Succ: -// CHECK-NEXT: Con [int] %0.1 Esc: R, Succ: (%0.2) -// CHECK-NEXT: Con [ref] %0.2 Esc: R, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 +// CHECK-NEXT: Val %0 Esc: R, Succ: +// CHECK-NEXT: Ret return Esc: R, Succ: %0 // CHECK-NEXT: End sil @escapes_via_return : $@convention(thin) () -> @owned X { bb0: @@ -152,14 +149,14 @@ bb0: // A linear chain of assignments is collapsed to a single node. // CHECK-LABEL: CG of test_linked_list -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) -// CHECK-NEXT: Val [ref] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%13) -// CHECK-NEXT: Val [ref] %4 Esc: A, Succ: (%2) -// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%2) -// CHECK-NEXT: Val [ref] %11 Esc: , Succ: (%2), %7, %13 +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %2) +// CHECK-NEXT: Val %1 Esc: A, Succ: ([rc] %2) +// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%13) +// CHECK-NEXT: Val %4 Esc: A, Succ: ([rc] %2) +// CHECK-NEXT: Val %7 Esc: %11, Succ: ([rc] %2) +// CHECK-NEXT: Val %11 Esc: %11, Succ: ([rc] %2), %7, %13 // CHECK-NEXT: Con %13 Esc: A, Succ: %0, %1, %4 -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %13 +// CHECK-NEXT: Ret return Esc: R, Succ: %13 // CHECK-NEXT: End sil @test_linked_list : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -187,14 +184,14 @@ bb2: // The same example as above but distributed over two functions. // CHECK-LABEL: CG of create_chain -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%8) -// CHECK-NEXT: Val [ref] %1 Esc: A, Succ: (%8) -// CHECK-NEXT: Val [ref] %4 Esc: A, Succ: (%8) -// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%8) -// CHECK-NEXT: Con [int] %8 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %8) +// CHECK-NEXT: Val %1 Esc: A, Succ: ([rc] %8) +// CHECK-NEXT: Val %4 Esc: A, Succ: ([rc] %8) +// CHECK-NEXT: Val %7 Esc: %11, Succ: ([rc] %8) +// CHECK-NEXT: Con [rc] %8 Esc: A, Succ: (%8.1) // CHECK-NEXT: Con %8.1 Esc: A, Succ: %0, %1, %4 -// CHECK-NEXT: Val [ref] %11 Esc: , Succ: (%8), %8.1 -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %11 +// CHECK-NEXT: Val %11 Esc: R, Succ: ([rc] %8), %8.1 +// CHECK-NEXT: Ret return Esc: R, Succ: %11 // CHECK-NEXT: End sil @create_chain : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -214,11 +211,11 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of loadNext -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%3) -// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%3), %0, %4 -// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %4 Esc: A, Succ: (%3) -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %3) +// CHECK-NEXT: Val %2 Esc: %2, Succ: ([rc] %3), %0, %4 +// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con %4 Esc: A, Succ: ([rc] %3) +// CHECK-NEXT: Ret return Esc: R, Succ: %4 // CHECK-NEXT: End sil @loadNext : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -236,17 +233,15 @@ bb2: // Content nodes in the callee are duplicated in the caller. // CHECK-LABEL: CG of call_load_next3 -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con [int] %0.3 Esc: A, Succ: (%0.4) -// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%0.5) -// CHECK-NEXT: Con [int] %0.5 Esc: A, Succ: (%0.6) +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %0.1) +// CHECK-NEXT: Con [rc] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: ([rc] %0.3) +// CHECK-NEXT: Con [rc] %0.3 Esc: A, Succ: (%0.4) +// CHECK-NEXT: Con %0.4 Esc: A, Succ: ([rc] %0.5) +// CHECK-NEXT: Con [rc] %0.5 Esc: A, Succ: (%0.6) // CHECK-NEXT: Con %0.6 Esc: A, Succ: -// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1), %0.6 -// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: A, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %2 +// CHECK-NEXT: Val %2 Esc: R, Succ: %0.6 +// CHECK-NEXT: Ret return Esc: R, Succ: %2 // CHECK-NEXT: End sil @call_load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -256,16 +251,14 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of load_next3 -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) -// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %4 Esc: A, Succ: (%5) -// CHECK-NEXT: Con [int] %5 Esc: A, Succ: (%6) +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) +// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con %2 Esc: A, Succ: ([rc] %3) +// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con %4 Esc: A, Succ: ([rc] %5) +// CHECK-NEXT: Con [rc] %5 Esc: A, Succ: (%6) // CHECK-NEXT: Con %6 Esc: A, Succ: -// CHECK-NEXT: Con [int] %6.1 Esc: A, Succ: (%6.2) -// CHECK-NEXT: Con %6.2 Esc: A, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 +// CHECK-NEXT: Ret return Esc: R, Succ: %6 // CHECK-NEXT: End sil @load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -284,12 +277,10 @@ sil_global @global_x : $X // The argument escapes because it is stored to a global variable in the callee. // CHECK-LABEL: CG of call_store_pointer -// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: (%5) -// CHECK-NEXT: Con [int] %5 Esc: G, Succ: (%6) +// CHECK-NEXT: Arg %0 Esc: G, Succ: ([rc] %5) +// CHECK-NEXT: Con [rc] %5 Esc: G, Succ: (%6) // CHECK-NEXT: Con %6 Esc: G, Succ: -// CHECK-NEXT: Con [int] %6.1 Esc: G, Succ: (%6.2) -// CHECK-NEXT: Con [ref] %6.2 Esc: G, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 +// CHECK-NEXT: Ret return Esc: R, Succ: %6 // CHECK-NEXT: End sil @call_store_pointer : $@convention(thin) (@owned Pointer) -> @owned X { bb0(%0 : $Pointer): @@ -303,9 +294,9 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of store_pointer -// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) -// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg %0 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @store_pointer : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -319,10 +310,10 @@ bb0(%0 : $Pointer): // global variable in the callee. // CHECK-LABEL: CG of store_content -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%3) -// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) -// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %4 -// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %3) +// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: G, Succ: %4 +// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%4) // CHECK-NEXT: Con %4 Esc: G, Succ: // CHECK-NEXT: End sil @store_content : $@convention(thin) (@owned Pointer) -> () { @@ -337,12 +328,10 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of call_store_content -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%4) -// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%5) +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %4) +// CHECK-NEXT: Con [rc] %4 Esc: A, Succ: (%5) // CHECK-NEXT: Con %5 Esc: G, Succ: -// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2) -// CHECK-NEXT: Con [ref] %5.2 Esc: G, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 +// CHECK-NEXT: Ret return Esc: R, Succ: %5 // CHECK-NEXT: End sil @call_store_content : $@convention(thin) (@owned Pointer) -> @owned X { bb0(%0 : $Pointer): @@ -356,9 +345,9 @@ bb0(%0 : $Pointer): // CHECK-LABEL: CG of copy_addr_content // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.1 +// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1 // CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con [ref] %1.1 Esc: A, Succ: +// CHECK-NEXT: Con %1.1 Esc: A, Succ: // CHECK-NEXT: End sil @copy_addr_content : $@convention(thin) (@in_guaranteed Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -369,9 +358,9 @@ bb0(%0: $*Int32, %1: $*Int32): // CHECK-LABEL: CG of copy_addr_take_content // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.1 +// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1 // CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con [ref] %1.1 Esc: A, Succ: +// CHECK-NEXT: Con %1.1 Esc: A, Succ: // CHECK-NEXT: End sil @copy_addr_take_content : $@convention(thin) (@in Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -381,10 +370,10 @@ bb0(%0: $*Int32, %1: $*Int32): } // CHECK-LABEL: CG of copy_addr_noinit_content -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: +// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) +// CHECK-NEXT: Con %0.1 Esc: G, Succ: +// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: G, Succ: // CHECK-NEXT: End sil @copy_addr_noinit_content : $@convention(thin) (@in Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -394,10 +383,10 @@ bb0(%0: $*Int32, %1: $*Int32): } // CHECK-LABEL: CG of call_copy_addr_content -// CHECK-NEXT: Val %0 Esc: , Succ: (%0.1) -// CHECK-NEXT: Con [ref] %0.1 Esc: %3, Succ: %1.1 -// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) -// CHECK-NEXT: Con [ref] %1.1 Esc: %3, Succ: +// CHECK-NEXT: Val %0 Esc: %3, Succ: (%0.1) +// CHECK-NEXT: Con %0.1 Esc: %3, Succ: %1.1 +// CHECK-NEXT: Val %1 Esc: %3, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: %3, Succ: // CHECK-NEXT: End sil @call_copy_addr_content : $@convention(thin) () -> () { %0 = alloc_stack $Int32 @@ -415,17 +404,14 @@ sil @call_copy_addr_content : $@convention(thin) () -> () { // of Y's box _could_ capture Y.x in Y's deinit. // CHECK-LABEL: CG of test_partial_apply -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: -// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) -// CHECK-NEXT: Con [ref] %1.2 Esc: G, Succ: -// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: -// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%1.1), %1 -// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%7) -// CHECK-NEXT: Val [ref] %6 Esc: , Succ: (%7) -// CHECK-NEXT: Con [int] %7 Esc: %14,%15,%16,%17, Succ: (%7.1) +// CHECK-NEXT: Arg %1 Esc: G, Succ: +// CHECK-NEXT: Arg %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con %2.1 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: %14,%15,%17, Succ: ([rc] %7) +// CHECK-NEXT: Val %6 Esc: %14,%15,%16, Succ: ([rc] %7) +// CHECK-NEXT: Con [rc] %7 Esc: %14,%15,%16,%17, Succ: (%7.1) // CHECK-NEXT: Con %7.1 Esc: %14,%15,%16,%17, Succ: %2 -// CHECK-NEXT: Val [ref] %12 Esc: , Succ: %3, %6 +// CHECK-NEXT: Val %12 Esc: %14,%15, Succ: %3, %6 // CHECK-NEXT: End sil @test_partial_apply : $@convention(thin) (Int64, @owned X, @owned Y) -> Int64 { bb0(%0 : $Int64, %1 : $X, %2 : $Y): @@ -448,21 +434,16 @@ bb0(%0 : $Int64, %1 : $X, %2 : $Y): } // CHECK-LABEL: CG of closure1 -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) -// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%3) -// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%4) -// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Arg %0 Esc: G, Succ: +// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %3) +// CHECK-NEXT: Arg %2 Esc: A, Succ: ([rc] %4) +// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%3.1) // CHECK-NEXT: Con %3.1 Esc: A, Succ: (%3.2) -// CHECK-NEXT: Con [int] %3.2 Esc: A, Succ: (%3.3) -// CHECK-NEXT: Con %3.3 Esc: A, Succ: (%3.4) -// CHECK-NEXT: Con %3.4 Esc: G, Succ: -// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: A, Succ: (%4.2) -// CHECK-NEXT: Con [int] %4.2 Esc: A, Succ: -// CHECK-NEXT: Con %4.3 Esc: A, Succ: (%0.1), %0 -// CHECK-NEXT: Val [ref] %7 Esc: , Succ: %2 +// CHECK-NEXT: Con %3.2 Esc: G, Succ: +// CHECK-NEXT: Con [rc] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con %4.1 Esc: A, Succ: ([rc] %4.2) +// CHECK-NEXT: Con [rc] %4.2 Esc: G, Succ: +// CHECK-NEXT: Val %7 Esc: %8, Succ: %2 // CHECK-NEXT: End sil @closure1 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } , @owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } , %2 : $<τ_0_0> { var τ_0_0 } ): @@ -477,13 +458,12 @@ bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } , %2 : $<τ_0_0> { var τ_0_0 } // CHECK-LABEL: CG of closure2 -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: A, Succ: (%4) -// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: A, Succ: %0 +// CHECK-NEXT: Arg %0 Esc: G, Succ: +// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %2) +// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: A, Succ: ([rc] %4) +// CHECK-NEXT: Con [rc] %4 Esc: G, Succ: (%4.1) +// CHECK-NEXT: Con %4.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @closure2 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } ) -> () { bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } ): @@ -499,13 +479,11 @@ bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } ): // Test partial_apply. The box escapes in the callee. // CHECK-LABEL: CG of test_escaped_box -// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) -// CHECK-NEXT: Con [int] %2 Esc: G, Succ: (%2.1) +// CHECK-NEXT: Val %1 Esc: G, Succ: ([rc] %2) +// CHECK-NEXT: Con [rc] %2 Esc: G, Succ: (%2.1) // CHECK-NEXT: Con %2.1 Esc: G, Succ: (%2.2) -// CHECK-NEXT: Con [int] %2.2 Esc: G, Succ: -// CHECK-NEXT: Con %2.3 Esc: G, Succ: -// CHECK-NEXT: Con %2.4 Esc: G, Succ: -// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1 +// CHECK-NEXT: Con %2.2 Esc: G, Succ: +// CHECK-NEXT: Val %6 Esc: G, Succ: %1 // CHECK-NEXT: End sil @test_escaped_box : $@convention(thin) (Int64) -> Int64 { bb0(%0 : $Int64): @@ -524,12 +502,10 @@ bb0(%0 : $Int64): } // CHECK-LABEL: CG of let_box_escape -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) -// CHECK-NEXT: Con [int] %1 Esc: G, Succ: (%1.1) +// CHECK-NEXT: Arg %0 Esc: G, Succ: ([rc] %1) +// CHECK-NEXT: Con [rc] %1 Esc: G, Succ: (%1.1) // CHECK-NEXT: Con %1.1 Esc: G, Succ: (%1.2) -// CHECK-NEXT: Con [int] %1.2 Esc: G, Succ: (%1.3) -// CHECK-NEXT: Con %1.3 Esc: G, Succ: (%1.4) -// CHECK-NEXT: Con %1.4 Esc: G, Succ: +// CHECK-NEXT: Con %1.2 Esc: G, Succ: // CHECK-NEXT: End sil @let_box_escape : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $<τ_0_0> { var τ_0_0 } ): @@ -548,10 +524,9 @@ sil @takebox : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> ( // The partial_apply itself escapes and therefore also the box escapes. // CHECK-LABEL: CG of test_escaped_partial_apply -// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) -// CHECK-NEXT: Con [int] %2 Esc: G, Succ: -// CHECK-NEXT: Con %2.1 Esc: G, Succ: -// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1 +// CHECK-NEXT: Val %1 Esc: G, Succ: ([rc] %2) +// CHECK-NEXT: Con [rc] %2 Esc: G, Succ: +// CHECK-NEXT: Val %6 Esc: G, Succ: %1 // CHECK-NEXT: End sil @test_escaped_partial_apply : $@convention(thin) (Int64) -> () { bb0(%0 : $Int64): @@ -569,12 +544,10 @@ bb0(%0 : $Int64): } // CHECK-LABEL: CG of closure3 -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) -// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) +// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%1.1) // CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2) -// CHECK-NEXT: Con [int] %1.2 Esc: A, Succ: -// CHECK-NEXT: Con %1.3 Esc: A, Succ: (%1.4) -// CHECK-NEXT: Con %1.4 Esc: G, Succ: +// CHECK-NEXT: Con %1.2 Esc: G, Succ: // CHECK-NEXT: End sil @closure3 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $<τ_0_0> { var τ_0_0 } ): @@ -591,13 +564,11 @@ sil @take_partial_apply : $@convention(thin) (@owned @callee_owned () -> Int64) sil_global @global_ln : $LinkedNode // CHECK-LABEL: CG of load_next_recursive -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) -// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) +// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) // CHECK-NEXT: Con %2 Esc: G, Succ: -// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1), %2 -// CHECK-NEXT: Con [int] %4.1 Esc: G, Succ: (%4.2) -// CHECK-NEXT: Con %4.2 Esc: G, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 +// CHECK-NEXT: Val %4 Esc: G, Succ: %2 +// CHECK-NEXT: Ret return Esc: R, Succ: %4 // CHECK-NEXT: End sil @load_next_recursive : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -609,13 +580,12 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of let_escape -// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) -// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %0 -// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%0.1), %0 -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 +// CHECK-NEXT: Arg %0 Esc: G, Succ: ([rc] %0.1) +// CHECK-NEXT: Con [rc] %0.1 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0 +// CHECK-NEXT: Val %4 Esc: G, Succ: %0 +// CHECK-NEXT: Ret return Esc: R, Succ: %4 // CHECK-NEXT: End sil @let_escape : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -627,12 +597,12 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of return_same -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%5.1) -// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%5.1), %5.2 -// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%5.1), %0, %3 -// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2) -// CHECK-NEXT: Con %5.2 Esc: G, Succ: (%5.1) -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %3.1) +// CHECK-NEXT: Val %3 Esc: G, Succ: ([rc] %3.1), %3.2 +// CHECK-NEXT: Con [rc] %3.1 Esc: G, Succ: (%3.2) +// CHECK-NEXT: Con %3.2 Esc: G, Succ: ([rc] %3.1) +// CHECK-NEXT: Val %5 Esc: R, Succ: %0, %3 +// CHECK-NEXT: Ret return Esc: R, Succ: %5 // CHECK-NEXT: End sil @return_same : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -650,15 +620,15 @@ bb2(%5 : $LinkedNode): // Another recursion test. // CHECK-LABEL: CG of loadNext2 -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) -// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%4.1) -// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1), %2.2, %4.2 -// CHECK-NEXT: Con [int] %4.1 Esc: A, Succ: (%4.2) -// CHECK-NEXT: Con %4.2 Esc: A, Succ: (%4.1) -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) +// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con %2 Esc: A, Succ: ([rc] %2.1) +// CHECK-NEXT: Con [rc] %2.1 Esc: A, Succ: (%2.2) +// CHECK-NEXT: Con %2.2 Esc: A, Succ: ([rc] %2.3) +// CHECK-NEXT: Con [rc] %2.3 Esc: A, Succ: (%2.4) +// CHECK-NEXT: Con %2.4 Esc: A, Succ: ([rc] %2.3) +// CHECK-NEXT: Val %4 Esc: R, Succ: %2.2, %2.4 +// CHECK-NEXT: Ret return Esc: R, Succ: %4 // CHECK-NEXT: End sil @loadNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -670,14 +640,14 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of returnNext2 -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%5) -// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%8.1), %8.2 -// CHECK-NEXT: Con [int] %5 Esc: A, Succ: (%6) -// CHECK-NEXT: Con %6 Esc: A, Succ: (%8.1) -// CHECK-NEXT: Val [ref] %8 Esc: , Succ: (%8.1), %3, %6 -// CHECK-NEXT: Con [int] %8.1 Esc: A, Succ: (%8.2) -// CHECK-NEXT: Con %8.2 Esc: A, Succ: (%8.1) -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %8 +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %5) +// CHECK-NEXT: Val %3 Esc: R, Succ: ([rc] %3.1), %3.2 +// CHECK-NEXT: Con [rc] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con %3.2 Esc: A, Succ: ([rc] %3.1) +// CHECK-NEXT: Con [rc] %5 Esc: A, Succ: (%6) +// CHECK-NEXT: Con %6 Esc: A, Succ: ([rc] %3.1) +// CHECK-NEXT: Val %8 Esc: R, Succ: %3, %6 +// CHECK-NEXT: Ret return Esc: R, Succ: %8 // CHECK-NEXT: End sil @returnNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -700,12 +670,12 @@ bb3(%8 : $LinkedNode): // A single-cycle recursion test. // CHECK-LABEL: CG of single_cycle_recursion -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) -// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: A, Succ: (%2) -// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%2), %3 -// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%2), %0, %5 -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %7 +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %2) +// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: A, Succ: ([rc] %2) +// CHECK-NEXT: Val %5 Esc: R, Succ: ([rc] %2), %3 +// CHECK-NEXT: Val %7 Esc: R, Succ: %0, %5 +// CHECK-NEXT: Ret return Esc: R, Succ: %7 // CHECK-NEXT: End sil @single_cycle_recursion : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -725,11 +695,9 @@ bb2(%5 : $LinkedNode): // Test if a try_apply is represented correctly in the connection graph. // CHECK-LABEL: CG of call_throwing_func -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: -// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%0.1), %0 -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 +// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Val %3 Esc: R, Succ: %0 +// CHECK-NEXT: Ret return Esc: R, Succ: %3 // CHECK-NEXT: End sil @call_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -744,12 +712,10 @@ bb2(%5 : $Error): } // CHECK-LABEL: CG of throwing_func -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) -// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 +// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Con %3.1 Esc: G, Succ: +// CHECK-NEXT: Ret return Esc: R, Succ: %0 // CHECK-NEXT: End sil @throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -769,13 +735,11 @@ bb2: // Test if a try_apply to an unknown function is handled correctly. // CHECK-LABEL: CG of call_unknown_throwing_func -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) -// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: -// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%3.1) -// CHECK-NEXT: Con [int] %3.1 Esc: G, Succ: (%3.2) -// CHECK-NEXT: Con [ref] %3.2 Esc: G, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 +// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) +// CHECK-NEXT: Con %0.1 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Con %3.1 Esc: G, Succ: +// CHECK-NEXT: Ret return Esc: R, Succ: %3 // CHECK-NEXT: End sil @call_unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -794,14 +758,12 @@ sil @unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error // Test that the deinit of a box itself does not capture anything. // CHECK-LABEL: CG of test_release_of_partial_apply_with_box -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: G, Succ: -// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) -// CHECK-NEXT: Con [int] %2 Esc: %6, Succ: (%2.1) +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con %0.1 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: %6, Succ: ([rc] %2) +// CHECK-NEXT: Con [rc] %2 Esc: %6, Succ: (%2.1) // CHECK-NEXT: Con %2.1 Esc: %6, Succ: %0 -// CHECK-NEXT: Val [ref] %5 Esc: , Succ: %1 +// CHECK-NEXT: Val %5 Esc: %6, Succ: %1 // CHECK-NEXT: End sil @test_release_of_partial_apply_with_box : $@convention(thin) (@owned Y) -> () { bb0(%0 : $Y): @@ -820,9 +782,9 @@ sil @take_y_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () // Test is an unknown value is merged correctly into the caller graph. // CHECK-LABEL: CG of store_to_unknown_reference -// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: -// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%3) -// CHECK-NEXT: Con [int] %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Arg %0 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: G, Succ: ([rc] %3) +// CHECK-NEXT: Con [rc] %3 Esc: G, Succ: (%3.1) // CHECK-NEXT: Con %3.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @store_to_unknown_reference : $@convention(thin) (@owned X) -> () { @@ -836,10 +798,9 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of get_reference -// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1) -// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: G, Succ: - // CHECK-NEXT: Ret [ref] return Esc: , Succ: %1 +// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: G, Succ: +// CHECK-NEXT: Ret return Esc: R, Succ: %1 // CHECK-NEXT: End sil @get_reference : $@convention(thin) () -> @owned Y { bb0: @@ -855,11 +816,9 @@ sil @unknown_get_reference : $@convention(thin) () -> @owned Y sil @unknown_set_y : $@convention(thin) () -> @out Y // CHECK-LABEL: CG of get_y -// CHECK-NEXT: Val %0 Esc: , Succ: (%3) -// CHECK-NEXT: Con [ref] %3 Esc: G, Succ: -// CHECK-NEXT: Con [int] %3.1 Esc: G, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: G, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 +// CHECK-NEXT: Val %0 Esc: G, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: G, Succ: +// CHECK-NEXT: Ret return Esc: R, Succ: %3 // CHECK-NEXT: End sil @get_y : $@convention(thin) () -> @owned Y { bb0: @@ -872,9 +831,9 @@ bb0: } // CHECK-LABEL: CG of create_and_store_x -// CHECK-NEXT: Val [ref] %0 Esc: G, Succ: -// CHECK-NEXT: Val [ref] %2 Esc: G, Succ: (%3) -// CHECK-NEXT: Con [int] %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Val %0 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: G, Succ: ([rc] %3) +// CHECK-NEXT: Con [rc] %3 Esc: G, Succ: (%3.1) // CHECK-NEXT: Con %3.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @create_and_store_x : $@convention(thin) () -> () { @@ -891,13 +850,11 @@ bb0: // Test types which are considered as pointers. // CHECK-LABEL: CG of pointer_types -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: -// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %0, %1 -// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%7.1), %4 -// CHECK-NEXT: Con [int] %7.1 Esc: A, Succ: (%7.2) -// CHECK-NEXT: Con %7.2 Esc: A, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %7 +// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg %1 Esc: A, Succ: +// CHECK-NEXT: Val %4 Esc: R, Succ: %0, %1 +// CHECK-NEXT: Val %7 Esc: R, Succ: %4 +// CHECK-NEXT: Ret return Esc: R, Succ: %7 // CHECK-NEXT: End sil @pointer_types : $@convention(thin) (@owned Y, @owned Y) -> @owned Y { bb0(%0 : $Y, %1 : $Y): @@ -916,9 +873,9 @@ bb1(%7 : $(Pointer, Pointer)): // CHECK-LABEL: CG of defer_edge_cycle // CHECK-NEXT: Arg %0 Esc: A, Succ: (%2) // CHECK-NEXT: Arg %1 Esc: A, Succ: (%4) -// CHECK-NEXT: Con [ref] %2 Esc: A, Succ: (%6), %4 -// CHECK-NEXT: Con [ref] %4 Esc: A, Succ: %2 -// CHECK-NEXT: Con [int] %6 Esc: A, Succ: (%7) +// CHECK-NEXT: Con %2 Esc: A, Succ: ([rc] %6), %4 +// CHECK-NEXT: Con %4 Esc: A, Succ: %2 +// CHECK-NEXT: Con [rc] %6 Esc: A, Succ: (%7) // CHECK-NEXT: Con %7 Esc: A, Succ: // CHECK-NEXT: End sil @defer_edge_cycle : $@convention(thin) (@inout Y, @inout Y) -> () { @@ -934,10 +891,9 @@ entry(%0 : $*Y, %1 : $*Y): } // CHECK-LABEL: CG of take_c_func -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 +// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) +// CHECK-NEXT: Con %0.1 Esc: G, Succ: +// CHECK-NEXT: Ret return Esc: R, Succ: %0 // CHECK-NEXT: End sil @take_c_func : $@convention(thin) (@convention(c) () -> ()) -> @convention(c) () -> () { bb0(%0 : $@convention(c) () -> ()): @@ -954,9 +910,8 @@ bb0: } // CHECK-LABEL: CG of pass_c_func -// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1) -// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: -// CHECK-NEXT: Con %2.2 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) +// CHECK-NEXT: Con %2.1 Esc: G, Succ: // CHECK-NEXT: End sil @pass_c_func : $@convention(thin) () -> () { bb0: @@ -969,14 +924,12 @@ bb0: // CHECK-LABEL: CG of test_select_enum -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: -// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: -// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: -// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) -// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: -// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %1, %2, %3 -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 +// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg %1 Esc: A, Succ: +// CHECK-NEXT: Arg %2 Esc: A, Succ: +// CHECK-NEXT: Arg %3 Esc: A, Succ: +// CHECK-NEXT: Val %4 Esc: R, Succ: %1, %2, %3 +// CHECK-NEXT: Ret return Esc: R, Succ: %4 // CHECK-NEXT: End sil @test_select_enum : $@convention(thin) (PointerEnum2, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $PointerEnum2, %1 : $X, %2 : $X, %3 : $X): @@ -986,13 +939,11 @@ bb0(%0 : $PointerEnum2, %1 : $X, %2 : $X, %3 : $X): // CHECK-LABEL: CG of test_select_enum_addr // CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%3.1) -// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%3.1) -// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: (%3.1) -// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) -// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: -// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %1, %2, %3 -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 +// CHECK-NEXT: Arg %1 Esc: A, Succ: +// CHECK-NEXT: Arg %2 Esc: A, Succ: +// CHECK-NEXT: Arg %3 Esc: A, Succ: +// CHECK-NEXT: Val %4 Esc: R, Succ: %1, %2, %3 +// CHECK-NEXT: Ret return Esc: R, Succ: %4 // CHECK-NEXT: End sil @test_select_enum_addr : $@convention(thin) (@in PointerEnum2, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $*PointerEnum2, %1 : $X, %2 : $X, %3 : $X): @@ -1001,13 +952,11 @@ bb0(%0 : $*PointerEnum2, %1 : $X, %2 : $X, %3 : $X): } // CHECK-LABEL: CG of test_select_value -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: -// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: -// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: -// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) -// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: -// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1, %2, %3 -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 +// CHECK-NEXT: Arg %1 Esc: A, Succ: +// CHECK-NEXT: Arg %2 Esc: A, Succ: +// CHECK-NEXT: Arg %3 Esc: A, Succ: +// CHECK-NEXT: Val %6 Esc: R, Succ: %1, %2, %3 +// CHECK-NEXT: Ret return Esc: R, Succ: %6 // CHECK-NEXT: End sil @test_select_value : $@convention(thin) (Builtin.Int64, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X): @@ -1018,10 +967,10 @@ bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X): } // CHECK-LABEL: CG of test_existential_addr -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: Val %1 Esc: , Succ: (%2) // CHECK-NEXT: Con %2 Esc: , Succ: (%2.1) -// CHECK-NEXT: Con [ref] %2.1 Esc: , Succ: %0 +// CHECK-NEXT: Con %2.1 Esc: , Succ: %0 // CHECK-NEXT: End sil @test_existential_addr : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -1036,7 +985,7 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of test_existential_ref -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: End sil @test_existential_ref : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1051,9 +1000,9 @@ bb0(%0 : $X): // Check that we don't crash on this. // CHECK-LABEL: CG of test_unknown_store -// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: , Succ: (%2.1) -// CHECK-NEXT: Con [ref] %2.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg %0 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) +// CHECK-NEXT: Con %2.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @test_unknown_store : $@convention(thin) (@owned ErrorClass) -> () { bb0(%0 : $ErrorClass): @@ -1065,10 +1014,8 @@ bb0(%0 : $ErrorClass): } // CHECK-LABEL: CG of test_raw_pointer_to_ref -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 +// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Ret return Esc: R, Succ: %0 // CHECK-NEXT: End sil @test_raw_pointer_to_ref : $@convention(thin) (@owned X) -> @owned X { bb0(%0 : $X): @@ -1078,10 +1025,8 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of test_bridge_object_to_ref -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 +// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Ret return Esc: R, Succ: %0 // CHECK-NEXT: End sil @test_bridge_object_to_ref : $@convention(thin) (@owned X, Builtin.Word) -> @owned X { bb0(%0 : $X, %1 : $Builtin.Word): @@ -1092,8 +1037,8 @@ bb0(%0 : $X, %1 : $Builtin.Word): // CHECK-LABEL: CG of test_address_to_pointer // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1 -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1 +// CHECK-NEXT: Arg %1 Esc: A, Succ: // CHECK-NEXT: End sil @test_address_to_pointer : $@convention(thin) (@owned X) -> @out X { bb0(%0 : $*X, %1 : $X): @@ -1105,10 +1050,8 @@ bb0(%0 : $*X, %1 : $X): } // CHECK-LABEL: CG of test_casts -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 +// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Ret return Esc: R, Succ: %0 // CHECK-NEXT: End sil @test_casts : $@convention(thin) (@owned AnyObject) -> @owned X { bb0(%0 : $AnyObject): @@ -1131,10 +1074,10 @@ bb0(%0 : $*U, %1 : $*T, %2 : $@thick U.Type): sil_global @global_y : $SomeData // CHECK-LABEL: CG of test_node_merge_during_struct_inst -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%8) +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%8) // CHECK-NEXT: Val %1 Esc: G, Succ: (%8) // CHECK-NEXT: Val %4 Esc: , Succ: (%8) -// CHECK-NEXT: Con [ref] %8 Esc: G, Succ: (%8), %1 +// CHECK-NEXT: Con %8 Esc: G, Succ: (%8), %1 // CHECK-NEXT: Val %10 Esc: , Succ: %0, %4, %8 // CHECK-NEXT: End sil @test_node_merge_during_struct_inst : $@convention(thin) (Y) -> () { @@ -1161,9 +1104,7 @@ bb0(%0 : $Y): } // CHECK-LABEL: CG of arraysemantics_is_native_no_typecheck -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_is_native_no_typecheck : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1175,9 +1116,7 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_check_subscript -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_check_subscript : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1194,9 +1133,7 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_check_index -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_check_index : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1212,9 +1149,9 @@ bb0(%0 : $Array): // CHECK-LABEL: CG of arraysemantics_get_element // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.2 -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con [int] %1.1 Esc: A, Succ: (%1.2) +// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.2 +// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %1.1) +// CHECK-NEXT: Con [rc] %1.1 Esc: A, Succ: (%1.2) // CHECK-NEXT: Con %1.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_element : $@convention(thin) (Array) -> @out X { @@ -1233,7 +1170,6 @@ bb0(%io : $*X, %1 : $Array): // CHECK-LABEL: CG of arraysemantics_make_mutable // CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_make_mutable : $@convention(thin) (@inout Array) -> () { bb0(%0 : $*Array): @@ -1245,10 +1181,9 @@ bb0(%0 : $*Array): } // CHECK-LABEL: CG of arraysemantics_get_element_address -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: -// CHECK-NEXT: Con %0.2 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: , Succ: %0.1 +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %0.1) +// CHECK-NEXT: Con [rc] %0.1 Esc: A, Succ: +// CHECK-NEXT: Val %4 Esc: , Succ: [rc] %0.1 // CHECK-NEXT: End sil @arraysemantics_get_element_address : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1263,9 +1198,7 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_get_count -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_count : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1278,9 +1211,7 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_get_capacity -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_capacity : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1294,14 +1225,12 @@ bb0(%0 : $Array): // CHECK-LABEL: CG of arraysemantics_withUnsafeMutableBufferPointer // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con [int] %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con [ref] %0.3 Esc: G, Succ: -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) -// CHECK-NEXT: Con [ref] %3.1 Esc: %4, Succ: +// CHECK-NEXT: Con %0.1 Esc: A, Succ: ([rc] %0.2) +// CHECK-NEXT: Con [rc] %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con %0.3 Esc: G, Succ: +// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: %4, Succ: // CHECK-NEXT: End sil @arraysemantics_withUnsafeMutableBufferPointer : $@convention(thin) (@inout Array, @owned @callee_owned (@inout X) -> (@out (), @error Error)) -> () { bb(%0 : $*Array, %1 : $@callee_owned (@inout X) -> (@out (), @error Error)): @@ -1316,12 +1245,12 @@ bb(%0 : $*Array, %1 : $@callee_owned (@inout X) -> (@out (), @error Error)): } // CHECK-LABEL: CG of arraysemantics_createUninitialized -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%6) -// CHECK-NEXT: Val [ref] %5 Esc: , Succ: %2 -// CHECK-NEXT: Con [int] %6 Esc: R, Succ: (%6.1) -// CHECK-NEXT: Con %6.1 Esc: R, Succ: %0 -// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 +// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Val %2 Esc: R, Succ: (%2.1) +// CHECK-NEXT: Con %2.1 Esc: R, Succ: %0 +// CHECK-NEXT: Val %4 Esc: R, Succ: ([rc] %6) +// CHECK-NEXT: Con [rc] %6 Esc: R, Succ: %2 +// CHECK-NEXT: Ret return Esc: R, Succ: %4 // CHECK-NEXT: End sil @arraysemantics_createUninitialized : $@convention(thin) (@owned X) -> @owned Array { bb0(%0 : $X): @@ -1355,17 +1284,11 @@ sil [_semantics "array.uninitialized"] @createUninitialized : $@convention(metho sil @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject // CHECK-LABEL: CG of semantics_pair_no_escaping_closure -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: -// CHECK-NEXT: Con [int] %1.1 Esc: A, Succ: (%1.2) -// CHECK-NEXT: Con [ref] %1.2 Esc: A, Succ: -// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: G, Succ: -// CHECK-NEXT: Val %4 Esc: , Succ: (%4.1) -// CHECK-NEXT: Con [ref] %4.1 Esc: %5, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg %1 Esc: A, Succ: +// CHECK-NEXT: Arg %2 Esc: G, Succ: (%2.1) +// CHECK-NEXT: Con %2.1 Esc: G, Succ: +// CHECK-NEXT: Val %4 Esc: %5, Succ: // CHECK-NEXT: End sil @semantics_pair_no_escaping_closure : $@convention(thin) (@owned X, @guaranteed X, @owned @callee_owned (X, X) -> (@out X, @error Error)) -> () { bb(%0 : $X, %1 : $X, %2: $@callee_owned (X, X) -> (@out X, @error Error)): @@ -1378,14 +1301,10 @@ bb(%0 : $X, %1 : $X, %2: $@callee_owned (X, X) -> (@out X, @error Error)): } // CHECK-LABEL: CG of semantics_self_no_escaping_closure -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: , Succ: -// CHECK-NEXT: Con [ref] %3.1 Esc: %4, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: %4, Succ: // CHECK-NEXT: End sil @semantics_self_no_escaping_closure : $@convention(thin) (@guaranteed X, @owned @callee_owned (X, X) -> (@out X, @error Error)) -> () { bb(%0 : $X, %1: $@callee_owned (X, X) -> (@out X, @error Error)): @@ -1398,7 +1317,7 @@ bb(%0 : $X, %1: $@callee_owned (X, X) -> (@out X, @error Error)): } // CHECK-LABEL: CG of check_dealloc_ref -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: End sil @check_dealloc_ref : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1408,7 +1327,7 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of check_set_deallocating -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: End sil @check_set_deallocating : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1421,9 +1340,7 @@ bb0(%0 : $X): // happen due to strong_release and figure out that the object will not // globally escape. // CHECK-LABEL: CG of check_non_escaping_implicit_destructor_invocation_via_strong_release -// CHECK-NEXT: Val [ref] %0 Esc: , Succ: -// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) -// CHECK-NEXT: Con [ref] %0.2 Esc: %1, Succ: +// CHECK-NEXT: Val %0 Esc: %1, Succ: // CHECK-NEXT: End sil @check_non_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () { bb0: @@ -1438,9 +1355,7 @@ bb0: // happen due to strong_release and figure out that the object will not // globally escape. // CHECK-LABEL: CG of check_non_escaping_implicit_destructor_invocation_via_release_value -// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) -// CHECK-NEXT: Con [ref] %0.2 Esc: %1, Succ: +// CHECK-NEXT: Val %0 Esc: %1, Succ: // CHECK-NEXT: End sil @check_non_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () { bb0: @@ -1455,8 +1370,8 @@ bb0: // happen due to strong_release and figure out that the object will // globally escape. // CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_strong_release -// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Val %0 Esc: %1, Succ: ([rc] %0.1) +// CHECK-NEXT: Con [rc] %0.1 Esc: %1, Succ: (%0.2) // CHECK-NEXT: Con %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @check_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () { @@ -1472,8 +1387,8 @@ bb0: // happen due to strong_release and figure out that the object will // globally escape. // CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_release_value -// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Val %0 Esc: %1, Succ: ([rc] %0.1) +// CHECK-NEXT: Con [rc] %0.1 Esc: %1, Succ: (%0.2) // CHECK-NEXT: Con %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @check_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () { @@ -1491,11 +1406,9 @@ bb0: // invoked by strong_release it would also determine that these objects // do not escape from the function. // CHECK-LABEL: CG of check_is_local_object_through_bb_args -// CHECK-LABEL: Val [ref] %2 Esc: , Succ: (%2.1) -// CHECK-LABEL: Con [int] %2.1 Esc: %7, Succ: (%2.2) -// CHECK-LABEL: Con [ref] %2.2 Esc: %7, Succ: -// CHECK-LABEL: Val [ref] %4 Esc: , Succ: (%2.1) -// CHECK-LABEL: Val [ref] %6 Esc: , Succ: %2, %4 +// CHECK-LABEL: Val %2 Esc: %6,%7, Succ: +// CHECK-LABEL: Val %4 Esc: %6,%7, Succ: +// CHECK-LABEL: Val %6 Esc: %6,%7, Succ: %2, %4 // CHECK-LABEL: End sil @check_is_local_object_through_bb_args: $@convention(thin) (Builtin.Int1) -> () { bb0(%0 : $Builtin.Int1): @@ -1516,10 +1429,7 @@ bb3(%6: $X): } // CHECK-LABEL: CG of check_look_through_thin_to_thick -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: -// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1) -// CHECK-NEXT: Con [int] %1.1 Esc: %2, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: %2, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: End sil @check_look_through_thin_to_thick: $(@convention(thin) () -> ()) -> () { bb0(%0 : $@convention(thin) () -> ()): @@ -1531,7 +1441,7 @@ bb0(%0 : $@convention(thin) () -> ()): // X.deinit // CHECK-LABEL: CG of $s4main1XCfD -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: End sil @$s4main1XCfD: $@convention(method) (@owned X) -> () { bb0(%0 : $X): @@ -1542,11 +1452,11 @@ bb0(%0 : $X): // Z.deinit // CHECK-LABEL: CG of $s4main1ZCfD -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) -// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) +// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) // CHECK-NEXT: Con %2 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) -// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: %2 +// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Con %3.1 Esc: G, Succ: %2 // CHECK-NEXT: End sil @$s4main1ZCfD: $@convention(method) (@owned Z) -> () { bb0(%0 : $Z): @@ -1575,9 +1485,8 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of call_public_external_func -// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) -// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Val %0 Esc: G, Succ: (%0.1) +// CHECK-NEXT: Con %0.1 Esc: G, Succ: // CHECK-NEXT: End sil @call_public_external_func : $@convention(thin) () -> () { bb0: @@ -1606,13 +1515,12 @@ bb2: // begin_apply result) is just marked as escaping. // CHECK-LABEL: CG of call_coroutine -// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Val %0 Esc: G, Succ: (%0.1) +// CHECK-NEXT: Con %0.1 Esc: G, Succ: (%0.2) // CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Con %0.3 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: , Succ: (%2.1) -// CHECK-NEXT: Con [ref] %2.1 Esc: G, Succ: %4 -// CHECK-NEXT: Val [ref] %4 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) +// CHECK-NEXT: Con %2.1 Esc: G, Succ: %4 +// CHECK-NEXT: Val %4 Esc: G, Succ: // CHECK-NEXT: End sil @call_coroutine : $@convention(thin) () -> () { bb0: @@ -1629,14 +1537,14 @@ bb0: // Test the absence of redundant pointsTo edges // CHECK-LABEL: CG of testInitializePointsToLeaf -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%13) -// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%13), %0.2 -// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %2 -// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%13), %0.2 -// CHECK-NEXT: Val [ref] %12 Esc: , Succ: (%13), %7 -// CHECK-NEXT: Con [int] %13 Esc: A, Succ: (%14) +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %0.1) +// CHECK-NEXT: Con [rc] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: ([rc] %13) +// CHECK-NEXT: Val %2 Esc: %4, Succ: %0.2 +// CHECK-NEXT: Val %4 Esc: %4, Succ: %2 +// CHECK-NEXT: Val %7 Esc: %12, Succ: ([rc] %13), %0.2 +// CHECK-NEXT: Val %12 Esc: %12, Succ: ([rc] %13), %7 +// CHECK-NEXT: Con [rc] %13 Esc: A, Succ: (%14) // CHECK-NEXT: Con %14 Esc: A, Succ: // CHECK-LABEL: End class C { @@ -1685,14 +1593,14 @@ bb10(%arg2 : $LinkedNode): // a defer edge from a node with uninitialized pointsTo to a node with // already-initialized pointsTo. // CHECK-LABEL: CG of testInitializePointsToRedundant -// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) -// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %2) +// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %2) +// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%3) // CHECK-NEXT: Con %3 Esc: A, Succ: -// CHECK-NEXT: Val [ref] %7 Esc: , Succ: %0 -// CHECK-NEXT: Val [ref] %12 Esc: , Succ: %1 -// CHECK-NEXT: Val [ref] %14 Esc: , Succ: (%2), %1, %12 -// CHECK-NEXT: Val [ref] %18 Esc: , Succ: %7, %14 +// CHECK-NEXT: Val %7 Esc: %7,%18, Succ: %0 +// CHECK-NEXT: Val %12 Esc: %12,%14,%18, Succ: %1 +// CHECK-NEXT: Val %14 Esc: %18, Succ: ([rc] %2), %1, %12 +// CHECK-NEXT: Val %18 Esc: %18, Succ: %7, %14 // CHECK-LABEL: End sil @testInitializePointsToMerge : $@convention(method) (@guaranteed C, @guaranteed C) -> C { bb0(%0: $C, %1 : $C): @@ -1747,50 +1655,3 @@ bb10(%arg3 : $C): %66 = tuple () return %66 : $() } - -// Test canEscapeToUsePoint with defer edges between non-reference-type nodes. -// -// Unfortunately, the only way I can think of to create defer edges -// between non-reference nodes is with address-type block arguments. -// This will be banned soon, so the test will need to be disabled, but -// this is still an important corner case in the EscapeAnalysis -// logic. To keep the test working, and be able to test many more -// important corner cases, we should introduce testing modes that -// introduce spurious but predictable defer edges and node merges. -// -// Here, canEscapeTo is called on %bbarg (%5) whose node is not -// marked escaping. But it does have a defer edge to the local address -// (%0) which does cause the address to escape via the call site. -// -// CHECK-LABEL: CG of testDeferPointer -// CHECK-NEXT: Val %0 Esc: , Succ: (%0.1) -// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: , Succ: (%0.1) -// CHECK-NEXT: Val %5 Esc: , Succ: %0, %1 -// CHECK-LABEL: End -// CHECK: MayEscape: %5 = argument of bb3 : $*Builtin.Int32 -// CHECK-NEXT: to %{{.*}} = apply %{{.*}}(%0) : $@convention(thin) (@inout Builtin.Int32) -> () -sil @takeAdr : $@convention(thin) (@inout Builtin.Int32) -> () - -sil hidden @testDeferPointer : $@convention(thin) () -> () { -bb0: - %iadr1 = alloc_stack $Builtin.Int32 - %iadr2 = alloc_stack $Builtin.Int32 - cond_br undef, bb1, bb2 - -bb1: - br bb3(%iadr1: $*Builtin.Int32) - -bb2: - br bb3(%iadr2: $*Builtin.Int32) - -bb3(%bbarg: $*Builtin.Int32): - %val = integer_literal $Builtin.Int32, 1 - store %val to %bbarg : $*Builtin.Int32 - %ftake = function_ref @takeAdr : $@convention(thin) (@inout Builtin.Int32) -> () - %call = apply %ftake(%iadr1) : $@convention(thin) (@inout Builtin.Int32) -> () - dealloc_stack %iadr2 : $*Builtin.Int32 - dealloc_stack %iadr1 : $*Builtin.Int32 - %z = tuple () - return %z : $() -} diff --git a/test/SILOptimizer/escape_analysis_dead_store.sil b/test/SILOptimizer/escape_analysis_dead_store.sil deleted file mode 100644 index 9a649975407cb..0000000000000 --- a/test/SILOptimizer/escape_analysis_dead_store.sil +++ /dev/null @@ -1,138 +0,0 @@ -// RUN: %target-sil-opt %s -dead-store-elim -enable-sil-verify-all | %FileCheck %s - -sil_stage canonical - -import Builtin -import Swift -import SwiftShims - -final class X { - init() -} - -public struct S { - @_hasStorage var x: X { get set } - @_hasStorage var i: Int { get set } - init(x: X, i: Int) -} - -@_hasStorage @_hasInitialValue var gg: X { get set } - -@inline(never) func takex(_ x: X) - -sil [noinline] @takeX : $@convention(thin) (@guaranteed X) -> () - -// Test that escape analysis does not consider an inout argument to -// escape at a call site even though it's reference-type field does -// escape. Dead store elimination asks MemoryBehaviorVisitor whether -// the apply may read from the inout argument. This call into -// canEscapeToUsePoint, which should return false because the inout -// structure itself is not exposed to the call, only it's -// reference-type field is. -// -// CHECK-LABEL: sil @testInoutNoEscape -// CHECK-NOT: store -// CHECK: apply -// CHECK: store -// CHECK-NOT: store -// CHECK: } // end sil function 'testInoutNoEscape' -sil @testInoutNoEscape : $@convention(thin) (@inout S, @guaranteed S) -> () { -bb0(%0 : $*S, %1 : $S): - %4 = struct_extract %1 : $S, #S.x - %5 = struct_element_addr %0 : $*S, #S.x - %6 = load %5 : $*X - strong_retain %4 : $X - strong_release %6 : $X - store %1 to %0 : $*S - %10 = function_ref @takeX : $@convention(thin) (@guaranteed X) -> () - strong_retain %4 : $X - %12 = apply %10(%4) : $@convention(thin) (@guaranteed X) -> () - release_value %1 : $S - store %1 to %0 : $*S - %15 = tuple () - return %15 : $() -} - -// ============================================================================= -// Test that a store writing back into a container is not eliminated -// when the container's interior pointer later escapes into a function -// that reads from the pointer. - -final internal class TestArrayContainer { - @_hasStorage @_hasInitialValue internal final var pointer: UnsafeMutablePointer { get set } - @_hasStorage @_hasInitialValue internal final var storage: ContiguousArray { get set } - @_optimize(none) @inline(never) internal final func append(_ arg: Int32) - internal final func va_list() -> UnsafeMutableRawPointer - init() -} - -sil @UnsafeMutablePointer_load_Int64 : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> // user: %5 -// ContiguousArray.append(_:) -sil @$ss15ContiguousArrayV6appendyyxnF : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () - - -// Helper that reads from a raw pointer. -sil hidden [noinline] @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool { -bb0(%0 : $UnsafeMutableRawPointer): - %1 = integer_literal $Builtin.Int64, 0 - %2 = struct $Int64 (%1 : $Builtin.Int64) - %3 = function_ref @UnsafeMutablePointer_load_Int64 : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> - %4 = apply %3(%2, %0) : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> // users: %18, %6 - %5 = integer_literal $Builtin.Int1, -1 - %6 = struct $Bool (%5 : $Builtin.Int1) - return %6 : $Bool -} - -// TestArrayContainer.append(_:) -// Helper that produces a nonempty array. -sil hidden [noinline] [Onone] @TestArrayContainer_append : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () { -bb0(%0 : $Int32, %1 : $TestArrayContainer): - %2 = alloc_stack $Int32 - store %0 to %2 : $*Int32 - %4 = ref_element_addr %1 : $TestArrayContainer, #TestArrayContainer.storage - %5 = function_ref @$ss15ContiguousArrayV6appendyyxnF : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () - %6 = apply %5(%2, %4) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () - dealloc_stack %2 : $*Int32 - %8 = tuple () - return %8 : $() -} - -// CHECK-LABEL: sil [noinline] @testContainerPointer : $@convention(thin) () -> Bool { -// CHECK: [[ALLOC:%.*]] = alloc_ref [stack] $TestArrayContainer -// CHECK: [[PTR:%.*]] = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.pointer -// CHECK: [[LOAD:%.*]] = load %{{.*}} : $*__ContiguousArrayStorageBase -// CHECK: [[ELTS:%.*]] = ref_tail_addr [[LOAD]] : $__ContiguousArrayStorageBase, $Int32 -// CHECK: [[ELTPTR:%.*]] = address_to_pointer [[ELTS]] : $*Int32 to $Builtin.RawPointer -// CHECK: [[UMP:%.*]] = struct $UnsafeMutablePointer ([[ELTPTR]] : $Builtin.RawPointer) -// CHECK: store [[UMP]] to [[PTR]] : $*UnsafeMutablePointer -// CHECK: [[F:%.*]] = function_ref @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool -// CHECK: apply [[F]](%{{.*}}) : $@convention(thin) (UnsafeMutableRawPointer) -> Bool -// CHECK: fix_lifetime %0 : $TestArrayContainer -// CHECK-LABEL: } // end sil function 'testContainerPointer' -sil [noinline] @testContainerPointer : $@convention(thin) () -> Bool { -bb0: - %0 = alloc_ref [stack] $TestArrayContainer - %1 = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.pointer - %2 = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.storage - %3 = integer_literal $Builtin.Int32, 42 - %4 = struct $Int32 (%3 : $Builtin.Int32) - %5 = function_ref @TestArrayContainer_append : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () - %6 = apply %5(%4, %0) : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () - %7 = struct_element_addr %2 : $*ContiguousArray, #ContiguousArray._buffer - %8 = struct_element_addr %7 : $*_ContiguousArrayBuffer, #_ContiguousArrayBuffer._storage - %9 = load %8 : $*__ContiguousArrayStorageBase - %10 = ref_tail_addr %9 : $__ContiguousArrayStorageBase, $Int32 - %11 = address_to_pointer %10 : $*Int32 to $Builtin.RawPointer - %12 = struct $UnsafeMutablePointer (%11 : $Builtin.RawPointer) - store %12 to %1 : $*UnsafeMutablePointer - %14 = address_to_pointer %1 : $*UnsafeMutablePointer to $Builtin.RawPointer - %15 = struct $UnsafeMutableRawPointer (%14 : $Builtin.RawPointer) - %16 = function_ref @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool - %17 = apply %16(%15) : $@convention(thin) (UnsafeMutableRawPointer) -> Bool - fix_lifetime %0 : $TestArrayContainer - set_deallocating %0 : $TestArrayContainer - strong_release %9 : $__ContiguousArrayStorageBase - dealloc_ref %0 : $TestArrayContainer - dealloc_ref [stack] %0 : $TestArrayContainer - return %17 : $Bool -} diff --git a/test/SILOptimizer/sil_combine_concrete_existential.swift b/test/SILOptimizer/sil_combine_concrete_existential.swift index b202d5fbd0f63..f7e49003c9333 100644 --- a/test/SILOptimizer/sil_combine_concrete_existential.swift +++ b/test/SILOptimizer/sil_combine_concrete_existential.swift @@ -101,7 +101,7 @@ struct SS: PPP { // CHECK-LABEL: } // end sil function '$s32sil_combine_concrete_existential37testWitnessReturnOptionalIndirectSelfyyF' public func testWitnessReturnOptionalIndirectSelf() { let p: PPP = S() - _ = p.returnsOptionalIndirect()?.returnsOptionalIndirect() + p.returnsOptionalIndirect()?.returnsOptionalIndirect() } //===----------------------------------------------------------------------===// From 8d712af96575df088e98e5283a82fe891c8884b9 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 18 Dec 2019 10:25:09 -0800 Subject: [PATCH 087/478] [cmake] Add support for exporting frameworks/libraries into cmake export files. I think this was just an oversight. The new cmake 3.16 seems to choke if we do not add SourceKit to the exports file since there are dependencies upon it in other swift libraries. --- .../cmake/modules/AddSwiftSourceKit.cmake | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake index 39e011519c35e..8efd59710305b 100644 --- a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake +++ b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake @@ -198,6 +198,13 @@ macro(add_sourcekit_library name) COMPONENT "${SOURCEKITLIB_INSTALL_IN_COMPONENT}") set_target_properties(${name} PROPERTIES FOLDER "SourceKit libraries") add_sourcekit_default_compiler_flags("${name}") + + swift_is_installing_component("${SOURCEKITLIB_INSTALL_IN_COMPONENT}" is_installing) + if(NOT is_installing) + set_property(GLOBAL APPEND PROPERTY SWIFT_BUILDTREE_EXPORTS ${name}) + else() + set_property(GLOBAL APPEND PROPERTY SWIFT_EXPORTS ${name}) + endif() endmacro() # Add a new SourceKit executable. @@ -324,7 +331,6 @@ macro(add_sourcekit_framework name) endif() endif() - if (SOURCEKIT_DEPLOYMENT_OS MATCHES "^macosx") set_output_directory(${name} BINARY_DIR ${SOURCEKIT_RUNTIME_OUTPUT_INTDIR} @@ -374,6 +380,14 @@ macro(add_sourcekit_framework name) COMMAND ${CMAKE_COMMAND} -E copy "${hdr}" "${framework_location}/Headers/${hdrname}") endforeach() endif() + + swift_is_installing_component("${SOURCEKITFW_INSTALL_IN_COMPONENT}" is_installing) + if(NOT is_installing) + set_property(GLOBAL APPEND PROPERTY SWIFT_BUILDTREE_EXPORTS ${name}) + else() + set_property(GLOBAL APPEND PROPERTY SWIFT_EXPORTS ${name}) + endif() + add_sourcekit_default_compiler_flags("${name}") endmacro(add_sourcekit_framework) From ee5ce77c7f9f99dcea1ccc6c342330c6f185aadf Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Fri, 6 Dec 2019 13:24:14 -0800 Subject: [PATCH 088/478] [CS] Push callee resolution into finishApply Instead of passing the resolved callee, pass the callee locator. This avoids every caller having to resolve the callee themselves. --- lib/Sema/CSApply.cpp | 61 ++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index e9e409e069f43..1cad4c76c283d 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -1057,7 +1057,7 @@ namespace { } } - return finishApply(apply, memberRef, openedType, locator); + return finishApply(apply, openedType, locator, memberLocator); } /// Convert the given literal expression via a protocol pair. @@ -1113,15 +1113,16 @@ namespace { /// \param apply The function application to finish type-checking, which /// may be a newly-built expression. /// - /// \param callee The callee for the function being applied. - /// /// \param openedType The "opened" type this expression had during /// type checking, which will be used to specialize the resulting, /// type-checked expression appropriately. /// /// \param locator The locator for the original expression. - Expr *finishApply(ApplyExpr *apply, ConcreteDeclRef callee, Type openedType, - ConstraintLocatorBuilder locator); + /// + /// \param calleeLocator The locator that identifies the apply's callee. + Expr *finishApply(ApplyExpr *apply, Type openedType, + ConstraintLocatorBuilder locator, + ConstraintLocatorBuilder calleeLocator); // Resolve `@dynamicCallable` applications. Expr *finishApplyDynamicCallable(ApplyExpr *apply, @@ -2390,17 +2391,16 @@ namespace { // If there was an argument, apply it. if (auto arg = expr->getArgument()) { - // Find the callee. Note this may be different to the member being - // referenced for things like callAsFunction. + // Get the callee locator. Note this may be different to the locator for + // the member being referenced for things like callAsFunction. auto *calleeLoc = cs.getCalleeLocator(exprLoc); - auto calleeOverload = solution.getOverloadChoice(calleeLoc); - auto callee = resolveConcreteDeclRef(calleeOverload.choice.getDecl(), - calleeLoc); + + // Build and finish the apply. ApplyExpr *apply = CallExpr::create( ctx, result, arg, expr->getArgumentLabels(), expr->getArgumentLabelLocs(), expr->hasTrailingClosure(), /*implicit=*/expr->isImplicit(), Type(), getType); - result = finishApply(apply, callee, Type(), exprLoc); + result = finishApply(apply, Type(), exprLoc, calleeLoc); // FIXME: Application could fail, because some of the solutions // are not expressible in AST (yet?), like certain tuple-to-tuple @@ -2579,9 +2579,8 @@ namespace { auto *call = new (cs.getASTContext()) DotSyntaxCallExpr(ctorRef, dotLoc, base); - return finishApply(call, callee, cs.getType(expr), - ConstraintLocatorBuilder( - cs.getConstraintLocator(expr))); + return finishApply(call, cs.getType(expr), cs.getConstraintLocator(expr), + ctorLocator); } Expr *applyMemberRefExpr(Expr *expr, Expr *base, SourceLoc dotLoc, @@ -3027,17 +3026,8 @@ namespace { Expr *visitApplyExpr(ApplyExpr *expr) { auto *calleeLoc = CalleeLocators[expr]; assert(calleeLoc); - - // Resolve the callee for the application if we have one. Note that we're - // using `resolveConcreteDeclRef` instead `solution.resolveLocatorToDecl` - // here to benefit from ExprRewriter's cache of concrete refs. - ConcreteDeclRef callee; - if (auto overload = solution.getOverloadChoiceIfAvailable(calleeLoc)) { - auto *decl = overload->choice.getDeclOrNull(); - callee = resolveConcreteDeclRef(decl, calleeLoc); - } - return finishApply(expr, callee, cs.getType(expr), - cs.getConstraintLocator(expr)); + return finishApply(expr, cs.getType(expr), cs.getConstraintLocator(expr), + calleeLoc); } Expr *visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *expr) { @@ -6559,9 +6549,9 @@ ExprRewriter::finishApplyDynamicCallable(ApplyExpr *apply, return result; } -Expr *ExprRewriter::finishApply(ApplyExpr *apply, ConcreteDeclRef callee, - Type openedType, - ConstraintLocatorBuilder locator) { +Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, + ConstraintLocatorBuilder locator, + ConstraintLocatorBuilder calleeLocator) { auto &ctx = cs.getASTContext(); auto fn = apply->getFn(); @@ -6705,6 +6695,15 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, ConcreteDeclRef callee, llvm_unreachable("Unhandled DeclTypeCheckingSemantics in switch."); }; + // Resolve the callee for the application if we have one. + ConcreteDeclRef callee; + auto *calleeLoc = cs.getConstraintLocator(calleeLocator); + auto overload = solution.getOverloadChoiceIfAvailable(calleeLoc); + if (overload) { + auto *decl = overload->choice.getDeclOrNull(); + callee = resolveConcreteDeclRef(decl, calleeLoc); + } + // Resolve `callAsFunction` and `@dynamicCallable` applications. auto applyFunctionLoc = locator.withPathElement(ConstraintLocator::ApplyFunction); @@ -6854,12 +6853,8 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, ConcreteDeclRef callee, assert(ty->getNominalOrBoundGenericNominal() || ty->is() || ty->isExistentialType() || ty->is()); - // We have the constructor. - auto choice = selected->choice; - // Consider the constructor decl reference expr 'implicit', but the // constructor call expr itself has the apply's 'implicitness'. - auto ctorRef = resolveConcreteDeclRef(choice.getDecl(), ctorLocator); Expr *declRef = buildMemberRef(fn, /*dotLoc=*/SourceLoc(), *selected, DeclNameLoc(fn->getEndLoc()), locator, ctorLocator, /*Implicit=*/true, @@ -6870,7 +6865,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, ConcreteDeclRef callee, apply->setFn(declRef); // Tail-recur to actually call the constructor. - return finishApply(apply, ctorRef, openedType, locator); + return finishApply(apply, openedType, locator, ctorLocator); } // Return the precedence-yielding parent of 'expr', along with the index of From 40d11716f78f91d972f58ff3b3019a1aaf932ca7 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Fri, 6 Dec 2019 09:44:18 -0800 Subject: [PATCH 089/478] [CS] Use custom locator element for `callAsFunction` Introduce a `ImplicitCallAsFunction` locator path element to represent an implicit member reference to `callAsFunction`. Then adjust CSApply a little to check whether it's finishing an apply for a callable type, and if so build the implicit member access. --- lib/Sema/CSApply.cpp | 29 +++++++++++++------------- lib/Sema/CSSimplify.cpp | 6 ++---- lib/Sema/ConstraintLocator.cpp | 5 +++++ lib/Sema/ConstraintLocatorPathElts.def | 3 +++ lib/Sema/ConstraintSystem.cpp | 13 +++++++----- 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1cad4c76c283d..b6640f9cd647c 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6455,13 +6455,15 @@ static bool isValidDynamicCallableMethod(FuncDecl *method, // Build a reference to a `callAsFunction` method. static Expr *buildCallAsFunctionMethodRef( ExprRewriter &rewriter, ApplyExpr *apply, SelectedOverload selected, - ConstraintLocatorBuilder applyFunctionLoc) { - auto *fn = apply->getFn(); + ConstraintLocator *calleeLoc) { + assert(calleeLoc->isLastElement()); + assert(cast(selected.choice.getDecl())->isCallAsFunctionMethod()); + // Create direct reference to `callAsFunction` method. + auto *fn = apply->getFn(); auto *declRef = rewriter.buildMemberRef( fn, /*dotLoc*/ SourceLoc(), selected, DeclNameLoc(fn->getEndLoc()), - applyFunctionLoc, applyFunctionLoc, /*implicit*/ true, - AccessSemantics::Ordinary); + calleeLoc, calleeLoc, /*implicit*/ true, AccessSemantics::Ordinary); if (!declRef) return nullptr; declRef->setImplicit(apply->isImplicit()); @@ -6704,7 +6706,15 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, callee = resolveConcreteDeclRef(decl, calleeLoc); } - // Resolve `callAsFunction` and `@dynamicCallable` applications. + // If this is an implicit call to a `callAsFunction` method, build the + // appropriate member reference. + if (cs.getType(fn)->getRValueType()->isCallableNominalType(dc)) { + fn = buildCallAsFunctionMethodRef(*this, apply, *overload, calleeLoc); + if (!fn) + return nullptr; + } + + // Resolve a `@dynamicCallable` application. auto applyFunctionLoc = locator.withPathElement(ConstraintLocator::ApplyFunction); if (auto selected = solution.getOverloadChoiceIfAvailable( @@ -6717,15 +6727,6 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, if (isValidDynamicCallableMethod(method, methodType)) return finishApplyDynamicCallable( apply, *selected, method, methodType, applyFunctionLoc); - - // If this is an implicit call to a callAsFunction method, build the - // appropriate member reference. - if (method->isCallAsFunctionMethod()) { - fn = buildCallAsFunctionMethodRef(*this, apply, *selected, - applyFunctionLoc); - if (!fn) - return nullptr; - } } } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 63003c4e61ac1..ade64007ade5b 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -7391,18 +7391,16 @@ ConstraintSystem::simplifyApplicableFnConstraint( // is true. if (desugar2->isCallableNominalType(DC)) { auto memberLoc = getConstraintLocator( - outerLocator.withPathElement(ConstraintLocator::Member)); + locator.withPathElement(ConstraintLocator::ImplicitCallAsFunction)); // Add a `callAsFunction` member constraint, binding the member type to a // type variable. auto memberTy = createTypeVariable(memberLoc, /*options=*/0); // TODO: Revisit this if `static func callAsFunction` is to be supported. // Static member constraint requires `FunctionRefKind::DoubleApply`. - // TODO: Use a custom locator element to identify this member constraint - // instead of just pointing to the function expr. addValueMemberConstraint(origLValueType2, DeclNameRef(ctx.Id_callAsFunction), memberTy, DC, FunctionRefKind::SingleApply, - /*outerAlternatives*/ {}, locator); + /*outerAlternatives*/ {}, memberLoc); // Add new applicable function constraint based on the member type // variable. addConstraint(ConstraintKind::ApplicableFunction, func1, memberTy, diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 8c4801dce58d6..cfd69c73e988c 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -114,6 +114,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::KeyPathComponentResult: case ConstraintLocator::Condition: case ConstraintLocator::DynamicCallable: + case ConstraintLocator::ImplicitCallAsFunction: return 0; case ConstraintLocator::FunctionArgument: @@ -458,6 +459,10 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { case DynamicCallable: out << "implicit call to @dynamicCallable method"; break; + + case ImplicitCallAsFunction: + out << "implicit reference to callAsFunction"; + break; } } out << ']'; diff --git a/lib/Sema/ConstraintLocatorPathElts.def b/lib/Sema/ConstraintLocatorPathElts.def index 631216485e037..5342ce5edef33 100644 --- a/lib/Sema/ConstraintLocatorPathElts.def +++ b/lib/Sema/ConstraintLocatorPathElts.def @@ -76,6 +76,9 @@ SIMPLE_LOCATOR_PATH_ELT(FunctionResult) /// FIXME: Add support for named generic arguments? CUSTOM_LOCATOR_PATH_ELT(GenericArgument) +/// An implicit reference to a 'callAsFunction' method of a nominal type. +SIMPLE_LOCATOR_PATH_ELT(ImplicitCallAsFunction) + /// Locator for a binding from an IUO disjunction choice. SIMPLE_LOCATOR_PATH_ELT(ImplicitlyUnwrappedDisjunctionChoice) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 1f1e2c484ef33..3231d1e789cce 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -506,9 +506,11 @@ ConstraintSystem::getCalleeLocator(ConstraintLocator *locator, } // Handle an apply of a nominal type which supports callAsFunction. - if (fnTy->isCallableNominalType(DC)) - return getConstraintLocator(anchor, ConstraintLocator::ApplyFunction); - + if (fnTy->isCallableNominalType(DC)) { + return getConstraintLocator(anchor, + {LocatorPathElt::ApplyFunction(), + LocatorPathElt::ImplicitCallAsFunction()}); + } return nullptr; }; @@ -3145,8 +3147,9 @@ void constraints::simplifyLocator(Expr *&anchor, case ConstraintLocator::LValueConversion: case ConstraintLocator::RValueAdjustment: case ConstraintLocator::UnresolvedMember: - // Arguments in autoclosure positions, lvalue and rvalue adjustments, and - // scalar-to-tuple conversions, and unresolved members are + case ConstraintLocator::ImplicitCallAsFunction: + // Arguments in autoclosure positions, lvalue and rvalue adjustments, + // unresolved members, and implicit callAsFunction references are // implicit. path = path.slice(1); continue; From 1d66571bc7d1c01a9d3d8ec2c319316e283fa7c2 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Tue, 17 Dec 2019 22:47:40 +0000 Subject: [PATCH 090/478] [AutoDiff upstream] `@derivative` attribute type-checking fixes. Upstream `@derivative` attribute type-checking fixes regarding derivative generic signatures with all concrete generic parameters. Cherry-picked from: - https://github.com/apple/swift/pull/28762 - https://github.com/apple/swift/pull/28772 --- lib/Sema/TypeCheckAttr.cpp | 37 ++++++++++++++---- .../Sema/derivative_attr_type_checking.swift | 38 +++++++++++++++++-- 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 600503aa5ffe8..0b18da0af1fe7 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3263,25 +3263,34 @@ static bool checkFunctionSignature( return false; } + // Map type into the required function type's generic signature, if it exists. + // This is significant when the required generic signature has same-type + // requirements while the candidate generic signature does not. + auto mapType = [&](Type type) { + if (!requiredGenSig) + return type->getCanonicalType(); + return requiredGenSig->getCanonicalTypeInContext(type); + }; + // Check that parameter types match, disregarding labels. if (required->getNumParams() != candidateFnTy->getNumParams()) return false; if (!std::equal(required->getParams().begin(), required->getParams().end(), candidateFnTy->getParams().begin(), - [](AnyFunctionType::Param x, AnyFunctionType::Param y) { - return x.getPlainType()->isEqual(y.getPlainType()); + [&](AnyFunctionType::Param x, AnyFunctionType::Param y) { + return x.getPlainType()->isEqual(mapType(y.getPlainType())); })) return false; // If required result type is not a function type, check that result types // match exactly. auto requiredResultFnTy = dyn_cast(required.getResult()); + auto candidateResultTy = mapType(candidateFnTy.getResult()); if (!requiredResultFnTy) { auto requiredResultTupleTy = dyn_cast(required.getResult()); - auto candidateResultTupleTy = - dyn_cast(candidateFnTy.getResult()); + auto candidateResultTupleTy = dyn_cast(candidateResultTy); if (!requiredResultTupleTy || !candidateResultTupleTy) - return required.getResult()->isEqual(candidateFnTy.getResult()); + return required.getResult()->isEqual(candidateResultTy); // If result types are tuple types, check that element types match, // ignoring labels. if (requiredResultTupleTy->getNumElements() != @@ -3294,7 +3303,7 @@ static bool checkFunctionSignature( } // Required result type is a function. Recurse. - return checkFunctionSignature(requiredResultFnTy, candidateFnTy.getResult()); + return checkFunctionSignature(requiredResultFnTy, candidateResultTy); }; // Returns an `AnyFunctionType` with the same `ExtInfo` as `fnType`, but with @@ -3578,8 +3587,20 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { auto resultTanType = valueResultConf.getTypeWitnessByName( valueResultType, Ctx.Id_TangentVector); + // Compute the actual differential/pullback type that we use for comparison + // with the expected type. We must canonicalize the derivative interface type + // before extracting the differential/pullback type from it, so that the + // derivative interface type generic signature is available for simplifying + // types. + CanType canActualResultType = derivativeInterfaceType->getCanonicalType(); + while (isa(canActualResultType)) { + canActualResultType = + cast(canActualResultType).getResult(); + } + CanType actualFuncEltType = + cast(canActualResultType).getElementType(1); + // Compute expected differential/pullback type. - auto funcEltType = funcResultElt.getType(); Type expectedFuncEltType; if (kind == AutoDiffDerivativeFunctionKind::JVP) { auto diffParams = map>( @@ -3595,7 +3616,7 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { expectedFuncEltType = expectedFuncEltType->mapTypeOutOfContext(); // Check if differential/pullback type matches expected type. - if (!funcEltType->isEqual(expectedFuncEltType)) { + if (!actualFuncEltType->isEqual(expectedFuncEltType)) { // Emit differential/pullback type mismatch error on attribute. diagnoseAndRemoveAttr(attr, diag::derivative_attr_result_func_type_mismatch, funcResultElt.getName(), originalAFD->getFullName()); diff --git a/test/AutoDiff/Sema/derivative_attr_type_checking.swift b/test/AutoDiff/Sema/derivative_attr_type_checking.swift index c3d7f9e9613bf..bd1b055f12615 100644 --- a/test/AutoDiff/Sema/derivative_attr_type_checking.swift +++ b/test/AutoDiff/Sema/derivative_attr_type_checking.swift @@ -276,7 +276,7 @@ func req1(_ x: T) -> T { return x } @derivative(of: req1) -func vjpReq1(_ x: T) -> ( +func vjpExtraConformanceConstraint(_ x: T) -> ( value: T, pullback: (T.TangentVector) -> T.TangentVector ) { return (x, { $0 }) @@ -286,12 +286,42 @@ func req2(_ x: T, _ y: U) -> T { return x } @derivative(of: req2) -func vjpReq2(_ x: T, _ y: U) - -> (value: T, pullback: (T) -> (T, U)) -where T == T.TangentVector, U == U.TangentVector, T: CustomStringConvertible { +func vjpExtraConformanceConstraints( _ x: T, _ y: U) -> ( + value: T, pullback: (T) -> (T, U) +) where T == T.TangentVector, U == U.TangentVector, T: CustomStringConvertible { return (x, { ($0, .zero) }) } +// Test `@derivative` declaration with extra same-type requirements. +func req3(_ x: T) -> T { + return x +} +@derivative(of: req3) +func vjpSameTypeRequirementsGenericParametersAllConcrete(_ x: T) -> ( + value: T, pullback: (T.TangentVector) -> T.TangentVector +) where T: Differentiable, T.TangentVector == Float { + return (x, { $0 }) +} + +struct Wrapper: Equatable { + var x: T + init(_ x: T) { self.x = x } +} +extension Wrapper: AdditiveArithmetic where T: AdditiveArithmetic { + static var zero: Self { .init(.zero) } + static func + (lhs: Self, rhs: Self) -> Self { .init(lhs.x + rhs.x) } + static func - (lhs: Self, rhs: Self) -> Self { .init(lhs.x - rhs.x) } +} +extension Wrapper: Differentiable where T: Differentiable, T == T.TangentVector { + typealias TangentVector = Wrapper +} +extension Wrapper where T: Differentiable, T == T.TangentVector { + @derivative(of: init(_:)) + static func vjpInit(_ x: T) -> (value: Self, pullback: (Wrapper.TangentVector) -> (T)) { + fatalError() + } +} + // Test class methods. class Super { From 935686460ccb9e19284744866ab00b59f4cd35f2 Mon Sep 17 00:00:00 2001 From: Ravi Kandhadai Date: Fri, 22 Nov 2019 15:04:30 -0800 Subject: [PATCH 091/478] [SIL Optimization] Create a new utility InstructionDeleter to delete instructions and eliminate dead code. This is meant to be a replacement for the utility: recursivelyDeleteTriviallyDeadInstructions. The new utility performs more aggresive dead-code elimination for ownership SIL. This patch also migrates most non-force-delete uses of recursivelyDeleteTriviallyDeadInstructions to the new utility. and migrates one force-delete use of recursivelyDeleteTriviallyDeadInstructions (in IRGenPrepare) to use the new utility. --- include/swift/AST/Builtins.def | 6 +- include/swift/AST/DiagnosticsSIL.def | 3 + include/swift/SILOptimizer/Utils/ConstExpr.h | 2 + .../swift/SILOptimizer/Utils/InstOptUtils.h | 99 +++++- lib/SILOptimizer/LoopTransforms/LICM.cpp | 2 +- .../Mandatory/DefiniteInitialization.cpp | 7 +- lib/SILOptimizer/Mandatory/IRGenPrepare.cpp | 9 +- .../Mandatory/OSLogOptimization.cpp | 96 ++++-- .../Mandatory/PredictableMemOpt.cpp | 6 +- lib/SILOptimizer/Mandatory/SILGenCleanup.cpp | 14 +- .../SILCombiner/SILCombinerApplyVisitors.cpp | 6 +- lib/SILOptimizer/Transforms/SILCodeMotion.cpp | 2 +- .../ConstantEvaluableSubsetChecker.cpp | 5 +- lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp | 8 +- lib/SILOptimizer/Utils/CFGOptUtils.cpp | 2 +- lib/SILOptimizer/Utils/ConstExpr.cpp | 7 +- lib/SILOptimizer/Utils/ConstantFolding.cpp | 36 +-- lib/SILOptimizer/Utils/InstOptUtils.cpp | 283 +++++++++++++++++- .../OSLogPrototypeCompileDiagnostics.swift | 6 +- .../OSLogPrototypeCompileTest.sil | 131 ++++++-- .../OSLogPrototypeCompileTest.swift | 29 ++ .../access_marker_mandatory.swift | 2 - .../predictable_memopt_ownership.sil | 5 +- 23 files changed, 658 insertions(+), 108 deletions(-) diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 03809bcef7008..4b5cd1888a30a 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -641,8 +641,10 @@ BUILTIN_MISC_OPERATION(PoundAssert, "poundAssert", "", Special) /// globalStringTablePointer has type String -> Builtin.RawPointer. /// It returns an immortal, global string table pointer for strings constructed -/// from string literals. -BUILTIN_MISC_OPERATION_WITH_SILGEN(GlobalStringTablePointer, "globalStringTablePointer", "", Special) +/// from string literals. We consider it effects as readnone meaning that it +/// does not read any memory (note that even though it reads from a string, it +/// is a pure value and therefore we can consider it as readnone). +BUILTIN_MISC_OPERATION_WITH_SILGEN(GlobalStringTablePointer, "globalStringTablePointer", "n", Special) #undef BUILTIN_MISC_OPERATION_WITH_SILGEN diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index 206b8b0d8a820..e46a33d263bc3 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -528,6 +528,9 @@ ERROR(oslog_non_constant_interpolation, none, "'OSLogInterpolation' instance " ERROR(oslog_property_not_constant, none, "'OSLogInterpolation.%0' is not a " "constant", (StringRef)) +ERROR(oslog_message_alive_after_opts, none, "OSLogMessage instance must not " + "be explicitly created and must be deletable", ()) + ERROR(global_string_pointer_on_non_constant, none, "globalStringTablePointer " "builtin must used only on string literals", ()) diff --git a/include/swift/SILOptimizer/Utils/ConstExpr.h b/include/swift/SILOptimizer/Utils/ConstExpr.h index 85f01ebbd0964..aa2cf66a086c7 100644 --- a/include/swift/SILOptimizer/Utils/ConstExpr.h +++ b/include/swift/SILOptimizer/Utils/ConstExpr.h @@ -207,6 +207,8 @@ class ConstExprStepEvaluator { void dumpState(); }; +bool hasConstantEvaluableAnnotation(SILFunction *fun); + bool isConstantEvaluable(SILFunction *fun); /// Return true if and only if the given function \p fun is specially modeled diff --git a/include/swift/SILOptimizer/Utils/InstOptUtils.h b/include/swift/SILOptimizer/Utils/InstOptUtils.h index b1ca5dfccb8f0..2abe532c36b5e 100644 --- a/include/swift/SILOptimizer/Utils/InstOptUtils.h +++ b/include/swift/SILOptimizer/Utils/InstOptUtils.h @@ -56,8 +56,101 @@ NullablePtr createIncrementBefore(SILValue ptr, NullablePtr createDecrementBefore(SILValue ptr, SILInstruction *insertpt); +/// A utility for deleting one or more instructions belonging to a function, and +/// cleaning up any dead code resulting from deleting those instructions. Use +/// this utility instead of +/// \c recursivelyDeleteTriviallyDeadInstruction. +class InstructionDeleter { +private: + // A set vector of instructions that are found to be dead. The ordering + // of instructions in this set is important as when a dead instruction is + // removed, new instructions will be generated to fix the lifetime of the + // instruction's operands. This has to be deterministic. + SmallSetVector deadInstructions; + + void deleteInstruction(SILInstruction *inst, + llvm::function_ref callback, + bool fixOperandLifetimes); + +public: + InstructionDeleter() {} + + /// If the instruction \p inst is dead, record it so that it can be cleaned + /// up. + void trackIfDead(SILInstruction *inst); + + /// Delete the instruction \p inst and record instructions that may become + /// dead because of the removal of \c inst. This function will add necessary + /// ownership instructions to fix the lifetimes of the operands of \c inst to + /// compensate for its deletion. This function will not clean up dead code + /// resulting from the instruction's removal. To do so, invoke the method \c + /// cleanupDeadCode of this instance, once the SIL of the contaning function + /// is made consistent. + /// + /// \pre the function containing \c inst must be using ownership SIL. + /// \pre the instruction to be deleted must not have any use other than + /// incidental uses. + /// + /// \param callback a callback called whenever an instruction + /// is deleted. + void forceDeleteAndFixLifetimes( + SILInstruction *inst, + llvm::function_ref callback = + [](SILInstruction *) {}); + + /// Delete the instruction \p inst and record instructions that may become + /// dead because of the removal of \c inst. If in ownership SIL, use the + /// \c forceDeleteAndFixLifetimes function instead, unless under special + /// circumstances where the client must handle fixing lifetimes of the + /// operands of the deleted instructions. This function will not fix the + /// lifetimes of the operands of \c inst once it is deleted. This function + /// will not clean up dead code resulting from the instruction's removal. To + /// do so, invoke the method \c cleanupDeadCode of this instance, once the SIL + /// of the contaning function is made consistent. + /// + /// \pre the instruction to be deleted must not have any use other than + /// incidental uses. + /// + /// \param callback a callback called whenever an instruction + /// is deleted. + void forceDelete( + SILInstruction *inst, + llvm::function_ref callback = + [](SILInstruction *) {}); + + /// Clean up dead instructions that are tracked by this instance and all + /// instructions that transitively become dead. + /// + /// \pre the function contaning dead instructions must be consistent (i.e., no + /// under or over releases). Note that if \c forceDelete call leaves the + /// function body in an inconsistent state, it needs to be made consistent + /// before this method is invoked. + /// + /// \param callback a callback called whenever an instruction is deleted. + void + cleanUpDeadInstructions(llvm::function_ref callback = + [](SILInstruction *) {}); +}; + +/// If \c inst is dead, delete it and recursively eliminate all code that +/// becomes dead because of that. If more than one instruction must +/// be checked/deleted use the \c InstructionDeleter utility. +/// +/// This function will add necessary compensation code to fix the lifetimes of +/// the operands of the deleted instructions. +/// +/// \pre the SIL function containing the instruction is assumed to be +/// consistent, i.e., does not have under or over releases. +/// +/// \param callback a callback called whenever an instruction is deleted. +void eliminateDeadInstruction( + SILInstruction *inst, llvm::function_ref callback = + [](SILInstruction *) {}); + /// For each of the given instructions, if they are dead delete them -/// along with their dead operands. +/// along with their dead operands. Note this utility must be phased out and +/// replaced by \c eliminateDeadInstruction and +/// \c InstructionDeleter utilities. /// /// \param inst The ArrayRef of instructions to be deleted. /// \param force If Force is set, don't check if the top level instructions @@ -69,7 +162,9 @@ void recursivelyDeleteTriviallyDeadInstructions( }); /// If the given instruction is dead, delete it along with its dead -/// operands. +/// operands. Note this utility must be phased out and replaced by +/// \c eliminateDeadInstruction and +/// \c InstructionDeleter utilities. /// /// \param inst The instruction to be deleted. /// \param force If Force is set, don't check if the top level instruction is diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index 3f9380540c1be..53650a6bf37c8 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -991,7 +991,7 @@ void LoopTreeOptimization::hoistLoadsAndStores(SILValue addr, SILLoop *loop, Ins } // In case the value is only stored but never loaded in the loop. - recursivelyDeleteTriviallyDeadInstructions(initialLoad); + eliminateDeadInstruction(initialLoad); } bool LoopTreeOptimization::hoistAllLoadsAndStores(SILLoop *loop) { diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 531d68083f6be..29f8bc8785d1c 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -2010,8 +2010,13 @@ void LifetimeChecker::deleteDeadRelease(unsigned ReleaseID) { SILInstruction *Release = Destroys[ReleaseID]; if (isa(Release)) { SILValue Addr = Release->getOperand(0); - if (auto *AddrI = Addr->getDefiningInstruction()) + if (auto *AddrI = Addr->getDefiningInstruction()) { + // FIXME: AddrI will not be deleted (nor its operands) when Release is + // still using AddrI's result. Fix this, and migrate to using + // InstructionDeleter utility instead of + // recursivelyDeadTriviallyDeadInstructions. recursivelyDeleteTriviallyDeadInstructions(AddrI); + } } Release->eraseFromParent(); Destroys[ReleaseID] = nullptr; diff --git a/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp b/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp index 6da647c694476..c1c98ee765284 100644 --- a/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp +++ b/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp @@ -56,12 +56,17 @@ static bool cleanFunction(SILFunction &fn) { LLVM_FALLTHROUGH; } case BuiltinValueKind::PoundAssert: - case BuiltinValueKind::StaticReport: + case BuiltinValueKind::StaticReport: { // The call to the builtin should get removed before we reach // IRGen. - recursivelyDeleteTriviallyDeadInstructions(bi, /* Force */ true); + InstructionDeleter deleter; + deleter.forceDelete(bi); + // StaticReport only takes trivial operands, and therefore doesn't + // require fixing the lifetime of its operands. + deleter.cleanUpDeadInstructions(); madeChange = true; break; + } default: break; } diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index 71493c2422ed0..3af084a808556 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -262,8 +262,7 @@ static bool shouldAttemptEvaluation(SILInstruction *inst) { SILFunction *calleeFun = apply->getCalleeFunction(); if (!calleeFun) return false; - return isKnownConstantEvaluableFunction(calleeFun) || - isConstantEvaluable(calleeFun); + return isConstantEvaluable(calleeFun); } /// Skip or evaluate the given instruction based on the evaluation policy and @@ -780,20 +779,11 @@ static SILValue emitCodeForSymbolicValue(SymbolicValue symVal, } } -/// Collect the end points of the instructions that are data dependent on \c -/// value. A instruction is data dependent on \c value if its result may -/// transitively depends on \c value. Note that data dependencies through -/// addresses are not tracked by this function. -/// -/// \param value SILValue that is not an address. -/// \param fun SILFunction that defines \c value. -/// \param endUsers buffer for storing the found end points of the data -/// dependence chain. -static void -getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun, - SmallVectorImpl &endUsers) { - assert(!value->getType().isAddress()); - +/// Given a SILValue \p value, compute the set of transitive users of the value +/// (excluding value itself) by following the use-def chain starting at value. +/// Note that this function does not follow use-def chains though branches. +static void getTransitiveUsers(SILValue value, + SmallVectorImpl &users) { // Collect the instructions that are data dependent on the value using a // fix point iteration. SmallPtrSet visitedUsers; @@ -810,17 +800,40 @@ getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun, llvm::copy(user->getResults(), std::back_inserter(worklist)); } } - // At this point, visitedUsers have all the transitive, data-dependent uses. - // Compute the lifetime frontier of all the uses which are the instructions - // following the last uses. Every exit from the last uses will have a - // lifetime frontier. + users.append(visitedUsers.begin(), visitedUsers.end()); +} + +/// Collect the end points of the instructions that are data dependent on \c +/// value. A instruction is data dependent on \c value if its result may +/// transitively depends on \c value. Note that data dependencies through +/// addresses are not tracked by this function. +/// +/// \param value SILValue that is not an address. +/// \param fun SILFunction that defines \c value. +/// \param endUsers buffer for storing the found end points of the data +/// dependence chain. +static void +getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun, + SmallVectorImpl &endUsers) { + assert(!value->getType().isAddress()); + + SmallVector transitiveUsers; + // Get transitive users of value, ignoring use-def chain going through + // branches. These transitive users define the end points of the constant + // evaluation. Igoring use-def chains through branches causes constant + // evaluation to miss some constant folding opportunities. This can be + // relaxed in the future, if necessary. + getTransitiveUsers(value, transitiveUsers); + + // Compute the lifetime frontier of all the transitive uses which are the + // instructions following the last uses. Every exit from the last uses will + // have a lifetime frontier. SILInstruction *valueDefinition = value->getDefiningInstruction(); SILInstruction *def = valueDefinition ? valueDefinition : &(value->getParentBlock()->front()); ValueLifetimeAnalysis lifetimeAnalysis = - ValueLifetimeAnalysis(def, SmallVector( - visitedUsers.begin(), visitedUsers.end())); + ValueLifetimeAnalysis(def, transitiveUsers); ValueLifetimeAnalysis::Frontier frontier; bool hasCriticlEdges = lifetimeAnalysis.computeFrontier( frontier, ValueLifetimeAnalysis::DontModifyCFG); @@ -936,7 +949,7 @@ static void replaceAllUsesAndFixLifetimes(SILValue foldedVal, static void substituteConstants(FoldState &foldState) { ConstExprStepEvaluator &evaluator = foldState.constantEvaluator; // Instructions that are possibly dead since their results are folded. - SmallVector possiblyDeadInsts; + SmallVector possiblyDeadInsts; for (SILValue constantSILValue : foldState.getConstantSILValues()) { SymbolicValue constantSymbolicVal = @@ -980,13 +993,11 @@ static void substituteConstants(FoldState &foldState) { replaceAllUsesAndFixLifetimes(foldedSILVal, constantSILValue, fun); possiblyDeadInsts.push_back(definingInst); } - recursivelyDeleteTriviallyDeadInstructions(possiblyDeadInsts, /*force*/ false, - [&](SILInstruction *DeadI) {}); } /// Check whether OSLogMessage and OSLogInterpolation instances and all their /// stored properties are constants. If not, it indicates errors that are due to -/// incorrect implementation OSLogMessage either in the overlay or in the +/// incorrect implementation of OSLogMessage either in the overlay or in the /// extensions created by users. Detect and emit diagnostics for such errors. /// The diagnostics here are for os log library authors. static bool checkOSLogMessageIsConstant(SingleValueInstruction *osLogMessage, @@ -1048,6 +1059,37 @@ static bool checkOSLogMessageIsConstant(SingleValueInstruction *osLogMessage, return errorDetected; } +/// Try to dead-code eliminate the OSLogMessage instance \c oslogMessage passed +/// to the os log call and clean up its dependencies. If the instance cannot be +/// eliminated, it implies that either the instance is not auto-generated or the +/// implementation of the os log overlay is incorrect. Therefore emit +/// diagnostics in such cases. +static void tryEliminateOSLogMessage(SingleValueInstruction *oslogMessage) { + // Collect the set of root instructions that could be dead due to constant + // folding. These include the oslogMessage initialzer call and its transitive + // users. + SmallVector oslogMessageUsers; + getTransitiveUsers(oslogMessage, oslogMessageUsers); + + InstructionDeleter deleter; + for (SILInstruction *user : oslogMessageUsers) + deleter.trackIfDead(user); + deleter.trackIfDead(oslogMessage); + + bool isOSLogMessageDead = false; + deleter.cleanUpDeadInstructions([&](SILInstruction *deadInst) { + if (deadInst == oslogMessage) + isOSLogMessageDead = true; + }); + // At this point, the OSLogMessage instance must be deleted if + // the overlay implementation (or its extensions by users) is correct. + if (!isOSLogMessageDead) { + SILFunction *fun = oslogMessage->getFunction(); + diagnose(fun->getASTContext(), oslogMessage->getLoc().getSourceLoc(), + diag::oslog_message_alive_after_opts); + } +} + /// Constant evaluate instructions starting from 'start' and fold the uses /// of the value 'oslogMessage'. Stop when oslogMessageValue is released. static bool constantFold(SILInstruction *start, @@ -1076,6 +1118,8 @@ static bool constantFold(SILInstruction *start, return false; substituteConstants(state); + + tryEliminateOSLogMessage(oslogMessage); return true; } diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 4ad37b981c35a..d9c84565225f9 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -2118,7 +2118,7 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) { SILValue addr = li->getOperand(); li->eraseFromParent(); if (auto *addrI = addr->getDefiningInstruction()) - recursivelyDeleteTriviallyDeadInstructions(addrI); + eliminateDeadInstruction(addrI); return true; } @@ -2139,7 +2139,7 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) { SILValue addr = li->getOperand(); li->eraseFromParent(); if (auto *addrI = addr->getDefiningInstruction()) - recursivelyDeleteTriviallyDeadInstructions(addrI); + eliminateDeadInstruction(addrI); return true; } @@ -2242,7 +2242,7 @@ bool AllocOptimize::promoteLoadBorrow(LoadBorrowInst *lbi) { SILValue addr = lbi->getOperand(); lbi->eraseFromParent(); if (auto *addrI = addr->getDefiningInstruction()) - recursivelyDeleteTriviallyDeadInstructions(addrI); + eliminateDeadInstruction(addrI); return true; } diff --git a/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp b/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp index eb4c12f4d990f..e13226fd4d6c5 100644 --- a/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp +++ b/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp @@ -52,14 +52,12 @@ struct SILGenCanonicalize final : CanonicalizeInstruction { SILInstruction *deadOperInst = *deadOperands.begin(); // Make sure at least the first instruction is removed from the set. deadOperands.erase(deadOperInst); - recursivelyDeleteTriviallyDeadInstructions( - deadOperInst, false, - [&](SILInstruction *deadInst) { - LLVM_DEBUG(llvm::dbgs() << "Trivially dead: " << *deadInst); - if (nextII == deadInst->getIterator()) - ++nextII; - deadOperands.erase(deadInst); - }); + eliminateDeadInstruction(deadOperInst, [&](SILInstruction *deadInst) { + LLVM_DEBUG(llvm::dbgs() << "Trivially dead: " << *deadInst); + if (nextII == deadInst->getIterator()) + ++nextII; + deadOperands.erase(deadInst); + }); } return nextII; } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 90607e637f7d9..3aac12abe25d9 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -919,7 +919,11 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( // apply. Since the apply was never rewritten, if they aren't removed here, // they will be removed later as dead when visited by SILCombine, causing // SILCombine to loop infinitely, creating and destroying the casts. - recursivelyDeleteTriviallyDeadInstructions(*Builder.getTrackingList()); + InstructionDeleter deleter; + for (SILInstruction *inst : *Builder.getTrackingList()) { + deleter.trackIfDead(inst); + } + deleter.cleanUpDeadInstructions(); Builder.getTrackingList()->clear(); return nullptr; } diff --git a/lib/SILOptimizer/Transforms/SILCodeMotion.cpp b/lib/SILOptimizer/Transforms/SILCodeMotion.cpp index d7bae9dd2a835..f688bc6921507 100644 --- a/lib/SILOptimizer/Transforms/SILCodeMotion.cpp +++ b/lib/SILOptimizer/Transforms/SILCodeMotion.cpp @@ -1261,7 +1261,7 @@ static bool sinkArgument(EnumCaseDataflowContext &Context, SILBasicBlock *BB, un TI->setOperand(ArgNum, CloneInst->getOperand(*DifferentOperandIndex)); // Now delete the clone as we only needed it operand. if (CloneInst != FSI) - recursivelyDeleteTriviallyDeadInstructions(CloneInst); + eliminateDeadInstruction(CloneInst); ++CloneIt; } assert(CloneIt == Clones.end() && "Clone/pred mismatch"); diff --git a/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp b/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp index 0050c37e36ee0..9d91a3b0b49bd 100644 --- a/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp +++ b/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp @@ -145,7 +145,8 @@ class ConstantEvaluableSubsetChecker : public SILModuleTransform { evaluatedFunctions.insert(callee); SILModule &calleeModule = callee->getModule(); - if (callee->isAvailableExternally() && isConstantEvaluable(callee) && + if (callee->isAvailableExternally() && + hasConstantEvaluableAnnotation(callee) && callee->getOptimizationMode() != OptimizationMode::NoOptimization) { diagnose(calleeModule.getASTContext(), callee->getLocation().getSourceLoc(), @@ -161,7 +162,7 @@ class ConstantEvaluableSubsetChecker : public SILModuleTransform { for (SILFunction &fun : *module) { // Record functions annotated as constant evaluable. - if (isConstantEvaluable(&fun)) { + if (hasConstantEvaluableAnnotation(&fun)) { constantEvaluableFunctions.insert(&fun); continue; } diff --git a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp index 526e2e6f07870..e3b08f8c9e458 100644 --- a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp +++ b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp @@ -140,6 +140,7 @@ void BasicBlockCloner::sinkAddressProjections() { // Because the address projections chains will be disjoint (an instruction // in one chain cannot use the result of an instruction in another chain), // the order they are sunk does not matter. + InstructionDeleter deleter; for (auto ii = origBB->begin(), ie = origBB->end(); ii != ie;) { bool canSink = sinkProj.analyzeAddressProjections(&*ii); (void)canSink; @@ -150,13 +151,10 @@ void BasicBlockCloner::sinkAddressProjections() { && "canCloneInstruction should catch this."); auto nextII = std::next(ii); - recursivelyDeleteTriviallyDeadInstructions( - &*ii, false, [&nextII](SILInstruction *deadInst) { - if (deadInst->getIterator() == nextII) - ++nextII; - }); + deleter.trackIfDead(&*ii); ii = nextII; } + deleter.cleanUpDeadInstructions(); } // Populate 'projections' with the chain of address projections leading diff --git a/lib/SILOptimizer/Utils/CFGOptUtils.cpp b/lib/SILOptimizer/Utils/CFGOptUtils.cpp index 13c1e0a1e9c02..3c31c4e16236c 100644 --- a/lib/SILOptimizer/Utils/CFGOptUtils.cpp +++ b/lib/SILOptimizer/Utils/CFGOptUtils.cpp @@ -86,7 +86,7 @@ deleteTriviallyDeadOperandsOfDeadArgument(MutableArrayRef termOperands, if (!i) return; op.set(SILUndef::get(op.get()->getType(), *i->getFunction())); - recursivelyDeleteTriviallyDeadInstructions(i); + eliminateDeadInstruction(i); } // Our implementation assumes that our caller is attempting to remove a dead diff --git a/lib/SILOptimizer/Utils/ConstExpr.cpp b/lib/SILOptimizer/Utils/ConstExpr.cpp index fabe4b89523cb..0d3d72b081b8a 100644 --- a/lib/SILOptimizer/Utils/ConstExpr.cpp +++ b/lib/SILOptimizer/Utils/ConstExpr.cpp @@ -2129,7 +2129,12 @@ bool swift::isKnownConstantEvaluableFunction(SILFunction *fun) { return classifyFunction(fun).hasValue(); } -bool swift::isConstantEvaluable(SILFunction *fun) { +bool swift::hasConstantEvaluableAnnotation(SILFunction *fun) { assert(fun && "fun should not be nullptr"); return fun->hasSemanticsAttr("constant_evaluable"); } + +bool swift::isConstantEvaluable(SILFunction *fun) { + return hasConstantEvaluableAnnotation(fun) || + isKnownConstantEvaluableFunction(fun); +} diff --git a/lib/SILOptimizer/Utils/ConstantFolding.cpp b/lib/SILOptimizer/Utils/ConstantFolding.cpp index 5ca34dfc33995..af43e75cf71f2 100644 --- a/lib/SILOptimizer/Utils/ConstantFolding.cpp +++ b/lib/SILOptimizer/Utils/ConstantFolding.cpp @@ -1696,7 +1696,6 @@ ConstantFolder::processWorkList() { // This is used to avoid duplicate error reporting in case we reach the same // instruction from different entry points in the WorkList. llvm::DenseSet ErrorSet; - llvm::SetVector FoldedUsers; CastOptimizer CastOpt(FuncBuilder, nullptr /*SILBuilderContext*/, /* replaceValueUsesAction */ [&](SILValue oldValue, SILValue newValue) { @@ -1751,7 +1750,7 @@ ConstantFolder::processWorkList() { // Schedule users for constant folding. WorkList.insert(AssertConfInt); // Delete the call. - recursivelyDeleteTriviallyDeadInstructions(BI); + eliminateDeadInstruction(BI); InvalidateInstructions = true; continue; @@ -1819,9 +1818,8 @@ ConstantFolder::processWorkList() { if (constantFoldGlobalStringTablePointerBuiltin(cast(I), EnableDiagnostics)) { // Here, the bulitin instruction got folded, so clean it up. - recursivelyDeleteTriviallyDeadInstructions( - I, /*force*/ true, - [&](SILInstruction *DeadI) { WorkList.remove(DeadI); }); + eliminateDeadInstruction( + I, [&](SILInstruction *DeadI) { WorkList.remove(DeadI); }); InvalidateInstructions = true; } continue; @@ -1873,7 +1871,7 @@ ConstantFolder::processWorkList() { } // Go through all users of the constant and try to fold them. - FoldedUsers.clear(); + InstructionDeleter deleter; for (auto Result : I->getResults()) { for (auto *Use : Result->getUses()) { SILInstruction *User = Use->getUser(); @@ -1897,7 +1895,7 @@ ConstantFolder::processWorkList() { // this as part of the constant folding logic, because there is no value // they can produce (other than empty tuple, which is wasteful). if (isa(User)) - FoldedUsers.insert(User); + deleter.trackIfDead(User); // See if we have an instruction that is read none and has a stateless // inverse. If we do, add it to the worklist so we can check its users @@ -1963,10 +1961,7 @@ ConstantFolder::processWorkList() { if (C->getDefiningInstruction() == User) continue; - // Ok, we have succeeded. Add user to the FoldedUsers list and perform - // the necessary cleanups, RAUWs, etc. - FoldedUsers.insert(User); - InvalidateInstructions = true; + // Ok, we have succeeded. ++NumInstFolded; // We were able to fold, so all users should use the new folded @@ -2005,11 +2000,16 @@ ConstantFolder::processWorkList() { // In contrast, if we realize that RAUWing %3 does nothing and skip // it, we exit the worklist as expected. SILValue r = User->getResult(Index); - if (r->use_empty()) + if (r->use_empty()) { + deleter.trackIfDead(User); continue; + } // Otherwise, do the RAUW. User->getResult(Index)->replaceAllUsesWith(C); + // Record the user if it is dead to perform the necessary cleanups + // later. + deleter.trackIfDead(User); // The new constant could be further folded now, add it to the // worklist. @@ -2018,18 +2018,12 @@ ConstantFolder::processWorkList() { } } } - // Eagerly DCE. We do this after visiting all users to ensure we don't // invalidate the uses iterator. - ArrayRef UserArray = FoldedUsers.getArrayRef(); - if (!UserArray.empty()) { + deleter.cleanUpDeadInstructions([&](SILInstruction *DeadI) { + WorkList.remove(DeadI); InvalidateInstructions = true; - } - - recursivelyDeleteTriviallyDeadInstructions(UserArray, false, - [&](SILInstruction *DeadI) { - WorkList.remove(DeadI); - }); + }); } // TODO: refactor this code outside of the method. Passes should not merge diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 4515a5230ad94..f97e107514edb 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -12,8 +12,8 @@ #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/AST/GenericSignature.h" -#include "swift/AST/SubstitutionMap.h" #include "swift/AST/SemanticAttrs.h" +#include "swift/AST/SubstitutionMap.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/DebugUtils.h" #include "swift/SIL/DynamicCasts.h" @@ -27,6 +27,7 @@ #include "swift/SILOptimizer/Analysis/Analysis.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" +#include "swift/SILOptimizer/Utils/ConstExpr.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringSwitch.h" @@ -171,9 +172,283 @@ bool swift::isIntermediateRelease(SILInstruction *inst, return false; } +static bool hasOnlyEndOfScopeOrDestroyUses(SILInstruction *inst) { + for (SILValue result : inst->getResults()) { + for (Operand *use : result->getUses()) { + SILInstruction *user = use->getUser(); + bool isDebugUser = user->isDebugInstruction(); + if (!isa(user) && !isEndOfScopeMarker(user) && + !isDebugUser) + return false; + // Include debug uses only in Onone mode. + if (isDebugUser && inst->getFunction()->getEffectiveOptimizationMode() <= + OptimizationMode::NoOptimization) + return false; + } + } + return true; +} + +/// Return true iff the \p applySite calls a constant evaluable function and if +/// it is read-only which implies the following: +/// (1) The call does not write into any memory location. +/// (2) The call may destroy owned parameters i.e., consume them. +/// (3) The call does not throw or exit the program. +static bool isReadOnlyConstantEvaluableCall(FullApplySite applySite) { + assert(applySite); + SILFunction *callee = applySite.getCalleeFunction(); + if (!callee || !isConstantEvaluable(callee)) { + return false; + } + // Here all effects of the call is restricted to its indirect results, which + // must have value semantics. If there are no indirect results, the call must + // be read-only, except for consuming its operands. + return applySite.getNumIndirectSILResults() == 0; +} + +/// A scope-affecting instruction is an instruction which may end the scope of +/// its operand or may produce scoped results that require cleaning up. E.g. +/// begin_borrow, begin_access, copy_value, a call that produces a owned value +/// are scoped instructions. The scope of the results of the first two +/// instructions end with an end_borrow/acess instruction, while those of the +/// latter two end with a consuming operation like destroy_value instruction. +/// These instruction may also end the scope of its operand e.g. a call could +/// consume owned arguments thereby ending its scope. Dead-code eliminating a +/// scope-affecting instruction requires fixing the lifetime of the non-trivial +/// operands of the instruction and requires cleaning up the end-of-scope uses +/// of non-trivial results. +/// +/// \param inst instruction that checked for liveness. +static bool isScopeAffectingInstructionDead(SILInstruction *inst) { + SILFunction *fun = inst->getFunction(); + assert(fun && "Instruction has no function."); + // Only support ownership SIL for scoped instructions. + if (!fun->hasOwnership()) { + return false; + } + // If the instruction has any use other than end of scope use or destroy_value + // use, bail out. + if (!hasOnlyEndOfScopeOrDestroyUses(inst)) { + return false; + } + // If inst is a copy or beginning of scope, inst is dead, since we know that + // it is used only in a destroy_value or end-of-scope instruction. + if (getSingleValueCopyOrCast(inst)) + return true; + + switch (inst->getKind()) { + case SILInstructionKind::LoadBorrowInst: { + // A load_borrow only used in an end_borrow is dead. + return true; + } + case SILInstructionKind::LoadInst: { + LoadOwnershipQualifier loadOwnershipQual = + cast(inst)->getOwnershipQualifier(); + // If the load creates a copy, it is dead, since we know that if at all it + // is used, it is only in a destroy_value instruction. + return (loadOwnershipQual == LoadOwnershipQualifier::Copy || + loadOwnershipQual == LoadOwnershipQualifier::Trivial); + // TODO: we can handle load [take] but we would have to know that the + // operand has been consumed. Note that OperandOwnershipKind map does not + // say this for load. + } + case SILInstructionKind::PartialApplyInst: { + // Partial applies that are only used in destroys cannot have any effect on + // the program state, provided the values they capture are explicitly + // destroyed. + return true; + } + case SILInstructionKind::StructInst: + case SILInstructionKind::EnumInst: + case SILInstructionKind::TupleInst: + case SILInstructionKind::ConvertFunctionInst: + case SILInstructionKind::DestructureStructInst: + case SILInstructionKind::DestructureTupleInst: { + // All these ownership forwarding instructions that are only used in + // destroys are dead provided the values they consume are destroyed + // explicitly. + return true; + } + case SILInstructionKind::ApplyInst: { + // Given a call to a function annotated as constant_evaluable, the call + // will be removed as long as the following holds: + // 1. If the call destroys its arguments: when removing the call, a destroy + // of each argument is added. + // 2. If the call returns a dead value i.e., a value that is only + // destroyed: both the call and corresponding destroy will be removed. + // Note that a value returned by a constant evaluable function must either + // contain constant pure values (trivial instances or array/string + // literals), or the arguments passed to the call. Given that its arguments + // will be destroyed explicitly, it is okay to remove the destroys of the + // return value of a constant evaluable function. + FullApplySite applySite(cast(inst)); + return isReadOnlyConstantEvaluableCall(applySite); + } + default: { + return false; + } + } +} + +void InstructionDeleter::trackIfDead(SILInstruction *inst) { + if (isInstructionTriviallyDead(inst) || + isScopeAffectingInstructionDead(inst)) { + assert(!isIncidentalUse(inst) && !isa(inst) && + "Incidental uses cannot be removed in isolation. " + "They would be removed iff the operand is dead"); + deadInstructions.insert(inst); + } +} + +/// Given an \p operand that belongs to an instruction that will be removed, +/// destroy the operand just before the instruction, if the instruction consumes +/// \p operand. This function will result in a double consume, which is expected +/// to be resolved when the caller deletes the original instruction. This +/// function works only on ownership SIL. +static void destroyConsumedOperandOfDeadInst(Operand &operand) { + assert(operand.get() && operand.getUser()); + SILInstruction *deadInst = operand.getUser(); + SILFunction *fun = deadInst->getFunction(); + assert(fun->hasOwnership()); + + SILValue operandValue = operand.get(); + if (operandValue->getType().isTrivial(*fun)) + return; + // A scope ending instruction cannot be deleted in isolation without removing + // the instruction defining its operand as well. + assert(!isEndOfScopeMarker(deadInst) && !isa(deadInst) && + "lifetime ending instruction is deleted without its operand"); + ValueOwnershipKind operandOwnershipKind = operandValue.getOwnershipKind(); + UseLifetimeConstraint lifetimeConstraint = + operand.getOwnershipKindMap().getLifetimeConstraint(operandOwnershipKind); + if (lifetimeConstraint == UseLifetimeConstraint::MustBeInvalidated) { + // Since deadInst cannot be an end-of-scope instruction (asserted above), + // this must be a consuming use of an owned value. + assert(operandOwnershipKind == ValueOwnershipKind::Owned); + SILBuilderWithScope builder(deadInst); + builder.emitDestroyValueOperation(deadInst->getLoc(), operandValue); + } +} + namespace { using CallbackTy = llvm::function_ref; -} // end anonymous namespace +} // namespace + +void InstructionDeleter::deleteInstruction(SILInstruction *inst, + CallbackTy callback, + bool fixOperandLifetimes) { + // We cannot fix operand lifetimes in non-ownership SIL. + assert(!fixOperandLifetimes || inst->getFunction()->hasOwnership()); + // Collect instruction and its immediate uses and check if they are all + // incidental uses. Also, invoke the callback on the instruction and its uses. + // Note that the Callback is invoked before deleting anything to ensure that + // the SIL is valid at the time of the callback. + SmallVector toDeleteInsts; + toDeleteInsts.push_back(inst); + callback(inst); + for (SILValue result : inst->getResults()) { + for (Operand *use : result->getUses()) { + SILInstruction *user = use->getUser(); + assert(isIncidentalUse(user) || isa(user)); + callback(user); + toDeleteInsts.push_back(user); + } + } + // Record definitions of instruction's operands. Also, in case an operand is + // consumed by inst, emit necessary compensation code. + SmallVector operandDefinitions; + for (Operand &operand : inst->getAllOperands()) { + SILValue operandValue = operand.get(); + assert(operandValue && + "Instruction's operand are deleted before the instruction"); + SILInstruction *defInst = operandValue->getDefiningInstruction(); + // If the operand has a defining instruction, it could be potentially + // dead. Therefore, record the definition. + if (defInst) + operandDefinitions.push_back(defInst); + // The scope of the operand could be ended by inst. Therefore, emit + // any compensating code needed to end the scope of the operand value + // once inst is deleted. + if (fixOperandLifetimes) + destroyConsumedOperandOfDeadInst(operand); + } + // First drop all references from all instructions to be deleted and then + // erase the instruction. Note that this is done in this order so that when an + // instruction is deleted, its uses would have dropped their references. + for (SILInstruction *inst : toDeleteInsts) { + inst->dropAllReferences(); + } + for (SILInstruction *inst : toDeleteInsts) { + inst->eraseFromParent(); + } + // Record operand definitions that become dead now. + for (SILInstruction *operandValInst : operandDefinitions) { + trackIfDead(operandValInst); + } +} + +void InstructionDeleter::cleanUpDeadInstructions(CallbackTy callback) { + SILFunction *fun = nullptr; + if (!deadInstructions.empty()) + fun = deadInstructions.front()->getFunction(); + while (!deadInstructions.empty()) { + SmallVector currentDeadInsts(deadInstructions.begin(), + deadInstructions.end()); + // Though deadInstructions is cleared here, calls to deleteInstruction may + // append to deadInstructions. So we need to iterate until this it is empty. + deadInstructions.clear(); + for (SILInstruction *deadInst : currentDeadInsts) { + // deadInst will not have been deleted in the previous iterations, + // because, by definition, deleteInstruction will only delete an earlier + // instruction and its incidental/destroy uses. The former cannot be + // deadInst as deadInstructions is a set vector, and the latter cannot be + // in deadInstructions as they are incidental uses which are never added + // to deadInstructions. + deleteInstruction(deadInst, callback, /*Fix lifetime of operands*/ + fun->hasOwnership()); + } + } +} + +static bool hasOnlyIncidentalUses(SILInstruction *inst, + bool disallowDebugUses = false) { + for (SILValue result : inst->getResults()) { + for (Operand *use : result->getUses()) { + SILInstruction *user = use->getUser(); + if (!isIncidentalUse(user)) + return false; + if (disallowDebugUses && user->isDebugInstruction()) + return false; + } + } + return true; +} + +void InstructionDeleter::forceDeleteAndFixLifetimes(SILInstruction *inst, + CallbackTy callback) { + SILFunction *fun = inst->getFunction(); + assert(fun->hasOwnership()); + bool disallowDebugUses = + fun->getEffectiveOptimizationMode() <= OptimizationMode::NoOptimization; + assert(hasOnlyIncidentalUses(inst, disallowDebugUses)); + deleteInstruction(inst, callback, /*Fix lifetime of operands*/ true); +} + +void InstructionDeleter::forceDelete(SILInstruction *inst, + CallbackTy callback) { + bool disallowDebugUses = + inst->getFunction()->getEffectiveOptimizationMode() <= + OptimizationMode::NoOptimization; + assert(hasOnlyIncidentalUses(inst, disallowDebugUses)); + deleteInstruction(inst, callback, /*Fix lifetime of operands*/ false); +} + +void swift::eliminateDeadInstruction(SILInstruction *inst, + CallbackTy callback) { + InstructionDeleter deleter; + deleter.trackIfDead(inst); + deleter.cleanUpDeadInstructions(callback); +} void swift::recursivelyDeleteTriviallyDeadInstructions( ArrayRef ia, bool force, CallbackTy callback) { @@ -205,8 +480,8 @@ void swift::recursivelyDeleteTriviallyDeadInstructions( // If the operand is an instruction that is only used by the instruction // being deleted, delete it. if (auto *operandValInst = operandVal->getDefiningInstruction()) - if (!deadInsts.count(operandValInst) - && isInstructionTriviallyDead(operandValInst)) + if (!deadInsts.count(operandValInst) && + isInstructionTriviallyDead(operandValInst)) nextInsts.insert(operandValInst); } diff --git a/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift b/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift index e5cc838e2daec..36c524e27055e 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift +++ b/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift @@ -30,17 +30,15 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // expected-error @-2 {{globalStringTablePointer builtin must used only on string literals}} } - // FIXME: the following test should produce diagnostics and is a not - // valid uses of the log APIs. The string interpolation passed to the os log - // call must be apart of the log call, it cannot be constructed earlier. - // It nonetheless works fine, but should be rejected. func testNoninlinedOSLogMessage(h: Logger) { let logMessage: OSLogMessage = "Minimum integer value: \(Int.min)" + // expected-error @-1 {{OSLogMessage instance must not be explicitly created and must be deletable}} h.log(level: .debug, logMessage) } func testNoninlinedOSLogMessageComplex(h: Logger, b: Bool) { let logMessage: OSLogMessage = "Maximum integer value: \(Int.max)" + // expected-error @-1 {{OSLogMessage instance must not be explicitly created and must be deletable}} if !b { return; } diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.sil b/test/SILOptimizer/OSLogPrototypeCompileTest.sil index 9fc968d400de9..744e1043f16c9 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.sil +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.sil @@ -57,7 +57,6 @@ bb0: destroy_value %7 : $OSLogMessageStub %13 = tuple () return %13 : $() - // CHECK-NOT: {{%.*}} = struct_extract {{%.*}} : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[BORROW:%[0-9]+]]) // CHECK-DAG: [[BORROW]] = begin_borrow [[STRINGCONST:%[0-9]+]] @@ -98,8 +97,6 @@ bb0: dealloc_stack %11 : $*String %15 = tuple () return %15 : $() - // Skip the first literal. - // CHECK: {{%.*}} = string_literal utf8 "some long message: %llx" // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "some long message: %llx" // CHECK: [[STRINGINIT:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK: [[STRINGCONST:%[0-9]+]] = apply [[STRINGINIT]]([[LIT]], {{%.*}}, {{%.*}}, {{%.*}}) @@ -175,13 +172,12 @@ bb0: destroy_value %9 : $String %12 = tuple () return %12 : $() - // CHECK-NOT: ({{%.*}}) = destructure_struct {{%.*}} : $OSLogInterpolationStub - // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString - // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[STRINGCONST:%[0-9]+]]) - // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) - // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC - // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" - // CHECK-DAG: destroy_value [[STRINGCONST]] : $String + // CHECK-DAG [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG {{%.*}} = apply [[STRINGUSE]]([[STRINGCONST:%[0-9]+]]) + // CHECK-DAG [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG [[LIT]] = string_literal utf8 "test message: %lld" + // CHECK-DAG destroy_value [[STRINGCONST]] : $String } // Test that the OSLogOptimization pass does not fold instructions that define @@ -215,7 +211,6 @@ bb0: destroy_value %7 : $OSLogMessageStub %15 = tuple () return %15 : $() - // CHECK-NOT: {{%.*}} = struct_extract {{%.*}} : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[BORROW:%[0-9]+]]) // CHECK-DAG: [[BORROW]] = begin_borrow [[COPYVAL:%[0-9]+]] @@ -275,8 +270,6 @@ bb5: // We must have all string literals at the beginning of the borrowed scope, // and destorys of the literals at the end of the borrow scope. - // Skip the first literal which is the original one. - // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "test message: %lld" // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "test message: %lld" // CHECK: [[STRINGINIT:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK: [[STRINGCONST:%[0-9]+]] = apply [[STRINGINIT]]([[LIT]], {{%.*}}, {{%.*}}, {{%.*}}) @@ -535,8 +528,7 @@ bb0: destroy_value %2 : $OSLogMessageStringArrayStub %8 = tuple () return %8 : $() - // Skip the first instance of "ab". - // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "ab" + // The first instance of "ab" will be dead code eliminated. // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "ab" // CHECK: [[STRINGINIT:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK: [[STRINGCONST:%[0-9]+]] = apply [[STRINGINIT]]([[LIT]], {{%.*}}, {{%.*}}, {{%.*}}) @@ -741,7 +733,7 @@ bb0(%0 : @guaranteed $String): destroy_value %6 : $OSLogMessageStringCapture %18 = tuple () return %18 : $() - // CHECK: [[FUNREFORIG:%[0-9]+]] = function_ref @idString + // The first instance of function_ref @idString will be dead code eliminated. // CHECK: [[ORIGCAPTURE:%[0-9]+]] = copy_value %0 : $String // CHECK: [[NEWCAPTURE:%[0-9]+]] = copy_value [[ORIGCAPTURE]] : $String // CHECK: [[FUNREF:%[0-9]+]] = function_ref @idString @@ -795,10 +787,9 @@ bb0: dealloc_stack %1 : $*Int64 %18 = tuple () return %18 : $() - // CHECK: [[FUNREFORIG:%[0-9]+]] = function_ref @genericFunction - // CHECK: [[CLOSUREORIG:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREFORIG]]([[CAPTURE1:%[0-9]+]], [[CAPTURE2:%[0-9]+]]) + // The first instance of function_ref @genericFunction will be dead-code eliminated. // CHECK: [[FUNREF:%[0-9]+]] = function_ref @genericFunction - // CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREF]]([[CAPTURE1]], [[CAPTURE2]]) + // CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREF]]([[CAPTURE1:%[0-9]+]], [[CAPTURE2:%[0-9]+]]) // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[CLOSURE]] : $@callee_guaranteed () -> Int32 // CHECK: [[USE:%[0-9]+]] = function_ref @useClosure // CHECK: apply [[USE]]([[BORROW]]) @@ -872,3 +863,105 @@ bb0: %8 = tuple () return %8 : $() } + +// The following tests are for checking dead-code elimination performed by the +// OSLogOptimization pass. + +struct OSLogInterpolationDCEStub { + var formatString: String +} + +struct OSLogMessageDCEStub { + var interpolation: OSLogInterpolationDCEStub +} + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageDCEInit : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub { +bb0(%0 : @owned $OSLogInterpolationDCEStub): + %1 = struct $OSLogMessageDCEStub (%0 : $OSLogInterpolationDCEStub) + return %1 : $OSLogMessageDCEStub +} + +// CHECK-LABEL: @testDCEOfStructCreation +sil [ossa] @testDCEOfStructCreation : $@convention(thin) () -> () { +bb0: + %0 = string_literal utf8 "some message" + %1 = integer_literal $Builtin.Word, 12 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = struct $OSLogInterpolationDCEStub(%5 : $String) + %7 = copy_value %6 : $OSLogInterpolationDCEStub + %8 = function_ref @oslogMessageDCEInit : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %9 = apply %8(%7) : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %10 = begin_borrow %9 : $OSLogMessageDCEStub + end_borrow %10 : $OSLogMessageDCEStub + destroy_value %9 : $OSLogMessageDCEStub + destroy_value %6 : $OSLogInterpolationDCEStub + %13 = tuple () + return %13 : $() + // CHECK: bb0 + // CHECK-NEXT: [[EMPTYTUP:%[0-9]+]] = tuple () + // CHECK-NEXT: return [[EMPTYTUP]] +} + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageGuaranteedInit : $@convention(thin) (@guaranteed OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub { +bb0(%0 : @guaranteed $OSLogInterpolationDCEStub): + %2 = copy_value %0 : $OSLogInterpolationDCEStub + %3 = struct $OSLogMessageDCEStub (%2 : $OSLogInterpolationDCEStub) + return %3 : $OSLogMessageDCEStub +} + +// CHECK-LABEL: @testDCEOfGuaranteedStructCreation +sil [ossa] @testDCEOfGuaranteedStructCreation : $@convention(thin) () -> () { +bb0: + %0 = string_literal utf8 "some message" + %1 = integer_literal $Builtin.Word, 12 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = struct $OSLogInterpolationDCEStub(%5 : $String) + %7 = begin_borrow %6 : $OSLogInterpolationDCEStub + %9 = function_ref @oslogMessageGuaranteedInit : $@convention(thin) (@guaranteed OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %10 = apply %9(%7) : $@convention(thin) (@guaranteed OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + destroy_value %10 : $OSLogMessageDCEStub + end_borrow %7 : $OSLogInterpolationDCEStub + destroy_value %6 : $OSLogInterpolationDCEStub + %13 = tuple () + return %13 : $() + // CHECK: bb0 + // CHECK-NEXT: [[EMPTYTUP:%[0-9]+]] = tuple () + // CHECK-NEXT: return [[EMPTYTUP]] +} + +// CHECK-LABEL: @testLifetimeAdjustmentOfDCE +sil [ossa] @testLifetimeAdjustmentOfDCE : $@convention(thin) () -> () { +bb0: + %0 = string_literal utf8 "some message" + %1 = integer_literal $Builtin.Word, 12 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = copy_value %5 : $String + %7 = struct $OSLogInterpolationDCEStub(%5 : $String) + %8 = function_ref @oslogMessageDCEInit : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %9 = apply %8(%7) : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + destroy_value %9 : $OSLogMessageDCEStub + %11 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %12 = apply %11(%6) : $@convention(thin) (@guaranteed String) -> () + destroy_value %6 : $String + %13 = tuple () + return %13 : $() + // CHECK: [[STRINGINITREF:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK: [[STRING:%[0-9]+]] = apply [[STRINGINITREF]]( + // CHECK-NEXT: [[COPY:%[0-9]+]] = copy_value [[STRING]] + // CHECK-NEXT: destroy_value [[STRING]] + // CHECK-NOT: OSLogInterpolationDCEStub + // CHECK-NOT: OSLogMessageDCEStub + // CHECK: return +} diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.swift b/test/SILOptimizer/OSLogPrototypeCompileTest.swift index 39293a7f1cbe0..a0360b3b4dd8d 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.swift +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.swift @@ -460,5 +460,34 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 6 } + + // CHECK-LABEL: @$s25OSLogPrototypeCompileTest23testDeadCodeEliminationL_1h6number8num32bit6stringy0aB06LoggerV_Sis5Int32VSStF + func testDeadCodeElimination( + h: Logger, + number: Int, + num32bit: Int32, + string: String + ) { + h.log("A message with no data") + h.log("smallstring") + h.log( + level: .error, + """ + A message with many interpolations \(number), \(num32bit), \(string), \ + and a suffix + """) + h.log( + level: .info, + """ + \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \ + \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \ + \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \ + \(1) \(1) \(1) \(1) \(1) \(48) \(49) + """) + let concatString = string + ":" + String(number) + h.log("\(concatString)") + // CHECK-NOT: OSLogMessage + // CHECK-LABEL: end sil function '$s25OSLogPrototypeCompileTest23testDeadCodeEliminationL_1h6number8num32bit6stringy0aB06LoggerV_Sis5Int32VSStF' + } } diff --git a/test/SILOptimizer/access_marker_mandatory.swift b/test/SILOptimizer/access_marker_mandatory.swift index 0346b3a86bd31..97e27d5756542 100644 --- a/test/SILOptimizer/access_marker_mandatory.swift +++ b/test/SILOptimizer/access_marker_mandatory.swift @@ -49,8 +49,6 @@ func takeS(_ s: S) {} // CHECK: [[ADDRI:%.*]] = struct_element_addr [[WRITE]] : $*S, #S.i // CHECK: store %{{.*}} to [[ADDRI]] : $*Int // CHECK: end_access [[WRITE]] -// CHECK: [[READ:%.*]] = begin_access [read] [static] [[STK]] : $*S -// CHECK: end_access [[READ]] // CHECK: [[FTAKE:%.*]] = function_ref @$s23access_marker_mandatory5takeSyyAA1SVF : $@convention(thin) (@guaranteed S) -> () // CHECK: apply [[FTAKE]](%{{.*}}) : $@convention(thin) (@guaranteed S) -> () // CHECK-LABEL: } // end sil function '$s23access_marker_mandatory14modifyAndReadS1oyyXl_tF' diff --git a/test/SILOptimizer/predictable_memopt_ownership.sil b/test/SILOptimizer/predictable_memopt_ownership.sil index e18ace13123ac..d88adf2eb0558 100644 --- a/test/SILOptimizer/predictable_memopt_ownership.sil +++ b/test/SILOptimizer/predictable_memopt_ownership.sil @@ -428,7 +428,7 @@ bb0: // CHECK-LABEL: sil [ossa] @dead_allocation_1 sil [ossa] @dead_allocation_1 : $@convention(thin) (@owned Optional) -> () { bb0(%0 : @owned $Optional): -// CHECK: copy_value %0 +// CHECK-NOT: alloc_stack %1 = alloc_stack $Optional %2 = alloc_stack $Optional store %0 to [init] %2 : $*Optional @@ -438,6 +438,7 @@ bb0(%0 : @owned $Optional): dealloc_stack %2 : $*Optional destroy_addr %1 : $*Optional dealloc_stack %1 : $*Optional +// CHECK: destroy_value %0 %3 = tuple () return %3 : $() } @@ -445,7 +446,6 @@ bb0(%0 : @owned $Optional): // CHECK-LABEL: sil [ossa] @dead_allocation_2 sil [ossa] @dead_allocation_2 : $@convention(thin) (@owned Optional) -> () { bb0(%0 : @owned $Optional): -// CHECK: copy_value %0 // CHECK-NOT: alloc_stack %1 = alloc_stack $Optional %2 = alloc_stack $Optional @@ -457,6 +457,7 @@ bb0(%0 : @owned $Optional): destroy_addr %1 : $*Optional dealloc_stack %1 : $*Optional %3 = tuple () +// CHECK: destroy_value %0 return %3 : $() } From b7532f9190fda409775d045291063d587321c756 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 18 Dec 2019 13:21:07 -0800 Subject: [PATCH 092/478] [Property wrappers] Diagnose unavailable wrappedValue Fixes rdar://problem/53678041 and rdar://problem/57676337; the former was because we diagnosed unavailable wrappedValue at the null source location originall, then started suppressing that diagnostic altogether. Produce a proper diagnostic now. --- lib/Sema/TypeCheckStorage.cpp | 16 ++++++++++- test/decl/var/property_wrappers.swift | 39 +++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index c4912589871cc..b11d452a78c3c 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -17,6 +17,7 @@ #include "CodeSynthesis.h" #include "TypeChecker.h" +#include "TypeCheckAvailability.h" #include "TypeCheckDecl.h" #include "TypeCheckType.h" #include "swift/AST/ASTContext.h" @@ -694,7 +695,20 @@ static Expr *buildStorageReference(AccessorDecl *accessor, // Perform accesses to the wrappedValues along the composition chain. for (unsigned i : range(firstWrapperIdx, lastWrapperIdx)) { auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(i); - underlyingVars.push_back(wrapperInfo.valueVar); + auto wrappedValue = wrapperInfo.valueVar; + + // Check for availability of wrappedValue. + if (accessor->getAccessorKind() == AccessorKind::Get || + accessor->getAccessorKind() == AccessorKind::Read) { + if (auto *attr = wrappedValue->getAttrs().getUnavailable(ctx)) { + diagnoseExplicitUnavailability( + wrappedValue, + var->getAttachedPropertyWrappers()[i]->getRangeWithAt(), + var->getDeclContext(), nullptr); + } + } + + underlyingVars.push_back(wrappedValue); } semantics = AccessSemantics::DirectToStorage; selfAccessKind = SelfAccessorKind::Peer; diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index 78480954fe3f1..914652b69d83d 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -9,12 +9,11 @@ struct Wrapper { init(stored: T) { self._stored = stored } - + var wrappedValue: T { get { _stored } set { _stored = newValue } } - } @propertyWrapper @@ -837,6 +836,42 @@ struct UsesExplicitClosures { var y: Int } +// --------------------------------------------------------------------------- +// Enclosing instance diagnostics +// --------------------------------------------------------------------------- +@propertyWrapper +struct Observable { + private var stored: Value + + init(wrappedValue: Value) { + self.stored = wrappedValue + } + + @available(*, unavailable, message: "must be in a class") + var wrappedValue: Value { // expected-note{{'wrappedValue' has been explicitly marked unavailable here}} + get { fatalError("called wrappedValue getter") } + set { fatalError("called wrappedValue setter") } + } + + static subscript( + _enclosingInstance observed: EnclosingSelf, + wrapped wrappedKeyPath: ReferenceWritableKeyPath, + storage storageKeyPath: ReferenceWritableKeyPath + ) -> Value { + get { + observed[keyPath: storageKeyPath].stored + } + set { + observed[keyPath: storageKeyPath].stored = newValue + } + } +} + +struct MyObservedValueType { + @Observable // expected-error{{'wrappedValue' is unavailable: must be in a class}} + var observedProperty = 17 +} + // --------------------------------------------------------------------------- // Miscellaneous bugs // --------------------------------------------------------------------------- From 0823d8b6e47d2d7869dcdb6dc08f03461bd23ab8 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Wed, 18 Dec 2019 13:33:06 -0800 Subject: [PATCH 093/478] Check for nullptr inside NominalTypeDecl::isResilient rdar://58049956 --- lib/AST/Decl.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 594a14915ab27..5cf8d55f85146 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3533,6 +3533,14 @@ bool NominalTypeDecl::isResilient() const { return getModuleContext()->isResilient(); } +static bool isOriginallyDefinedIn(const Decl *D, const ModuleDecl* MD) { + if (!MD) + return false; + if (D->getAlternateModuleName().empty()) + return false; + return D->getAlternateModuleName() == MD->getName().str(); +} + bool NominalTypeDecl::isResilient(ModuleDecl *M, ResilienceExpansion expansion) const { switch (expansion) { @@ -3542,8 +3550,8 @@ bool NominalTypeDecl::isResilient(ModuleDecl *M, // We consider this decl belongs to the module either it's currently // defined in this module or it's originally defined in this module, which // is specified by @_originallyDefinedIn - return M != getModuleContext() && - M->getName().str() != getAlternateModuleName() && isResilient(); + return M != getModuleContext() && !isOriginallyDefinedIn(this, M) && + isResilient(); } llvm_unreachable("bad resilience expansion"); } From 1ddb916b0ef787f1c86053c41be163ad4408d60b Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 18 Dec 2019 13:46:19 -0800 Subject: [PATCH 094/478] [gardening] Eliminate dead variable. --- lib/SILGen/SILGenConvert.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index 0602dd7117830..fc5aac49dfbe5 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -827,12 +827,10 @@ ManagedValue SILGenFunction::emitExistentialErasure( loc, SILType::getPrimitiveObjectType(anyObjectTy), concreteFormalType, concreteValue, {}); }; - - auto concreteTLPtr = &concreteTL; + if (this->F.getLoweredFunctionType()->isPseudogeneric()) { if (anyObjectTy && concreteFormalType->is()) { concreteFormalType = anyObjectTy; - concreteTLPtr = &getTypeLowering(anyObjectTy); F = eraseToAnyObject; } } From df6d25fa8a386e497a3be59a6b9b568c0b9d3507 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 18 Dec 2019 13:46:55 -0800 Subject: [PATCH 095/478] [gardening] Be more defensive around a potentially uninitialized Store by initializing it to nullptr and asserting that it is not nullptr (i.e. was assigned) before using it. --- lib/SILOptimizer/IPO/GlobalOpt.cpp | 3 ++- lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index d79c3961731e5..82be150b7ba62 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -567,7 +567,7 @@ static SILFunction *genGetterFromInit(SILOptFunctionBuilder &FunctionBuilder, // Find the store instruction auto *BB = GetterF->getEntryBlock(); SILValue Val; - SILInstruction *Store; + SILInstruction *Store = nullptr; for (auto II = BB->begin(), E = BB->end(); II != E;) { auto &I = *II++; if (isa(&I)) { @@ -586,6 +586,7 @@ static SILFunction *genGetterFromInit(SILOptFunctionBuilder &FunctionBuilder, B.createReturn(RI->getLoc(), Val); eraseUsesOfInstruction(RI); recursivelyDeleteTriviallyDeadInstructions(RI, true); + assert(Store && "Did not find a store?!"); recursivelyDeleteTriviallyDeadInstructions(Store, true); return GetterF; } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp index eee7bf3a22fe2..7d9a15df654cf 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp @@ -164,11 +164,13 @@ visitPointerToAddressInst(PointerToAddressInst *PTAI) { // %addr = pointer_to_address %ptr, [strict] $T // %result = index_addr %addr, %distance // - BuiltinInst *Bytes; + BuiltinInst *Bytes = nullptr; if (match(PTAI->getOperand(), m_IndexRawPointerInst( m_ValueBase(), m_TupleExtractOperation(m_BuiltinInst(Bytes), 0)))) { + assert(Bytes != nullptr && + "Bytes should have been assigned a non-null value"); if (match(Bytes, m_ApplyInst(BuiltinValueKind::SMulOver, m_ValueBase(), m_ApplyInst(BuiltinValueKind::Strideof, m_MetatypeInst(Metatype)), From 68960005f64312078ee94cd142c2196db0d8bbc2 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 18 Dec 2019 13:52:39 -0800 Subject: [PATCH 096/478] [gardening] Remove unused variable. --- .../FunctionSignatureTransforms/ExistentialTransform.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp index 6b5b0e29ff8a0..108a2545e1f60 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp @@ -322,7 +322,6 @@ ExistentialTransform::createExistentialSpecializedFunctionType() { SILModule &M = F->getModule(); auto &Ctx = M.getASTContext(); GenericSignature NewGenericSig; - GenericEnvironment *NewGenericEnv; /// If the original function is generic, then maintain the same. auto OrigGenericSig = FTy->getInvocationGenericSignature(); @@ -341,8 +340,6 @@ ExistentialTransform::createExistentialSpecializedFunctionType() { std::move(Requirements)}, GenericSignature()); - NewGenericEnv = NewGenericSig->getGenericEnvironment(); - /// Create a lambda for GenericParams. auto getCanonicalType = [&](Type t) -> CanType { return t->getCanonicalType(NewGenericSig); From f32af9b047c6108e2333988c81fb6c08859e2e14 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Wed, 18 Dec 2019 17:15:51 -0500 Subject: [PATCH 097/478] [Runtime] Adjust lazy name API to a hook-based call that can have multiple hooks. Use the API from the headers if present. rdar://problem/57674583 --- stdlib/public/runtime/Metadata.cpp | 33 +++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index cc631048bc78d..fe45da10a2dc4 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -2244,22 +2244,35 @@ static void initGenericClassObjCName(ClassMetadata *theClass) { getROData(theMetaclass)->Name = name; } -static bool installLazyClassNameHandler() { - auto _objc_setLazyClassNamer = - (void (*)(char * (*)(Class))) - dlsym(RTLD_NEXT, "_objc_setLazyClassNamer"); - if (_objc_setLazyClassNamer == nullptr) - return false; +static bool installLazyClassNameHook() { +#if !OBJC_SETHOOK_LAZYCLASSNAMER_DEFINED + using objc_hook_lazyClassNamer = + const char * _Nullable (*)(_Nonnull Class cls); + auto objc_setHook_lazyClassNamer = + (void (*)(objc_hook_lazyClassNamer, objc_hook_lazyClassNamer *)) + dlsym(RTLD_NEXT, "objc_setHook_lazyClassNamer"); +#endif - _objc_setLazyClassNamer([](Class theClass) { + static objc_hook_lazyClassNamer oldHook; + auto myHook = [](Class theClass) -> const char * { ClassMetadata *metadata = (ClassMetadata *)theClass; - return copyGenericClassObjCName(metadata); - }); + if (metadata->isTypeMetadata()) + return copyGenericClassObjCName(metadata); + return oldHook(theClass); + }; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + if (objc_setHook_lazyClassNamer == nullptr) + return false; + objc_setHook_lazyClassNamer(myHook, &oldHook); +#pragma clang diagnostic pop + return true; } static void setUpGenericClassObjCName(ClassMetadata *theClass) { - bool supportsLazyNames = SWIFT_LAZY_CONSTANT(installLazyClassNameHandler()); + bool supportsLazyNames = SWIFT_LAZY_CONSTANT(installLazyClassNameHook()); if (supportsLazyNames) { getROData(theClass)->Name = nullptr; auto theMetaclass = (ClassMetadata *)object_getClass((id)theClass); From a6fd37e12869510899bb2edb5c8c838cdd35bba9 Mon Sep 17 00:00:00 2001 From: Alex Langford Date: Wed, 18 Dec 2019 14:31:52 -0800 Subject: [PATCH 098/478] [build] Remove make_relative_symlink from build-script-impl Nothing uses this anymore, remove it. --- utils/build-script-impl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index ba3e769cd8b4b..7191dbc198d54 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -922,14 +922,6 @@ function cmake_needs_to_specify_standard_computed_defaults() { fi } -function make_relative_symlink() { - local SOURCE=$1 - local TARGET=$2 - local TARGET_DIR=$(dirname "$2") - local RELATIVE_SOURCE=$(python -c "import os.path; print(os.path.relpath(\"${SOURCE}\", \"${TARGET_DIR}\"))") - call ln -sf "${RELATIVE_SOURCE}" "${TARGET}" -} - # Sanitize the list of cross-compilation targets. # # In the Build/Host/Target paradigm: From 0ce856b06c0a4062c9a3e5093e4f45b3ab328f7a Mon Sep 17 00:00:00 2001 From: David Smith Date: Wed, 18 Dec 2019 15:06:22 -0800 Subject: [PATCH 099/478] Add a test for incorrectly constructing a Set from an NSArray --- test/stdlib/NSSetAPI.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/stdlib/NSSetAPI.swift b/test/stdlib/NSSetAPI.swift index 182d976d6d27b..39fdfc64ac5fb 100644 --- a/test/stdlib/NSSetAPI.swift +++ b/test/stdlib/NSSetAPI.swift @@ -67,6 +67,12 @@ NSSetAPI.test("AnyHashable containing NSSet that contains an NSSet") { } } +NSSetAPI.test("Incorrectly constructed Set for backwards compatibility") { + let array:NSArray = [NSObject()] as NSArray + let wrongSet = Set(_immutableCocoaSet: array) + print(wrongSet.startIndex) +} + var NSOrderedSetAPI = TestSuite("NSOrderedSetAPI") NSOrderedSetAPI.test("Sequence") { From 2a79331ac3c26027cf158adfc716e717ad579478 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Wed, 18 Dec 2019 16:07:51 -0800 Subject: [PATCH 100/478] add -enable/disable-only-one-dependency-file flag, on by default --- include/swift/Driver/Compilation.h | 28 ++++ include/swift/Driver/Job.h | 5 + include/swift/Option/Options.td | 10 ++ lib/Driver/Compilation.cpp | 22 +++- lib/Driver/Driver.cpp | 108 ++++++++++------ lib/Driver/Job.cpp | 4 + lib/Driver/ToolChain.cpp | 5 + test/Driver/advanced_output_file_map.swift | 122 ++++++++++++++---- .../Driver/batch_mode_size_limit.swift | 8 +- ..._size_limit_only_one_dependency-file.swift | 36 ++++++ 10 files changed, 275 insertions(+), 73 deletions(-) create mode 100644 validation-test/Driver/batch_mode_size_limit_only_one_dependency-file.swift diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index fdd50cb0c08ab..eaff74cfce578 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -257,6 +257,23 @@ class Compilation { /// limit filelists will be used. size_t FilelistThreshold; + /// Because each frontend job outputs the same info in its .d file, only do it + /// on the first job that actually runs. Write out dummies for the rest of the + /// jobs. This hack saves a lot of time in the build system when incrementally + /// building a project with many files. Record if a scheduled job has already + /// added -emit-dependency-path. + bool HaveAlreadyAddedDependencyPath = false; + +public: + /// When set, only the first scheduled frontend job gets the argument needed + /// to produce a make-style dependency file. The other jobs create dummy files + /// in the driver. This hack speeds up incremental compilation by reducing the + /// time for the build system to read each dependency file, which are all + /// identical. This optimization can be disabled by passing + /// -disable-only-one-dependency-file on the command line. + const bool OnlyOneDependencyFile; + +private: /// Scaffolding to permit experimentation with finer-grained dependencies and /// faster rebuilds. const bool EnableFineGrainedDependencies; @@ -309,6 +326,7 @@ class Compilation { bool SaveTemps = false, bool ShowDriverTimeCompilation = false, std::unique_ptr Stats = nullptr, + bool OnlyOneDependencyFile = false, bool EnableFineGrainedDependencies = false, bool VerifyFineGrainedDependencyGraphAfterEveryImport = false, bool EmitFineGrainedDependencyDotFileAfterEveryImport = false, @@ -427,6 +445,16 @@ class Compilation { return FilelistThreshold; } + /// Since every make-style dependency file contains + /// the same information, incremental builds are sped up by only emitting one + /// of those files. Since the build system expects to see the files existing, + /// create dummy files for those jobs that don't emit real dependencies. + /// \param path The dependency file path + /// \param addDependencyPath A function to add an -emit-dependency-path + /// argument + void addDependencyPathOrCreateDummy(StringRef path, + function_ref addDependencyPath); + UnifiedStatsReporter *getStatsReporter() const { return Stats.get(); } diff --git a/include/swift/Driver/Job.h b/include/swift/Driver/Job.h index 4627d4651e591..d36d3f3b3ff2d 100644 --- a/include/swift/Driver/Job.h +++ b/include/swift/Driver/Job.h @@ -194,6 +194,11 @@ class CommandOutput { /// first primary input. StringRef getAdditionalOutputForType(file_types::ID type) const; + /// Assuming (and asserting) that there are one or more input pairs, return true if there exists + /// an _additional_ (not primary) output of type \p type associated with the + /// first primary input. + bool hasAdditionalOutputForType(file_types::ID type) const; + /// Return a vector of additional (not primary) outputs of type \p type /// associated with the primary inputs. /// diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 71a01084e7945..bdb5a2e9e362c 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -139,6 +139,16 @@ def enable_fine_grained_dependencies : Flag<["-"], "enable-fine-grained-dependencies">, Flags<[FrontendOption, HelpHidden]>, HelpText<"Experimental work-in-progress to be more selective about incremental recompilation">; + +def enable_only_one_dependency_file : +Flag<["-"], "enable-only-one-dependency-file">, Flags<[DoesNotAffectIncrementalBuild]>, + HelpText<"Enables incremental build optimization that only produces one dependencies file">; + +def disable_only_one_dependency_file : +Flag<["-"], "disable-only-one-dependency-file">, Flags<[DoesNotAffectIncrementalBuild]>, + HelpText<"Disables incremental build optimization that only produces one dependencies file">; + + def enable_source_range_dependencies : Flag<["-"], "enable-source-range-dependencies">, Flags<[]>, HelpText<"Try using source range information">; diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 6cf8a34e6a378..0051ff5f33cda 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -48,6 +48,7 @@ #include "CompilationRecord.h" +#include #include #define DEBUG_TYPE "batch-mode" @@ -121,6 +122,7 @@ Compilation::Compilation(DiagnosticEngine &Diags, bool SaveTemps, bool ShowDriverTimeCompilation, std::unique_ptr StatsReporter, + bool OnlyOneDependencyFile, bool EnableFineGrainedDependencies, bool VerifyFineGrainedDependencyGraphAfterEveryImport, bool EmitFineGrainedDependencyDotFileAfterEveryImport, @@ -149,6 +151,7 @@ Compilation::Compilation(DiagnosticEngine &Diags, ShowDriverTimeCompilation(ShowDriverTimeCompilation), Stats(std::move(StatsReporter)), FilelistThreshold(FilelistThreshold), + OnlyOneDependencyFile(OnlyOneDependencyFile), EnableFineGrainedDependencies(EnableFineGrainedDependencies), VerifyFineGrainedDependencyGraphAfterEveryImport( VerifyFineGrainedDependencyGraphAfterEveryImport), @@ -156,7 +159,8 @@ Compilation::Compilation(DiagnosticEngine &Diags, EmitFineGrainedDependencyDotFileAfterEveryImport), FineGrainedDependenciesIncludeIntrafileOnes( FineGrainedDependenciesIncludeIntrafileOnes), - EnableSourceRangeDependencies(EnableSourceRangeDependencies) { + EnableSourceRangeDependencies(EnableSourceRangeDependencies) + { if (CompareIncrementalSchemes) IncrementalComparator.emplace( // Ensure the references are to inst vars, NOT arguments @@ -2032,3 +2036,19 @@ unsigned Compilation::countSwiftInputs() const { ++inputCount; return inputCount; } + +void Compilation::addDependencyPathOrCreateDummy( + StringRef depPath, function_ref addDependencyPath) { + + if (!OnlyOneDependencyFile) { + addDependencyPath(); + return; + } + if (!HaveAlreadyAddedDependencyPath) { + addDependencyPath(); + HaveAlreadyAddedDependencyPath = true; + } else if (!depPath.empty()) { + // Create dummy empty file + std::ofstream(depPath.str().c_str()); + } +} diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index ba58e599b415e..4eee305e71807 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -951,6 +951,11 @@ Driver::buildCompilation(const ToolChain &TC, ArgList->hasArg(options::OPT_driver_time_compilation); std::unique_ptr StatsReporter = createStatsReporter(ArgList.get(), Inputs, OI, DefaultTargetTriple); + + const bool OnlyOneDependencyFile = + ArgList->hasFlag(options::OPT_enable_only_one_dependency_file, + options::OPT_disable_only_one_dependency_file, true); + // relies on the new dependency graph const bool EnableFineGrainedDependencies = ArgList->hasArg(options::OPT_enable_fine_grained_dependencies); @@ -984,6 +989,7 @@ Driver::buildCompilation(const ToolChain &TC, SaveTemps, ShowDriverTimeCompilation, std::move(StatsReporter), + OnlyOneDependencyFile, EnableFineGrainedDependencies, VerifyFineGrainedDependencyGraphAfterEveryImport, EmitFineGrainedDependencyDotFileAfterEveryImport, @@ -2425,60 +2431,74 @@ static bool hasExistingAdditionalOutput(CommandOutput &output, return false; } -static void addAuxiliaryOutput( +static llvm::SmallString<128> computeAuxiliaryOutputPath( Compilation &C, CommandOutput &output, file_types::ID outputType, const TypeToPathMap *outputMap, StringRef workingDirectory, StringRef outputPath = StringRef(), llvm::opt::OptSpecifier requireArg = llvm::opt::OptSpecifier()) { if (hasExistingAdditionalOutput(output, outputType, outputPath)) - return; + return {}; - StringRef outputMapPath; if (outputMap) { auto iter = outputMap->find(outputType); - if (iter != outputMap->end()) - outputMapPath = iter->second; + if (iter != outputMap->end()) { + StringRef outputMapPath = iter->second; + // Prefer a path from the OutputMap. + if (!outputMapPath.empty()) + return outputMapPath; + } } + if (!outputPath.empty()) + return outputPath; - if (!outputMapPath.empty()) { - // Prefer a path from the OutputMap. - output.setAdditionalOutputForType(outputType, outputMapPath); - } else if (!outputPath.empty()) { - output.setAdditionalOutputForType(outputType, outputPath); - } else if (requireArg.isValid() && !C.getArgs().getLastArg(requireArg)) { + if (requireArg.isValid() && !C.getArgs().getLastArg(requireArg)) { // This auxiliary output only exists if requireArg is passed, but it // wasn't this time. - return; - } else { - // Put the auxiliary output file next to "the" primary output file. - // - // FIXME: when we're in WMO and have multiple primary outputs, we derive the - // additional filename here from the _first_ primary output name, which - // means that in the derived OFM (in Job.cpp) the additional output will - // have a possibly-surprising name. But that's only half the problem: it - // also get associated with the first primary _input_, even when there are - // multiple primary inputs; really it should be associated with the build as - // a whole -- derived OFM input "" -- but that's a more general thing to - // fix. - llvm::SmallString<128> path; - if (output.getPrimaryOutputType() != file_types::TY_Nothing) - path = output.getPrimaryOutputFilenames()[0]; - else if (!output.getBaseInput(0).empty()) - path = llvm::sys::path::filename(output.getBaseInput(0)); - else { - formFilenameFromBaseAndExt(C.getOutputInfo().ModuleName, /*newExt=*/"", - workingDirectory, path); - } - assert(!path.empty()); - - bool isTempFile = C.isTemporaryFile(path); - llvm::sys::path::replace_extension( - path, file_types::getExtension(outputType)); - output.setAdditionalOutputForType(outputType, path); - if (isTempFile) - C.addTemporaryFile(path); + return {}; } + + // Put the auxiliary output file next to "the" primary output file. + // + // FIXME: when we're in WMO and have multiple primary outputs, we derive the + // additional filename here from the _first_ primary output name, which + // means that in the derived OFM (in Job.cpp) the additional output will + // have a possibly-surprising name. But that's only half the problem: it + // also get associated with the first primary _input_, even when there are + // multiple primary inputs; really it should be associated with the build as + // a whole -- derived OFM input "" -- but that's a more general thing to + // fix. + llvm::SmallString<128> path; + if (output.getPrimaryOutputType() != file_types::TY_Nothing) + path = output.getPrimaryOutputFilenames()[0]; + else if (!output.getBaseInput(0).empty()) + path = llvm::sys::path::filename(output.getBaseInput(0)); + else { + formFilenameFromBaseAndExt(C.getOutputInfo().ModuleName, /*newExt=*/"", + workingDirectory, path); + } + assert(!path.empty()); + + const bool isTempFile = C.isTemporaryFile(path); + llvm::sys::path::replace_extension(path, + file_types::getExtension(outputType)); + if (isTempFile) + C.addTemporaryFile(path); + return path; +} + +static void addAuxiliaryOutput( + Compilation &C, CommandOutput &output, file_types::ID outputType, + const TypeToPathMap *outputMap, StringRef workingDirectory, + StringRef outputPath = StringRef(), + llvm::opt::OptSpecifier requireArg = llvm::opt::OptSpecifier()) { + + const auto path = + computeAuxiliaryOutputPath(C, output, outputType, outputMap, + workingDirectory, outputPath, requireArg); + if (path.empty()) + return; + output.setAdditionalOutputForType(outputType, path); } static void addDiagFileOutputForPersistentPCHAction( @@ -3057,8 +3077,12 @@ void Driver::chooseDependenciesOutputPaths(Compilation &C, llvm::SmallString<128> &Buf, CommandOutput *Output) const { if (C.getArgs().hasArg(options::OPT_emit_dependencies)) { - addAuxiliaryOutput(C, *Output, file_types::TY_Dependencies, OutputMap, - workingDirectory); + auto depPath = computeAuxiliaryOutputPath( + C, *Output, file_types::TY_Dependencies, OutputMap, workingDirectory); + C.addDependencyPathOrCreateDummy(depPath, [&] { + addAuxiliaryOutput(C, *Output, file_types::TY_Dependencies, OutputMap, + workingDirectory); + }); } if (C.getIncrementalBuildEnabled()) { file_types::forEachIncrementalOutputType([&](file_types::ID type) { diff --git a/lib/Driver/Job.cpp b/lib/Driver/Job.cpp index 5627146dd7b46..a4e21ad0c9b6b 100644 --- a/lib/Driver/Job.cpp +++ b/lib/Driver/Job.cpp @@ -236,6 +236,10 @@ CommandOutput::getAdditionalOutputsForType(file_types::ID Type) const { return V; } +bool CommandOutput::hasAdditionalOutputForType(file_types::ID type) const { + return AdditionalOutputTypes.count(type); +} + StringRef CommandOutput::getAnyOutputForType(file_types::ID Type) const { if (PrimaryOutputType == Type) return getPrimaryOutputFilename(); diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index bf42c9dd02c7e..c8b8ab791692b 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -238,6 +238,11 @@ bool ToolChain::jobIsBatchable(const Compilation &C, const Job *A) const { auto const *CJActA = dyn_cast(&A->getSource()); if (!CJActA) return false; + // When having only one job output a dependency file, that job is not + // batchable since it has an oddball set of additional output types. + if (C.OnlyOneDependencyFile && + A->getOutput().hasAdditionalOutputForType(file_types::TY_Dependencies)) + return false; return findSingleSwiftInput(CJActA) != nullptr; } diff --git a/test/Driver/advanced_output_file_map.swift b/test/Driver/advanced_output_file_map.swift index 4a32e9a8f0aa2..bf96b09364d0e 100644 --- a/test/Driver/advanced_output_file_map.swift +++ b/test/Driver/advanced_output_file_map.swift @@ -1,27 +1,95 @@ -// RUN: echo "{\"%/s\": {\"object\": \"/build/obj/advanced_output_file_map.o\", \"swiftmodule\": \"/build/swiftmodule/advanced_output_file_map.swiftmodule\", \"swiftdoc\": "/build/swiftmodule/advanced_output_file_map_x.swiftdoc", \"diagnostics\": \"/build/dia/advanced_output_file_map.dia\", \"dependencies\": \"/build/d/advanced_output_file_map.d\"}, \"%/S/Inputs/main.swift\": {\"object\": \"/build/obj/main.o\", \"swiftmodule\": \"/build/swiftmodule/main.swiftmodule\", \"swiftdoc\": "/build/swiftmodule/main_x.swiftdoc", \"diagnostics\": \"/build/dia/main.dia\", \"dependencies\": \"/build/d/main.d\"}, \"%/S/Inputs/lib.swift\": {\"object\": \"/build/obj/lib.o\", \"swiftmodule\": \"/build/swiftmodule/lib.swiftmodule\", \"swiftdoc\": \"/build/swiftmodule/lib_x.swiftdoc\", \"diagnostics\": \"/build/dia/lib.dia\", \"dependencies\": \"/build/d/lib.d\"}}" > %t.json - -// RUN: %swiftc_driver -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o /build/advanced_output_file_map.out -emit-module-path /build/OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM -// RUN: %swiftc_driver -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o /build/advanced_output_file_map.out -emit-module-path /build/OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS - -// DUMPOFM: {{.*}}/Inputs/lib.swift -> object: "/build/obj/lib.o" -// DUMPOFM-NEXT: {{.*}}/Inputs/lib.swift -> dependencies: "/build/d/lib.d" -// DUMPOFM-NEXT: {{.*}}/Inputs/lib.swift -> swiftmodule: "/build/swiftmodule/lib.swiftmodule" -// DUMPOFM-NEXT: {{.*}}/Inputs/lib.swift -> swiftdoc: "/build/swiftmodule/lib_x.swiftdoc" -// DUMPOFM-NEXT: {{.*}}/Inputs/lib.swift -> diagnostics: "/build/dia/lib.dia" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> object: "/build/obj/main.o" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> dependencies: "/build/d/main.d" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> swiftmodule: "/build/swiftmodule/main.swiftmodule" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> swiftdoc: "/build/swiftmodule/main_x.swiftdoc" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> diagnostics: "/build/dia/main.dia" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> object: "/build/obj/advanced_output_file_map.o" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> dependencies: "/build/d/advanced_output_file_map.d" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftmodule: "/build/swiftmodule/advanced_output_file_map.swiftmodule" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftdoc: "/build/swiftmodule/advanced_output_file_map_x.swiftdoc" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> diagnostics: "/build/dia/advanced_output_file_map.dia" - -// BINDINGS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/advanced_output_file_map.swift"], output: {object: "/build/obj/advanced_output_file_map.o", dependencies: "/build/d/advanced_output_file_map.d", swiftmodule: "/build/swiftmodule/advanced_output_file_map.swiftmodule", swiftdoc: "/build/swiftmodule/advanced_output_file_map_x.swiftdoc", swiftsourceinfo: "/build/swiftmodule{{[/\\]}}advanced_output_file_map.swiftsourceinfo", diagnostics: "/build/dia/advanced_output_file_map.dia"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/main.swift"], output: {object: "/build/obj/main.o", dependencies: "/build/d/main.d", swiftmodule: "/build/swiftmodule/main.swiftmodule", swiftdoc: "/build/swiftmodule/main_x.swiftdoc", swiftsourceinfo: "/build/swiftmodule{{[/\\]}}main.swiftsourceinfo", diagnostics: "/build/dia/main.dia"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/lib.swift"], output: {object: "/build/obj/lib.o", dependencies: "/build/d/lib.d", swiftmodule: "/build/swiftmodule/lib.swiftmodule", swiftdoc: "/build/swiftmodule/lib_x.swiftdoc", swiftsourceinfo: "/build/swiftmodule{{[/\\]}}lib.swiftsourceinfo", diagnostics: "/build/dia/lib.dia"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["/build/obj/advanced_output_file_map.o", "/build/obj/main.o", "/build/obj/lib.o"], output: {swiftmodule: "/build/OutputFileMap.swiftmodule", swiftdoc: "/build{{[/\\]}}OutputFileMap.swiftdoc", swiftsourceinfo: "/build{{[/\\]}}OutputFileMap.swiftsourceinfo"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "ld{{(.exe)?}}", inputs: ["/build/obj/advanced_output_file_map.o", "/build/obj/main.o", "/build/obj/lib.o", "/build/OutputFileMap.swiftmodule"], output: {image: "/build/advanced_output_file_map.out"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "dsymutil{{(\.exe)?}}", inputs: ["/build/advanced_output_file_map.out"], output: {dSYM: "/build/advanced_output_file_map.out.dSYM"} +// Test both ways: -disable-only-one-dependency-file, and the default which should be same as -enable-only-one-dependency-file + +// RUN: %empty-directory(%t) + +// Create an output file map +// RUN: echo "{\"%/s\": {\"object\": \"./obj/advanced_output_file_map.o\", \"swiftmodule\": \"./swiftmodule/advanced_output_file_map.swiftmodule\", \"swiftdoc\": "./swiftmodule/advanced_output_file_map_x.swiftdoc", \"diagnostics\": \"./dia/advanced_output_file_map.dia\", \"dependencies\": \"./d/advanced_output_file_map.d\"}, " >%t/ofm.json +// RUN: echo " \"%/S/Inputs/main.swift\": {\"object\": \"./obj/main.o\", \"swiftmodule\": \"./swiftmodule/main.swiftmodule\", \"swiftdoc\": \"./swiftmodule/main_x.swiftdoc\", \"diagnostics\": \"./dia/main.dia\", \"dependencies\": \"./d/main.d\"}, " >> %t/ofm.json +// RUN: echo " \"%/S/Inputs/lib.swift\": {\"object\": \"./obj/lib.o\", \"swiftmodule\": \"./swiftmodule/lib.swiftmodule\", \"swiftdoc\": \"./swiftmodule/lib_x.swiftdoc\", \"diagnostics\": \"./dia/lib.dia\", \"dependencies\": \"./d/lib.d\"}}" >> %t/ofm.json + +// With -disable-only-one-dependency-file + +// RUN: cd %t && %swiftc_driver -disable-only-one-dependency-file -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-DIS + +// DUMPOFM-DIS: {{.*}}/Inputs/lib.swift -> object: "./obj/lib.o" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/lib.swift -> dependencies: "./d/lib.d" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/lib.swift -> swiftmodule: "./swiftmodule/lib.swiftmodule" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/lib.swift -> swiftdoc: "./swiftmodule/lib_x.swiftdoc" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/lib.swift -> diagnostics: "./dia/lib.dia" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> object: "./obj/main.o" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> dependencies: "./d/main.d" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> swiftmodule: "./swiftmodule/main.swiftmodule" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> swiftdoc: "./swiftmodule/main_x.swiftdoc" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> diagnostics: "./dia/main.dia" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> object: "./obj/advanced_output_file_map.o" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> dependencies: "./d/advanced_output_file_map.d" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftmodule: "./swiftmodule/advanced_output_file_map.swiftmodule" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftdoc: "./swiftmodule/advanced_output_file_map_x.swiftdoc" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> diagnostics: "./dia/advanced_output_file_map.dia" + +// RUN: %empty-directory(%t/d) +// RUN: cd %t && %swiftc_driver -disable-only-one-dependency-file -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS-DIS + +// Should be no dummy files: +// RUN: test ! -e %t/d/advanced_output_file_map.d +// RUN: test ! -e %t/d/main.d +// RUN: test ! -e %t/d/lib.d + + +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/advanced_output_file_map.swift"], output: {object: "./obj/advanced_output_file_map.o", dependencies: "./d/advanced_output_file_map.d", swiftmodule: "./swiftmodule/advanced_output_file_map.swiftmodule", swiftdoc: "./swiftmodule/advanced_output_file_map_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}advanced_output_file_map.swiftsourceinfo", diagnostics: "./dia/advanced_output_file_map.dia"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/main.swift"], output: {object: "./obj/main.o", dependencies: "./d/main.d", swiftmodule: "./swiftmodule/main.swiftmodule", swiftdoc: "./swiftmodule/main_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}main.swiftsourceinfo", diagnostics: "./dia/main.dia"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/lib.swift"], output: {object: "./obj/lib.o", dependencies: "./d/lib.d", swiftmodule: "./swiftmodule/lib.swiftmodule", swiftdoc: "./swiftmodule/lib_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}lib.swiftsourceinfo", diagnostics: "./dia/lib.dia"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o"], output: {swiftmodule: "./OutputFileMap.swiftmodule", swiftdoc: ".{{[/\\]}}OutputFileMap.swiftdoc", swiftsourceinfo: ".{{[/\\]}}OutputFileMap.swiftsourceinfo"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "ld{{(.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o", "./OutputFileMap.swiftmodule"], output: {image: "./advanced_output_file_map.out"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "dsymutil{{(\.exe)?}}", inputs: ["./advanced_output_file_map.out"], output: {dSYM: "./advanced_output_file_map.out.dSYM"} + + +// With -enable-only-one-dependency-file + +// RUN: cd %t && %swiftc_driver -enable-only-one-dependency-file -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-ENA + + +// DUMPOFM-ENA: {{.*}}/Inputs/lib.swift -> object: "./obj/lib.o" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/lib.swift -> dependencies: "./d/lib.d" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/lib.swift -> swiftmodule: "./swiftmodule/lib.swiftmodule" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/lib.swift -> swiftdoc: "./swiftmodule/lib_x.swiftdoc" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/lib.swift -> diagnostics: "./dia/lib.dia" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> object: "./obj/main.o" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> dependencies: "./d/main.d" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> swiftmodule: "./swiftmodule/main.swiftmodule" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> swiftdoc: "./swiftmodule/main_x.swiftdoc" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> diagnostics: "./dia/main.dia" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> object: "./obj/advanced_output_file_map.o" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> dependencies: "./d/advanced_output_file_map.d" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftmodule: "./swiftmodule/advanced_output_file_map.swiftmodule" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftdoc: "./swiftmodule/advanced_output_file_map_x.swiftdoc" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> diagnostics: "./dia/advanced_output_file_map.dia" + +// RUN: %empty-directory(%t/d) +// RUN: cd %t && %swiftc_driver -enable-only-one-dependency-file -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS-ENA + + +// Should be two dummy files: +// RUN: test ! -e %t/d/advanced_output_file_map.d +// RUN: test -e %t/d/main.d -a ! -s %t/d/main.d +// RUN: test -e %t/d/lib.d -a ! -s %t/d/lib.d + + +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/advanced_output_file_map.swift"], output: {object: "./obj/advanced_output_file_map.o", dependencies: "./d/advanced_output_file_map.d", swiftmodule: "./swiftmodule/advanced_output_file_map.swiftmodule", swiftdoc: "./swiftmodule/advanced_output_file_map_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}advanced_output_file_map.swiftsourceinfo", diagnostics: "./dia/advanced_output_file_map.dia"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/main.swift"], output: {object: "./obj/main.o", swiftmodule: "./swiftmodule/main.swiftmodule", swiftdoc: "./swiftmodule/main_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}main.swiftsourceinfo", diagnostics: "./dia/main.dia"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/lib.swift"], output: {object: "./obj/lib.o", swiftmodule: "./swiftmodule/lib.swiftmodule", swiftdoc: "./swiftmodule/lib_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}lib.swiftsourceinfo", diagnostics: "./dia/lib.dia"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o"], output: {swiftmodule: "./OutputFileMap.swiftmodule", swiftdoc: ".{{[/\\]}}OutputFileMap.swiftdoc", swiftsourceinfo: ".{{[/\\]}}OutputFileMap.swiftsourceinfo"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "ld{{(.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o", "./OutputFileMap.swiftmodule"], output: {image: "./advanced_output_file_map.out"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "dsymutil{{(\.exe)?}}", inputs: ["./advanced_output_file_map.out"], output: {dSYM: "./advanced_output_file_map.out.dSYM"} + +// Defaulting to: -enable-only-one-dependency-file + +// RUN: %swiftc_driver -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-ENA + +// Should be two dummy files: + +// RUN: %empty-directory(%t/d) +// RUN: %swiftc_driver -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | tee /tmp/out | %FileCheck %/s -check-prefix=BINDINGS-ENA +// RUN: test ! -e %t/d/advanced_output_file_map.d +// RUN: test -e %t/d/main.d -a ! -s %t/d/main.d +// RUN: test -e %t/d/lib.d -a ! -s %t/d/lib.d diff --git a/validation-test/Driver/batch_mode_size_limit.swift b/validation-test/Driver/batch_mode_size_limit.swift index 0c55c326dacee..7fdb1d0f710f3 100644 --- a/validation-test/Driver/batch_mode_size_limit.swift +++ b/validation-test/Driver/batch_mode_size_limit.swift @@ -1,3 +1,5 @@ +// Without the only-one-dependency-file mode, all compile jobs are batchable, since the have the same output types. + // RUN: %empty-directory(%t) // RUN: touch %t/f_1_1.swift %t/f_1_2.swift %t/f_1_3.swift %t/f_1_4.swift %t/f_1_5.swift %t/f_1_6.swift %t/f_1_7.swift %t/f_1_8.swift %t/f_1_9.swift %t/f_1_10.swift // RUN: touch %t/f_2_1.swift %t/f_2_2.swift %t/f_2_3.swift %t/f_2_4.swift %t/f_2_5.swift %t/f_2_6.swift %t/f_2_7.swift %t/f_2_8.swift %t/f_2_9.swift %t/f_2_10.swift @@ -9,7 +11,7 @@ // RUN: touch %t/f_8_1.swift %t/f_8_2.swift %t/f_8_3.swift %t/f_8_4.swift %t/f_8_5.swift %t/f_8_6.swift %t/f_8_7.swift %t/f_8_8.swift %t/f_8_9.swift %t/f_8_10.swift // RUN: touch %t/f_9_1.swift %t/f_9_2.swift %t/f_9_3.swift %t/f_9_4.swift %t/f_9_5.swift %t/f_9_6.swift %t/f_9_7.swift %t/f_9_8.swift %t/f_9_9.swift %t/f_9_10.swift // RUN: touch %t/f_10_1.swift %t/f_10_2.swift %t/f_10_3.swift %t/f_10_4.swift %t/f_10_5.swift %t/f_10_6.swift %t/f_10_7.swift %t/f_10_8.swift %t/f_10_9.swift %t/f_10_10.swift -// RUN: %swiftc_driver -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 +// RUN: %swiftc_driver -disable-only-one-dependency-file -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 // RUN: %FileCheck %s <%t/out.txt // CHECK-NOT: unable to execute command // CHECK: Forming into 4 batches @@ -18,8 +20,8 @@ // CHECK: Forming batch job from 25 constituents // CHECK: Forming batch job from 25 constituents // -// RUN: %swiftc_driver -driver-show-job-lifecycle -driver-batch-size-limit 10 -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 -// RUN: %FileCheck %s <%t/out.txt -check-prefix=EXPLICIT-ARG +// RUN: %swiftc_driver -disable-only-one-dependency-file -driver-show-job-lifecycle -driver-batch-size-limit 10 -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out2.txt 2>&1 +// RUN: %FileCheck %s <%t/out2.txt -check-prefix=EXPLICIT-ARG // EXPLICIT-ARG-NOT: unable to execute command // EXPLICIT-ARG: Forming into 10 batches // EXPLICIT-ARG: Forming batch job from 10 constituents diff --git a/validation-test/Driver/batch_mode_size_limit_only_one_dependency-file.swift b/validation-test/Driver/batch_mode_size_limit_only_one_dependency-file.swift new file mode 100644 index 0000000000000..aca01575a7e3b --- /dev/null +++ b/validation-test/Driver/batch_mode_size_limit_only_one_dependency-file.swift @@ -0,0 +1,36 @@ +// When -enable-only-one-dependency-file (which is the default) the one compile job with the dependency output is unbatchable. + +// RUN: %empty-directory(%t) +// RUN: touch %t/f_1_1.swift %t/f_1_2.swift %t/f_1_3.swift %t/f_1_4.swift %t/f_1_5.swift %t/f_1_6.swift %t/f_1_7.swift %t/f_1_8.swift %t/f_1_9.swift %t/f_1_10.swift +// RUN: touch %t/f_2_1.swift %t/f_2_2.swift %t/f_2_3.swift %t/f_2_4.swift %t/f_2_5.swift %t/f_2_6.swift %t/f_2_7.swift %t/f_2_8.swift %t/f_2_9.swift %t/f_2_10.swift +// RUN: touch %t/f_3_1.swift %t/f_3_2.swift %t/f_3_3.swift %t/f_3_4.swift %t/f_3_5.swift %t/f_3_6.swift %t/f_3_7.swift %t/f_3_8.swift %t/f_3_9.swift %t/f_3_10.swift +// RUN: touch %t/f_4_1.swift %t/f_4_2.swift %t/f_4_3.swift %t/f_4_4.swift %t/f_4_5.swift %t/f_4_6.swift %t/f_4_7.swift %t/f_4_8.swift %t/f_4_9.swift %t/f_4_10.swift +// RUN: touch %t/f_5_1.swift %t/f_5_2.swift %t/f_5_3.swift %t/f_5_4.swift %t/f_5_5.swift %t/f_5_6.swift %t/f_5_7.swift %t/f_5_8.swift %t/f_5_9.swift %t/f_5_10.swift +// RUN: touch %t/f_6_1.swift %t/f_6_2.swift %t/f_6_3.swift %t/f_6_4.swift %t/f_6_5.swift %t/f_6_6.swift %t/f_6_7.swift %t/f_6_8.swift %t/f_6_9.swift %t/f_6_10.swift +// RUN: touch %t/f_7_1.swift %t/f_7_2.swift %t/f_7_3.swift %t/f_7_4.swift %t/f_7_5.swift %t/f_7_6.swift %t/f_7_7.swift %t/f_7_8.swift %t/f_7_9.swift %t/f_7_10.swift +// RUN: touch %t/f_8_1.swift %t/f_8_2.swift %t/f_8_3.swift %t/f_8_4.swift %t/f_8_5.swift %t/f_8_6.swift %t/f_8_7.swift %t/f_8_8.swift %t/f_8_9.swift %t/f_8_10.swift +// RUN: touch %t/f_9_1.swift %t/f_9_2.swift %t/f_9_3.swift %t/f_9_4.swift %t/f_9_5.swift %t/f_9_6.swift %t/f_9_7.swift %t/f_9_8.swift %t/f_9_9.swift %t/f_9_10.swift +// RUN: touch %t/f_10_1.swift %t/f_10_2.swift %t/f_10_3.swift %t/f_10_4.swift %t/f_10_5.swift %t/f_10_6.swift %t/f_10_7.swift %t/f_10_8.swift %t/f_10_9.swift %t/f_10_10.swift +// RUN: %swiftc_driver -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 +// RUN: %FileCheck %s <%t/out.txt +// CHECK-NOT: unable to execute command +// CHECK: Forming into 4 batches +// CHECK-DAG: Forming batch job from 25 constituents +// CHECK-DAG: Forming batch job from 25 constituents +// CHECK-DAG: Forming batch job from 25 constituents +// CHECK-DAG: Forming batch job from 24 constituents +// +// RUN: %swiftc_driver -driver-show-job-lifecycle -driver-batch-size-limit 10 -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out2.txt 2>&1 +// RUN: %FileCheck %s <%t/out2.txt -check-prefix=EXPLICIT-ARG +// EXPLICIT-ARG-NOT: unable to execute command +// EXPLICIT-ARG: Forming into 10 batches +// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DAG: Forming batch job from 9 constituents From d2f7a27190ffa9079acd6145c8a492e80c8f428e Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 17 Dec 2019 16:19:53 -0800 Subject: [PATCH 101/478] [NFC] Const-ify Some ClangImporter Interfaces --- lib/ClangImporter/ImportDecl.cpp | 18 +++++++++--------- lib/ClangImporter/ImporterImpl.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index f734bb31d4d93..d00b6fcfbcc76 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -104,7 +104,7 @@ getDefaultMakeStructRawValuedOptions() { return opts; } -static bool isInSystemModule(DeclContext *D) { +static bool isInSystemModule(const DeclContext *D) { return cast(D->getModuleScopeContext())->isSystemModule(); } @@ -4091,7 +4091,7 @@ namespace { /// Check whether we have already imported a method with the given /// selector in the given context. bool isMethodAlreadyImported(ObjCSelector selector, bool isInstance, - DeclContext *dc, + const DeclContext *dc, llvm::function_ref filter) { // We only need to perform this check for classes. auto classDecl @@ -4425,7 +4425,7 @@ namespace { /// NSArray(capacity: 1024) /// \endcode ConstructorDecl *importConstructor(const clang::ObjCMethodDecl *objcMethod, - DeclContext *dc, + const DeclContext *dc, bool implicit, Optional kind, bool required); @@ -4454,7 +4454,7 @@ namespace { /// This variant of the function is responsible for actually binding the /// constructor declaration appropriately. ConstructorDecl *importConstructor(const clang::ObjCMethodDecl *objcMethod, - DeclContext *dc, + const DeclContext *dc, bool implicit, CtorInitializerKind kind, bool required, @@ -4527,7 +4527,7 @@ namespace { /// Import constructors from our superclasses (and their /// categories/extensions), effectively "inheriting" constructors. - void importInheritedConstructors(ClassDecl *classDecl, + void importInheritedConstructors(const ClassDecl *classDecl, SmallVectorImpl &newMembers); Decl *VisitObjCCategoryDecl(const clang::ObjCCategoryDecl *decl) { @@ -6083,7 +6083,7 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName, } ConstructorDecl *SwiftDeclConverter::importConstructor( - const clang::ObjCMethodDecl *objcMethod, DeclContext *dc, bool implicit, + const clang::ObjCMethodDecl *objcMethod, const DeclContext *dc, bool implicit, Optional kind, bool required) { // Only methods in the 'init' family can become constructors. assert(isInitMethod(objcMethod) && "Not a real init method"); @@ -6234,7 +6234,7 @@ bool SwiftDeclConverter::existingConstructorIsWorse( /// This variant of the function is responsible for actually binding the /// constructor declaration appropriately. ConstructorDecl *SwiftDeclConverter::importConstructor( - const clang::ObjCMethodDecl *objcMethod, DeclContext *dc, bool implicit, + const clang::ObjCMethodDecl *objcMethod, const DeclContext *dc, bool implicit, CtorInitializerKind kind, bool required, ObjCSelector selector, ImportedName importedName, ArrayRef args, bool variadic, bool &redundant) { @@ -6354,7 +6354,7 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( /*NameLoc=*/SourceLoc(), failability, /*FailabilityLoc=*/SourceLoc(), /*Throws=*/importedName.getErrorInfo().hasValue(), /*ThrowsLoc=*/SourceLoc(), bodyParams, - /*GenericParams=*/nullptr, dc); + /*GenericParams=*/nullptr, const_cast(dc)); addObjCAttribute(result, selector); @@ -7189,7 +7189,7 @@ void SwiftDeclConverter::importNonOverriddenMirroredMethods(DeclContext *dc, } void SwiftDeclConverter::importInheritedConstructors( - ClassDecl *classDecl, SmallVectorImpl &newMembers) { + const ClassDecl *classDecl, SmallVectorImpl &newMembers) { if (!classDecl->hasSuperclass()) return; diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index dc09261781c61..b90f8b7f3cb08 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -492,7 +492,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// Keep track of initializer declarations that correspond to /// imported methods. llvm::DenseMap< - std::tuple, + std::tuple, ConstructorDecl *> Constructors; /// Keep track of all initializers that have been imported into a From ac75f31aca73d104eb0c9d2510b263f97b483511 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 17 Dec 2019 16:22:27 -0800 Subject: [PATCH 102/478] Drop unused argument to mirrored member importing --- lib/ClangImporter/ImportDecl.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index d00b6fcfbcc76..6c9c6c0a26a04 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -6941,8 +6941,7 @@ Optional SwiftDeclConverter::importObjCGenericParams( void SwiftDeclConverter::importMirroredProtocolMembers( const clang::ObjCContainerDecl *decl, DeclContext *dc, - ArrayRef protocols, SmallVectorImpl &members, - ASTContext &Ctx) { + ArrayRef protocols, SmallVectorImpl &members) { assert(dc); const clang::ObjCInterfaceDecl *interfaceDecl = nullptr; const ClangModuleUnit *declModule; @@ -8703,8 +8702,7 @@ void ClangImporter::Implementation::collectMembersToAdd( // Import mirrored declarations for protocols to which this category // or extension conforms. // FIXME: This is supposed to be a short-term hack. - converter.importMirroredProtocolMembers(objcContainer, DC, - protos, members, SwiftContext); + converter.importMirroredProtocolMembers(objcContainer, DC, protos, members); } void ClangImporter::Implementation::loadAllConformances( From a1b451470efbc7b8c076a53db47bbde3568e4564 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 17 Dec 2019 16:33:02 -0800 Subject: [PATCH 103/478] Teach loadNamedMembers to import inherited constructors --- lib/AST/NameLookup.cpp | 7 ------- lib/ClangImporter/ClangImporter.cpp | 10 +++++++++ lib/ClangImporter/ImportDecl.cpp | 21 ++++++++++--------- lib/ClangImporter/ImporterImpl.h | 4 ++++ test/ClangImporter/attr-swift_name.swift | 2 +- .../NamedLazyMembers/NamedLazyMembers.h | 12 +++++++++++ ...d_lazy_member_loading_objc_interface.swift | 14 +++++++++---- 7 files changed, 48 insertions(+), 22 deletions(-) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 48c4fdd102be2..02a3e9cd46a95 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1274,13 +1274,6 @@ TinyPtrVector NominalTypeDecl::lookupDirect( bool includeAttrImplements = flags.contains(LookupDirectFlags::IncludeAttrImplements); - // FIXME: At present, lazy member is not able to find inherited constructors - // in imported classes, because SwiftDeclConverter::importInheritedConstructors() - // is only called via ClangImporter::Implementation::loadAllMembers(). - if (hasClangNode() && - name.getBaseName() == DeclBaseName::createConstructor()) - useNamedLazyMemberLoading = false; - LLVM_DEBUG(llvm::dbgs() << getNameStr() << ".lookupDirect(" << name << ")" << ", isLookupTablePopulated()=" << isLookupTablePopulated() diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 125c39f0b27a9..e74b691817ed7 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3822,6 +3822,16 @@ ClangImporter::Implementation::loadNamedMembers( } } } + + if (N == DeclBaseName::createConstructor()) { + if (auto *classDecl = dyn_cast(D)) { + SmallVector ctors; + importInheritedConstructors(cast(CD), + classDecl, ctors); + for (auto ctor : ctors) + Members.push_back(cast(ctor)); + } + } return Members; } diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 6c9c6c0a26a04..46748e909aac0 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -4518,8 +4518,7 @@ namespace { void importMirroredProtocolMembers(const clang::ObjCContainerDecl *decl, DeclContext *dc, ArrayRef protocols, - SmallVectorImpl &members, - ASTContext &Ctx); + SmallVectorImpl &members); void importNonOverriddenMirroredMethods(DeclContext *dc, MutableArrayRef entries, @@ -8672,6 +8671,15 @@ void ClangImporter::Implementation::insertMembersAndAlternates( }); } +void ClangImporter::Implementation::importInheritedConstructors( + const clang::ObjCInterfaceDecl *curObjCClass, + const ClassDecl *classDecl, SmallVectorImpl &newMembers) { + if (curObjCClass->getName() != "Protocol") { + SwiftDeclConverter converter(*this, CurrentVersion); + converter.importInheritedConstructors(classDecl, newMembers); + } + } + void ClangImporter::Implementation::collectMembersToAdd( const clang::ObjCContainerDecl *objcContainer, Decl *D, DeclContext *DC, SmallVectorImpl &members) { @@ -8686,15 +8694,8 @@ void ClangImporter::Implementation::collectMembersToAdd( auto protos = getImportedProtocols(D); if (auto clangClass = dyn_cast(objcContainer)) { - auto swiftClass = cast(D); objcContainer = clangClass = clangClass->getDefinition(); - - // Imported inherited initializers. - if (clangClass->getName() != "Protocol") { - converter.importInheritedConstructors(const_cast(swiftClass), - members); - } - + importInheritedConstructors(clangClass, cast(D), members); } else if (auto clangProto = dyn_cast(objcContainer)) { objcContainer = clangProto->getDefinition(); diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index b90f8b7f3cb08..f1e24877cd594 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -812,6 +812,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation Decl *importMirroredDecl(const clang::NamedDecl *decl, DeclContext *dc, Version version, ProtocolDecl *proto); + void importInheritedConstructors(const clang::ObjCInterfaceDecl *curObjCClass, + const ClassDecl *classDecl, + SmallVectorImpl &newMembers); + /// Utility function for building simple generic signatures. GenericSignature buildGenericSignature(GenericParamList *genericParams, DeclContext *dc); diff --git a/test/ClangImporter/attr-swift_name.swift b/test/ClangImporter/attr-swift_name.swift index 7cb5da9580bbe..f0cb32c55e385 100644 --- a/test/ClangImporter/attr-swift_name.swift +++ b/test/ClangImporter/attr-swift_name.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t.mcp) -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -Xcc -w -typecheck %s -module-cache-path %t.mcp 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -Xcc -w -typecheck %s -module-cache-path %t.mcp -disable-named-lazy-member-loading 2>&1 | %FileCheck %s // REQUIRES: objc_interop diff --git a/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h b/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h index f018637dc7ebb..86d93cc55e0d0 100644 --- a/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h +++ b/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h @@ -26,6 +26,8 @@ // Don't conform to the protocol; that loads all protocol members. @interface SimpleDoer +- (instancetype)initWithValue: (int)value; + // These are names we're hoping don't interfere with Doer, above. + (SimpleDoer*)Doer; + (SimpleDoer*)DoerOfNoWork; @@ -107,4 +109,14 @@ @interface SimpleDoerSubclass : SimpleDoer - (void)simplyDoSomeWorkWithSpeed:(int)s thoroughness:(int)t NS_SWIFT_NAME(simplyDoVeryImportantWork(speed:thoroughness:)); + +- (void)exuberantlyGoForWalk; +- (void)exuberantlyTakeNap; +- (void)exuberantlyEatMeal; +- (void)exuberantlyTidyHome; +- (void)exuberantlyCallFamily; +- (void)exuberantlySingSong; +- (void)exuberantlyReadBook; +- (void)exuberantlyAttendLecture; +- (void)exuberantlyWriteLetter; @end diff --git a/test/NameBinding/named_lazy_member_loading_objc_interface.swift b/test/NameBinding/named_lazy_member_loading_objc_interface.swift index c3d9be7d1a6b5..d4201213bc328 100644 --- a/test/NameBinding/named_lazy_member_loading_objc_interface.swift +++ b/test/NameBinding/named_lazy_member_loading_objc_interface.swift @@ -8,17 +8,23 @@ // Check that named-lazy-member-loading reduces the number of Decls deserialized // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre -primary-file %s %S/Inputs/NamedLazyMembers/NamedLazyMembersExt.swift // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post -primary-file %s %S/Inputs/NamedLazyMembers/NamedLazyMembersExt.swift -// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities <= -1' %t/stats-pre %t/stats-post +// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities <= -10' %t/stats-pre %t/stats-post import NamedLazyMembers -public func bar(d: SimpleDoerSubclass) { - let _ = d.simplyDoVeryImportantWork(speed: 10, motivation: 42) +public func bar() { + let d = SimpleDoerSubclass(value: 123)! + let _ = d.simplyDoVeryImportantWork(speed: 10, motivation: 42) } -public func foo(d: SimpleDoer) { +public func foo() { + let d = SimpleDoer(value: 123)! let _ = d.simplyDoSomeWork() let _ = d.simplyDoSomeWork(withSpeed:10) let _ = d.simplyDoVeryImportantWork(speed:10, thoroughness:12) let _ = d.simplyDoSomeWorkWithSpeed(speed:10, levelOfAlacrity:12) } + +// Make sure that simply subclassing an imported subclass doesn't page in all +// members. +class MostDoerSubclass : SimpleDoerSubclass {} From b654bc358954a32a385050fbdc1f040a27c3fa49 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 18 Dec 2019 16:07:00 -0800 Subject: [PATCH 104/478] Force load all members of iterable contexts in the DWARF Importer The DWARF Importer does not yet know how to answer a request at the level of a single member. All the tests that saw this as an observable behavior were actually seeing a complete cache fill from lazy member loading an initializer fail over to flushing the member table and rebuilding it by forcing all the members. Given that the cache flush is something we're trying to avoid in general, this is obviously undesirable behavior. Now that we're no longer flushing the cache for initializers, the DWARF Importer needs to simulate the old behavior and completely deserialize members of loaded types. This pessimization is justified anyways, considering if you're loading from DWARF, you're probably about to print the type and its fields anyways. --- lib/ClangImporter/DWARFImporter.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/ClangImporter/DWARFImporter.cpp b/lib/ClangImporter/DWARFImporter.cpp index 654fd287d9ce4..c1168b8d2569d 100644 --- a/lib/ClangImporter/DWARFImporter.cpp +++ b/lib/ClangImporter/DWARFImporter.cpp @@ -128,6 +128,17 @@ ModuleDecl *ClangImporter::Implementation::loadModuleDWARF( return decl; } +// This function exists to defeat the lazy member importing mechanism. The +// DWARFImporter is not capable of loading individual members, so it cannot +// benefit from this optimization yet anyhow. Besides, if you're importing a +// type here, you more than likely want to dump it and its fields. Loading all +// members populates lookup tables in the Clang Importer and ensures the +// absence of cache-fill-related side effects. +static void forceLoadAllMembers(IterableDeclContext *IDC) { + if (!IDC) return; + IDC->loadAllMembers(); +} + void ClangImporter::Implementation::lookupValueDWARF( DeclName name, NLKind lookupKind, Identifier inModule, SmallVectorImpl &results) { @@ -150,8 +161,10 @@ void ClangImporter::Implementation::lookupValueDWARF( continue; if (swiftDecl->getFullName().matchesRef(name) && - swiftDecl->getDeclContext()->isModuleScopeContext()) + swiftDecl->getDeclContext()->isModuleScopeContext()) { + forceLoadAllMembers(dyn_cast(swiftDecl)); results.push_back(swiftDecl); + } } } @@ -174,8 +187,10 @@ void ClangImporter::Implementation::lookupTypeDeclDWARF( Decl *importedDecl = cast_or_null( importDeclReal(namedDecl->getMostRecentDecl(), CurrentVersion)); - if (auto *importedType = dyn_cast_or_null(importedDecl)) + if (auto *importedType = dyn_cast_or_null(importedDecl)) { + forceLoadAllMembers(dyn_cast(importedType)); receiver(importedType); + } } } From 3e47e23aeadd5cf03affa4468b50f4ad906d00f6 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 13 Dec 2019 13:51:20 -0800 Subject: [PATCH 105/478] [semantic-arc] Small cleanups + some comments. NFC. --- .../Mandatory/SemanticARCOpts.cpp | 59 ++++++++----------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp index f75872d3dc225..2333a2fb84a3d 100644 --- a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp @@ -36,10 +36,6 @@ STATISTIC(NumEliminatedInsts, "number of removed instructions"); STATISTIC(NumLoadCopyConvertedToLoadBorrow, "number of load_copy converted to load_borrow"); -//===----------------------------------------------------------------------===// -// Utility -//===----------------------------------------------------------------------===// - //===----------------------------------------------------------------------===// // Live Range Modeling //===----------------------------------------------------------------------===// @@ -653,12 +649,14 @@ bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi) bool SemanticARCOptVisitor::visitCopyValueInst(CopyValueInst *cvi) { // If our copy value inst has only destroy_value users, it is a dead live // range. Try to eliminate them. - if (eliminateDeadLiveRangeCopyValue(cvi)) + if (eliminateDeadLiveRangeCopyValue(cvi)) { return true; + } // Then try to perform the guaranteed copy value optimization. - if (performGuaranteedCopyValueOptimization(cvi)) + if (performGuaranteedCopyValueOptimization(cvi)) { return true; + } return false; } @@ -667,35 +665,12 @@ bool SemanticARCOptVisitor::visitCopyValueInst(CopyValueInst *cvi) { // load [copy] Optimizations //===----------------------------------------------------------------------===// -// A flow insensitive analysis that tells the load [copy] analysis if the -// storage has 0, 1, >1 writes to it. -// -// In the case of 0 writes, we return CanOptimizeLoadCopyResult::Always. -// -// In the case of 1 write, we return OnlyIfStorageIsLocal. We are taking -// advantage of definite initialization implying that an alloc_stack must be -// written to once before any loads from the memory location. Thus if we are -// local and see 1 write, we can still change to load_borrow if all other uses -// check out. -// -// If there is 2+ writes, we can not optimize = (. - -bool mayFunctionMutateArgument(const AccessedStorage &storage, SILFunction &f) { - auto *arg = cast(storage.getArgument()); - - // Then check if we have an in_guaranteed argument. In this case, we can - // always optimize load [copy] from this. - if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) - return false; - - // For now just return false. - return true; -} - -// Then find our accessed storage to determine whether it provides a guarantee -// for the loaded value. namespace { +/// A class that computes in a flow insensitive way if we can prove that our +/// storage is either never written to, or is initialized exactly once and never +/// written to again. In both cases, we can convert load [copy] -> load_borrow +/// safely. class StorageGuaranteesLoadVisitor : public AccessUseDefChainVisitor { @@ -733,9 +708,21 @@ class StorageGuaranteesLoadVisitor } void visitArgumentAccess(SILFunctionArgument *arg) { - return answer(mayFunctionMutateArgument( - AccessedStorage(arg, AccessedStorage::Argument), - ARCOpt.F)); + // If this load_copy is from an indirect in_guaranteed argument, then we + // know for sure that it will never be written to. + if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) { + return answer(false); + } + + // TODO: This should be extended: + // + // 1. We should be able to analyze inout arguments and see if the inout + // argument is never actually written to in a flow insensitive way. + // + // 2. We should be able to analyze in arguments and see if they are only + // ever destroyed at the end of the function. In such a case, we may be + // able to also to promote load [copy] from such args to load_borrow. + return answer(true); } void visitGlobalAccess(SILValue global) { From d0e957885f4a39338555c9ab21476924b770ced7 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Wed, 18 Dec 2019 17:17:29 -0800 Subject: [PATCH 106/478] [NFC] Use CompoundDeclName for zero-arg names Currently DeclName uses an inline representation for compound names with no argument labels. This is more compact, but it costs a spare bit that we need for other purposes. This commit switches over to representing this using a separate CompoundDeclName allocation with zero trailing argument labels instead. --- include/swift/AST/Identifier.h | 46 +++++++++++++--------------------- lib/AST/ASTContext.cpp | 9 ++----- 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/include/swift/AST/Identifier.h b/include/swift/AST/Identifier.h index f0106a68db56f..05b595d3953eb 100644 --- a/include/swift/AST/Identifier.h +++ b/include/swift/AST/Identifier.h @@ -402,9 +402,7 @@ class DeclName { size_t NumArgs; explicit CompoundDeclName(DeclBaseName BaseName, size_t NumArgs) - : BaseName(BaseName), NumArgs(NumArgs) { - assert(NumArgs > 0 && "Should use IdentifierAndCompound"); - } + : BaseName(BaseName), NumArgs(NumArgs) { } ArrayRef getArgumentNames() const { return {getTrailingObjects(), NumArgs}; @@ -422,16 +420,12 @@ class DeclName { } }; - // A single stored identifier, along with a bit stating whether it is the - // base name for a zero-argument compound name. - typedef llvm::PointerIntPair BaseNameAndCompound; - - // Either a single identifier piece stored inline (with a bit to say whether - // it is simple or compound), or a reference to a compound declaration name. - llvm::PointerUnion SimpleOrCompound; + /// Either a single identifier piece stored inline, or a reference to a + /// compound declaration name. + llvm::PointerUnion BaseNameOrCompound; explicit DeclName(void *Opaque) - : SimpleOrCompound(decltype(SimpleOrCompound)::getFromOpaqueValue(Opaque)) + : BaseNameOrCompound(decltype(BaseNameOrCompound)::getFromOpaqueValue(Opaque)) {} void initialize(ASTContext &C, DeclBaseName baseName, @@ -439,11 +433,11 @@ class DeclName { public: /// Build a null name. - DeclName() : SimpleOrCompound(BaseNameAndCompound()) {} + DeclName() : BaseNameOrCompound(DeclBaseName()) {} /// Build a simple value name with one component. /*implicit*/ DeclName(DeclBaseName simpleName) - : SimpleOrCompound(BaseNameAndCompound(simpleName, false)) {} + : BaseNameOrCompound(simpleName) {} /*implicit*/ DeclName(Identifier simpleName) : DeclName(DeclBaseName(simpleName)) {} @@ -462,10 +456,10 @@ class DeclName { /// such as the 'foo' in 'func foo(x:Int, y:Int)' or the 'bar' in /// 'var bar: Int'. DeclBaseName getBaseName() const { - if (auto compound = SimpleOrCompound.dyn_cast()) + if (auto compound = BaseNameOrCompound.dyn_cast()) return compound->BaseName; - return SimpleOrCompound.get().getPointer(); + return BaseNameOrCompound.get(); } /// Assert that the base name is not special and return its identifier. @@ -478,7 +472,7 @@ class DeclName { /// Retrieve the names of the arguments, if there are any. ArrayRef getArgumentNames() const { - if (auto compound = SimpleOrCompound.dyn_cast()) + if (auto compound = BaseNameOrCompound.dyn_cast()) return compound->getArgumentNames(); return { }; @@ -487,25 +481,19 @@ class DeclName { bool isSpecial() const { return getBaseName().isSpecial(); } explicit operator bool() const { - if (SimpleOrCompound.dyn_cast()) + if (BaseNameOrCompound.dyn_cast()) return true; - return !SimpleOrCompound.get().getPointer().empty(); + return !BaseNameOrCompound.get().empty(); } /// True if this is a simple one-component name. bool isSimpleName() const { - if (SimpleOrCompound.dyn_cast()) - return false; - - return !SimpleOrCompound.get().getInt(); + return BaseNameOrCompound.is(); } /// True if this is a compound name. bool isCompoundName() const { - if (SimpleOrCompound.dyn_cast()) - return true; - - return SimpleOrCompound.get().getInt(); + return !isSimpleName(); } /// True if this name is a simple one-component name identical to the @@ -540,7 +528,7 @@ class DeclName { /// matches a simple name lookup or when the full compound name matches. bool matchesRef(DeclName refName) const { // Identical names always match. - if (SimpleOrCompound == refName.SimpleOrCompound) + if (BaseNameOrCompound == refName.BaseNameOrCompound) return true; // If the reference is a simple name, try simple name matching. if (refName.isSimpleName()) @@ -594,7 +582,7 @@ class DeclName { return lhs.compare(rhs) >= 0; } - void *getOpaqueValue() const { return SimpleOrCompound.getOpaqueValue(); } + void *getOpaqueValue() const { return BaseNameOrCompound.getOpaqueValue(); } static DeclName getFromOpaqueValue(void *p) { return DeclName(p); } /// Get a string representation of the name, @@ -913,7 +901,7 @@ namespace llvm { static inline swift::DeclName getFromVoidPointer(void *ptr) { return swift::DeclName::getFromOpaqueValue(ptr); } - enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable - 2 }; + enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable - 1 }; }; // DeclNames hash just like pointers. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 805f472be89d4..584fca9325b24 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3939,18 +3939,13 @@ void DeclName::CompoundDeclName::Profile(llvm::FoldingSetNodeID &id, void DeclName::initialize(ASTContext &C, DeclBaseName baseName, ArrayRef argumentNames) { - if (argumentNames.empty()) { - SimpleOrCompound = BaseNameAndCompound(baseName, true); - return; - } - llvm::FoldingSetNodeID id; CompoundDeclName::Profile(id, baseName, argumentNames); void *insert = nullptr; if (CompoundDeclName *compoundName = C.getImpl().CompoundNames.FindNodeOrInsertPos(id, insert)) { - SimpleOrCompound = compoundName; + BaseNameOrCompound = compoundName; return; } @@ -3960,7 +3955,7 @@ void DeclName::initialize(ASTContext &C, DeclBaseName baseName, auto compoundName = new (buf) CompoundDeclName(baseName,argumentNames.size()); std::uninitialized_copy(argumentNames.begin(), argumentNames.end(), compoundName->getArgumentNames().begin()); - SimpleOrCompound = compoundName; + BaseNameOrCompound = compoundName; C.getImpl().CompoundNames.InsertNode(compoundName, insert); } From f51f3b884d37ce23db36d8f705d2fa6b290881a1 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 18 Nov 2019 14:48:46 +0900 Subject: [PATCH 107/478] [SourceKit] Support VFS in 'typecontextinfo' and 'conformingmethods' req Align with code completion. --- .../include/SourceKit/Core/LangSupport.h | 6 ++-- .../lib/SwiftLang/SwiftCompletion.cpp | 3 +- .../SwiftLang/SwiftConformingMethodList.cpp | 26 ++++++++++---- .../lib/SwiftLang/SwiftLangSupport.h | 6 ++-- .../lib/SwiftLang/SwiftTypeContextInfo.cpp | 36 ++++++++++++------- .../tools/sourcekitd/lib/API/Requests.cpp | 24 ++++++++----- 6 files changed, 70 insertions(+), 31 deletions(-) diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index b1737186dcc58..f1e13394e611d 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -801,13 +801,15 @@ class LangSupport { virtual void getExpressionContextInfo(llvm::MemoryBuffer *inputBuf, unsigned Offset, ArrayRef Args, - TypeContextInfoConsumer &Consumer) = 0; + TypeContextInfoConsumer &Consumer, + Optional vfsOptions) = 0; virtual void getConformingMethodList(llvm::MemoryBuffer *inputBuf, unsigned Offset, ArrayRef Args, ArrayRef ExpectedTypes, - ConformingMethodListConsumer &Consumer) = 0; + ConformingMethodListConsumer &Consumer, + Optional vfsOptions) = 0; virtual void getStatistics(StatisticsReceiver) = 0; }; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index d3912b1e2c6ed..d884d1c5ea73d 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -1177,7 +1177,8 @@ static void transformAndForwardResults( void SwiftLangSupport::codeCompleteOpen( StringRef name, llvm::MemoryBuffer *inputBuf, unsigned offset, OptionsDictionary *options, ArrayRef rawFilterRules, - GroupedCodeCompletionConsumer &consumer, ArrayRef args, Optional vfsOptions) { + GroupedCodeCompletionConsumer &consumer, ArrayRef args, + Optional vfsOptions) { StringRef filterText; unsigned resultOffset = 0; unsigned maxResults = 0; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp index 375cd44d639e5..3994e4124526f 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp @@ -29,7 +29,9 @@ static bool swiftConformingMethodListImpl( SwiftLangSupport &Lang, llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, ArrayRef Args, ArrayRef ExpectedTypeNames, - ide::ConformingMethodListConsumer &Consumer, std::string &Error) { + ide::ConformingMethodListConsumer &Consumer, + llvm::IntrusiveRefCntPtr FileSystem, + std::string &Error) { auto bufferIdentifier = Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier()); @@ -59,7 +61,7 @@ static bool swiftConformingMethodListImpl( CompilerInvocation Invocation; bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, CI.getDiags(), bufferIdentifier, Error); + Invocation, Args, CI.getDiags(), bufferIdentifier, FileSystem, Error); if (Failed) return false; if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { @@ -81,6 +83,10 @@ static bool swiftConformingMethodListImpl( Invocation.setCodeCompletionFactory(callbacksFactory.get()); + if (FileSystem != llvm::vfs::getRealFileSystem()) { + CI.getSourceMgr().setFileSystem(FileSystem); + } + if (CI.setup(Invocation)) { // FIXME: error? return true; @@ -94,7 +100,15 @@ static bool swiftConformingMethodListImpl( void SwiftLangSupport::getConformingMethodList( llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, ArrayRef Args, ArrayRef ExpectedTypeNames, - SourceKit::ConformingMethodListConsumer &SKConsumer) { + SourceKit::ConformingMethodListConsumer &SKConsumer, + Optional vfsOptions) { + std::string error; + + // FIXME: the use of None as primary file is to match the fact we do not read + // the document contents using the editor documents infrastructure. + auto fileSystem = getFileSystem(vfsOptions, /*primaryFile=*/None, error); + if (!fileSystem) + return SKConsumer.failed(error); class Consumer : public ide::ConformingMethodListConsumer { SourceKit::ConformingMethodListConsumer &SKConsumer; @@ -210,9 +224,9 @@ void SwiftLangSupport::getConformingMethodList( } } Consumer(SKConsumer); - std::string Error; if (!swiftConformingMethodListImpl(*this, UnresolvedInputFile, Offset, Args, - ExpectedTypeNames, Consumer, Error)) { - SKConsumer.failed(Error); + ExpectedTypeNames, Consumer, fileSystem, + error)) { + SKConsumer.failed(error); } } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 5096798ab6a6a..21da3c982afaf 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -585,12 +585,14 @@ class SwiftLangSupport : public LangSupport { void getExpressionContextInfo(llvm::MemoryBuffer *inputBuf, unsigned Offset, ArrayRef Args, - TypeContextInfoConsumer &Consumer) override; + TypeContextInfoConsumer &Consumer, + Optional vfsOptions) override; void getConformingMethodList(llvm::MemoryBuffer *inputBuf, unsigned Offset, ArrayRef Args, ArrayRef ExpectedTypes, - ConformingMethodListConsumer &Consumer) override; + ConformingMethodListConsumer &Consumer, + Optional vfsOptions) override; void getStatistics(StatisticsReceiver) override; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp index 40ababa58c06c..80f7f408847dc 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp @@ -24,12 +24,12 @@ using namespace SourceKit; using namespace swift; using namespace ide; -static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang, - llvm::MemoryBuffer *UnresolvedInputFile, - unsigned Offset, - ArrayRef Args, - ide::TypeContextInfoConsumer &Consumer, - std::string &Error) { +static bool swiftTypeContextInfoImpl( + SwiftLangSupport &Lang, llvm::MemoryBuffer *UnresolvedInputFile, + unsigned Offset, ide::TypeContextInfoConsumer &Consumer, + ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + std::string &Error) { auto bufferIdentifier = Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier()); @@ -59,7 +59,7 @@ static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang, CompilerInvocation Invocation; bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, CI.getDiags(), bufferIdentifier, Error); + Invocation, Args, CI.getDiags(), bufferIdentifier, FileSystem, Error); if (Failed) return false; if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { @@ -80,6 +80,10 @@ static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang, Invocation.setCodeCompletionFactory(callbacksFactory.get()); + if (FileSystem != llvm::vfs::getRealFileSystem()) { + CI.getSourceMgr().setFileSystem(FileSystem); + } + if (CI.setup(Invocation)) { // FIXME: error? return true; @@ -93,7 +97,16 @@ static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang, void SwiftLangSupport::getExpressionContextInfo( llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, ArrayRef Args, - SourceKit::TypeContextInfoConsumer &SKConsumer) { + SourceKit::TypeContextInfoConsumer &SKConsumer, + Optional vfsOptions) { + std::string error; + + // FIXME: the use of None as primary file is to match the fact we do not read + // the document contents using the editor documents infrastructure. + auto fileSystem = getFileSystem(vfsOptions, /*primaryFile=*/None, error); + if (!fileSystem) + return SKConsumer.failed(error); + class Consumer : public ide::TypeContextInfoConsumer { SourceKit::TypeContextInfoConsumer &SKConsumer; @@ -187,9 +200,8 @@ void SwiftLangSupport::getExpressionContextInfo( } } Consumer(SKConsumer); - std::string Error; - if (!swiftTypeContextInfoImpl(*this, UnresolvedInputFile, Offset, Args, - Consumer, Error)) { - SKConsumer.failed(Error); + if (!swiftTypeContextInfoImpl(*this, UnresolvedInputFile, Offset, Consumer, + Args, fileSystem, error)) { + SKConsumer.failed(error); } } diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp index 66d184e0cf544..a1c14d088d9e4 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp @@ -175,12 +175,14 @@ static sourcekitd_response_t codeCompleteClose(StringRef name, int64_t Offset); static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, int64_t Offset, - ArrayRef Args); + ArrayRef Args, + Optional vfsOptions); static sourcekitd_response_t conformingMethodList(llvm::MemoryBuffer *InputBuf, int64_t Offset, ArrayRef Args, - ArrayRef ExpectedTypes); + ArrayRef ExpectedTypes, + Optional vfsOptions); static sourcekitd_response_t editorOpen(StringRef Name, llvm::MemoryBuffer *Buf, @@ -976,7 +978,8 @@ static void handleSemanticRequest( int64_t Offset; if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) return Rec(createErrorRequestInvalid("missing 'key.offset'")); - return Rec(typeContextInfo(InputBuf.get(), Offset, Args)); + return Rec(typeContextInfo(InputBuf.get(), Offset, Args, + std::move(vfsOptions))); } if (ReqUID == RequestConformingMethodList) { @@ -991,7 +994,8 @@ static void handleSemanticRequest( if (Req.getStringArray(KeyExpectedTypes, ExpectedTypeNames, true)) return Rec(createErrorRequestInvalid("invalid 'key.expectedtypes'")); return Rec( - conformingMethodList(InputBuf.get(), Offset, Args, ExpectedTypeNames)); + conformingMethodList(InputBuf.get(), Offset, Args, ExpectedTypeNames, + std::move(vfsOptions))); } if (!SourceFile.hasValue()) @@ -2205,7 +2209,8 @@ void SKGroupedCodeCompletionConsumer::setNextRequestStart(unsigned offset) { static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, int64_t Offset, - ArrayRef Args) { + ArrayRef Args, + Optional vfsOptions) { ResponseBuilder RespBuilder; class Consumer : public TypeContextInfoConsumer { @@ -2242,7 +2247,8 @@ static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, } Consumer(RespBuilder); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); - Lang.getExpressionContextInfo(InputBuf, Offset, Args, Consumer); + Lang.getExpressionContextInfo(InputBuf, Offset, Args, Consumer, + std::move(vfsOptions)); if (Consumer.isError()) return createErrorRequestFailed(Consumer.getErrorDescription()); @@ -2256,7 +2262,8 @@ static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, static sourcekitd_response_t conformingMethodList(llvm::MemoryBuffer *InputBuf, int64_t Offset, ArrayRef Args, - ArrayRef ExpectedTypes) { + ArrayRef ExpectedTypes, + Optional vfsOptions) { ResponseBuilder RespBuilder; class Consumer : public ConformingMethodListConsumer { @@ -2293,7 +2300,8 @@ conformingMethodList(llvm::MemoryBuffer *InputBuf, int64_t Offset, } Consumer(RespBuilder); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); - Lang.getConformingMethodList(InputBuf, Offset, Args, ExpectedTypes, Consumer); + Lang.getConformingMethodList(InputBuf, Offset, Args, ExpectedTypes, Consumer, + std::move(vfsOptions)); if (Consumer.isError()) return createErrorRequestFailed(Consumer.getErrorDescription()); From cd284d127f093d853880f13d16ed96c690de9b05 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 18 Nov 2019 15:48:02 +0900 Subject: [PATCH 108/478] [CodeCompletion] Align swiftCodeCompleteImpl() with other similar funcs Specifically, align with swiftTypeContextInfoImpl() and swiftConformingMethodListImpl() --- .../lib/SwiftLang/SwiftCompletion.cpp | 57 +++++++------------ 1 file changed, 19 insertions(+), 38 deletions(-) diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index d884d1c5ea73d..faac2f85827e0 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -128,15 +128,12 @@ static bool swiftCodeCompleteImpl( // Resolve symlinks for the input file; we resolve them for the input files // in the arguments as well. // FIXME: We need the Swift equivalent of Clang's FileEntry. - auto InputFile = llvm::MemoryBuffer::getMemBuffer( - UnresolvedInputFile->getBuffer(), - Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier())); - - auto origBuffSize = InputFile->getBufferSize(); - unsigned CodeCompletionOffset = Offset; - if (CodeCompletionOffset > origBuffSize) { - CodeCompletionOffset = origBuffSize; - } + auto bufferIdentifier = + Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier()); + + auto origOffset = Offset; + auto newBuffer = SwiftLangSupport::makeCodeCompletionMemoryBuffer( + UnresolvedInputFile, Offset, bufferIdentifier); CompilerInstance CI; // Display diagnostics to stderr. @@ -148,24 +145,22 @@ static bool swiftCodeCompleteImpl( if (TracedOp.enabled()) { CI.addDiagnosticConsumer(&TraceDiags); trace::SwiftInvocation SwiftArgs; - trace::initTraceInfo(SwiftArgs, InputFile->getBufferIdentifier(), Args); + trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); TracedOp.setDiagnosticProvider( [&TraceDiags](SmallVectorImpl &diags) { TraceDiags.getAllDiagnostics(diags); }); - TracedOp.start(SwiftArgs, - {std::make_pair("OriginalOffset", std::to_string(Offset)), - std::make_pair("Offset", - std::to_string(CodeCompletionOffset))}); + TracedOp.start( + SwiftArgs, + {std::make_pair("OriginalOffset", std::to_string(origOffset)), + std::make_pair("Offset", std::to_string(Offset))}); } CompilerInvocation Invocation; bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, CI.getDiags(), InputFile->getBufferIdentifier(), - FileSystem, Error); - if (Failed) { + Invocation, Args, CI.getDiags(), bufferIdentifier, FileSystem, Error); + if (Failed) return false; - } if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { Error = "no input filenames specified"; return false; @@ -175,31 +170,17 @@ static bool swiftCodeCompleteImpl( // because they're somewhat heavy operations and aren't needed for completion. Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - const char *Position = InputFile->getBufferStart() + CodeCompletionOffset; - std::unique_ptr NewBuffer = - llvm::WritableMemoryBuffer::getNewUninitMemBuffer( - InputFile->getBufferSize() + 1, - InputFile->getBufferIdentifier()); - char *NewBuf = NewBuffer->getBufferStart(); - char *NewPos = std::copy(InputFile->getBufferStart(), Position, NewBuf); - *NewPos = '\0'; - std::copy(Position, InputFile->getBufferEnd(), NewPos+1); - - Invocation.setCodeCompletionPoint(NewBuffer.get(), CodeCompletionOffset); - - auto swiftCache = Lang.getCodeCompletionCache(); // Pin the cache. - ide::CodeCompletionContext CompletionContext(swiftCache->getCache()); + Invocation.setCodeCompletionPoint(newBuffer.get(), Offset); // Create a factory for code completion callbacks that will feed the // Consumer. - std::unique_ptr CompletionCallbacksFactory( - ide::makeCodeCompletionCallbacksFactory(CompletionContext, - SwiftConsumer)); + auto swiftCache = Lang.getCodeCompletionCache(); // Pin the cache. + ide::CodeCompletionContext CompletionContext(swiftCache->getCache()); - Invocation.setCodeCompletionFactory(CompletionCallbacksFactory.get()); + std::unique_ptr callbacksFactory( + ide::makeCodeCompletionCallbacksFactory(CompletionContext, SwiftConsumer)); - // FIXME: We need to be passing the buffers from the open documents. - // It is not a huge problem in practice because Xcode auto-saves constantly. + Invocation.setCodeCompletionFactory(callbacksFactory.get()); if (FileSystem != llvm::vfs::getRealFileSystem()) { CI.getSourceMgr().setFileSystem(FileSystem); From ff97c06e8d4bfcf356febc9b319d4d7a8f35aee2 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 20 Nov 2019 15:56:03 +0900 Subject: [PATCH 109/478] [CodeCompletion] Use offsets in the buffer for second pass state So that we can use the different the buffer for the second pass from the first pass. --- include/swift/Parse/Parser.h | 6 +-- include/swift/Parse/PersistentParserState.h | 60 ++++++++++----------- lib/Parse/ParseDecl.cpp | 10 ++-- lib/Parse/ParseStmt.cpp | 3 +- lib/Parse/Parser.cpp | 23 ++++---- lib/Parse/PersistentParserState.cpp | 12 ++++- 6 files changed, 59 insertions(+), 55 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 202780a8f1485..e99b82f7bbd79 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1642,12 +1642,8 @@ class Parser { //===--------------------------------------------------------------------===// // Code completion second pass. - static void - performCodeCompletionSecondPass(PersistentParserState &ParserState, - CodeCompletionCallbacksFactory &Factory); - void performCodeCompletionSecondPassImpl( - PersistentParserState::CodeCompletionDelayedDeclState &info); + CodeCompletionDelayedDeclState &info); }; /// Describes a parsed declaration name. diff --git a/include/swift/Parse/PersistentParserState.h b/include/swift/Parse/PersistentParserState.h index 44f53983c6ca6..9e04bcce56457 100644 --- a/include/swift/Parse/PersistentParserState.h +++ b/include/swift/Parse/PersistentParserState.h @@ -29,6 +29,33 @@ class SourceFile; class DeclContext; class IterableDeclContext; +enum class CodeCompletionDelayedDeclKind { + TopLevelCodeDecl, + Decl, + FunctionBody, +}; + +class CodeCompletionDelayedDeclState { +public: + const CodeCompletionDelayedDeclKind Kind; + const unsigned Flags; + DeclContext *const ParentContext; + SavedScope Scope; + const unsigned StartOffset; + const unsigned EndOffset; + const unsigned PrevOffset; + + SavedScope takeScope() { return std::move(Scope); } + + CodeCompletionDelayedDeclState(CodeCompletionDelayedDeclKind Kind, + unsigned Flags, DeclContext *ParentContext, + SavedScope &&Scope, unsigned StartOffset, + unsigned EndOffset, unsigned PrevOffset) + : Kind(Kind), Flags(Flags), ParentContext(ParentContext), + Scope(std::move(Scope)), StartOffset(StartOffset), EndOffset(EndOffset), + PrevOffset(PrevOffset) {} +}; + /// Parser state persistent across multiple parses. class PersistentParserState { public: @@ -39,36 +66,6 @@ class PersistentParserState { bool isValid() const { return Loc.isValid(); } }; - enum class CodeCompletionDelayedDeclKind { - TopLevelCodeDecl, - Decl, - FunctionBody, - }; - - class CodeCompletionDelayedDeclState { - friend class PersistentParserState; - friend class Parser; - CodeCompletionDelayedDeclKind Kind; - unsigned Flags; - DeclContext *ParentContext; - ParserPos BodyPos; - SourceLoc BodyEnd; - SavedScope Scope; - - SavedScope takeScope() { - return std::move(Scope); - } - - public: - CodeCompletionDelayedDeclState(CodeCompletionDelayedDeclKind Kind, - unsigned Flags, DeclContext *ParentContext, - SourceRange BodyRange, SourceLoc PreviousLoc, - SavedScope &&Scope) - : Kind(Kind), Flags(Flags), - ParentContext(ParentContext), BodyPos{BodyRange.Start, PreviousLoc}, - BodyEnd(BodyRange.End), Scope(std::move(Scope)) {} - }; - bool InPoundLineEnvironment = false; // FIXME: When condition evaluation moves to a later phase, remove this bit // and adjust the client call 'performParseOnly'. @@ -92,7 +89,8 @@ class PersistentParserState { PersistentParserState(ASTContext &ctx) : PersistentParserState() { } ~PersistentParserState(); - void setCodeCompletionDelayedDeclState(CodeCompletionDelayedDeclKind Kind, + void setCodeCompletionDelayedDeclState(SourceManager &SM, unsigned BufferID, + CodeCompletionDelayedDeclKind Kind, unsigned Flags, DeclContext *ParentContext, SourceRange BodyRange, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index f0646822a0538..47eb122894cb7 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3437,7 +3437,8 @@ void Parser::consumeDecl(ParserPosition BeginParserPosition, SourceLoc BeginLoc = Tok.getLoc(); State->setCodeCompletionDelayedDeclState( - PersistentParserState::CodeCompletionDelayedDeclKind::Decl, + SourceMgr, L->getBufferID(), + CodeCompletionDelayedDeclKind::Decl, Flags.toRaw(), CurDeclContext, {BeginLoc, EndLoc}, BeginParserPosition.PreviousLoc); @@ -3953,7 +3954,7 @@ std::vector Parser::parseDeclListDelayed(IterableDeclContext *IDC) { return { }; } - auto BeginParserPosition = getParserPosition({BodyRange.Start,BodyRange.End}); + auto BeginParserPosition = getParserPosition({BodyRange.Start, SourceLoc()}); auto EndLexerState = L->getStateForEndOfTokenLoc(BodyRange.End); // ParserPositionRAII needs a primed parser to restore to. @@ -6014,7 +6015,8 @@ void Parser::consumeAbstractFunctionBody(AbstractFunctionDecl *AFD, if (isCodeCompletionFirstPass()) { if (SourceMgr.rangeContainsCodeCompletionLoc(BodyRange)) { State->setCodeCompletionDelayedDeclState( - PersistentParserState::CodeCompletionDelayedDeclKind::FunctionBody, + SourceMgr, L->getBufferID(), + CodeCompletionDelayedDeclKind::FunctionBody, PD_Default, AFD, BodyRange, BeginParserPosition.PreviousLoc); } else { AFD->setBodySkipped(BodyRange); @@ -6302,7 +6304,7 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { "function body should be delayed"); auto bodyRange = AFD->getBodySourceRange(); - auto BeginParserPosition = getParserPosition({bodyRange.Start,bodyRange.End}); + auto BeginParserPosition = getParserPosition({bodyRange.Start,SourceLoc()}); auto EndLexerState = L->getStateForEndOfTokenLoc(AFD->getEndLoc()); // ParserPositionRAII needs a primed parser to restore to. diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index ff625a4326f79..57daf5ae77b02 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -257,7 +257,8 @@ void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition, backtrackToPosition(BeginParserPosition); SourceLoc BeginLoc = Tok.getLoc(); State->setCodeCompletionDelayedDeclState( - PersistentParserState::CodeCompletionDelayedDeclKind::TopLevelCodeDecl, + SourceMgr, L->getBufferID(), + CodeCompletionDelayedDeclKind::TopLevelCodeDecl, PD_Default, TLCD, {BeginLoc, EndLoc}, BeginParserPosition.PreviousLoc); // Skip the rest of the file to prevent the parser from constructing the AST diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 083ba4fa9f3d1..3e425735216f5 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -113,12 +113,6 @@ void SILParserTUStateBase::anchor() { } void swift::performCodeCompletionSecondPass( PersistentParserState &ParserState, CodeCompletionCallbacksFactory &Factory) { - Parser::performCodeCompletionSecondPass(ParserState, Factory); -} - -void Parser::performCodeCompletionSecondPass( - PersistentParserState &ParserState, - CodeCompletionCallbacksFactory &Factory) { if (!ParserState.hasCodeCompletionDelayedDeclState()) return; @@ -128,7 +122,7 @@ void Parser::performCodeCompletionSecondPass( FrontendStatsTracer tracer(Ctx.Stats, "CodeCompletionSecondPass"); - auto BufferID = Ctx.SourceMgr.findBufferContainingLoc(state->BodyPos.Loc); + auto BufferID = Ctx.SourceMgr.getCodeCompletionBufferID(); Parser TheParser(BufferID, SF, nullptr, &ParserState, nullptr); std::unique_ptr CodeCompletion( @@ -139,12 +133,17 @@ void Parser::performCodeCompletionSecondPass( } void Parser::performCodeCompletionSecondPassImpl( - PersistentParserState::CodeCompletionDelayedDeclState &info) { + CodeCompletionDelayedDeclState &info) { // Disable libSyntax creation in the delayed parsing. SyntaxContext->disable(); + auto BufferID = L->getBufferID(); + auto startLoc = SourceMgr.getLocForOffset(BufferID, info.StartOffset); + SourceLoc prevLoc; + if (info.PrevOffset != ~0U) + prevLoc = SourceMgr.getLocForOffset(BufferID, info.PrevOffset); // Set the parser position to the start of the delayed decl or the body. - restoreParserPosition(getParserPosition(info.BodyPos)); + restoreParserPosition(getParserPosition({startLoc, prevLoc})); // Do not delay parsing in the second pass. llvm::SaveAndRestore DisableDelayedBody(DelayBodyParsing, false); @@ -155,7 +154,7 @@ void Parser::performCodeCompletionSecondPassImpl( DeclContext *DC = info.ParentContext; switch (info.Kind) { - case PersistentParserState::CodeCompletionDelayedDeclKind::TopLevelCodeDecl: { + case CodeCompletionDelayedDeclKind::TopLevelCodeDecl: { // Re-enter the top-level code decl context. // FIXME: this can issue discriminators out-of-order? auto *TLCD = cast(DC); @@ -171,7 +170,7 @@ void Parser::performCodeCompletionSecondPassImpl( break; } - case PersistentParserState::CodeCompletionDelayedDeclKind::Decl: { + case CodeCompletionDelayedDeclKind::Decl: { assert((DC->isTypeContext() || DC->isModuleScopeContext()) && "Delayed decl must be a type member or a top-level decl"); ContextChange CC(*this, DC); @@ -191,7 +190,7 @@ void Parser::performCodeCompletionSecondPassImpl( break; } - case PersistentParserState::CodeCompletionDelayedDeclKind::FunctionBody: { + case CodeCompletionDelayedDeclKind::FunctionBody: { auto *AFD = cast(DC); ParseFunctionBody CC(*this, AFD); setLocalDiscriminatorToParamList(AFD->getParameters()); diff --git a/lib/Parse/PersistentParserState.cpp b/lib/Parse/PersistentParserState.cpp index 5244697ecba2d..0b439c09da79e 100644 --- a/lib/Parse/PersistentParserState.cpp +++ b/lib/Parse/PersistentParserState.cpp @@ -17,6 +17,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/Basic/SourceManager.h" #include "swift/Parse/PersistentParserState.h" using namespace swift; @@ -26,13 +27,20 @@ PersistentParserState::PersistentParserState() { } PersistentParserState::~PersistentParserState() { } void PersistentParserState::setCodeCompletionDelayedDeclState( + SourceManager &SM, unsigned BufferID, CodeCompletionDelayedDeclKind Kind, unsigned Flags, DeclContext *ParentContext, SourceRange BodyRange, SourceLoc PreviousLoc) { assert(!CodeCompletionDelayedDeclStat.get() && "only one decl can be delayed for code completion"); + unsigned startOffset = SM.getLocOffsetInBuffer(BodyRange.Start, BufferID); + unsigned endOffset = SM.getLocOffsetInBuffer(BodyRange.End, BufferID); + unsigned prevOffset = ~0U; + if (PreviousLoc.isValid()) + prevOffset = SM.getLocOffsetInBuffer(PreviousLoc, BufferID); + CodeCompletionDelayedDeclStat.reset(new CodeCompletionDelayedDeclState( - Kind, Flags, ParentContext, BodyRange, PreviousLoc, - ScopeInfo.saveCurrentScope())); + Kind, Flags, ParentContext, ScopeInfo.saveCurrentScope(), + startOffset, endOffset, prevOffset)); } void PersistentParserState::delayDeclList(IterableDeclContext *D) { From 62c44126b60bafc13631ae81c51c52a3c3208bb4 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 18 Nov 2019 18:02:41 +0900 Subject: [PATCH 110/478] [SourceKit] Reuse compiler instance between multiple completion - Introduce ide::CompletionInstance to manage CompilerInstance - `CompletionInstance` vends the cached CompilerInstance when: -- The compiler arguments (i.e. CompilerInvocation) has has not changed -- The primary file is the same -- The completion happens inside function bodies in both previous and current completion -- The interface hash of the primary file has not changed - Otherwise, it vends a fresh CompilerInstance and cache it for the next completion rdar://problem/20787086 --- include/swift/AST/Decl.h | 14 +- include/swift/AST/SourceFile.h | 6 +- include/swift/Frontend/Frontend.h | 25 +- include/swift/IDE/CompletionInstance.h | 62 ++++ include/swift/Parse/LocalContext.h | 1 + include/swift/Parse/PersistentParserState.h | 18 +- include/swift/Parse/Scope.h | 2 + lib/AST/Decl.cpp | 21 +- lib/Frontend/Frontend.cpp | 6 - lib/IDE/CMakeLists.txt | 1 + lib/IDE/CompletionInstance.cpp | 276 ++++++++++++++++++ lib/Parse/ParseDecl.cpp | 31 +- lib/Parse/Parser.cpp | 13 +- lib/Parse/PersistentParserState.cpp | 18 +- lib/Parse/Scope.cpp | 14 +- .../lib/SwiftLang/CodeCompletionOrganizer.cpp | 5 +- .../lib/SwiftLang/CodeCompletionOrganizer.h | 2 +- .../lib/SwiftLang/SwiftCompletion.cpp | 67 ++--- .../SwiftLang/SwiftConformingMethodList.cpp | 55 ++-- .../lib/SwiftLang/SwiftLangSupport.cpp | 24 +- .../lib/SwiftLang/SwiftLangSupport.h | 13 +- .../lib/SwiftLang/SwiftTypeContextInfo.cpp | 55 ++-- tools/swift-ide-test/swift-ide-test.cpp | 178 +++-------- 23 files changed, 589 insertions(+), 318 deletions(-) create mode 100644 include/swift/IDE/CompletionInstance.h create mode 100644 lib/IDE/CompletionInstance.cpp diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 527f684081ca8..9f007fa1c9001 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5841,13 +5841,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// \sa hasBody() BraceStmt *getBody(bool canSynthesize = true) const; - void setBody(BraceStmt *S, BodyKind NewBodyKind = BodyKind::Parsed) { - assert(getBodyKind() != BodyKind::Skipped && - "cannot set a body if it was skipped"); - - Body = S; - setBodyKind(NewBodyKind); - } + void setBody(BraceStmt *S, BodyKind NewBodyKind = BodyKind::Parsed); /// Note that the body was skipped for this function. Function body /// cannot be attached after this call. @@ -5866,7 +5860,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Note that parsing for the body was delayed. void setBodyDelayed(SourceRange bodyRange) { - assert(getBodyKind() == BodyKind::None); + assert(getBodyKind() == BodyKind::None || + getBodyKind() == BodyKind::Skipped); assert(bodyRange.isValid()); BodyRange = bodyRange; setBodyKind(BodyKind::Unparsed); @@ -6657,6 +6652,9 @@ class ConstructorDecl : public AbstractFunctionDecl { /// initializer. BodyInitKind getDelegatingOrChainedInitKind(DiagnosticEngine *diags, ApplyExpr **init = nullptr) const; + void clearCachedDelegatingOrChainedInitKind() { + Bits.ConstructorDecl.ComputedBodyInitKind = 0; + } /// Whether this constructor is required. bool isRequired() const { diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index 43899fbafa4d2..0ac86a6bcbe28 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -404,9 +404,11 @@ class SourceFile final : public FileUnit { InterfaceHash->update(a); } - void getInterfaceHash(llvm::SmallString<32> &str) { + void getInterfaceHash(llvm::SmallString<32> &str) const { + // Copy to preserve idempotence. + llvm::MD5 md5 = *InterfaceHash; llvm::MD5::MD5Result result; - InterfaceHash->final(result); + md5.final(result); llvm::MD5::stringifyResult(result, str); } diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 12565e1dfe8f6..3c0777805fd56 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -318,20 +318,11 @@ class CompilerInvocation { return CodeCompletionOffset != ~0U; } - void setCodeCompletionFactory(CodeCompletionCallbacksFactory *Factory) { - CodeCompletionFactory = Factory; - disableASTScopeLookup(); - } - /// Called from lldb, see rdar://53971116 void disableASTScopeLookup() { LangOpts.EnableASTScopeLookup = false; } - CodeCompletionCallbacksFactory *getCodeCompletionFactory() const { - return CodeCompletionFactory; - } - /// Retrieve a module hash string that is suitable for uniquely /// identifying the conditions under which the module was built, for use /// in generating a cached PCH file for the bridging header. @@ -483,6 +474,10 @@ class CompilerInstance { Diagnostics.addConsumer(*DC); } + void removeDiagnosticConsumer(DiagnosticConsumer *DC) { + Diagnostics.removeConsumer(*DC); + } + void createDependencyTracker(bool TrackSystemDeps) { assert(!Context && "must be called before setup()"); DepTracker = llvm::make_unique(TrackSystemDeps); @@ -547,6 +542,18 @@ class CompilerInstance { /// Returns true if there was an error during setup. bool setup(const CompilerInvocation &Invocation); + const CompilerInvocation &getInvocation() { + return Invocation; + } + + bool hasPersistentParserState() const { + return bool(PersistentState); + } + + PersistentParserState &getPersistentParserState() { + return *PersistentState.get(); + } + private: /// Set up the file system by loading and validating all VFS overlay YAML /// files. If the process of validating VFS files failed, or the overlay diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h new file mode 100644 index 0000000000000..d57f5c066abcf --- /dev/null +++ b/include/swift/IDE/CompletionInstance.h @@ -0,0 +1,62 @@ +//===--- CompletionInstance.h ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 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_IDE_COMPLETIONINSTANCE_H +#define SWIFT_IDE_COMPLETIONINSTANCE_H + +#include "swift/Frontend/Frontend.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" + +namespace swift { + +class CompilerInstance; +class CompilerInvocation; +class DiagnosticConsumer; + +namespace ide { + +/// Copy a memory buffer inserting '0' at the position of \c origBuf. +std::unique_ptr +makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, + unsigned &Offset, + llvm::StringRef bufferIdentifier); + +class CompletionInstance { + std::unique_ptr CachedCI = nullptr; + + swift::CompilerInstance * + getReusingCompilerInstance(const swift::CompilerInvocation &Invocation, + llvm::MemoryBuffer *completionBuffer, + unsigned int Offset, DiagnosticConsumer *DiagC); + + swift::CompilerInstance *renewCompilerInstance( + swift::CompilerInvocation &Invocation, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + std::string &Error, DiagnosticConsumer *DiagC); + +public: + swift::CompilerInstance *getCompilerInstance( + swift::CompilerInvocation &Invocation, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + std::string &Error, DiagnosticConsumer *DiagC); +}; + +} // namespace ide +} // namespace swift + +#endif // SWIFT_IDE_COMPLETIONINSTANCE_H diff --git a/include/swift/Parse/LocalContext.h b/include/swift/Parse/LocalContext.h index 8e4b2b281f5a0..1c02f0dfdc9b5 100644 --- a/include/swift/Parse/LocalContext.h +++ b/include/swift/Parse/LocalContext.h @@ -19,6 +19,7 @@ #define SWIFT_PARSE_LOCALCONTEXT_H #include "llvm/ADT/DenseMap.h" +#include "swift/AST/Identifier.h" #include namespace swift { diff --git a/include/swift/Parse/PersistentParserState.h b/include/swift/Parse/PersistentParserState.h index 9e04bcce56457..6eb337b6781ea 100644 --- a/include/swift/Parse/PersistentParserState.h +++ b/include/swift/Parse/PersistentParserState.h @@ -37,13 +37,13 @@ enum class CodeCompletionDelayedDeclKind { class CodeCompletionDelayedDeclState { public: - const CodeCompletionDelayedDeclKind Kind; - const unsigned Flags; - DeclContext *const ParentContext; + CodeCompletionDelayedDeclKind Kind; + unsigned Flags; + DeclContext *ParentContext; SavedScope Scope; - const unsigned StartOffset; - const unsigned EndOffset; - const unsigned PrevOffset; + unsigned StartOffset; + unsigned EndOffset; + unsigned PrevOffset; SavedScope takeScope() { return std::move(Scope); } @@ -95,11 +95,17 @@ class PersistentParserState { DeclContext *ParentContext, SourceRange BodyRange, SourceLoc PreviousLoc); + void restoreCodeCompletionDelayedDeclState( + const CodeCompletionDelayedDeclState &other); bool hasCodeCompletionDelayedDeclState() { return CodeCompletionDelayedDeclStat.get() != nullptr; } + CodeCompletionDelayedDeclState &getCodeCompletionDelayedDeclState() { + return *CodeCompletionDelayedDeclStat.get(); + } + std::unique_ptr takeCodeCompletionDelayedDeclState() { assert(hasCodeCompletionDelayedDeclState()); diff --git a/include/swift/Parse/Scope.h b/include/swift/Parse/Scope.h index 4fe00b028bef2..c10b97f35daee 100644 --- a/include/swift/Parse/Scope.h +++ b/include/swift/Parse/Scope.h @@ -141,6 +141,8 @@ class Scope { bool isResolvable() const; public: + Scope(ScopeInfo &SI, ScopeKind SC, bool isInactiveConfigBlock = false); + /// Create a lexical scope of the specified kind. Scope(Parser *P, ScopeKind SC, bool isInactiveConfigBlock = false); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 594a14915ab27..4176d4fb83639 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6621,6 +6621,20 @@ BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const { nullptr); } +void AbstractFunctionDecl::setBody(BraceStmt *S, BodyKind NewBodyKind) { + assert(getBodyKind() != BodyKind::Skipped && + "cannot set a body if it was skipped"); + + Body = S; + setBodyKind(NewBodyKind); + + // Need to recompute init body kind. + if (NewBodyKind < BodyKind::TypeChecked) { + if (auto *ctor = dyn_cast(this)) + ctor->clearCachedDelegatingOrChainedInitKind(); + } +} + SourceRange AbstractFunctionDecl::getBodySourceRange() const { switch (getBodyKind()) { case BodyKind::None: @@ -7397,8 +7411,11 @@ ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags, // If we already computed the result, return it. if (Bits.ConstructorDecl.ComputedBodyInitKind) { - return static_cast( - Bits.ConstructorDecl.ComputedBodyInitKind - 1); + auto Kind = static_cast( + Bits.ConstructorDecl.ComputedBodyInitKind - 1); + assert((Kind == BodyInitKind::None || !init) && + "can't return cached result with the init expr"); + return Kind; } diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 09004274db7b3..f43739bf2bde8 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -894,12 +894,6 @@ void CompilerInstance::parseAndCheckTypesUpTo( } }); - if (Invocation.isCodeCompletion()) { - assert(limitStage == SourceFile::NameBound); - performCodeCompletionSecondPass(*PersistentState.get(), - *Invocation.getCodeCompletionFactory()); - } - // If the limiting AST stage is name binding, we're done. if (limitStage <= SourceFile::NameBound) { return; diff --git a/lib/IDE/CMakeLists.txt b/lib/IDE/CMakeLists.txt index 042571855a62a..40bf8dbb52dee 100644 --- a/lib/IDE/CMakeLists.txt +++ b/lib/IDE/CMakeLists.txt @@ -2,6 +2,7 @@ add_swift_host_library(swiftIDE STATIC CodeCompletion.cpp CodeCompletionCache.cpp CommentConversion.cpp + CompletionInstance.cpp ConformingMethodList.cpp ExprContextAnalysis.cpp Formatting.cpp diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp new file mode 100644 index 0000000000000..d03d34e88f065 --- /dev/null +++ b/lib/IDE/CompletionInstance.cpp @@ -0,0 +1,276 @@ +//===--- CompletionInstance.cpp -------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/IDE/CompletionInstance.h" + +#include "swift/AST/ASTContext.h" +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/Module.h" +#include "swift/AST/SourceFile.h" +#include "swift/Basic/LangOptions.h" +#include "swift/Basic/SourceManager.h" +#include "swift/Driver/FrontendUtil.h" +#include "swift/Frontend/Frontend.h" +#include "swift/Parse/PersistentParserState.h" +#include "swift/Subsystems.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace swift; +using namespace ide; + +std::unique_ptr +swift::ide::makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, + unsigned &Offset, + StringRef bufferIdentifier) { + + auto origBuffSize = origBuf->getBufferSize(); + if (Offset > origBuffSize) + Offset = origBuffSize; + + auto newBuffer = llvm::WritableMemoryBuffer::getNewUninitMemBuffer( + origBuffSize + 1, bufferIdentifier); + auto *pos = origBuf->getBufferStart() + Offset; + auto *newPos = + std::copy(origBuf->getBufferStart(), pos, newBuffer->getBufferStart()); + *newPos = '\0'; + std::copy(pos, origBuf->getBufferEnd(), newPos + 1); + + return std::unique_ptr(newBuffer.release()); +} + +namespace { +/// Returns index number of \p D in \p Decls . If it's not found, returns ~0. +template +unsigned findIndexInRange(Decl *D, const Range &Decls) { + unsigned N = 0; + for (auto I = Decls.begin(), E = Decls.end(); I != E; ++I) { + if (*I == D) + return N; + ++N; + } + return ~0U; +} + +/// Return the element at \p N in \p Decls . +template Decl *getElementAt(const Range &Decls, unsigned N) { + assert(std::distance(Decls.begin(), Decls.end()) > N); + auto I = Decls.begin(); + std::advance(I, N); + return *I; +} + +/// Find the equivalent \c DeclContext with \p DC from \p SF AST. +/// This assumes the AST which contains \p DC has exact the same structure with +/// \p SF. +/// FIXME: This doesn't support IfConfigDecl blocks. If \p DC is in an inactive +/// config block, this function probably returns false. +static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC, + SourceFile *SF) { + auto *newDC = DC; + // NOTE: Shortcut for DC->getParentSourceFile() == SF case is not needed + // because they should be always different. + + // Get the index path in the current AST. + SmallVector IndexStack; + do { + auto *D = newDC->getAsDecl(); + auto *parentDC = newDC->getParent(); + unsigned N; + if (auto parentSF = dyn_cast(parentDC)) + N = findIndexInRange(D, parentSF->Decls); + else if (auto parentIDC = + dyn_cast(parentDC->getAsDecl())) + N = findIndexInRange(D, parentIDC->getMembers()); + else + llvm_unreachable("invalid DC kind for finding equivalent DC (indexpath)"); + + // Not found in the decl context tree. + // FIXME: Probably DC is in an inactive #if block. + if (N == ~0U) { + return nullptr; + } + + IndexStack.push_back(N); + newDC = parentDC; + } while (!newDC->isModuleScopeContext()); + + assert(isa(newDC) && "DC should be in a SourceFile"); + + // Query the equivalent decl context from the base SourceFile using the index + // path. + newDC = SF; + do { + auto N = IndexStack.pop_back_val(); + Decl *D; + if (auto parentSF = dyn_cast(newDC)) + D = getElementAt(parentSF->Decls, N); + else if (auto parentIDC = dyn_cast(newDC->getAsDecl())) + D = getElementAt(parentIDC->getMembers(), N); + else + llvm_unreachable("invalid DC kind for finding equivalent DC (query)"); + newDC = dyn_cast(D); + } while (!IndexStack.empty()); + + assert(newDC->getContextKind() == DC->getContextKind()); + + return newDC; +} +} // namespace + +CompilerInstance *CompletionInstance::getReusingCompilerInstance( + const swift::CompilerInvocation &Invocation, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + DiagnosticConsumer *DiagC) { + if (!CachedCI) + return nullptr; + + if (Invocation.getPCHHash() != CachedCI->getInvocation().getPCHHash()) + return nullptr; + + auto &oldState = CachedCI->getPersistentParserState(); + if (!oldState.hasCodeCompletionDelayedDeclState()) + return nullptr; + + auto &SM = CachedCI->getSourceMgr(); + if (SM.getIdentifierForBuffer(SM.getCodeCompletionBufferID()) != + completionBuffer->getBufferIdentifier()) + return nullptr; + + auto &oldInfo = oldState.getCodeCompletionDelayedDeclState(); + + // Currently, only completions within a function body is supported. + if (oldInfo.Kind != CodeCompletionDelayedDeclKind::FunctionBody) + return nullptr; + + auto newBufferID = SM.addMemBufferCopy(completionBuffer); + SM.setCodeCompletionPoint(newBufferID, Offset); + + // Parse the new buffer into temporary SourceFile. + LangOptions langOpts; + langOpts.DisableParserLookup = true; + TypeCheckerOptions typeckOpts; + SearchPathOptions searchPathOpts; + DiagnosticEngine Diags(SM); + std::unique_ptr Ctx( + ASTContext::get(langOpts, typeckOpts, searchPathOpts, SM, Diags)); + registerIDERequestFunctions(Ctx->evaluator); + registerTypeCheckerRequestFunctions(Ctx->evaluator); + ModuleDecl *M = ModuleDecl::create(Identifier(), *Ctx); + unsigned BufferID = SM.getCodeCompletionBufferID(); + PersistentParserState newState; + SourceFile *newSF = + new (*Ctx) SourceFile(*M, SourceFileKind::Library, BufferID, + SourceFile::ImplicitModuleImportKind::None); + newSF->enableInterfaceHash(); + parseIntoSourceFileFull(*newSF, BufferID, &newState); + // Couldn't find any completion token? + if (!newState.hasCodeCompletionDelayedDeclState()) + return nullptr; + + auto &newInfo = newState.getCodeCompletionDelayedDeclState(); + + // The new completion must happens in function body too. + if (newInfo.Kind != CodeCompletionDelayedDeclKind::FunctionBody) + return nullptr; + + auto *oldSF = oldInfo.ParentContext->getParentSourceFile(); + + // If the interface has changed, AST must be refreshed. + llvm::SmallString<32> oldInterfaceHash{}; + llvm::SmallString<32> newInterfaceHash{}; + oldSF->getInterfaceHash(oldInterfaceHash); + newSF->getInterfaceHash(newInterfaceHash); + if (oldInterfaceHash != newInterfaceHash) + return nullptr; + + DeclContext *DC = + getEquivalentDeclContextFromSourceFile(newInfo.ParentContext, oldSF); + if (!DC) + return nullptr; + + // OK, we can perform fast completion for this. Update the orignal delayed + // decl state. + + // Construct dummy scopes. We don't need to restore the original scope + // because they are probably not 'isResolvable()' anyway. + auto &SI = oldState.getScopeInfo(); + assert(SI.getCurrentScope() == nullptr); + Scope Top(SI, ScopeKind::TopLevel); + Scope Body(SI, ScopeKind::FunctionBody); + + oldInfo.ParentContext = DC; + oldInfo.StartOffset = newInfo.StartOffset; + oldInfo.EndOffset = newInfo.EndOffset; + oldInfo.PrevOffset = newInfo.PrevOffset; + oldState.restoreCodeCompletionDelayedDeclState(oldInfo); + + auto *AFD = cast(DC); + if (AFD->isBodySkipped()) + AFD->setBodyDelayed(AFD->getBodySourceRange()); + if (DiagC) + CachedCI->addDiagnosticConsumer(DiagC); + return CachedCI.get(); +} + +CompilerInstance *CompletionInstance::renewCompilerInstance( + swift::CompilerInvocation &Invocation, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + std::string &Error, DiagnosticConsumer *DiagC) { + CachedCI.reset(); + CachedCI = std::make_unique(); + auto *CI = CachedCI.get(); + if (DiagC) + CachedCI->addDiagnosticConsumer(DiagC); + + if (FileSystem != llvm::vfs::getRealFileSystem()) + CI->getSourceMgr().setFileSystem(FileSystem); + + Invocation.setCodeCompletionPoint(completionBuffer, Offset); + + if (CI->setup(Invocation)) { + Error = "failed to setup compiler instance"; + return nullptr; + } + registerIDERequestFunctions(CI->getASTContext().evaluator); + + return CI; +} + +CompilerInstance *swift::ide::CompletionInstance::getCompilerInstance( + swift::CompilerInvocation &Invocation, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + std::string &Error, DiagnosticConsumer *DiagC) { + + // Always disable source location resolutions from .swiftsourceinfo file + // because they're somewhat heavy operations and aren't needed for completion. + Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; + + // Disable to build syntax tree because code-completion skips some portion of + // source text. That breaks an invariant of syntax tree building. + Invocation.getLangOptions().BuildSyntaxTree = false; + + // FIXME: ASTScopeLookup doesn't support code completion yet. + Invocation.disableASTScopeLookup(); + + if (auto *cached = getReusingCompilerInstance(Invocation, completionBuffer, + Offset, DiagC)) + return cached; + + if (auto *renewed = renewCompilerInstance( + Invocation, FileSystem, completionBuffer, Offset, Error, DiagC)) + return renewed; + + assert(!Error.empty()); + return nullptr; +} diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 47eb122894cb7..075fec86cdb37 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -6211,19 +6211,6 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, /// Parse function body into \p AFD. void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { - Scope S(this, ScopeKind::FunctionBody); - - // Enter the arguments for the function into a new function-body scope. We - // need this even if there is no function body to detect argument name - // duplication. - if (auto *P = AFD->getImplicitSelfDecl()) - addToScope(P); - addParametersToScope(AFD->getParameters()); - - // Establish the new context. - ParseFunctionBody CC(*this, AFD); - setLocalDiscriminatorToParamList(AFD->getParameters()); - if (!Tok.is(tok::l_brace)) { checkForInputIncomplete(); return; @@ -6241,6 +6228,19 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { return; } + Scope S(this, ScopeKind::FunctionBody); + + // Enter the arguments for the function into a new function-body scope. We + // need this even if there is no function body to detect argument name + // duplication. + if (auto *P = AFD->getImplicitSelfDecl()) + addToScope(P); + addParametersToScope(AFD->getParameters()); + + // Establish the new context. + ParseFunctionBody CC(*this, AFD); + setLocalDiscriminatorToParamList(AFD->getParameters()); + if (Context.Stats) Context.Stats->getFrontendCounters().NumFunctionsParsed++; @@ -6304,7 +6304,7 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { "function body should be delayed"); auto bodyRange = AFD->getBodySourceRange(); - auto BeginParserPosition = getParserPosition({bodyRange.Start,SourceLoc()}); + auto BeginParserPosition = getParserPosition({bodyRange.Start, SourceLoc()}); auto EndLexerState = L->getStateForEndOfTokenLoc(AFD->getEndLoc()); // ParserPositionRAII needs a primed parser to restore to. @@ -6326,6 +6326,9 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { // Re-enter the lexical scope. Scope TopLevelScope(this, ScopeKind::TopLevel); Scope S(this, ScopeKind::FunctionBody); + if (auto *P = AFD->getImplicitSelfDecl()) + addToScope(P); + addParametersToScope(AFD->getParameters()); ParseFunctionBody CC(*this, AFD); setLocalDiscriminatorToParamList(AFD->getParameters()); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 3e425735216f5..063b742c87623 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -192,6 +192,11 @@ void Parser::performCodeCompletionSecondPassImpl( case CodeCompletionDelayedDeclKind::FunctionBody: { auto *AFD = cast(DC); + + if (auto *P = AFD->getImplicitSelfDecl()) + addToScope(P); + addParametersToScope(AFD->getParameters()); + ParseFunctionBody CC(*this, AFD); setLocalDiscriminatorToParamList(AFD->getParameters()); @@ -205,6 +210,8 @@ void Parser::performCodeCompletionSecondPassImpl( "Second pass should not set any code completion info"); CodeCompletion->doneParsing(); + + State->restoreCodeCompletionDelayedDeclState(info); } swift::Parser::BacktrackingScope::~BacktrackingScope() { @@ -430,11 +437,11 @@ class TokenRecorder: public ConsumeTokenReceiver { } public: - TokenRecorder(SourceFile &SF): + TokenRecorder(SourceFile &SF, unsigned BufferID): Ctx(SF.getASTContext()), SM(SF.getASTContext().SourceMgr), Bag(SF.getTokenVector()), - BufferID(SF.getBufferID().getValue()) {}; + BufferID(BufferID) {}; void finalize() override { @@ -516,7 +523,7 @@ Parser::Parser(std::unique_ptr Lex, SourceFile &SF, Context(SF.getASTContext()), DelayBodyParsing(DelayBodyParsing), TokReceiver(SF.shouldCollectToken() ? - new TokenRecorder(SF) : + new TokenRecorder(SF, L->getBufferID()) : new ConsumeTokenReceiver()), SyntaxContext(new SyntaxParsingContext(SyntaxContext, SF, L->getBufferID(), diff --git a/lib/Parse/PersistentParserState.cpp b/lib/Parse/PersistentParserState.cpp index 0b439c09da79e..4c758ad055421 100644 --- a/lib/Parse/PersistentParserState.cpp +++ b/lib/Parse/PersistentParserState.cpp @@ -27,9 +27,9 @@ PersistentParserState::PersistentParserState() { } PersistentParserState::~PersistentParserState() { } void PersistentParserState::setCodeCompletionDelayedDeclState( - SourceManager &SM, unsigned BufferID, - CodeCompletionDelayedDeclKind Kind, unsigned Flags, - DeclContext *ParentContext, SourceRange BodyRange, SourceLoc PreviousLoc) { + SourceManager &SM, unsigned BufferID, CodeCompletionDelayedDeclKind Kind, + unsigned Flags, DeclContext *ParentContext, SourceRange BodyRange, + SourceLoc PreviousLoc) { assert(!CodeCompletionDelayedDeclStat.get() && "only one decl can be delayed for code completion"); unsigned startOffset = SM.getLocOffsetInBuffer(BodyRange.Start, BufferID); @@ -39,8 +39,16 @@ void PersistentParserState::setCodeCompletionDelayedDeclState( prevOffset = SM.getLocOffsetInBuffer(PreviousLoc, BufferID); CodeCompletionDelayedDeclStat.reset(new CodeCompletionDelayedDeclState( - Kind, Flags, ParentContext, ScopeInfo.saveCurrentScope(), - startOffset, endOffset, prevOffset)); + Kind, Flags, ParentContext, ScopeInfo.saveCurrentScope(), startOffset, + endOffset, prevOffset)); +} + +void PersistentParserState::restoreCodeCompletionDelayedDeclState( + const CodeCompletionDelayedDeclState &other) { + CodeCompletionDelayedDeclStat.reset(new CodeCompletionDelayedDeclState( + other.Kind, other.Flags, other.ParentContext, + ScopeInfo.saveCurrentScope(), other.StartOffset, other.EndOffset, + other.PrevOffset)); } void PersistentParserState::delayDeclList(IterableDeclContext *D) { diff --git a/lib/Parse/Scope.cpp b/lib/Parse/Scope.cpp index d8ebb202d4b91..0ae1ff976c196 100644 --- a/lib/Parse/Scope.cpp +++ b/lib/Parse/Scope.cpp @@ -50,14 +50,14 @@ static bool isResolvableScope(ScopeKind SK) { } Scope::Scope(Parser *P, ScopeKind SC, bool isInactiveConfigBlock) - : SI(P->getScopeInfo()), - HTScope(SI.HT, SI.CurScope ? &SI.CurScope->HTScope : nullptr), - PrevScope(SI.CurScope), - PrevResolvableDepth(SI.ResolvableDepth), - Kind(SC), - IsInactiveConfigBlock(isInactiveConfigBlock) { + : Scope(P->getScopeInfo(), SC, isInactiveConfigBlock) {} + +Scope::Scope(ScopeInfo &SI, ScopeKind SC, bool isInactiveConfigBlock) + : SI(SI), HTScope(SI.HT, SI.CurScope ? &SI.CurScope->HTScope : nullptr), + PrevScope(SI.CurScope), PrevResolvableDepth(SI.ResolvableDepth), Kind(SC), + IsInactiveConfigBlock(isInactiveConfigBlock) { assert(PrevScope || Kind == ScopeKind::TopLevel); - + if (SI.CurScope) { Depth = SI.CurScope->Depth + 1; IsInactiveConfigBlock |= SI.CurScope->IsInactiveConfigBlock; diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp index 2fc29e21328b1..83635923ba70f 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp @@ -58,7 +58,7 @@ class ImportDepth { public: ImportDepth() = default; - ImportDepth(ASTContext &context, CompilerInvocation &invocation); + ImportDepth(ASTContext &context, const CompilerInvocation &invocation); Optional lookup(StringRef module) { auto I = depths.find(module); @@ -320,7 +320,8 @@ CodeCompletionViewRef CodeCompletionOrganizer::takeResultsView() { // ImportDepth //===----------------------------------------------------------------------===// -ImportDepth::ImportDepth(ASTContext &context, CompilerInvocation &invocation) { +ImportDepth::ImportDepth(ASTContext &context, + const CompilerInvocation &invocation) { llvm::DenseSet seen; std::deque> worklist; diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h index 9d79f2ea55b6c..d649596c5db0f 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h @@ -53,7 +53,7 @@ struct Options { struct SwiftCompletionInfo { swift::ASTContext *swiftASTContext = nullptr; - swift::CompilerInvocation *invocation = nullptr; + const swift::CompilerInvocation *invocation = nullptr; swift::ide::CodeCompletionContext *completionContext = nullptr; }; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index faac2f85827e0..dad7f085ab6d3 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -22,6 +22,7 @@ #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/CodeCompletionCache.h" +#include "swift/IDE/CompletionInstance.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" @@ -90,7 +91,7 @@ struct SwiftCodeCompletionConsumer : handleResultsImpl(handleResultsImpl) {} void setContext(swift::ASTContext *context, - swift::CompilerInvocation *invocation, + const swift::CompilerInvocation *invocation, swift::ide::CodeCompletionContext *completionContext) { swiftContext.swiftASTContext = context; swiftContext.invocation = invocation; @@ -128,26 +129,30 @@ static bool swiftCodeCompleteImpl( // Resolve symlinks for the input file; we resolve them for the input files // in the arguments as well. // FIXME: We need the Swift equivalent of Clang's FileEntry. - auto bufferIdentifier = - Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier()); + llvm::SmallString<128> bufferIdentifier; + if (auto err = FileSystem->getRealPath( + UnresolvedInputFile->getBufferIdentifier(), bufferIdentifier)) + bufferIdentifier = UnresolvedInputFile->getBufferIdentifier(); + // Create a buffer for code completion. This contains '\0' at 'Offset' + // position of 'UnresolvedInputFile' buffer. auto origOffset = Offset; - auto newBuffer = SwiftLangSupport::makeCodeCompletionMemoryBuffer( + auto newBuffer = ide::makeCodeCompletionMemoryBuffer( UnresolvedInputFile, Offset, bufferIdentifier); - CompilerInstance CI; - // Display diagnostics to stderr. + SourceManager SM; + DiagnosticEngine Diags(SM); PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); - EditorDiagConsumer TraceDiags; - trace::TracedOperation TracedOp(trace::OperationKind::CodeCompletion); + trace::TracedOperation TracedOp{trace::OperationKind::CodeCompletion}; + + Diags.addConsumer(PrintDiags); if (TracedOp.enabled()) { - CI.addDiagnosticConsumer(&TraceDiags); + Diags.addConsumer(TraceDiags); trace::SwiftInvocation SwiftArgs; trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); TracedOp.setDiagnosticProvider( - [&TraceDiags](SmallVectorImpl &diags) { + [&](SmallVectorImpl &diags) { TraceDiags.getAllDiagnostics(diags); }); TracedOp.start( @@ -155,48 +160,40 @@ static bool swiftCodeCompleteImpl( {std::make_pair("OriginalOffset", std::to_string(origOffset)), std::make_pair("Offset", std::to_string(Offset))}); } + ForwardingDiagnosticConsumer CIDiags(Diags); CompilerInvocation Invocation; bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, CI.getDiags(), bufferIdentifier, FileSystem, Error); + Invocation, Args, Diags, newBuffer->getBufferIdentifier(), FileSystem, + Error); if (Failed) return false; if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { Error = "no input filenames specified"; return false; } + CompilerInstance *CI = Lang.getCompletionInstance().getCompilerInstance( + Invocation, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); + if (!CI) + return false; + SWIFT_DEFER { CI->removeDiagnosticConsumer(&CIDiags); }; - // Always disable source location resolutions from .swiftsourceinfo file - // because they're somewhat heavy operations and aren't needed for completion. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - - Invocation.setCodeCompletionPoint(newBuffer.get(), Offset); + // Perform the parsing and completion. + if (!CI->hasPersistentParserState()) + CI->performParseAndResolveImportsOnly(); // Create a factory for code completion callbacks that will feed the // Consumer. auto swiftCache = Lang.getCodeCompletionCache(); // Pin the cache. ide::CodeCompletionContext CompletionContext(swiftCache->getCache()); - std::unique_ptr callbacksFactory( - ide::makeCodeCompletionCallbacksFactory(CompletionContext, SwiftConsumer)); - - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - - if (FileSystem != llvm::vfs::getRealFileSystem()) { - CI.getSourceMgr().setFileSystem(FileSystem); - } - - if (CI.setup(Invocation)) { - // FIXME: error? - return true; - } + ide::makeCodeCompletionCallbacksFactory(CompletionContext, + SwiftConsumer)); - CloseClangModuleFiles scopedCloseFiles( - *CI.getASTContext().getClangModuleLoader()); - SwiftConsumer.setContext(&CI.getASTContext(), &Invocation, + SwiftConsumer.setContext(&CI->getASTContext(), &CI->getInvocation(), &CompletionContext); - registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator); - CI.performParseAndResolveImportsOnly(); + performCodeCompletionSecondPass(CI->getPersistentParserState(), + *callbacksFactory); SwiftConsumer.clearContext(); return true; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp index 3994e4124526f..21bddaf663a19 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp @@ -16,6 +16,7 @@ #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/ConformingMethodList.h" +#include "swift/IDE/CompletionInstance.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Comment.h" #include "clang/AST/Decl.h" @@ -32,25 +33,30 @@ static bool swiftConformingMethodListImpl( ide::ConformingMethodListConsumer &Consumer, llvm::IntrusiveRefCntPtr FileSystem, std::string &Error) { - auto bufferIdentifier = - Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier()); + + // Resolve symlinks for the input file. + llvm::SmallString<128> bufferIdentifier; + if (auto err = FileSystem->getRealPath( + UnresolvedInputFile->getBufferIdentifier(), bufferIdentifier)) + bufferIdentifier = UnresolvedInputFile->getBufferIdentifier(); auto origOffset = Offset; - auto newBuffer = SwiftLangSupport::makeCodeCompletionMemoryBuffer( + auto newBuffer = ide::makeCodeCompletionMemoryBuffer( UnresolvedInputFile, Offset, bufferIdentifier); - CompilerInstance CI; + SourceManager SM; + DiagnosticEngine Diags(SM); PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); - EditorDiagConsumer TraceDiags; - trace::TracedOperation TracedOp(trace::OperationKind::CodeCompletion); + trace::TracedOperation TracedOp{trace::OperationKind::CodeCompletion}; + + Diags.addConsumer(PrintDiags); if (TracedOp.enabled()) { - CI.addDiagnosticConsumer(&TraceDiags); + Diags.addConsumer(TraceDiags); trace::SwiftInvocation SwiftArgs; trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); TracedOp.setDiagnosticProvider( - [&TraceDiags](SmallVectorImpl &diags) { + [&](SmallVectorImpl &diags) { TraceDiags.getAllDiagnostics(diags); }); TracedOp.start( @@ -58,22 +64,27 @@ static bool swiftConformingMethodListImpl( {std::make_pair("OriginalOffset", std::to_string(origOffset)), std::make_pair("Offset", std::to_string(Offset))}); } + ForwardingDiagnosticConsumer CIDiags(Diags); CompilerInvocation Invocation; bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, CI.getDiags(), bufferIdentifier, FileSystem, Error); + Invocation, Args, Diags, newBuffer->getBufferIdentifier(), FileSystem, + Error); if (Failed) return false; if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { Error = "no input filenames specified"; return false; } + CompilerInstance *CI = Lang.getCompletionInstance().getCompilerInstance( + Invocation, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); + if (!CI) + return false; + SWIFT_DEFER { CI->removeDiagnosticConsumer(&CIDiags); }; - // Always disable source location resolutions from .swiftsourceinfo file - // because they're somewhat heavy operations and aren't needed for completion. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - - Invocation.setCodeCompletionPoint(newBuffer.get(), Offset); + // Perform the parsing and completion. + if (!CI->hasPersistentParserState()) + CI->performParseAndResolveImportsOnly(); // Create a factory for code completion callbacks that will feed the // Consumer. @@ -81,18 +92,8 @@ static bool swiftConformingMethodListImpl( ide::makeConformingMethodListCallbacksFactory(ExpectedTypeNames, Consumer)); - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - - if (FileSystem != llvm::vfs::getRealFileSystem()) { - CI.getSourceMgr().setFileSystem(FileSystem); - } - - if (CI.setup(Invocation)) { - // FIXME: error? - return true; - } - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performParseAndResolveImportsOnly(); + performCodeCompletionSecondPass(CI->getPersistentParserState(), + *callbacksFactory); return true; } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index 1bd390a5fa96f..db3aaeeca4d97 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -25,6 +25,7 @@ #include "swift/Config.h" #include "swift/IDE/CodeCompletion.h" #include "swift/IDE/CodeCompletionCache.h" +#include "swift/IDE/CompletionInstance.h" #include "swift/IDE/SyntaxModel.h" #include "swift/IDE/Utils.h" @@ -266,6 +267,9 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) ASTMgr = std::make_shared(EditorDocuments, SKCtx.getGlobalConfiguration(), Stats, RuntimeResourcePath); + + CompletionInst = std::make_unique(); + // By default, just use the in-memory cache. CCCache->inMemory = llvm::make_unique(); @@ -276,26 +280,6 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) SwiftLangSupport::~SwiftLangSupport() { } -std::unique_ptr -SwiftLangSupport::makeCodeCompletionMemoryBuffer( - const llvm::MemoryBuffer *origBuf, unsigned &Offset, - const std::string bufferIdentifier) { - - auto origBuffSize = origBuf->getBufferSize(); - if (Offset > origBuffSize) - Offset = origBuffSize; - - auto newBuffer = llvm::WritableMemoryBuffer::getNewUninitMemBuffer( - origBuffSize + 1, bufferIdentifier); - auto *pos = origBuf->getBufferStart() + Offset; - auto *newPos = - std::copy(origBuf->getBufferStart(), pos, newBuffer->getBufferStart()); - *newPos = '\0'; - std::copy(pos, origBuf->getBufferEnd(), newPos + 1); - - return std::unique_ptr(newBuffer.release()); -} - UIdent SwiftLangSupport::getUIDForDecl(const Decl *D, bool IsRef) { return UIdentVisitor(IsRef).visit(const_cast(D)); } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 21da3c982afaf..8d10c1386b34e 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -50,6 +50,7 @@ namespace syntax { namespace ide { class CodeCompletionCache; + class CompletionInstance; class OnDiskCodeCompletionCache; class SourceEditConsumer; enum class CodeCompletionDeclKind; @@ -294,6 +295,7 @@ class SwiftLangSupport : public LangSupport { ThreadSafeRefCntPtr CustomCompletions; std::shared_ptr Stats; llvm::StringMap> FileSystemProviders; + std::unique_ptr CompletionInst; public: explicit SwiftLangSupport(SourceKit::Context &SKCtx); @@ -313,6 +315,10 @@ class SwiftLangSupport : public LangSupport { return CCCache; } + swift::ide::CompletionInstance &getCompletionInstance() { + return *CompletionInst; + } + /// Returns the FileSystemProvider registered under Name, or nullptr if not /// found. FileSystemProvider *getFileSystemProvider(StringRef Name); @@ -342,13 +348,6 @@ class SwiftLangSupport : public LangSupport { getFileSystem(const Optional &vfsOptions, Optional primaryFile, std::string &error); - /// Copy a memory buffer inserting '0' at the position of \c origBuf. - // TODO: Share with code completion. - static std::unique_ptr - makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, - unsigned &Offset, - const std::string bufferIdentifier); - static SourceKit::UIdent getUIDForDecl(const swift::Decl *D, bool IsRef = false); static SourceKit::UIdent getUIDForExtensionOfDecl(const swift::Decl *D); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp index 80f7f408847dc..313fc9cbf54ab 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp @@ -15,6 +15,7 @@ #include "SwiftEditorDiagConsumer.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/IDE/CompletionInstance.h" #include "swift/IDE/TypeContextInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Comment.h" @@ -30,25 +31,30 @@ static bool swiftTypeContextInfoImpl( ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, std::string &Error) { - auto bufferIdentifier = - Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier()); + + // Resolve symlinks for the input file. + llvm::SmallString<128> bufferIdentifier; + if (auto err = FileSystem->getRealPath( + UnresolvedInputFile->getBufferIdentifier(), bufferIdentifier)) + bufferIdentifier = UnresolvedInputFile->getBufferIdentifier(); auto origOffset = Offset; - auto newBuffer = SwiftLangSupport::makeCodeCompletionMemoryBuffer( + auto newBuffer = ide::makeCodeCompletionMemoryBuffer( UnresolvedInputFile, Offset, bufferIdentifier); - CompilerInstance CI; + SourceManager SM; + DiagnosticEngine Diags(SM); PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); - EditorDiagConsumer TraceDiags; - trace::TracedOperation TracedOp(trace::OperationKind::CodeCompletion); + trace::TracedOperation TracedOp{trace::OperationKind::CodeCompletion}; + + Diags.addConsumer(PrintDiags); if (TracedOp.enabled()) { - CI.addDiagnosticConsumer(&TraceDiags); + Diags.addConsumer(TraceDiags); trace::SwiftInvocation SwiftArgs; trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); TracedOp.setDiagnosticProvider( - [&TraceDiags](SmallVectorImpl &diags) { + [&](SmallVectorImpl &diags) { TraceDiags.getAllDiagnostics(diags); }); TracedOp.start( @@ -56,40 +62,35 @@ static bool swiftTypeContextInfoImpl( {std::make_pair("OriginalOffset", std::to_string(origOffset)), std::make_pair("Offset", std::to_string(Offset))}); } + ForwardingDiagnosticConsumer CIDiags(Diags); CompilerInvocation Invocation; bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, CI.getDiags(), bufferIdentifier, FileSystem, Error); + Invocation, Args, Diags, newBuffer->getBufferIdentifier(), FileSystem, + Error); if (Failed) return false; if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { Error = "no input filenames specified"; return false; } + CompilerInstance *CI = Lang.getCompletionInstance().getCompilerInstance( + Invocation, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); + if (!CI) + return false; + SWIFT_DEFER { CI->removeDiagnosticConsumer(&CIDiags); }; - // Always disable source location resolutions from .swiftsourceinfo file - // because they're somewhat heavy operations and aren't needed for completion. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - - Invocation.setCodeCompletionPoint(newBuffer.get(), Offset); + // Perform the parsing and completion. + if (!CI->hasPersistentParserState()) + CI->performParseAndResolveImportsOnly(); // Create a factory for code completion callbacks that will feed the // Consumer. std::unique_ptr callbacksFactory( ide::makeTypeContextInfoCallbacksFactory(Consumer)); - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - - if (FileSystem != llvm::vfs::getRealFileSystem()) { - CI.getSourceMgr().setFileSystem(FileSystem); - } - - if (CI.setup(Invocation)) { - // FIXME: error? - return true; - } - registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator); - CI.performParseAndResolveImportsOnly(); + performCodeCompletionSecondPass(CI->getPersistentParserState(), + *callbacksFactory); return true; } diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 71f7349105577..74101c9505f69 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -33,6 +33,7 @@ #include "swift/Driver/FrontendUtil.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/IDE/CompletionInstance.h" #include "swift/IDE/CodeCompletion.h" #include "swift/IDE/CommentConversion.h" #include "swift/IDE/ConformingMethodList.h" @@ -716,11 +717,13 @@ removeCodeCompletionTokens(llvm::MemoryBuffer *Input, Input->getBufferIdentifier())); } -static int doTypeContextInfo(const CompilerInvocation &InitInvok, - StringRef SourceFilename, - StringRef SecondSourceFileName, - StringRef CodeCompletionToken, - bool CodeCompletionDiagnostics) { +static bool doCodeCompletionImpl( + CodeCompletionCallbacksFactory *callbacksFactory, + const CompilerInvocation &InitInvok, + StringRef SourceFilename, + StringRef SecondSourceFileName, + StringRef CodeCompletionToken, + bool CodeCompletionDiagnostics) { llvm::ErrorOr> FileBufOrErr = llvm::MemoryBuffer::getFile(SourceFilename); if (!FileBufOrErr) { @@ -746,16 +749,31 @@ static int doTypeContextInfo(const CompilerInvocation &InitInvok, CompilerInvocation Invocation(InitInvok); - // Disable source location resolutions from .swiftsourceinfo file because - // they are somewhat heavy operations and are not needed for completions. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; + if (!SecondSourceFileName.empty()) { + Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( + SecondSourceFileName); + } - // Disable to build syntax tree because code-completion skips some portion of - // source text. That breaks an invariant of syntax tree building. - Invocation.getLangOptions().BuildSyntaxTree = false; + std::string Error; + PrintingDiagnosticConsumer PrintDiags; + CompletionInstance CompletionInst; + auto *CI = CompletionInst.getCompilerInstance( + Invocation, llvm::vfs::getRealFileSystem(), CleanFile.get(), Offset, + Error, CodeCompletionDiagnostics ? &PrintDiags : nullptr); + if (!CI) + return 1; - Invocation.setCodeCompletionPoint(CleanFile.get(), Offset); + CI->performParseAndResolveImportsOnly(); + performCodeCompletionSecondPass(CI->getPersistentParserState(), + *callbacksFactory); + return 0; +} +static int doTypeContextInfo(const CompilerInvocation &InitInvok, + StringRef SourceFilename, + StringRef SecondSourceFileName, + StringRef CodeCompletionToken, + bool CodeCompletionDiagnostics) { // Create a CodeCompletionConsumer. std::unique_ptr Consumer( new ide::PrintingTypeContextInfoConsumer(llvm::outs())); @@ -765,23 +783,9 @@ static int doTypeContextInfo(const CompilerInvocation &InitInvok, std::unique_ptr callbacksFactory( ide::makeTypeContextInfoCallbacksFactory(*Consumer)); - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - if (!SecondSourceFileName.empty()) { - Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( - SecondSourceFileName); - } - CompilerInstance CI; - - PrintingDiagnosticConsumer PrintDiags; - if (CodeCompletionDiagnostics) { - // Display diagnostics to stderr. - CI.addDiagnosticConsumer(&PrintDiags); - } - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performParseAndResolveImportsOnly(); - return 0; + return doCodeCompletionImpl(callbacksFactory.get(), InitInvok, SourceFilename, + SecondSourceFileName, CodeCompletionToken, + CodeCompletionDiagnostics); } static int @@ -790,41 +794,6 @@ doConformingMethodList(const CompilerInvocation &InitInvok, StringRef CodeCompletionToken, bool CodeCompletionDiagnostics, const std::vector expectedTypeNames) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; - return 1; - } - - unsigned Offset; - - std::unique_ptr CleanFile(removeCodeCompletionTokens( - FileBufOrErr.get().get(), CodeCompletionToken, &Offset)); - - if (Offset == ~0U) { - llvm::errs() << "could not find code completion token \"" - << CodeCompletionToken << "\"\n"; - return 1; - } - llvm::outs() << "found code completion token " << CodeCompletionToken - << " at offset " << Offset << "\n"; - llvm::errs() << "found code completion token " << CodeCompletionToken - << " at offset " << Offset << "\n"; - - CompilerInvocation Invocation(InitInvok); - - // Disable source location resolutions from .swiftsourceinfo file because - // they are somewhat heavy operations and are not needed for completions. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - - // Disable to build syntax tree because code-completion skips some portion of - // source text. That breaks an invariant of syntax tree building. - Invocation.getLangOptions().BuildSyntaxTree = false; - - Invocation.setCodeCompletionPoint(CleanFile.get(), Offset); - SmallVector typeNames; for (auto &name : expectedTypeNames) typeNames.push_back(name.c_str()); @@ -838,23 +807,9 @@ doConformingMethodList(const CompilerInvocation &InitInvok, std::unique_ptr callbacksFactory( ide::makeConformingMethodListCallbacksFactory(typeNames, *Consumer)); - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - if (!SecondSourceFileName.empty()) { - Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( - SecondSourceFileName); - } - CompilerInstance CI; - - PrintingDiagnosticConsumer PrintDiags; - if (CodeCompletionDiagnostics) { - // Display diagnostics to stderr. - CI.addDiagnosticConsumer(&PrintDiags); - } - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performParseAndResolveImportsOnly(); - return 0; + return doCodeCompletionImpl(callbacksFactory.get(), InitInvok, SourceFilename, + SecondSourceFileName, CodeCompletionToken, + CodeCompletionDiagnostics); } static int doCodeCompletion(const CompilerInvocation &InitInvok, @@ -864,42 +819,6 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok, bool CodeCompletionDiagnostics, bool CodeCompletionKeywords, bool CodeCompletionComments) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; - return 1; - } - - unsigned CodeCompletionOffset; - - std::unique_ptr CleanFile( - removeCodeCompletionTokens(FileBufOrErr.get().get(), CodeCompletionToken, - &CodeCompletionOffset)); - - if (CodeCompletionOffset == ~0U) { - llvm::errs() << "could not find code completion token \"" - << CodeCompletionToken << "\"\n"; - return 1; - } - llvm::outs() << "found code completion token " << CodeCompletionToken - << " at offset " << CodeCompletionOffset << "\n"; - llvm::errs() << "found code completion token " << CodeCompletionToken - << " at offset " << CodeCompletionOffset << "\n"; - - CompilerInvocation Invocation(InitInvok); - - Invocation.setCodeCompletionPoint(CleanFile.get(), CodeCompletionOffset); - - // Disable source location resolutions from .swiftsourceinfo file because - // they are somewhat heavy operations and are not needed for completions. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - - // Disable to build syntax tree because code-completion skips some portion of - // source text. That breaks an invariant of syntax tree building. - Invocation.getLangOptions().BuildSyntaxTree = false; - std::unique_ptr OnDiskCache; if (!options::CompletionCachePath.empty()) { OnDiskCache = llvm::make_unique( @@ -915,27 +834,12 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok, // Create a factory for code completion callbacks that will feed the // Consumer. - std::unique_ptr CompletionCallbacksFactory( - ide::makeCodeCompletionCallbacksFactory(CompletionContext, - *Consumer)); - - Invocation.setCodeCompletionFactory(CompletionCallbacksFactory.get()); - if (!SecondSourceFileName.empty()) { - Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( - SecondSourceFileName); - } - CompilerInstance CI; + std::unique_ptr callbacksFactory( + ide::makeCodeCompletionCallbacksFactory(CompletionContext, *Consumer)); - PrintingDiagnosticConsumer PrintDiags; - if (CodeCompletionDiagnostics) { - // Display diagnostics to stderr. - CI.addDiagnosticConsumer(&PrintDiags); - } - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performParseAndResolveImportsOnly(); - return 0; + return doCodeCompletionImpl(callbacksFactory.get(), InitInvok, SourceFilename, + SecondSourceFileName, CodeCompletionToken, + CodeCompletionDiagnostics); } static int doREPLCodeCompletion(const CompilerInvocation &InitInvok, From c1530eedca469c21e4a2a246b59c08e44c9d5f04 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 11 Dec 2019 11:35:38 -0800 Subject: [PATCH 111/478] [SourceKit] Add option to enable ASTContext reusing for code completion --- include/swift/IDE/CompletionInstance.h | 5 +++++ lib/IDE/CompletionInstance.cpp | 3 +++ .../include/SourceKit/Core/LangSupport.h | 1 + .../lib/SwiftLang/CodeCompletionOrganizer.h | 1 + .../lib/SwiftLang/SwiftCompletion.cpp | 22 +++++++++++++++++++ .../lib/SwiftLang/SwiftLangSupport.h | 1 + .../tools/sourcekitd/lib/API/Requests.cpp | 14 ++++++++++-- 7 files changed, 45 insertions(+), 2 deletions(-) diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h index d57f5c066abcf..3ee5977e6d56c 100644 --- a/include/swift/IDE/CompletionInstance.h +++ b/include/swift/IDE/CompletionInstance.h @@ -36,6 +36,7 @@ makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, class CompletionInstance { std::unique_ptr CachedCI = nullptr; + bool EnableASTCaching = false; swift::CompilerInstance * getReusingCompilerInstance(const swift::CompilerInvocation &Invocation, @@ -49,6 +50,10 @@ class CompletionInstance { std::string &Error, DiagnosticConsumer *DiagC); public: + void setEnableASTCaching(bool Flag) { + EnableASTCaching = Flag; + } + swift::CompilerInstance *getCompilerInstance( swift::CompilerInvocation &Invocation, llvm::IntrusiveRefCntPtr FileSystem, diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index d03d34e88f065..3227cba9793d8 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -130,6 +130,9 @@ CompilerInstance *CompletionInstance::getReusingCompilerInstance( const swift::CompilerInvocation &Invocation, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, DiagnosticConsumer *DiagC) { + if (!EnableASTCaching) + return nullptr; + if (!CachedCI) return nullptr; diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index f1e13394e611d..5341716393419 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -653,6 +653,7 @@ class LangSupport { virtual void codeComplete(llvm::MemoryBuffer *InputBuf, unsigned Offset, + OptionsDictionary *options, CodeCompletionConsumer &Consumer, ArrayRef Args, Optional vfsOptions) = 0; diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h index d649596c5db0f..27d53585c5af6 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h @@ -42,6 +42,7 @@ struct Options { bool hideLowPriority = true; bool hideByNameStyle = true; bool fuzzyMatching = true; + bool reuseASTContextIfPossible = false; unsigned minFuzzyLength = 2; unsigned showTopNonLiteralResults = 3; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index dad7f085ab6d3..1719dd7d187e5 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -199,11 +199,29 @@ static bool swiftCodeCompleteImpl( return true; } +static void translateCodeCompletionOptions(OptionsDictionary &from, + CodeCompletion::Options &to, + StringRef &filterText, + unsigned &resultOffset, + unsigned &maxResults); + void SwiftLangSupport::codeComplete( llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, + OptionsDictionary *options, SourceKit::CodeCompletionConsumer &SKConsumer, ArrayRef Args, Optional vfsOptions) { + + CodeCompletion::Options CCOpts; + if (options) { + StringRef filterText; + unsigned resultOffset = 0; + unsigned maxResults = 0; + translateCodeCompletionOptions(*options, CCOpts, filterText, resultOffset, + maxResults); + } + CompletionInst->setEnableASTCaching(CCOpts.reuseASTContextIfPossible); + std::string error; // FIXME: the use of None as primary file is to match the fact we do not read // the document contents using the editor documents infrastructure. @@ -823,6 +841,7 @@ static void translateCodeCompletionOptions(OptionsDictionary &from, static UIdent KeyContextWeight("key.codecomplete.sort.contextweight"); static UIdent KeyFuzzyWeight("key.codecomplete.sort.fuzzyweight"); static UIdent KeyPopularityBonus("key.codecomplete.sort.popularitybonus"); + static UIdent KeyReuseASTContext("key.codecomplete.reuseastcontext"); from.valueForOption(KeySortByName, to.sortByName); from.valueForOption(KeyUseImportDepth, to.useImportDepth); from.valueForOption(KeyGroupOverloads, to.groupOverloads); @@ -846,6 +865,7 @@ static void translateCodeCompletionOptions(OptionsDictionary &from, from.valueForOption(KeyPopularityBonus, to.popularityBonus); from.valueForOption(KeyHideByName, to.hideByNameStyle); from.valueForOption(KeyTopNonLiteral, to.showTopNonLiteralResults); + from.valueForOption(KeyReuseASTContext, to.reuseASTContextIfPossible); } /// Canonicalize a name that is in the format of a reference to a function into @@ -1164,6 +1184,7 @@ void SwiftLangSupport::codeCompleteOpen( if (options) translateCodeCompletionOptions(*options, CCOpts, filterText, resultOffset, maxResults); + CompletionInst->setEnableASTCaching(CCOpts.reuseASTContextIfPossible); std::string error; // FIXME: the use of None as primary file is to match the fact we do not read @@ -1284,6 +1305,7 @@ void SwiftLangSupport::codeCompleteUpdate( if (options) translateCodeCompletionOptions(*options, CCOpts, filterText, resultOffset, maxResults); + CompletionInst->setEnableASTCaching(CCOpts.reuseASTContextIfPossible); NameToPopularityMap *nameToPopularity = nullptr; // This reference must outlive the uses of nameToPopularity. diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 8d10c1386b34e..25af34e4ca6d6 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -445,6 +445,7 @@ class SwiftLangSupport : public LangSupport { void codeComplete( llvm::MemoryBuffer *InputBuf, unsigned Offset, + OptionsDictionary *options, SourceKit::CodeCompletionConsumer &Consumer, ArrayRef Args, Optional vfsOptions) override; diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp index a1c14d088d9e4..ccade323104fe 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp @@ -158,6 +158,7 @@ static void findRelatedIdents(StringRef Filename, static sourcekitd_response_t codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset, + Optional optionsDict, ArrayRef Args, Optional vfsOptions); static sourcekitd_response_t codeCompleteOpen(StringRef name, @@ -940,7 +941,9 @@ static void handleSemanticRequest( int64_t Offset; if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) return Rec(createErrorRequestInvalid("missing 'key.offset'")); - return Rec(codeComplete(InputBuf.get(), Offset, Args, std::move(vfsOptions))); + Optional options = Req.getDictionary(KeyCodeCompleteOptions); + return Rec(codeComplete(InputBuf.get(), Offset, options, Args, + std::move(vfsOptions))); } if (ReqUID == RequestCodeCompleteOpen) { @@ -1932,12 +1935,19 @@ class SKCodeCompletionConsumer : public CodeCompletionConsumer { static sourcekitd_response_t codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset, + Optional optionsDict, ArrayRef Args, Optional vfsOptions) { ResponseBuilder RespBuilder; SKCodeCompletionConsumer CCC(RespBuilder); + + std::unique_ptr options; + if (optionsDict) + options = std::make_unique(*optionsDict); + LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); - Lang.codeComplete(InputBuf, Offset, CCC, Args, std::move(vfsOptions)); + Lang.codeComplete(InputBuf, Offset, options.get(), CCC, Args, + std::move(vfsOptions)); return CCC.createResponse(); } From a83c7e86b7a333377ad678f044a53c098da648c1 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 11 Dec 2019 12:50:15 -0800 Subject: [PATCH 112/478] [SourceKit] Calculate CompilerInvocation hash --- lib/IDE/CompletionInstance.cpp | 71 +++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 3227cba9793d8..a060db238c39c 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -23,6 +23,7 @@ #include "swift/Parse/PersistentParserState.h" #include "swift/Subsystems.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/ADT/Hashing.h" using namespace swift; using namespace ide; @@ -124,6 +125,73 @@ static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC, return newDC; } + +/// Calculate the hash value of the \c CompilerInvocation. +/// This should take all options affecting completion results into account. +/// If the hashes between multiple completion invocation are different, we +/// cannot reuse the CompilerInstance. +static llvm::hash_code calculateInvocationHash(const CompilerInvocation &Inv) { + llvm::hash_code hash(0); + + auto &frontendOpts = Inv.getFrontendOptions(); + for (const InputFile &Input : frontendOpts.InputsAndOutputs.getAllInputs()) + hash = llvm::hash_combine(hash, Input.file()); + hash = llvm::hash_combine( + hash, + frontendOpts.InputKind, + llvm::makeArrayRef(frontendOpts.ImplicitImportModuleNames), + frontendOpts.ImplicitObjCHeaderPath, + frontendOpts.ModuleName, + frontendOpts.PrebuiltModuleCachePath, + llvm::makeArrayRef(frontendOpts.PreferInterfaceForModules), + frontendOpts.ParseStdlib, + frontendOpts.EnableSourceImport, + frontendOpts.ImportUnderlyingModule); + + auto &langOpts = Inv.getLangOptions(); + hash = llvm::hash_combine( + hash, + langOpts.Target.str(), + llvm::VersionTuple(langOpts.EffectiveLanguageVersion).getAsString(), + llvm::VersionTuple(langOpts.PackageDescriptionVersion).getAsString(), + langOpts.DisableAvailabilityChecking, + langOpts.EnableAccessControl, + langOpts.EnableAppExtensionRestrictions, + langOpts.RequireExplicitAvailability, + langOpts.RequireExplicitAvailabilityTarget, + langOpts.EnableObjCInterop, + langOpts.EnableCXXInterop, + langOpts.InferImportAsMember, + langOpts.EnableSwift3ObjCInference); + + auto &searchPathOpts = Inv.getSearchPathOptions(); + hash = llvm::hash_combine( + hash, + searchPathOpts.SDKPath, + llvm::makeArrayRef(searchPathOpts.ImportSearchPaths), + llvm::makeArrayRef(searchPathOpts.VFSOverlayFiles), + searchPathOpts.RuntimeResourcePath, + llvm::makeArrayRef(searchPathOpts.RuntimeLibraryImportPaths), + llvm::makeArrayRef(searchPathOpts.SkipRuntimeLibraryImportPaths)); + for (auto &P : searchPathOpts.FrameworkSearchPaths) + hash = llvm::hash_combine(hash, P.IsSystem, P.Path); + + auto &clangOpts = Inv.getClangImporterOptions(); + hash = llvm::hash_combine( + hash, + clangOpts.ModuleCachePath, + llvm::makeArrayRef(clangOpts.ExtraArgs), + clangOpts.OverrideResourceDir, + clangOpts.TargetCPU, + static_cast(clangOpts.Mode), + clangOpts.ImportForwardDeclarations, + clangOpts.InferImportAsMember, + clangOpts.DisableSwiftBridgeAttr, + clangOpts.DisableOverlayModules); + + return hash; +} + } // namespace CompilerInstance *CompletionInstance::getReusingCompilerInstance( @@ -136,7 +204,8 @@ CompilerInstance *CompletionInstance::getReusingCompilerInstance( if (!CachedCI) return nullptr; - if (Invocation.getPCHHash() != CachedCI->getInvocation().getPCHHash()) + if (calculateInvocationHash(Invocation) != + calculateInvocationHash(CachedCI->getInvocation())) return nullptr; auto &oldState = CachedCI->getPersistentParserState(); From fcc7e411f8e1e706a562fea77d35d092aa683b9f Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 11 Dec 2019 16:58:13 -0800 Subject: [PATCH 113/478] [SourceKit] Add a test case for fast code completion --- include/swift/AST/DiagnosticsFrontend.def | 3 +++ lib/IDE/CompletionInstance.cpp | 6 ++++++ tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp | 1 + 3 files changed, 10 insertions(+) diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index f739926aa92ff..c3beb96e145fb 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -214,6 +214,9 @@ ERROR(repl_must_be_initialized,none, ERROR(error_doing_code_completion,none, "compiler is in code completion mode (benign diagnostic)", ()) +WARNING(completion_reusing_astcontext,none, + "completion reusing previous ASTContext (benign diagnostic)", ()) + ERROR(verify_encountered_fatal,none, "fatal error encountered while in -verify mode", ()) diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index a060db238c39c..9e6ae2abfa3dc 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -14,6 +14,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/Module.h" #include "swift/AST/SourceFile.h" #include "swift/Basic/LangOptions.h" @@ -290,6 +291,11 @@ CompilerInstance *CompletionInstance::getReusingCompilerInstance( AFD->setBodyDelayed(AFD->getBodySourceRange()); if (DiagC) CachedCI->addDiagnosticConsumer(DiagC); + + CachedCI->getDiags().diagnose( + SM.getLocForOffset(BufferID, newInfo.StartOffset), + diag::completion_reusing_astcontext); + return CachedCI.get(); } diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 6a397dff7ff99..fb7396409f76d 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -573,6 +573,7 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { case SourceKitRequest::CodeComplete: sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCodeComplete); sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset); + addCodeCompleteOptions(Req, Opts); break; case SourceKitRequest::CodeCompleteOpen: From c3d582841db39bd4c221ca6460a38ef94802e81a Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 11 Dec 2019 17:53:04 -0800 Subject: [PATCH 114/478] [SourceKit] Limit compiler instance reuse count for completions --- include/swift/IDE/CompletionInstance.h | 2 ++ lib/IDE/CompletionInstance.cpp | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h index 3ee5977e6d56c..57413d901003a 100644 --- a/include/swift/IDE/CompletionInstance.h +++ b/include/swift/IDE/CompletionInstance.h @@ -37,6 +37,8 @@ makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, class CompletionInstance { std::unique_ptr CachedCI = nullptr; bool EnableASTCaching = false; + unsigned MaxASTReuseCount = 100; + unsigned CurrentASTReuseCount = 0; swift::CompilerInstance * getReusingCompilerInstance(const swift::CompilerInvocation &Invocation, diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 9e6ae2abfa3dc..019eb6c8a305c 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -202,6 +202,11 @@ CompilerInstance *CompletionInstance::getReusingCompilerInstance( if (!EnableASTCaching) return nullptr; + if (CurrentASTReuseCount >= MaxASTReuseCount) { + CurrentASTReuseCount = 0; + return nullptr; + } + if (!CachedCI) return nullptr; @@ -296,6 +301,8 @@ CompilerInstance *CompletionInstance::getReusingCompilerInstance( SM.getLocForOffset(BufferID, newInfo.StartOffset), diag::completion_reusing_astcontext); + CurrentASTReuseCount += 1; + return CachedCI.get(); } From 2160134dc3b684f2cce1a74420d6116a4032a8a8 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 13 Dec 2019 14:34:44 -0800 Subject: [PATCH 115/478] [SourceKit] Add test file for fast completion --- .../CodeComplete/complete_sequence.swift | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 test/SourceKit/CodeComplete/complete_sequence.swift diff --git a/test/SourceKit/CodeComplete/complete_sequence.swift b/test/SourceKit/CodeComplete/complete_sequence.swift new file mode 100644 index 0000000000000..226c7a3a1ec25 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence.swift @@ -0,0 +1,55 @@ +class Foo { + var x: Int + var y: Int + func fooMethod() {} +} +struct Bar { + var a: Int + var b: Int + func barMethod() {} +} +func foo(arg: Foo) { + _ = arg. +} +func bar(arg: Bar) { + _ = arg. +} + +// NOTE: Tests for 'key.codecomplete.reuseastcontex' option. + +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -pos=12:11 %s -- %s == \ +// RUN: -req=complete -pos=15:11 %s -- %s > %t.response +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response +// RUN: %FileCheck --check-prefix=TRACE_NORMAL %s < %t.response + +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -- %s > %t.response.reuseastcontext +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response.reuseastcontext +// RUN: %FileCheck --check-prefix=TRACE_REUSEAST %s < %t.response.reuseastcontext + +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "barMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "a" +// RESULT-DAG: key.name: "b" +// RESULT: ] + +// TRACE_NORMAL-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE_NORMAL-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE_NORMAL-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE_NORMAL-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" + +// TRACE_REUSEAST-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE_REUSEAST-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE_REUSEAST-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE_REUSEAST: key.description: "completion reusing previous ASTContext (benign diagnostic)" From 204ae49b38dc59db2ae3cd0349189e1b3bd1e9a2 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 17 Dec 2019 12:28:36 -0800 Subject: [PATCH 116/478] [CodeCompletion] Add doc-comment for CompletionInstance --- include/swift/IDE/CompletionInstance.h | 13 ++++++++++++- lib/IDE/CompletionInstance.cpp | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h index 57413d901003a..5549429a59dd0 100644 --- a/include/swift/IDE/CompletionInstance.h +++ b/include/swift/IDE/CompletionInstance.h @@ -28,23 +28,32 @@ class DiagnosticConsumer; namespace ide { -/// Copy a memory buffer inserting '0' at the position of \c origBuf. +/// Copy a memory buffer inserting '\0' at the position of \c origBuf. std::unique_ptr makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, unsigned &Offset, llvm::StringRef bufferIdentifier); +/// Manages \c CompilerInstance for completion like operations. class CompletionInstance { std::unique_ptr CachedCI = nullptr; bool EnableASTCaching = false; unsigned MaxASTReuseCount = 100; unsigned CurrentASTReuseCount = 0; + /// Returns cached \c CompilerInstance if it's usable for the specified + /// completion request. + /// Returns \c nullptr if the functionality is disabled, compiler argument has + /// changed, primary file is not the same, the \c Offset is not in function + /// bodies, or the interface hash of the file has changed. swift::CompilerInstance * getReusingCompilerInstance(const swift::CompilerInvocation &Invocation, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, DiagnosticConsumer *DiagC); + /// Returns new \c CompilerInstance for the completion request. Users still + /// have to call \c performParseAndResolveImportsOnly() , and perform the + /// second pass on it. swift::CompilerInstance *renewCompilerInstance( swift::CompilerInvocation &Invocation, llvm::IntrusiveRefCntPtr FileSystem, @@ -56,6 +65,8 @@ class CompletionInstance { EnableASTCaching = Flag; } + /// Returns \C CompilerInstance for the completion request. Users can check if + /// it's cached or not by 'hasPersistentParserState()'. swift::CompilerInstance *getCompilerInstance( swift::CompilerInvocation &Invocation, llvm::IntrusiveRefCntPtr FileSystem, diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 019eb6c8a305c..e641776f98e60 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -74,7 +74,7 @@ template Decl *getElementAt(const Range &Decls, unsigned N) { /// This assumes the AST which contains \p DC has exact the same structure with /// \p SF. /// FIXME: This doesn't support IfConfigDecl blocks. If \p DC is in an inactive -/// config block, this function probably returns false. +/// config block, this function returns \c false. static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC, SourceFile *SF) { auto *newDC = DC; From f2466deed8b8df4cebbe5c59c28e8c2a0850d9a9 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 17 Dec 2019 12:29:15 -0800 Subject: [PATCH 117/478] [CodeCompletion] Rename getReusingCompilerInstance() to getCachedCompilerInstance() --- include/swift/IDE/CompletionInstance.h | 6 +++--- lib/IDE/CompletionInstance.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h index 5549429a59dd0..0233a786f4345 100644 --- a/include/swift/IDE/CompletionInstance.h +++ b/include/swift/IDE/CompletionInstance.h @@ -47,9 +47,9 @@ class CompletionInstance { /// changed, primary file is not the same, the \c Offset is not in function /// bodies, or the interface hash of the file has changed. swift::CompilerInstance * - getReusingCompilerInstance(const swift::CompilerInvocation &Invocation, - llvm::MemoryBuffer *completionBuffer, - unsigned int Offset, DiagnosticConsumer *DiagC); + getCachedCompilerInstance(const swift::CompilerInvocation &Invocation, + llvm::MemoryBuffer *completionBuffer, + unsigned int Offset, DiagnosticConsumer *DiagC); /// Returns new \c CompilerInstance for the completion request. Users still /// have to call \c performParseAndResolveImportsOnly() , and perform the diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index e641776f98e60..04bf65a9fcefc 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -195,7 +195,7 @@ static llvm::hash_code calculateInvocationHash(const CompilerInvocation &Inv) { } // namespace -CompilerInstance *CompletionInstance::getReusingCompilerInstance( +CompilerInstance *CompletionInstance::getCachedCompilerInstance( const swift::CompilerInvocation &Invocation, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, DiagnosticConsumer *DiagC) { @@ -348,8 +348,8 @@ CompilerInstance *swift::ide::CompletionInstance::getCompilerInstance( // FIXME: ASTScopeLookup doesn't support code completion yet. Invocation.disableASTScopeLookup(); - if (auto *cached = getReusingCompilerInstance(Invocation, completionBuffer, - Offset, DiagC)) + if (auto *cached = getCachedCompilerInstance(Invocation, completionBuffer, + Offset, DiagC)) return cached; if (auto *renewed = renewCompilerInstance( From c5b1ada982b77d6a171f45aa27e9498062c9a385 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 17 Dec 2019 12:29:51 -0800 Subject: [PATCH 118/478] [SourceKit] Hold CompletionInstance with std::shared_ptr So we can pin it on the threads. --- tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp | 5 ++++- tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp | 5 ++++- tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h | 6 +++--- tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp | 5 ++++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 1719dd7d187e5..f29392664af82 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -172,7 +172,10 @@ static bool swiftCodeCompleteImpl( Error = "no input filenames specified"; return false; } - CompilerInstance *CI = Lang.getCompletionInstance().getCompilerInstance( + + // Pin completion instance. + auto CompletionInst = Lang.getCompletionInstance(); + CompilerInstance *CI = CompletionInst->getCompilerInstance( Invocation, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); if (!CI) return false; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp index 21bddaf663a19..58b6dcbba39a3 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp @@ -76,7 +76,10 @@ static bool swiftConformingMethodListImpl( Error = "no input filenames specified"; return false; } - CompilerInstance *CI = Lang.getCompletionInstance().getCompilerInstance( + + // Pin completion instance. + auto CompletionInst = Lang.getCompletionInstance(); + CompilerInstance *CI = CompletionInst->getCompilerInstance( Invocation, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); if (!CI) return false; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 25af34e4ca6d6..3472d2cb45efa 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -295,7 +295,7 @@ class SwiftLangSupport : public LangSupport { ThreadSafeRefCntPtr CustomCompletions; std::shared_ptr Stats; llvm::StringMap> FileSystemProviders; - std::unique_ptr CompletionInst; + std::shared_ptr CompletionInst; public: explicit SwiftLangSupport(SourceKit::Context &SKCtx); @@ -315,8 +315,8 @@ class SwiftLangSupport : public LangSupport { return CCCache; } - swift::ide::CompletionInstance &getCompletionInstance() { - return *CompletionInst; + std::shared_ptr getCompletionInstance() { + return CompletionInst; } /// Returns the FileSystemProvider registered under Name, or nullptr if not diff --git a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp index 313fc9cbf54ab..8925987d88960 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp @@ -74,7 +74,10 @@ static bool swiftTypeContextInfoImpl( Error = "no input filenames specified"; return false; } - CompilerInstance *CI = Lang.getCompletionInstance().getCompilerInstance( + + // Pin completion instance. + auto CompletionInst = Lang.getCompletionInstance(); + CompilerInstance *CI = CompletionInst->getCompilerInstance( Invocation, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); if (!CI) return false; From 0f4526792783c3913879986982c7e470b6ee75ee Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 17 Dec 2019 13:46:28 -0800 Subject: [PATCH 119/478] [CodeCompletion] Use the arguments to check the equality of the invocation Checking "selected" properties of CompilerInvocation is hard to keep it up to date. If the arguments are the same, the invocations are the same. --- include/swift/IDE/CompletionInstance.h | 14 ++- lib/IDE/CompletionInstance.cpp | 92 ++++--------------- .../lib/SwiftLang/SwiftCompletion.cpp | 2 +- .../SwiftLang/SwiftConformingMethodList.cpp | 2 +- .../lib/SwiftLang/SwiftTypeContextInfo.cpp | 2 +- tools/swift-ide-test/swift-ide-test.cpp | 4 +- 6 files changed, 31 insertions(+), 85 deletions(-) diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h index 0233a786f4345..218d1f6fa2656 100644 --- a/include/swift/IDE/CompletionInstance.h +++ b/include/swift/IDE/CompletionInstance.h @@ -14,6 +14,7 @@ #define SWIFT_IDE_COMPLETIONINSTANCE_H #include "swift/Frontend/Frontend.h" +#include "llvm/ADT/Hashing.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" @@ -37,6 +38,7 @@ makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, /// Manages \c CompilerInstance for completion like operations. class CompletionInstance { std::unique_ptr CachedCI = nullptr; + llvm::hash_code CachedArgsHash; bool EnableASTCaching = false; unsigned MaxASTReuseCount = 100; unsigned CurrentASTReuseCount = 0; @@ -48,6 +50,7 @@ class CompletionInstance { /// bodies, or the interface hash of the file has changed. swift::CompilerInstance * getCachedCompilerInstance(const swift::CompilerInvocation &Invocation, + llvm::hash_code ArgsHash, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, DiagnosticConsumer *DiagC); @@ -55,20 +58,23 @@ class CompletionInstance { /// have to call \c performParseAndResolveImportsOnly() , and perform the /// second pass on it. swift::CompilerInstance *renewCompilerInstance( - swift::CompilerInvocation &Invocation, + swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, std::string &Error, DiagnosticConsumer *DiagC); public: - void setEnableASTCaching(bool Flag) { - EnableASTCaching = Flag; - } + void setEnableASTCaching(bool Flag) { EnableASTCaching = Flag; } /// Returns \C CompilerInstance for the completion request. Users can check if /// it's cached or not by 'hasPersistentParserState()'. + /// + /// NOTE: \p Args is only used for checking the equaity of the invocation. + /// Since this function assumes that it is already normalized, exact the same + /// arguments including their order is considered as the same invocation. swift::CompilerInstance *getCompilerInstance( swift::CompilerInvocation &Invocation, + llvm::ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, std::string &Error, DiagnosticConsumer *DiagC); diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 04bf65a9fcefc..bd01538a3bc4b 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -23,8 +23,8 @@ #include "swift/Frontend/Frontend.h" #include "swift/Parse/PersistentParserState.h" #include "swift/Subsystems.h" -#include "llvm/Support/MemoryBuffer.h" #include "llvm/ADT/Hashing.h" +#include "llvm/Support/MemoryBuffer.h" using namespace swift; using namespace ide; @@ -127,76 +127,10 @@ static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC, return newDC; } -/// Calculate the hash value of the \c CompilerInvocation. -/// This should take all options affecting completion results into account. -/// If the hashes between multiple completion invocation are different, we -/// cannot reuse the CompilerInstance. -static llvm::hash_code calculateInvocationHash(const CompilerInvocation &Inv) { - llvm::hash_code hash(0); - - auto &frontendOpts = Inv.getFrontendOptions(); - for (const InputFile &Input : frontendOpts.InputsAndOutputs.getAllInputs()) - hash = llvm::hash_combine(hash, Input.file()); - hash = llvm::hash_combine( - hash, - frontendOpts.InputKind, - llvm::makeArrayRef(frontendOpts.ImplicitImportModuleNames), - frontendOpts.ImplicitObjCHeaderPath, - frontendOpts.ModuleName, - frontendOpts.PrebuiltModuleCachePath, - llvm::makeArrayRef(frontendOpts.PreferInterfaceForModules), - frontendOpts.ParseStdlib, - frontendOpts.EnableSourceImport, - frontendOpts.ImportUnderlyingModule); - - auto &langOpts = Inv.getLangOptions(); - hash = llvm::hash_combine( - hash, - langOpts.Target.str(), - llvm::VersionTuple(langOpts.EffectiveLanguageVersion).getAsString(), - llvm::VersionTuple(langOpts.PackageDescriptionVersion).getAsString(), - langOpts.DisableAvailabilityChecking, - langOpts.EnableAccessControl, - langOpts.EnableAppExtensionRestrictions, - langOpts.RequireExplicitAvailability, - langOpts.RequireExplicitAvailabilityTarget, - langOpts.EnableObjCInterop, - langOpts.EnableCXXInterop, - langOpts.InferImportAsMember, - langOpts.EnableSwift3ObjCInference); - - auto &searchPathOpts = Inv.getSearchPathOptions(); - hash = llvm::hash_combine( - hash, - searchPathOpts.SDKPath, - llvm::makeArrayRef(searchPathOpts.ImportSearchPaths), - llvm::makeArrayRef(searchPathOpts.VFSOverlayFiles), - searchPathOpts.RuntimeResourcePath, - llvm::makeArrayRef(searchPathOpts.RuntimeLibraryImportPaths), - llvm::makeArrayRef(searchPathOpts.SkipRuntimeLibraryImportPaths)); - for (auto &P : searchPathOpts.FrameworkSearchPaths) - hash = llvm::hash_combine(hash, P.IsSystem, P.Path); - - auto &clangOpts = Inv.getClangImporterOptions(); - hash = llvm::hash_combine( - hash, - clangOpts.ModuleCachePath, - llvm::makeArrayRef(clangOpts.ExtraArgs), - clangOpts.OverrideResourceDir, - clangOpts.TargetCPU, - static_cast(clangOpts.Mode), - clangOpts.ImportForwardDeclarations, - clangOpts.InferImportAsMember, - clangOpts.DisableSwiftBridgeAttr, - clangOpts.DisableOverlayModules); - - return hash; -} - } // namespace CompilerInstance *CompletionInstance::getCachedCompilerInstance( - const swift::CompilerInvocation &Invocation, + const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, DiagnosticConsumer *DiagC) { if (!EnableASTCaching) @@ -210,8 +144,7 @@ CompilerInstance *CompletionInstance::getCachedCompilerInstance( if (!CachedCI) return nullptr; - if (calculateInvocationHash(Invocation) != - calculateInvocationHash(CachedCI->getInvocation())) + if (ArgsHash != CachedArgsHash) return nullptr; auto &oldState = CachedCI->getPersistentParserState(); @@ -307,12 +240,13 @@ CompilerInstance *CompletionInstance::getCachedCompilerInstance( } CompilerInstance *CompletionInstance::renewCompilerInstance( - swift::CompilerInvocation &Invocation, + swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, std::string &Error, DiagnosticConsumer *DiagC) { CachedCI.reset(); CachedCI = std::make_unique(); + CachedArgsHash = ArgsHash; auto *CI = CachedCI.get(); if (DiagC) CachedCI->addDiagnosticConsumer(DiagC); @@ -332,7 +266,7 @@ CompilerInstance *CompletionInstance::renewCompilerInstance( } CompilerInstance *swift::ide::CompletionInstance::getCompilerInstance( - swift::CompilerInvocation &Invocation, + swift::CompilerInvocation &Invocation, llvm::ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, std::string &Error, DiagnosticConsumer *DiagC) { @@ -348,12 +282,18 @@ CompilerInstance *swift::ide::CompletionInstance::getCompilerInstance( // FIXME: ASTScopeLookup doesn't support code completion yet. Invocation.disableASTScopeLookup(); - if (auto *cached = getCachedCompilerInstance(Invocation, completionBuffer, - Offset, DiagC)) + // Compute the signature of the invocation. + llvm::hash_code ArgsHash(0); + for (auto arg : Args) + ArgsHash = llvm::hash_combine(ArgsHash, StringRef(arg)); + + if (auto *cached = getCachedCompilerInstance(Invocation, ArgsHash, + completionBuffer, Offset, DiagC)) return cached; - if (auto *renewed = renewCompilerInstance( - Invocation, FileSystem, completionBuffer, Offset, Error, DiagC)) + if (auto *renewed = + renewCompilerInstance(Invocation, ArgsHash, FileSystem, + completionBuffer, Offset, Error, DiagC)) return renewed; assert(!Error.empty()); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index f29392664af82..d301bb0d37f58 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -176,7 +176,7 @@ static bool swiftCodeCompleteImpl( // Pin completion instance. auto CompletionInst = Lang.getCompletionInstance(); CompilerInstance *CI = CompletionInst->getCompilerInstance( - Invocation, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); + Invocation, Args, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); if (!CI) return false; SWIFT_DEFER { CI->removeDiagnosticConsumer(&CIDiags); }; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp index 58b6dcbba39a3..f024ae0fc0d18 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp @@ -80,7 +80,7 @@ static bool swiftConformingMethodListImpl( // Pin completion instance. auto CompletionInst = Lang.getCompletionInstance(); CompilerInstance *CI = CompletionInst->getCompilerInstance( - Invocation, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); + Invocation, Args, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); if (!CI) return false; SWIFT_DEFER { CI->removeDiagnosticConsumer(&CIDiags); }; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp index 8925987d88960..caf1517dc7df5 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp @@ -78,7 +78,7 @@ static bool swiftTypeContextInfoImpl( // Pin completion instance. auto CompletionInst = Lang.getCompletionInstance(); CompilerInstance *CI = CompletionInst->getCompilerInstance( - Invocation, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); + Invocation, Args, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); if (!CI) return false; SWIFT_DEFER { CI->removeDiagnosticConsumer(&CIDiags); }; diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 74101c9505f69..d972832a61ee6 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -758,8 +758,8 @@ static bool doCodeCompletionImpl( PrintingDiagnosticConsumer PrintDiags; CompletionInstance CompletionInst; auto *CI = CompletionInst.getCompilerInstance( - Invocation, llvm::vfs::getRealFileSystem(), CleanFile.get(), Offset, - Error, CodeCompletionDiagnostics ? &PrintDiags : nullptr); + Invocation, /*Args=*/{}, llvm::vfs::getRealFileSystem(), CleanFile.get(), + Offset, Error, CodeCompletionDiagnostics ? &PrintDiags : nullptr); if (!CI) return 1; From fcb50d6354dc770b18735f10edb0589b5331ed88 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 18 Dec 2019 10:53:03 -0800 Subject: [PATCH 120/478] [SourceKit] Add more test cases for fast completions --- .../CodeComplete/complete_sequence.swift | 10 +++ .../complete_sequence_crossfile.swift | 55 ++++++++++++ .../CodeComplete/complete_sequence_edit.swift | 89 +++++++++++++++++++ .../tools/sourcekitd-test/sourcekitd-test.cpp | 3 + 4 files changed, 157 insertions(+) create mode 100644 test/SourceKit/CodeComplete/complete_sequence_crossfile.swift create mode 100644 test/SourceKit/CodeComplete/complete_sequence_edit.swift diff --git a/test/SourceKit/CodeComplete/complete_sequence.swift b/test/SourceKit/CodeComplete/complete_sequence.swift index 226c7a3a1ec25..1b1c8c8e6a1d1 100644 --- a/test/SourceKit/CodeComplete/complete_sequence.swift +++ b/test/SourceKit/CodeComplete/complete_sequence.swift @@ -17,6 +17,7 @@ func bar(arg: Bar) { // NOTE: Tests for 'key.codecomplete.reuseastcontex' option. +// Disabled. // RUN: %sourcekitd-test \ // RUN: -req=track-compiles == \ // RUN: -req=complete -pos=12:11 %s -- %s == \ @@ -24,6 +25,7 @@ func bar(arg: Bar) { // RUN: %FileCheck --check-prefix=RESULT %s < %t.response // RUN: %FileCheck --check-prefix=TRACE_NORMAL %s < %t.response +// Enabled. // RUN: %sourcekitd-test \ // RUN: -req=track-compiles == \ // RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -- %s == \ @@ -31,6 +33,14 @@ func bar(arg: Bar) { // RUN: %FileCheck --check-prefix=RESULT %s < %t.response.reuseastcontext // RUN: %FileCheck --check-prefix=TRACE_REUSEAST %s < %t.response.reuseastcontext +// Enabled - compiler argument mismatch. +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -- %s -DNOTUSED == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -- -DNOTUSED %s > %t.response.reuseastcontext_argmismatch +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response.reuseastcontext_argmismatch +// RUN: %FileCheck --check-prefix=TRACE_NORMAL %s < %t.response.reuseastcontext_argmismatch + // RESULT-LABEL: key.results: [ // RESULT-DAG: key.name: "fooMethod()" // RESULT-DAG: key.name: "self" diff --git a/test/SourceKit/CodeComplete/complete_sequence_crossfile.swift b/test/SourceKit/CodeComplete/complete_sequence_crossfile.swift new file mode 100644 index 0000000000000..bf7ac0fad0117 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_crossfile.swift @@ -0,0 +1,55 @@ +// BEGIN file1.swift +class Foo { + var x: Int + var y: Int +} + +func foo(arg: Foo) { + _ = arg. +} + +class Bar { + var a: Int + var b: Int + func barMethod() {} +} + +// BEGIN file2.swift +extension Foo { + func fooMethod() {} +} + +func bar(arg: Bar) { + _ = arg. +} + +extension Bar { + func barMethod() {} +} + +// BEGIN dummy.swift + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=7:11 %t/file1.swift -- %t/file1.swift %t/file2.swift == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=6:11 %t/file2.swift -- %t/file1.swift %t/file2.swift > %t.response +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response +// RUN: %FileCheck --check-prefix=TRACE %s < %t.response + +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "barMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "a" +// RESULT-DAG: key.name: "b" +// RESULT: ] + +// TRACE-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" diff --git a/test/SourceKit/CodeComplete/complete_sequence_edit.swift b/test/SourceKit/CodeComplete/complete_sequence_edit.swift new file mode 100644 index 0000000000000..37dae86946b5b --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_edit.swift @@ -0,0 +1,89 @@ +// BEGIN State1.swift +// Initial state. +class Foo { + var x: Int + var y: Int + func fooMethod() {} +} +func foo(arg: Foo) { + _ = arg. +} + +// BEGIN State2.swift +// Compatible change: implemented 'Foo.fooMethod()', indentation change, added white line. +class Foo { + var x: Int + var y: Int + func fooMethod() { + print(x + y) + } +} + +func foo(arg: Foo) { + _ = arg. +} + +// BEGIN State3.swift +// Incompatible change: added 'Foo.z' +class Foo { + var x: Int + var y: Int + var z: Int + func fooMethod() { + print(x + y) + } +} + +func foo(arg: Foo) { + _ = arg. +} + +// BEGIN DUMMY.swift + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=8:11 -name file.swift -text-input %t/State1.swift -- file.swift == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=11:13 -name file.swift -text-input %t/State2.swift -- file.swift == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:13 -name file.swift -text-input %t/State3.swift -- file.swift > %t.response +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response +// RUN: %FileCheck --check-prefix=TRACE %s < %t.response + +// RESULT-LABEL: key.results: [ +// RESULT-NOT: key.name: "z" +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] + +// RESULT-LABEL: key.results: [ +// RESULT-NOT: key.name: "z" +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] + +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT-DAG: key.name: "z" +// RESULT: ] + +// TRACE: key.notification: source.notification.compile-did-finish, +// TRACE-NEXT: key.diagnostics: [ +// TRACE-NEXT: ] + +// TRACE: key.notification: source.notification.compile-did-finish, +// TRACE-NEXT: key.diagnostics: [ +// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE: ] + +// TRACE: key.notification: source.notification.compile-did-finish, +// TRACE-NEXT: key.diagnostics: [ +// TRACE-NEXT: ] diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index fb7396409f76d..ab2c0683b914f 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -573,6 +573,7 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { case SourceKitRequest::CodeComplete: sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCodeComplete); sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset); + sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); addCodeCompleteOptions(Req, Opts); break; @@ -971,6 +972,8 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { if (Opts.SourceText) { sourcekitd_request_dictionary_set_string(Req, KeySourceText, Opts.SourceText->c_str()); + sourcekitd_request_dictionary_set_string(Req, KeySourceFile, + SemaName.c_str()); } if (!Opts.CompilerArgs.empty()) { From 044477e7a313f8ada80eccd338e5f0ee899f3b99 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 18 Dec 2019 14:07:36 -0800 Subject: [PATCH 121/478] [SourceKit/CodeCompletion] Use callback function to run the second pass To controls the lifetime of CompilerInstance within CompletionIntance. - Prevent from using the same CompilerInstance from multiple completion requests - Separate the stacktrace between "fast" and "normal" completions - Further code consolidation between various completion-like requests --- include/swift/IDE/CompletionInstance.h | 57 ++++++---- lib/IDE/CompletionInstance.cpp | 105 +++++++++++------- .../CodeComplete/complete_sequence_race.swift | 44 ++++++++ .../lib/SwiftLang/SwiftCompletion.cpp | 93 +++------------- .../SwiftLang/SwiftConformingMethodList.cpp | 78 ++----------- .../lib/SwiftLang/SwiftLangSupport.cpp | 64 +++++++++++ .../lib/SwiftLang/SwiftLangSupport.h | 10 ++ .../lib/SwiftLang/SwiftTypeContextInfo.cpp | 76 ++----------- tools/swift-ide-test/swift-ide-test.cpp | 16 ++- 9 files changed, 260 insertions(+), 283 deletions(-) create mode 100644 test/SourceKit/CodeComplete/complete_sequence_race.swift diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h index 218d1f6fa2656..ab004f012d3a8 100644 --- a/include/swift/IDE/CompletionInstance.h +++ b/include/swift/IDE/CompletionInstance.h @@ -37,47 +37,56 @@ makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, /// Manages \c CompilerInstance for completion like operations. class CompletionInstance { - std::unique_ptr CachedCI = nullptr; - llvm::hash_code CachedArgsHash; - bool EnableASTCaching = false; unsigned MaxASTReuseCount = 100; + bool EnableASTCaching = false; + + std::shared_ptr CachedCI = nullptr; + llvm::hash_code CachedArgsHash; unsigned CurrentASTReuseCount = 0; - /// Returns cached \c CompilerInstance if it's usable for the specified - /// completion request. - /// Returns \c nullptr if the functionality is disabled, compiler argument has + /// Calls \p Callback with cached \c CompilerInstance if it's usable for the + /// specified completion request. + /// Returns \c if the callback was called. Returns \c false if the + /// functionality is disabled, compiler argument has /// changed, primary file is not the same, the \c Offset is not in function /// bodies, or the interface hash of the file has changed. - swift::CompilerInstance * - getCachedCompilerInstance(const swift::CompilerInvocation &Invocation, - llvm::hash_code ArgsHash, - llvm::MemoryBuffer *completionBuffer, - unsigned int Offset, DiagnosticConsumer *DiagC); + bool performCachedOperaitonIfPossible( + const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + DiagnosticConsumer *DiagC, + llvm::function_ref Callback); - /// Returns new \c CompilerInstance for the completion request. Users still - /// have to call \c performParseAndResolveImportsOnly() , and perform the - /// second pass on it. - swift::CompilerInstance *renewCompilerInstance( + /// Calls \p Callback with new \c CompilerInstance for the completion + /// request. The \c CompilerInstace passed to the callback already performed + /// the first pass. + /// Returns \c false if it fails to setup the \c CompilerInstance. + bool performNewOperation( swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, - std::string &Error, DiagnosticConsumer *DiagC); + std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback); public: void setEnableASTCaching(bool Flag) { EnableASTCaching = Flag; } - /// Returns \C CompilerInstance for the completion request. Users can check if - /// it's cached or not by 'hasPersistentParserState()'. + /// Calls \p Callback with a \c CompilerInstance which is prepared for the + /// second pass. \p Callback is resposible to perform the second pass on it. + /// The \c CompilerInstance may be reused from the previous completions, + /// and may be cached for the next completion. + /// Return \c true if \p is successfully called, \c it fails. In failure + /// cases \p Error is populated with an error message. /// /// NOTE: \p Args is only used for checking the equaity of the invocation. /// Since this function assumes that it is already normalized, exact the same /// arguments including their order is considered as the same invocation. - swift::CompilerInstance *getCompilerInstance( - swift::CompilerInvocation &Invocation, - llvm::ArrayRef Args, - llvm::IntrusiveRefCntPtr FileSystem, - llvm::MemoryBuffer *completionBuffer, unsigned int Offset, - std::string &Error, DiagnosticConsumer *DiagC); + bool + performOperation(swift::CompilerInvocation &Invocation, + llvm::ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback); }; } // namespace ide diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index bd01538a3bc4b..3827d7c444f34 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -129,38 +129,40 @@ static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC, } // namespace -CompilerInstance *CompletionInstance::getCachedCompilerInstance( +bool CompletionInstance::performCachedOperaitonIfPossible( const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, - DiagnosticConsumer *DiagC) { - if (!EnableASTCaching) - return nullptr; + DiagnosticConsumer *DiagC, + llvm::function_ref Callback) { - if (CurrentASTReuseCount >= MaxASTReuseCount) { - CurrentASTReuseCount = 0; - return nullptr; - } + if (!EnableASTCaching) + return false; - if (!CachedCI) - return nullptr; + // Temporary move the CI so other threads don't use the same instance. + std::shared_ptr CI; + CI.swap(CachedCI); + if (!CI) + return false; + if (CurrentASTReuseCount >= MaxASTReuseCount) + return false; if (ArgsHash != CachedArgsHash) - return nullptr; + return false; - auto &oldState = CachedCI->getPersistentParserState(); + auto &oldState = CI->getPersistentParserState(); if (!oldState.hasCodeCompletionDelayedDeclState()) - return nullptr; + return false; - auto &SM = CachedCI->getSourceMgr(); + auto &SM = CI->getSourceMgr(); if (SM.getIdentifierForBuffer(SM.getCodeCompletionBufferID()) != completionBuffer->getBufferIdentifier()) - return nullptr; + return false; auto &oldInfo = oldState.getCodeCompletionDelayedDeclState(); // Currently, only completions within a function body is supported. if (oldInfo.Kind != CodeCompletionDelayedDeclKind::FunctionBody) - return nullptr; + return false; auto newBufferID = SM.addMemBufferCopy(completionBuffer); SM.setCodeCompletionPoint(newBufferID, Offset); @@ -185,13 +187,13 @@ CompilerInstance *CompletionInstance::getCachedCompilerInstance( parseIntoSourceFileFull(*newSF, BufferID, &newState); // Couldn't find any completion token? if (!newState.hasCodeCompletionDelayedDeclState()) - return nullptr; + return false; auto &newInfo = newState.getCodeCompletionDelayedDeclState(); // The new completion must happens in function body too. if (newInfo.Kind != CodeCompletionDelayedDeclKind::FunctionBody) - return nullptr; + return false; auto *oldSF = oldInfo.ParentContext->getParentSourceFile(); @@ -201,12 +203,12 @@ CompilerInstance *CompletionInstance::getCachedCompilerInstance( oldSF->getInterfaceHash(oldInterfaceHash); newSF->getInterfaceHash(newInterfaceHash); if (oldInterfaceHash != newInterfaceHash) - return nullptr; + return false; DeclContext *DC = getEquivalentDeclContextFromSourceFile(newInfo.ParentContext, oldSF); if (!DC) - return nullptr; + return false; // OK, we can perform fast completion for this. Update the orignal delayed // decl state. @@ -228,28 +230,33 @@ CompilerInstance *CompletionInstance::getCachedCompilerInstance( if (AFD->isBodySkipped()) AFD->setBodyDelayed(AFD->getBodySourceRange()); if (DiagC) - CachedCI->addDiagnosticConsumer(DiagC); + CI->addDiagnosticConsumer(DiagC); - CachedCI->getDiags().diagnose( - SM.getLocForOffset(BufferID, newInfo.StartOffset), - diag::completion_reusing_astcontext); + CI->getDiags().diagnose(SM.getLocForOffset(BufferID, newInfo.StartOffset), + diag::completion_reusing_astcontext); + Callback(*CI); + + if (DiagC) + CI->removeDiagnosticConsumer(DiagC); + + CachedCI.swap(CI); + CachedArgsHash = ArgsHash; CurrentASTReuseCount += 1; - return CachedCI.get(); + return true; } -CompilerInstance *CompletionInstance::renewCompilerInstance( +bool CompletionInstance::performNewOperation( swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, - std::string &Error, DiagnosticConsumer *DiagC) { + std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback) { CachedCI.reset(); - CachedCI = std::make_unique(); - CachedArgsHash = ArgsHash; - auto *CI = CachedCI.get(); + auto CI = std::make_shared(); if (DiagC) - CachedCI->addDiagnosticConsumer(DiagC); + CI->addDiagnosticConsumer(DiagC); if (FileSystem != llvm::vfs::getRealFileSystem()) CI->getSourceMgr().setFileSystem(FileSystem); @@ -258,18 +265,31 @@ CompilerInstance *CompletionInstance::renewCompilerInstance( if (CI->setup(Invocation)) { Error = "failed to setup compiler instance"; - return nullptr; + return false; } registerIDERequestFunctions(CI->getASTContext().evaluator); - return CI; + CI->performParseAndResolveImportsOnly(); + Callback(*CI); + + if (DiagC) + CI->removeDiagnosticConsumer(DiagC); + + if (EnableASTCaching) { + CachedCI.swap(CI); + CachedArgsHash = ArgsHash; + CurrentASTReuseCount = 0; + } + + return true; } -CompilerInstance *swift::ide::CompletionInstance::getCompilerInstance( +bool swift::ide::CompletionInstance::performOperation( swift::CompilerInvocation &Invocation, llvm::ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, - std::string &Error, DiagnosticConsumer *DiagC) { + std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback) { // Always disable source location resolutions from .swiftsourceinfo file // because they're somewhat heavy operations and aren't needed for completion. @@ -287,15 +307,14 @@ CompilerInstance *swift::ide::CompletionInstance::getCompilerInstance( for (auto arg : Args) ArgsHash = llvm::hash_combine(ArgsHash, StringRef(arg)); - if (auto *cached = getCachedCompilerInstance(Invocation, ArgsHash, - completionBuffer, Offset, DiagC)) - return cached; + if (performCachedOperaitonIfPossible(Invocation, ArgsHash, completionBuffer, + Offset, DiagC, Callback)) + return true; - if (auto *renewed = - renewCompilerInstance(Invocation, ArgsHash, FileSystem, - completionBuffer, Offset, Error, DiagC)) - return renewed; + if (performNewOperation(Invocation, ArgsHash, FileSystem, completionBuffer, + Offset, Error, DiagC, Callback)) + return true; assert(!Error.empty()); - return nullptr; + return false; } diff --git a/test/SourceKit/CodeComplete/complete_sequence_race.swift b/test/SourceKit/CodeComplete/complete_sequence_race.swift new file mode 100644 index 0000000000000..49eb816b66e5e --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_race.swift @@ -0,0 +1,44 @@ +class Foo { + var x: Int + var y: Int + func fooMethod() {} +} +struct Bar { + var a: Int + var b: Int + func barMethod() {} +} +func foo(arg: Foo) { + _ = arg. +} +func bar(arg: Bar) { + _ = arg. +} + +// NOTE: Test for simultaneous completion requests don't cause compiler crashes. + +// ReuseASTContext disabled. +// RUN: %sourcekitd-test \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s + +// ReuseASTContext enabled. +// RUN: %sourcekitd-test \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index d301bb0d37f58..2d18f0f8318fc 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -124,82 +124,23 @@ static bool swiftCodeCompleteImpl( ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, std::string &Error) { - assert(FileSystem); - - // Resolve symlinks for the input file; we resolve them for the input files - // in the arguments as well. - // FIXME: We need the Swift equivalent of Clang's FileEntry. - llvm::SmallString<128> bufferIdentifier; - if (auto err = FileSystem->getRealPath( - UnresolvedInputFile->getBufferIdentifier(), bufferIdentifier)) - bufferIdentifier = UnresolvedInputFile->getBufferIdentifier(); - - // Create a buffer for code completion. This contains '\0' at 'Offset' - // position of 'UnresolvedInputFile' buffer. - auto origOffset = Offset; - auto newBuffer = ide::makeCodeCompletionMemoryBuffer( - UnresolvedInputFile, Offset, bufferIdentifier); - - SourceManager SM; - DiagnosticEngine Diags(SM); - PrintingDiagnosticConsumer PrintDiags; - EditorDiagConsumer TraceDiags; - trace::TracedOperation TracedOp{trace::OperationKind::CodeCompletion}; - - Diags.addConsumer(PrintDiags); - if (TracedOp.enabled()) { - Diags.addConsumer(TraceDiags); - trace::SwiftInvocation SwiftArgs; - trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); - TracedOp.setDiagnosticProvider( - [&](SmallVectorImpl &diags) { - TraceDiags.getAllDiagnostics(diags); - }); - TracedOp.start( - SwiftArgs, - {std::make_pair("OriginalOffset", std::to_string(origOffset)), - std::make_pair("Offset", std::to_string(Offset))}); - } - ForwardingDiagnosticConsumer CIDiags(Diags); - - CompilerInvocation Invocation; - bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, Diags, newBuffer->getBufferIdentifier(), FileSystem, - Error); - if (Failed) - return false; - if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { - Error = "no input filenames specified"; - return false; - } - - // Pin completion instance. - auto CompletionInst = Lang.getCompletionInstance(); - CompilerInstance *CI = CompletionInst->getCompilerInstance( - Invocation, Args, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); - if (!CI) - return false; - SWIFT_DEFER { CI->removeDiagnosticConsumer(&CIDiags); }; - - // Perform the parsing and completion. - if (!CI->hasPersistentParserState()) - CI->performParseAndResolveImportsOnly(); - - // Create a factory for code completion callbacks that will feed the - // Consumer. - auto swiftCache = Lang.getCodeCompletionCache(); // Pin the cache. - ide::CodeCompletionContext CompletionContext(swiftCache->getCache()); - std::unique_ptr callbacksFactory( - ide::makeCodeCompletionCallbacksFactory(CompletionContext, - SwiftConsumer)); - - SwiftConsumer.setContext(&CI->getASTContext(), &CI->getInvocation(), - &CompletionContext); - performCodeCompletionSecondPass(CI->getPersistentParserState(), - *callbacksFactory); - SwiftConsumer.clearContext(); - - return true; + return Lang.performCompletionLikeOperation( + UnresolvedInputFile, Offset, Args, FileSystem, Error, + [&](CompilerInstance &CI) { + // Create a factory for code completion callbacks that will feed the + // Consumer. + auto swiftCache = Lang.getCodeCompletionCache(); // Pin the cache. + ide::CodeCompletionContext CompletionContext(swiftCache->getCache()); + std::unique_ptr callbacksFactory( + ide::makeCodeCompletionCallbacksFactory(CompletionContext, + SwiftConsumer)); + + SwiftConsumer.setContext(&CI.getASTContext(), &CI.getInvocation(), + &CompletionContext); + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + SwiftConsumer.clearContext(); + }); } static void translateCodeCompletionOptions(OptionsDictionary &from, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp index f024ae0fc0d18..f567c8b9a183f 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp @@ -33,72 +33,18 @@ static bool swiftConformingMethodListImpl( ide::ConformingMethodListConsumer &Consumer, llvm::IntrusiveRefCntPtr FileSystem, std::string &Error) { - - // Resolve symlinks for the input file. - llvm::SmallString<128> bufferIdentifier; - if (auto err = FileSystem->getRealPath( - UnresolvedInputFile->getBufferIdentifier(), bufferIdentifier)) - bufferIdentifier = UnresolvedInputFile->getBufferIdentifier(); - - auto origOffset = Offset; - auto newBuffer = ide::makeCodeCompletionMemoryBuffer( - UnresolvedInputFile, Offset, bufferIdentifier); - - SourceManager SM; - DiagnosticEngine Diags(SM); - PrintingDiagnosticConsumer PrintDiags; - EditorDiagConsumer TraceDiags; - trace::TracedOperation TracedOp{trace::OperationKind::CodeCompletion}; - - Diags.addConsumer(PrintDiags); - if (TracedOp.enabled()) { - Diags.addConsumer(TraceDiags); - trace::SwiftInvocation SwiftArgs; - trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); - TracedOp.setDiagnosticProvider( - [&](SmallVectorImpl &diags) { - TraceDiags.getAllDiagnostics(diags); - }); - TracedOp.start( - SwiftArgs, - {std::make_pair("OriginalOffset", std::to_string(origOffset)), - std::make_pair("Offset", std::to_string(Offset))}); - } - ForwardingDiagnosticConsumer CIDiags(Diags); - - CompilerInvocation Invocation; - bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, Diags, newBuffer->getBufferIdentifier(), FileSystem, - Error); - if (Failed) - return false; - if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { - Error = "no input filenames specified"; - return false; - } - - // Pin completion instance. - auto CompletionInst = Lang.getCompletionInstance(); - CompilerInstance *CI = CompletionInst->getCompilerInstance( - Invocation, Args, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); - if (!CI) - return false; - SWIFT_DEFER { CI->removeDiagnosticConsumer(&CIDiags); }; - - // Perform the parsing and completion. - if (!CI->hasPersistentParserState()) - CI->performParseAndResolveImportsOnly(); - - // Create a factory for code completion callbacks that will feed the - // Consumer. - std::unique_ptr callbacksFactory( - ide::makeConformingMethodListCallbacksFactory(ExpectedTypeNames, - Consumer)); - - performCodeCompletionSecondPass(CI->getPersistentParserState(), - *callbacksFactory); - - return true; + return Lang.performCompletionLikeOperation( + UnresolvedInputFile, Offset, Args, FileSystem, Error, + [&](CompilerInstance &CI) { + // Create a factory for code completion callbacks that will feed the + // Consumer. + std::unique_ptr callbacksFactory( + ide::makeConformingMethodListCallbacksFactory(ExpectedTypeNames, + Consumer)); + + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + }); } void SwiftLangSupport::getConformingMethodList( diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index db3aaeeca4d97..e2b1901596c0f 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -12,6 +12,7 @@ #include "SwiftLangSupport.h" #include "SwiftASTManager.h" +#include "SwiftEditorDiagConsumer.h" #include "SourceKit/Core/Context.h" #include "SourceKit/SwiftLang/Factory.h" #include "SourceKit/Support/FileSystemProvider.h" @@ -23,6 +24,7 @@ #include "swift/AST/SILOptions.h" #include "swift/AST/USRGeneration.h" #include "swift/Config.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/CodeCompletion.h" #include "swift/IDE/CodeCompletionCache.h" #include "swift/IDE/CompletionInstance.h" @@ -951,6 +953,68 @@ SwiftLangSupport::getFileSystem(const Optional &vfsOptions, return llvm::vfs::getRealFileSystem(); } +bool SwiftLangSupport::performCompletionLikeOperation( + llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, + ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + std::string &Error, llvm::function_ref Callback) { + assert(FileSystem); + + // Resolve symlinks for the input file; we resolve them for the input files + // in the arguments as well. + // FIXME: We need the Swift equivalent of Clang's FileEntry. + llvm::SmallString<128> bufferIdentifier; + if (auto err = FileSystem->getRealPath( + UnresolvedInputFile->getBufferIdentifier(), bufferIdentifier)) + bufferIdentifier = UnresolvedInputFile->getBufferIdentifier(); + + // Create a buffer for code completion. This contains '\0' at 'Offset' + // position of 'UnresolvedInputFile' buffer. + auto origOffset = Offset; + auto newBuffer = ide::makeCodeCompletionMemoryBuffer( + UnresolvedInputFile, Offset, bufferIdentifier); + + SourceManager SM; + DiagnosticEngine Diags(SM); + PrintingDiagnosticConsumer PrintDiags; + EditorDiagConsumer TraceDiags; + trace::TracedOperation TracedOp{trace::OperationKind::CodeCompletion}; + + Diags.addConsumer(PrintDiags); + if (TracedOp.enabled()) { + Diags.addConsumer(TraceDiags); + trace::SwiftInvocation SwiftArgs; + trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); + TracedOp.setDiagnosticProvider( + [&](SmallVectorImpl &diags) { + TraceDiags.getAllDiagnostics(diags); + }); + TracedOp.start( + SwiftArgs, + {std::make_pair("OriginalOffset", std::to_string(origOffset)), + std::make_pair("Offset", std::to_string(Offset))}); + } + ForwardingDiagnosticConsumer CIDiags(Diags); + + CompilerInvocation Invocation; + bool Failed = getASTManager()->initCompilerInvocation( + Invocation, Args, Diags, newBuffer->getBufferIdentifier(), FileSystem, + Error); + if (Failed) + return false; + if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { + Error = "no input filenames specified"; + return false; + } + + // Pin completion instance. + auto CompletionInst = getCompletionInstance(); + + return CompletionInst->performOperation(Invocation, Args, FileSystem, + newBuffer.get(), Offset, Error, + &CIDiags, Callback); +} + CloseClangModuleFiles::~CloseClangModuleFiles() { clang::Preprocessor &PP = loader.getClangPreprocessor(); clang::ModuleMap &ModMap = PP.getHeaderSearchInfo().getModuleMap(); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 3472d2cb45efa..a0ad2358d458b 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -436,6 +436,16 @@ class SwiftLangSupport : public LangSupport { /// returns the original path; static std::string resolvePathSymlinks(StringRef FilePath); + /// Perform a completion like operation. It initializes a \c CompilerInstance, + /// the calls \p Callback with it. \p Callback must perform the second pass + /// using that instance. + bool performCompletionLikeOperation( + llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, + ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + std::string &Error, + llvm::function_ref Callback); + //==========================================================================// // LangSupport Interface //==========================================================================// diff --git a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp index caf1517dc7df5..0134b818b8ff7 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp @@ -31,71 +31,17 @@ static bool swiftTypeContextInfoImpl( ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, std::string &Error) { - - // Resolve symlinks for the input file. - llvm::SmallString<128> bufferIdentifier; - if (auto err = FileSystem->getRealPath( - UnresolvedInputFile->getBufferIdentifier(), bufferIdentifier)) - bufferIdentifier = UnresolvedInputFile->getBufferIdentifier(); - - auto origOffset = Offset; - auto newBuffer = ide::makeCodeCompletionMemoryBuffer( - UnresolvedInputFile, Offset, bufferIdentifier); - - SourceManager SM; - DiagnosticEngine Diags(SM); - PrintingDiagnosticConsumer PrintDiags; - EditorDiagConsumer TraceDiags; - trace::TracedOperation TracedOp{trace::OperationKind::CodeCompletion}; - - Diags.addConsumer(PrintDiags); - if (TracedOp.enabled()) { - Diags.addConsumer(TraceDiags); - trace::SwiftInvocation SwiftArgs; - trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); - TracedOp.setDiagnosticProvider( - [&](SmallVectorImpl &diags) { - TraceDiags.getAllDiagnostics(diags); - }); - TracedOp.start( - SwiftArgs, - {std::make_pair("OriginalOffset", std::to_string(origOffset)), - std::make_pair("Offset", std::to_string(Offset))}); - } - ForwardingDiagnosticConsumer CIDiags(Diags); - - CompilerInvocation Invocation; - bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, Diags, newBuffer->getBufferIdentifier(), FileSystem, - Error); - if (Failed) - return false; - if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { - Error = "no input filenames specified"; - return false; - } - - // Pin completion instance. - auto CompletionInst = Lang.getCompletionInstance(); - CompilerInstance *CI = CompletionInst->getCompilerInstance( - Invocation, Args, FileSystem, newBuffer.get(), Offset, Error, &CIDiags); - if (!CI) - return false; - SWIFT_DEFER { CI->removeDiagnosticConsumer(&CIDiags); }; - - // Perform the parsing and completion. - if (!CI->hasPersistentParserState()) - CI->performParseAndResolveImportsOnly(); - - // Create a factory for code completion callbacks that will feed the - // Consumer. - std::unique_ptr callbacksFactory( - ide::makeTypeContextInfoCallbacksFactory(Consumer)); - - performCodeCompletionSecondPass(CI->getPersistentParserState(), - *callbacksFactory); - - return true; + return Lang.performCompletionLikeOperation( + UnresolvedInputFile, Offset, Args, FileSystem, Error, + [&](CompilerInstance &CI) { + // Create a factory for code completion callbacks that will feed the + // Consumer. + std::unique_ptr callbacksFactory( + ide::makeTypeContextInfoCallbacksFactory(Consumer)); + + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + }); } void SwiftLangSupport::getExpressionContextInfo( diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index d972832a61ee6..85b17c114294b 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -757,16 +757,14 @@ static bool doCodeCompletionImpl( std::string Error; PrintingDiagnosticConsumer PrintDiags; CompletionInstance CompletionInst; - auto *CI = CompletionInst.getCompilerInstance( + auto isSuccess = CompletionInst.performOperation( Invocation, /*Args=*/{}, llvm::vfs::getRealFileSystem(), CleanFile.get(), - Offset, Error, CodeCompletionDiagnostics ? &PrintDiags : nullptr); - if (!CI) - return 1; - - CI->performParseAndResolveImportsOnly(); - performCodeCompletionSecondPass(CI->getPersistentParserState(), - *callbacksFactory); - return 0; + Offset, Error, CodeCompletionDiagnostics ? &PrintDiags : nullptr, + [&](CompilerInstance &CI) { + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + }); + return isSuccess ? 0 : 1; } static int doTypeContextInfo(const CompilerInvocation &InitInvok, From fe07d4491bc0b0a64b7ddb7f2c8d5df23f2078ad Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 18 Dec 2019 14:44:49 -0800 Subject: [PATCH 122/478] [CodeCompletion] Pack cached instance information into single struct To prevent them from out-of-sync. --- include/swift/IDE/CompletionInstance.h | 9 +++-- lib/IDE/CompletionInstance.cpp | 53 +++++++++++++------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h index ab004f012d3a8..9188b8de13cec 100644 --- a/include/swift/IDE/CompletionInstance.h +++ b/include/swift/IDE/CompletionInstance.h @@ -40,9 +40,12 @@ class CompletionInstance { unsigned MaxASTReuseCount = 100; bool EnableASTCaching = false; - std::shared_ptr CachedCI = nullptr; - llvm::hash_code CachedArgsHash; - unsigned CurrentASTReuseCount = 0; + struct CachedInstance { + CompilerInstance CI; + llvm::hash_code ArgHash; + unsigned ReuseCound = 0; + }; + std::shared_ptr CachedInst; /// Calls \p Callback with cached \c CompilerInstance if it's usable for the /// specified completion request. diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 3827d7c444f34..6df97b4dda4b4 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -139,21 +139,23 @@ bool CompletionInstance::performCachedOperaitonIfPossible( return false; // Temporary move the CI so other threads don't use the same instance. - std::shared_ptr CI; - CI.swap(CachedCI); + std::shared_ptr CachedI(nullptr); + CachedInst.swap(CachedI); - if (!CI) + if (!CachedI) return false; - if (CurrentASTReuseCount >= MaxASTReuseCount) + if (CachedI->ReuseCound >= MaxASTReuseCount) return false; - if (ArgsHash != CachedArgsHash) + if (CachedI->ArgHash != ArgsHash) return false; - auto &oldState = CI->getPersistentParserState(); + auto &CI = CachedI->CI; + + auto &oldState = CachedI->CI.getPersistentParserState(); if (!oldState.hasCodeCompletionDelayedDeclState()) return false; - auto &SM = CI->getSourceMgr(); + auto &SM = CI.getSourceMgr(); if (SM.getIdentifierForBuffer(SM.getCodeCompletionBufferID()) != completionBuffer->getBufferIdentifier()) return false; @@ -230,19 +232,18 @@ bool CompletionInstance::performCachedOperaitonIfPossible( if (AFD->isBodySkipped()) AFD->setBodyDelayed(AFD->getBodySourceRange()); if (DiagC) - CI->addDiagnosticConsumer(DiagC); + CI.addDiagnosticConsumer(DiagC); - CI->getDiags().diagnose(SM.getLocForOffset(BufferID, newInfo.StartOffset), + CI.getDiags().diagnose(SM.getLocForOffset(BufferID, newInfo.StartOffset), diag::completion_reusing_astcontext); - Callback(*CI); + Callback(CI); if (DiagC) - CI->removeDiagnosticConsumer(DiagC); + CI.removeDiagnosticConsumer(DiagC); - CachedCI.swap(CI); - CachedArgsHash = ArgsHash; - CurrentASTReuseCount += 1; + CachedI->ReuseCound += 1; + CachedInst.swap(CachedI); return true; } @@ -253,32 +254,32 @@ bool CompletionInstance::performNewOperation( llvm::MemoryBuffer *completionBuffer, unsigned int Offset, std::string &Error, DiagnosticConsumer *DiagC, llvm::function_ref Callback) { - CachedCI.reset(); - auto CI = std::make_shared(); + CachedInst.reset(); + auto TheInstance = std::make_shared(); + auto &CI = TheInstance->CI; if (DiagC) - CI->addDiagnosticConsumer(DiagC); + CI.addDiagnosticConsumer(DiagC); if (FileSystem != llvm::vfs::getRealFileSystem()) - CI->getSourceMgr().setFileSystem(FileSystem); + CI.getSourceMgr().setFileSystem(FileSystem); Invocation.setCodeCompletionPoint(completionBuffer, Offset); - if (CI->setup(Invocation)) { + if (CI.setup(Invocation)) { Error = "failed to setup compiler instance"; return false; } - registerIDERequestFunctions(CI->getASTContext().evaluator); + registerIDERequestFunctions(CI.getASTContext().evaluator); - CI->performParseAndResolveImportsOnly(); - Callback(*CI); + CI.performParseAndResolveImportsOnly(); + Callback(CI); if (DiagC) - CI->removeDiagnosticConsumer(DiagC); + CI.removeDiagnosticConsumer(DiagC); if (EnableASTCaching) { - CachedCI.swap(CI); - CachedArgsHash = ArgsHash; - CurrentASTReuseCount = 0; + TheInstance->ArgHash = ArgsHash; + CachedInst.swap(TheInstance); } return true; From 2aec5d4d28593c581c916cac6053ba6bfc1fedd4 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 18 Dec 2019 21:27:05 -0800 Subject: [PATCH 123/478] [CodeCompletionn] Block completions in other threads if fast-completion is enabled. So they have higher chance to use the cached completion instance. If it's disabled, don't block, use an ephemeral instance so we can peform multiple completions simultaneously. --- include/swift/IDE/CompletionInstance.h | 11 +++--- lib/IDE/CompletionInstance.cpp | 35 ++++++++++--------- .../CodeComplete/complete_sequence_race.swift | 4 +++ 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h index 9188b8de13cec..f60bcd399418a 100644 --- a/include/swift/IDE/CompletionInstance.h +++ b/include/swift/IDE/CompletionInstance.h @@ -40,12 +40,11 @@ class CompletionInstance { unsigned MaxASTReuseCount = 100; bool EnableASTCaching = false; - struct CachedInstance { - CompilerInstance CI; - llvm::hash_code ArgHash; - unsigned ReuseCound = 0; - }; - std::shared_ptr CachedInst; + std::mutex mtx; + + std::unique_ptr CachedCI; + llvm::hash_code CachedArgHash; + unsigned CachedReuseCound = 0; /// Calls \p Callback with cached \c CompilerInstance if it's usable for the /// specified completion request. diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 6df97b4dda4b4..bcb915a9fd7dd 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -138,20 +138,17 @@ bool CompletionInstance::performCachedOperaitonIfPossible( if (!EnableASTCaching) return false; - // Temporary move the CI so other threads don't use the same instance. - std::shared_ptr CachedI(nullptr); - CachedInst.swap(CachedI); - - if (!CachedI) + if (!CachedCI) return false; - if (CachedI->ReuseCound >= MaxASTReuseCount) + + if (CachedReuseCound >= MaxASTReuseCount) return false; - if (CachedI->ArgHash != ArgsHash) + if (CachedArgHash != ArgsHash) return false; - auto &CI = CachedI->CI; + auto &CI = *CachedCI; - auto &oldState = CachedI->CI.getPersistentParserState(); + auto &oldState = CI.getPersistentParserState(); if (!oldState.hasCodeCompletionDelayedDeclState()) return false; @@ -242,8 +239,7 @@ bool CompletionInstance::performCachedOperaitonIfPossible( if (DiagC) CI.removeDiagnosticConsumer(DiagC); - CachedI->ReuseCound += 1; - CachedInst.swap(CachedI); + CachedReuseCound += 1; return true; } @@ -254,9 +250,9 @@ bool CompletionInstance::performNewOperation( llvm::MemoryBuffer *completionBuffer, unsigned int Offset, std::string &Error, DiagnosticConsumer *DiagC, llvm::function_ref Callback) { - CachedInst.reset(); - auto TheInstance = std::make_shared(); - auto &CI = TheInstance->CI; + + auto TheInstance = std::make_unique(); + auto &CI = *TheInstance; if (DiagC) CI.addDiagnosticConsumer(DiagC); @@ -278,8 +274,9 @@ bool CompletionInstance::performNewOperation( CI.removeDiagnosticConsumer(DiagC); if (EnableASTCaching) { - TheInstance->ArgHash = ArgsHash; - CachedInst.swap(TheInstance); + CachedCI = std::move(TheInstance); + CachedArgHash = ArgsHash; + CachedReuseCound = 0; } return true; @@ -308,6 +305,12 @@ bool swift::ide::CompletionInstance::performOperation( for (auto arg : Args) ArgsHash = llvm::hash_combine(ArgsHash, StringRef(arg)); + // If AST caching is enabled, block completions in other threads. So they + // have higher chance to use the cached completion instance. + Optional> lock; + if (EnableASTCaching) + lock.emplace(mtx); + if (performCachedOperaitonIfPossible(Invocation, ArgsHash, completionBuffer, Offset, DiagC, Callback)) return true; diff --git a/test/SourceKit/CodeComplete/complete_sequence_race.swift b/test/SourceKit/CodeComplete/complete_sequence_race.swift index 49eb816b66e5e..47cb1b147cc49 100644 --- a/test/SourceKit/CodeComplete/complete_sequence_race.swift +++ b/test/SourceKit/CodeComplete/complete_sequence_race.swift @@ -23,10 +23,12 @@ func bar(arg: Bar) { // RUN: -req=complete -pos=15:11 %s -async -- %s == \ // RUN: -req=complete -pos=12:11 %s -async -- %s == \ // RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=17:1 %s -async -- %s == \ // RUN: -req=complete -pos=12:11 %s -async -- %s == \ // RUN: -req=complete -pos=15:11 %s -async -- %s == \ // RUN: -req=complete -pos=12:11 %s -async -- %s == \ // RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=17:1 %s -async -- %s == \ // RUN: -req=complete -pos=12:11 %s -async -- %s == \ // RUN: -req=complete -pos=15:11 %s -async -- %s @@ -36,9 +38,11 @@ func bar(arg: Bar) { // RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ // RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ // RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=17:1 %s -async -- %s == \ // RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ // RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ // RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ // RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=17:1 %s -async -- %s == \ // RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ // RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s From 370bd1d25d44e13ba4c3da2517417389b53c2618 Mon Sep 17 00:00:00 2001 From: Ashley Garland Date: Wed, 18 Dec 2019 22:05:47 -0800 Subject: [PATCH 124/478] Update mailmap for bitjammer --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index a5478fcbb3ffd..45ac9d3125396 100644 --- a/.mailmap +++ b/.mailmap @@ -8,6 +8,7 @@ Amr Aboelela Ankit Aggarwal Argyrios Kyrtzidis Arsen Gasparyan +Ashley Garland Ben Cohen Ben Cohen Ben Cohen From 4e71ce53a9fd5304d4e61aa4fa2b18311a776be8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 18 Dec 2019 22:21:50 -0800 Subject: [PATCH 125/478] [IRGen] Canonicalize associated type witnesses. Associated type witnesses were not getting canonicalized with respect to their appropriate generic signatures, causing types to be emitted into the metadata that could not be properly demangled. Be consistent about providing a generic signature for canonicalization. Fixes SR-11642 / rdar://problem/56466693. --- lib/IRGen/GenMeta.cpp | 4 +-- lib/IRGen/GenProto.cpp | 13 +++++++--- lib/IRGen/IRGenModule.h | 3 ++- test/IRGen/associated_type_witness.swift | 31 ++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 1844cf3fc0134..33f532df12c26 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -824,6 +824,7 @@ namespace { auto witness = entry.getAssociatedTypeWitness().Witness->mapTypeOutOfContext(); return IGM.getAssociatedTypeWitness(witness, + Proto->getGenericSignature(), /*inProtocolContext=*/true); } @@ -1593,8 +1594,7 @@ namespace { // TargetRelativeDirectPointer SuperclassType; if (auto superclassType = getType()->getSuperclass()) { GenericSignature genericSig = getType()->getGenericSignature(); - B.addRelativeAddress(IGM.getTypeRef(superclassType->getCanonicalType(), - genericSig, + B.addRelativeAddress(IGM.getTypeRef(superclassType, genericSig, MangledTypeRefRole::Metadata) .first); } else { diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index e61ad1c1674e4..da56fd24393c4 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -1294,7 +1294,10 @@ class AccessorConformanceInfo : public ConformanceInfo { auto associate = Conformance.getTypeWitness(requirement.getAssociation()); llvm::Constant *witness = - IGM.getAssociatedTypeWitness(associate, /*inProtocolContext=*/false); + IGM.getAssociatedTypeWitness( + associate, + Conformance.getDeclContext()->getGenericSignatureOfContext(), + /*inProtocolContext=*/false); Table.addBitCast(witness, IGM.Int8PtrTy); } @@ -1427,6 +1430,7 @@ void WitnessTableBuilder::build() { } llvm::Constant *IRGenModule::getAssociatedTypeWitness(Type type, + GenericSignature sig, bool inProtocolContext) { // FIXME: If we can directly reference constant type metadata, do so. @@ -1435,7 +1439,7 @@ llvm::Constant *IRGenModule::getAssociatedTypeWitness(Type type, auto role = inProtocolContext ? MangledTypeRefRole::DefaultAssociatedTypeWitness : MangledTypeRefRole::Metadata; - auto typeRef = getTypeRef(type, /*generic signature*/nullptr, role).first; + auto typeRef = getTypeRef(type, sig, role).first; // Set the low bit to indicate that this is a mangled name. auto witness = llvm::ConstantExpr::getPtrToInt(typeRef, IntPtrTy); @@ -1604,7 +1608,10 @@ void WitnessTableBuilder::collectResilientWitnesses( auto associate = conformance.getTypeWitness(assocType); llvm::Constant *witness = - IGM.getAssociatedTypeWitness(associate, /*inProtocolContext=*/false); + IGM.getAssociatedTypeWitness( + associate, + conformance.getDeclContext()->getGenericSignatureOfContext(), + /*inProtocolContext=*/false); resilientWitnesses.push_back(witness); continue; } diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 1df95e74f923e..06232ed26457f 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1122,7 +1122,8 @@ class IRGenModule { CanGenericSignature genericSig); /// Produce an associated type witness that refers to the given type. - llvm::Constant *getAssociatedTypeWitness(Type type, bool inProtocolContext); + llvm::Constant *getAssociatedTypeWitness(Type type, GenericSignature sig, + bool inProtocolContext); void emitAssociatedTypeMetadataRecord(const RootProtocolConformance *C); void emitFieldDescriptor(const NominalTypeDecl *Decl); diff --git a/test/IRGen/associated_type_witness.swift b/test/IRGen/associated_type_witness.swift index 690c8eac25474..9342c8ba5addb 100644 --- a/test/IRGen/associated_type_witness.swift +++ b/test/IRGen/associated_type_witness.swift @@ -133,6 +133,37 @@ struct UsesVoid : HasSimpleAssoc { typealias Assoc = () } +// SR-11642: Failure to canonicalize type in associated type witness. +struct Validator { + let validatorFailureType: Any.Type +} + + +protocol ValidatorType { + associatedtype Data + associatedtype Failure + func validator() -> Validator +} + + +extension ValidatorType { + func validator() -> Validator { + .init(validatorFailureType: Failure.self) + } +} + + +// MARK: Failing example +extension Validator where T == String { + // GLOBAL: @"symbolic _____ySS__G 23associated_type_witness9ValidatorVAASSRszlE1VV7FailureV" + struct V: ValidatorType { + typealias Data = T // or String + + struct Failure {} + } +} + + // Protocol conformance descriptor for Computed : Assocked. // GLOBAL-LABEL: @"$s23associated_type_witness8ComputedVyxq_GAA8AssockedAAMc" = hidden constant // GLOBAL-SAME: i16 4, From 0291b367392a9cc12cdd58c0c2012db82cf58564 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Wed, 18 Dec 2019 20:04:27 -0800 Subject: [PATCH 126/478] off-by-default & fix tests --- lib/Driver/Driver.cpp | 2 +- test/Driver/advanced_output_file_map.swift | 12 +-- .../Driver/batch_mode_size_limit.swift | 86 ++++++++++++++----- ..._size_limit_only_one_dependency-file.swift | 36 -------- 4 files changed, 70 insertions(+), 66 deletions(-) delete mode 100644 validation-test/Driver/batch_mode_size_limit_only_one_dependency-file.swift diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 4eee305e71807..69d5b0b1bfced 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -954,7 +954,7 @@ Driver::buildCompilation(const ToolChain &TC, const bool OnlyOneDependencyFile = ArgList->hasFlag(options::OPT_enable_only_one_dependency_file, - options::OPT_disable_only_one_dependency_file, true); + options::OPT_disable_only_one_dependency_file, false); // relies on the new dependency graph const bool EnableFineGrainedDependencies = diff --git a/test/Driver/advanced_output_file_map.swift b/test/Driver/advanced_output_file_map.swift index bf96b09364d0e..7af1af35918ba 100644 --- a/test/Driver/advanced_output_file_map.swift +++ b/test/Driver/advanced_output_file_map.swift @@ -82,14 +82,14 @@ // BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "ld{{(.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o", "./OutputFileMap.swiftmodule"], output: {image: "./advanced_output_file_map.out"} // BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "dsymutil{{(\.exe)?}}", inputs: ["./advanced_output_file_map.out"], output: {dSYM: "./advanced_output_file_map.out.dSYM"} -// Defaulting to: -enable-only-one-dependency-file +// Defaulting to: -disable-only-one-dependency-file -// RUN: %swiftc_driver -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-ENA +// RUN: %swiftc_driver -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-DIS -// Should be two dummy files: // RUN: %empty-directory(%t/d) -// RUN: %swiftc_driver -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | tee /tmp/out | %FileCheck %/s -check-prefix=BINDINGS-ENA +// RUN: %swiftc_driver -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS-DIS +// Should be no dummy files: // RUN: test ! -e %t/d/advanced_output_file_map.d -// RUN: test -e %t/d/main.d -a ! -s %t/d/main.d -// RUN: test -e %t/d/lib.d -a ! -s %t/d/lib.d +// RUN: test ! -e %t/d/main.d +// RUN: test ! -e %t/d/lib.d diff --git a/validation-test/Driver/batch_mode_size_limit.swift b/validation-test/Driver/batch_mode_size_limit.swift index 7fdb1d0f710f3..4ca7918958460 100644 --- a/validation-test/Driver/batch_mode_size_limit.swift +++ b/validation-test/Driver/batch_mode_size_limit.swift @@ -1,4 +1,4 @@ -// Without the only-one-dependency-file mode, all compile jobs are batchable, since the have the same output types. +// When -enable-only-one-dependency-file (which is the default) the one compile job with the dependency output is unbatchable. // RUN: %empty-directory(%t) // RUN: touch %t/f_1_1.swift %t/f_1_2.swift %t/f_1_3.swift %t/f_1_4.swift %t/f_1_5.swift %t/f_1_6.swift %t/f_1_7.swift %t/f_1_8.swift %t/f_1_9.swift %t/f_1_10.swift @@ -11,26 +11,66 @@ // RUN: touch %t/f_8_1.swift %t/f_8_2.swift %t/f_8_3.swift %t/f_8_4.swift %t/f_8_5.swift %t/f_8_6.swift %t/f_8_7.swift %t/f_8_8.swift %t/f_8_9.swift %t/f_8_10.swift // RUN: touch %t/f_9_1.swift %t/f_9_2.swift %t/f_9_3.swift %t/f_9_4.swift %t/f_9_5.swift %t/f_9_6.swift %t/f_9_7.swift %t/f_9_8.swift %t/f_9_9.swift %t/f_9_10.swift // RUN: touch %t/f_10_1.swift %t/f_10_2.swift %t/f_10_3.swift %t/f_10_4.swift %t/f_10_5.swift %t/f_10_6.swift %t/f_10_7.swift %t/f_10_8.swift %t/f_10_9.swift %t/f_10_10.swift -// RUN: %swiftc_driver -disable-only-one-dependency-file -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 -// RUN: %FileCheck %s <%t/out.txt -// CHECK-NOT: unable to execute command -// CHECK: Forming into 4 batches -// CHECK: Forming batch job from 25 constituents -// CHECK: Forming batch job from 25 constituents -// CHECK: Forming batch job from 25 constituents -// CHECK: Forming batch job from 25 constituents -// +// RUN: %swiftc_driver -disable-only-one-dependency-file -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 + +// RUN: %FileCheck %s -check-prefix=CHECK-DIS <%t/out.txt +// CHECK-DIS-NOT: unable to execute command +// CHECK-DIS: Forming into 4 batches +// CHECK-DIS-DAG: Forming batch job from 25 constituents +// CHECK-DIS-DAG: Forming batch job from 25 constituents +// CHECK-DIS-DAG: Forming batch job from 25 constituents +// CHECK-DIS-DAG: Forming batch job from 25 constituents + + // RUN: %swiftc_driver -disable-only-one-dependency-file -driver-show-job-lifecycle -driver-batch-size-limit 10 -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out2.txt 2>&1 -// RUN: %FileCheck %s <%t/out2.txt -check-prefix=EXPLICIT-ARG -// EXPLICIT-ARG-NOT: unable to execute command -// EXPLICIT-ARG: Forming into 10 batches -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents +// RUN: %FileCheck %s <%t/out2.txt -check-prefix=EXPLICIT-ARG-DIS +// EXPLICIT-ARG-DIS-NOT: unable to execute command +// EXPLICIT-ARG-DIS: Forming into 10 batches +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents + + +// When -enable-only-one-dependency-file (which is the default) the one compile job with the dependency output is unbatchable. + +// RUN: %empty-directory(%t) +// RUN: touch %t/f_1_1.swift %t/f_1_2.swift %t/f_1_3.swift %t/f_1_4.swift %t/f_1_5.swift %t/f_1_6.swift %t/f_1_7.swift %t/f_1_8.swift %t/f_1_9.swift %t/f_1_10.swift +// RUN: touch %t/f_2_1.swift %t/f_2_2.swift %t/f_2_3.swift %t/f_2_4.swift %t/f_2_5.swift %t/f_2_6.swift %t/f_2_7.swift %t/f_2_8.swift %t/f_2_9.swift %t/f_2_10.swift +// RUN: touch %t/f_3_1.swift %t/f_3_2.swift %t/f_3_3.swift %t/f_3_4.swift %t/f_3_5.swift %t/f_3_6.swift %t/f_3_7.swift %t/f_3_8.swift %t/f_3_9.swift %t/f_3_10.swift +// RUN: touch %t/f_4_1.swift %t/f_4_2.swift %t/f_4_3.swift %t/f_4_4.swift %t/f_4_5.swift %t/f_4_6.swift %t/f_4_7.swift %t/f_4_8.swift %t/f_4_9.swift %t/f_4_10.swift +// RUN: touch %t/f_5_1.swift %t/f_5_2.swift %t/f_5_3.swift %t/f_5_4.swift %t/f_5_5.swift %t/f_5_6.swift %t/f_5_7.swift %t/f_5_8.swift %t/f_5_9.swift %t/f_5_10.swift +// RUN: touch %t/f_6_1.swift %t/f_6_2.swift %t/f_6_3.swift %t/f_6_4.swift %t/f_6_5.swift %t/f_6_6.swift %t/f_6_7.swift %t/f_6_8.swift %t/f_6_9.swift %t/f_6_10.swift +// RUN: touch %t/f_7_1.swift %t/f_7_2.swift %t/f_7_3.swift %t/f_7_4.swift %t/f_7_5.swift %t/f_7_6.swift %t/f_7_7.swift %t/f_7_8.swift %t/f_7_9.swift %t/f_7_10.swift +// RUN: touch %t/f_8_1.swift %t/f_8_2.swift %t/f_8_3.swift %t/f_8_4.swift %t/f_8_5.swift %t/f_8_6.swift %t/f_8_7.swift %t/f_8_8.swift %t/f_8_9.swift %t/f_8_10.swift +// RUN: touch %t/f_9_1.swift %t/f_9_2.swift %t/f_9_3.swift %t/f_9_4.swift %t/f_9_5.swift %t/f_9_6.swift %t/f_9_7.swift %t/f_9_8.swift %t/f_9_9.swift %t/f_9_10.swift +// RUN: touch %t/f_10_1.swift %t/f_10_2.swift %t/f_10_3.swift %t/f_10_4.swift %t/f_10_5.swift %t/f_10_6.swift %t/f_10_7.swift %t/f_10_8.swift %t/f_10_9.swift %t/f_10_10.swift +// RUN: %swiftc_driver -enable-only-one-dependency-file -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 +// RUN: %FileCheck %s -check-prefix=CHECK-ENA <%t/out.txt +// CHECK-ENA-NOT: unable to execute command +// CHECK-ENA: Forming into 4 batches +// CHECK-ENA-DAG: Forming batch job from 25 constituents +// CHECK-ENA-DAG: Forming batch job from 25 constituents +// CHECK-ENA-DAG: Forming batch job from 25 constituents +// CHECK-ENA-DAG: Forming batch job from 24 constituents +// +// RUN: %swiftc_driver -enable-only-one-dependency-file -driver-show-job-lifecycle -driver-batch-size-limit 10 -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out2.txt 2>&1 +// RUN: %FileCheck %s <%t/out2.txt -check-prefix=EXPLICIT-ARG-ENA +// EXPLICIT-ARG-ENA-NOT: unable to execute command +// EXPLICIT-ARG-ENA: Forming into 10 batches +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 9 constituents diff --git a/validation-test/Driver/batch_mode_size_limit_only_one_dependency-file.swift b/validation-test/Driver/batch_mode_size_limit_only_one_dependency-file.swift deleted file mode 100644 index aca01575a7e3b..0000000000000 --- a/validation-test/Driver/batch_mode_size_limit_only_one_dependency-file.swift +++ /dev/null @@ -1,36 +0,0 @@ -// When -enable-only-one-dependency-file (which is the default) the one compile job with the dependency output is unbatchable. - -// RUN: %empty-directory(%t) -// RUN: touch %t/f_1_1.swift %t/f_1_2.swift %t/f_1_3.swift %t/f_1_4.swift %t/f_1_5.swift %t/f_1_6.swift %t/f_1_7.swift %t/f_1_8.swift %t/f_1_9.swift %t/f_1_10.swift -// RUN: touch %t/f_2_1.swift %t/f_2_2.swift %t/f_2_3.swift %t/f_2_4.swift %t/f_2_5.swift %t/f_2_6.swift %t/f_2_7.swift %t/f_2_8.swift %t/f_2_9.swift %t/f_2_10.swift -// RUN: touch %t/f_3_1.swift %t/f_3_2.swift %t/f_3_3.swift %t/f_3_4.swift %t/f_3_5.swift %t/f_3_6.swift %t/f_3_7.swift %t/f_3_8.swift %t/f_3_9.swift %t/f_3_10.swift -// RUN: touch %t/f_4_1.swift %t/f_4_2.swift %t/f_4_3.swift %t/f_4_4.swift %t/f_4_5.swift %t/f_4_6.swift %t/f_4_7.swift %t/f_4_8.swift %t/f_4_9.swift %t/f_4_10.swift -// RUN: touch %t/f_5_1.swift %t/f_5_2.swift %t/f_5_3.swift %t/f_5_4.swift %t/f_5_5.swift %t/f_5_6.swift %t/f_5_7.swift %t/f_5_8.swift %t/f_5_9.swift %t/f_5_10.swift -// RUN: touch %t/f_6_1.swift %t/f_6_2.swift %t/f_6_3.swift %t/f_6_4.swift %t/f_6_5.swift %t/f_6_6.swift %t/f_6_7.swift %t/f_6_8.swift %t/f_6_9.swift %t/f_6_10.swift -// RUN: touch %t/f_7_1.swift %t/f_7_2.swift %t/f_7_3.swift %t/f_7_4.swift %t/f_7_5.swift %t/f_7_6.swift %t/f_7_7.swift %t/f_7_8.swift %t/f_7_9.swift %t/f_7_10.swift -// RUN: touch %t/f_8_1.swift %t/f_8_2.swift %t/f_8_3.swift %t/f_8_4.swift %t/f_8_5.swift %t/f_8_6.swift %t/f_8_7.swift %t/f_8_8.swift %t/f_8_9.swift %t/f_8_10.swift -// RUN: touch %t/f_9_1.swift %t/f_9_2.swift %t/f_9_3.swift %t/f_9_4.swift %t/f_9_5.swift %t/f_9_6.swift %t/f_9_7.swift %t/f_9_8.swift %t/f_9_9.swift %t/f_9_10.swift -// RUN: touch %t/f_10_1.swift %t/f_10_2.swift %t/f_10_3.swift %t/f_10_4.swift %t/f_10_5.swift %t/f_10_6.swift %t/f_10_7.swift %t/f_10_8.swift %t/f_10_9.swift %t/f_10_10.swift -// RUN: %swiftc_driver -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 -// RUN: %FileCheck %s <%t/out.txt -// CHECK-NOT: unable to execute command -// CHECK: Forming into 4 batches -// CHECK-DAG: Forming batch job from 25 constituents -// CHECK-DAG: Forming batch job from 25 constituents -// CHECK-DAG: Forming batch job from 25 constituents -// CHECK-DAG: Forming batch job from 24 constituents -// -// RUN: %swiftc_driver -driver-show-job-lifecycle -driver-batch-size-limit 10 -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out2.txt 2>&1 -// RUN: %FileCheck %s <%t/out2.txt -check-prefix=EXPLICIT-ARG -// EXPLICIT-ARG-NOT: unable to execute command -// EXPLICIT-ARG: Forming into 10 batches -// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents -// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents -// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents -// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents -// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents -// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents -// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents -// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents -// EXPLICIT-ARG-DAG: Forming batch job from 10 constituents -// EXPLICIT-ARG-DAG: Forming batch job from 9 constituents From 1a9bdaffa289158a9fa0d89a23a135d5c975fb74 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 17 Dec 2019 20:53:13 -0800 Subject: [PATCH 127/478] Refactor Direct Name Lookup The old name lookup would frequently try to flush and rebuild the name lookup cache. Instead, never flush the cache, and use the cache misses as an opportunity to load members and bring the lookup table up to date with any added extensions. --- include/swift/AST/Decl.h | 18 +--- lib/AST/Decl.cpp | 2 - lib/AST/NameLookup.cpp | 209 +++++++++++++++++---------------------- 3 files changed, 92 insertions(+), 137 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 527f684081ca8..b9219f23748c5 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3278,28 +3278,16 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// its extensions. /// /// The table itself is lazily constructed and updated when - /// lookupDirect() is called. The bit indicates whether the lookup - /// table has already added members by walking the declarations in - /// scope; it should be manipulated through \c isLookupTablePopulated() - /// and \c setLookupTablePopulated(). - llvm::PointerIntPair LookupTable; + /// lookupDirect() is called. + MemberLookupTable *LookupTable = nullptr; /// Prepare the lookup table to make it ready for lookups. void prepareLookupTable(); - /// True if the entries in \c LookupTable are complete--that is, if a - /// name is present, it contains all members with that name. - bool isLookupTablePopulated() const; - void setLookupTablePopulated(bool value); - /// Note that we have added a member into the iterable declaration context, /// so that it can also be added to the lookup table (if needed). void addedMember(Decl *member); - /// Note that we have added an extension into the nominal type, - /// so that its members can eventually be added to the lookup table. - void addedExtension(ExtensionDecl *ext); - /// A lookup table used to find the protocol conformances of /// a given nominal type. mutable ConformanceLookupTable *ConformanceTable = nullptr; @@ -3319,7 +3307,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { friend class DeclContext; friend class IterableDeclContext; friend ArrayRef - ValueDecl::getSatisfiedProtocolRequirements(bool Sorted) const; + ValueDecl::getSatisfiedProtocolRequirements(bool Sorted) const; protected: Type DeclaredTy; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 5cf8d55f85146..de3b5f649aa20 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3660,8 +3660,6 @@ void NominalTypeDecl::addExtension(ExtensionDecl *extension) { // Add to the end of the list. LastExtension->NextExtension.setPointer(extension); LastExtension = extension; - - addedExtension(extension); } ArrayRef NominalTypeDecl::getStoredProperties() const { diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 02a3e9cd46a95..d855303d0b728 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1041,9 +1041,9 @@ void NominalTypeDecl::addedMember(Decl *member) { if (!vd) return; - // If we have a lookup table, add the new member to it. - auto *lookup = LookupTable.getPointer(); - if (lookup && isLookupTablePopulated()) { + // If we have a lookup table, add the new member to it. If not, we'll pick up + // this member when we first create the table. + if (auto *lookup = LookupTable) { if (hasLazyMembers()) { // If we have lazy members, only add the new member to the lookup // table if we already have another member with the same name. @@ -1057,11 +1057,6 @@ void NominalTypeDecl::addedMember(Decl *member) { } } -void NominalTypeDecl::addedExtension(ExtensionDecl *ext) { - if (hasLazyMembers()) - setLookupTablePopulated(false); -} - void ExtensionDecl::addedMember(Decl *member) { // If this extension has already been bound to a nominal, add the new member // to the nominal's lookup table. @@ -1126,16 +1121,15 @@ void ExtensionDecl::addedMember(Decl *member) { // // If the IDC list is later populated and/or an extension is added _after_ // MemberLookupTable is constructed (and possibly has entries in it), -// MemberLookupTable is purged and reconstructed from IDC's list. +// MemberLookupTable is incrementally reconstituted with new members. static bool populateLookupTableEntryFromLazyIDCLoader(ASTContext &ctx, MemberLookupTable &LookupTable, DeclBaseName name, IterableDeclContext *IDC) { - if (IDC->isLoadingLazyMembers()) { - return false; - } + assert(!IDC->isLoadingLazyMembers() && + "Re-entrant member loading is not supported!"); IDC->setLoadingLazyMembers(true); auto ci = ctx.getOrCreateLazyIterableContextData(IDC, /*lazyLoader=*/nullptr); @@ -1175,33 +1169,31 @@ populateLookupTableEntryFromExtensions(ASTContext &ctx, NominalTypeDecl *nominal, DeclBaseName name) { for (auto e : nominal->getExtensions()) { - if (e->wasDeserialized() || e->hasClangNode()) { - assert(!e->hasUnparsedMembers()); - if (populateLookupTableEntryFromLazyIDCLoader(ctx, table, - name, e)) { - populateLookupTableEntryFromCurrentMembers(ctx, table, name, e); - } - } else { + // If we can retrieve the members of this extension without deserializing + // anything, do so now. + if (!e->wasDeserialized() && !e->hasClangNode()) { populateLookupTableEntryFromCurrentMembers(ctx, table, name, e); + continue; } - } -} - -bool NominalTypeDecl::isLookupTablePopulated() const { - return LookupTable.getInt(); -} -void NominalTypeDecl::setLookupTablePopulated(bool value) { - LookupTable.setInt(value); + assert(!e->hasUnparsedMembers()); + if (populateLookupTableEntryFromLazyIDCLoader(ctx, table, name, e)) { + populateLookupTableEntryFromCurrentMembers(ctx, table, name, e); + } + } } void NominalTypeDecl::prepareLookupTable() { - // If we haven't allocated the lookup table yet, do so now. - if (!LookupTable.getPointer()) { - auto &ctx = getASTContext(); - LookupTable.setPointer(new (ctx) MemberLookupTable(ctx)); + // If we have already allocated the lookup table, then there's nothing further + // to do. + if (LookupTable) { + return; } + // Otherwise start the first fill. + auto &ctx = getASTContext(); + LookupTable = new (ctx) MemberLookupTable(ctx); + if (hasLazyMembers()) { assert(!hasUnparsedMembers()); @@ -1209,30 +1201,21 @@ void NominalTypeDecl::prepareLookupTable() { // from those members already in the IDC member list_ such as implicits or // globals-as-members, then update table entries from the extensions that // have the same names as any such initial-population members. - if (!isLookupTablePopulated()) { - setLookupTablePopulated(true); - LookupTable.getPointer()->addMembers(getCurrentMembersWithoutLoading()); + LookupTable->addMembers(getCurrentMembersWithoutLoading()); - llvm::SetVector baseNamesPresent; - for (auto entry : *LookupTable.getPointer()) { - baseNamesPresent.insert(entry.getFirst().getBaseName()); - } + llvm::SmallSet baseNamesPresent; + for (auto entry : *LookupTable) { + auto baseName = entry.getFirst().getBaseName(); + if (!baseNamesPresent.insert(baseName).second) + continue; - for (auto baseName : baseNamesPresent) { - populateLookupTableEntryFromExtensions(getASTContext(), - *LookupTable.getPointer(), - this, baseName); - } + populateLookupTableEntryFromExtensions(getASTContext(), + *LookupTable, + this, baseName); } - } else { - // No lazy members: if the table needs population, populate the table - // en-masse; and in either case update the extensions. - if (!isLookupTablePopulated()) { - setLookupTablePopulated(true); - LookupTable.getPointer()->addMembers(getMembers()); - } - LookupTable.getPointer()->updateLookupTable(this); + LookupTable->addMembers(getMembers()); + LookupTable->updateLookupTable(this); } } @@ -1258,9 +1241,9 @@ maybeFilterOutAttrImplements(TinyPtrVector decls, return result; } -TinyPtrVector NominalTypeDecl::lookupDirect( - DeclName name, - OptionSet flags) { +TinyPtrVector +NominalTypeDecl::lookupDirect(DeclName name, + OptionSet flags) { ASTContext &ctx = getASTContext(); if (auto s = ctx.Stats) { ++s->getFrontendCounters().NominalTypeLookupDirectCount; @@ -1268,86 +1251,72 @@ TinyPtrVector NominalTypeDecl::lookupDirect( // We only use NamedLazyMemberLoading when a user opts-in and we have // not yet loaded all the members into the IDC list in the first place. - bool useNamedLazyMemberLoading = (ctx.LangOpts.NamedLazyMemberLoading && - hasLazyMembers()); + const bool useNamedLazyMemberLoading = (ctx.LangOpts.NamedLazyMemberLoading && + hasLazyMembers()); - bool includeAttrImplements = + const bool includeAttrImplements = flags.contains(LookupDirectFlags::IncludeAttrImplements); LLVM_DEBUG(llvm::dbgs() << getNameStr() << ".lookupDirect(" - << name << ")" - << ", isLookupTablePopulated()=" << isLookupTablePopulated() - << ", hasLazyMembers()=" << hasLazyMembers() - << ", useNamedLazyMemberLoading=" << useNamedLazyMemberLoading - << "\n"); - - // We check the LookupTable at most twice, possibly treating a miss in the - // first try as a cache-miss that we then do a cache-fill on, and retry. - for (int i = 0; i < 2; ++i) { - - // First, if we're _not_ doing NamedLazyMemberLoading, we make sure we've - // populated the IDC and brought it up to date with any extensions. This - // will flip the hasLazyMembers() flag to false as well. - if (!useNamedLazyMemberLoading) { - // It's possible that the lookup table exists but has information in it - // that is either currently out of date or soon to be out of date. - // This can happen two ways: - // - // - We've not yet indexed the members we have (isLookupTablePopulated() - // is zero). - // - // - We've still got more lazy members left to load; this can happen - // even if we _did_ index some members. - // - // In either of these cases, we want to reset the table to empty and - // mark it as needing reconstruction. - if (LookupTable.getPointer() && - (hasLazyMembers() || !isLookupTablePopulated())) { - LookupTable.getPointer()->clear(); - setLookupTablePopulated(false); - } - - (void)getMembers(); + << name << ")" + << ", hasLazyMembers()=" << hasLazyMembers() + << ", useNamedLazyMemberLoading=" << useNamedLazyMemberLoading + << "\n"); - // Make sure we have the complete list of members (in this nominal and in - // all extensions). - for (auto E : getExtensions()) - (void)E->getMembers(); - } - // Next, in all cases, prepare the lookup table for use, possibly - // repopulating it from the IDC if the IDC member list has just grown. - prepareLookupTable(); + prepareLookupTable(); + auto tryCacheLookup = + [=](MemberLookupTable *table, + DeclName name) -> Optional> { // Look for a declaration with this name. - auto known = LookupTable.getPointer()->find(name); + auto known = table->find(name); + if (known == table->end()) + return None; // We found something; return it. - if (known != LookupTable.getPointer()->end()) - return maybeFilterOutAttrImplements(known->second, name, - includeAttrImplements); + return maybeFilterOutAttrImplements(known->second, name, + includeAttrImplements); + }; - // If we have no more second chances, stop now. - if (!useNamedLazyMemberLoading || i > 0) - break; + auto updateLookupTable = [this](MemberLookupTable *table) { + // Make sure we have the complete list of members (in this nominal and in + // all extensions). + (void)getMembers(); - // If we get here, we had a cache-miss and _are_ using - // NamedLazyMemberLoading. Try to populate a _single_ entry in the - // MemberLookupTable from both this nominal and all of its extensions, and - // retry. Any failure to load here flips the useNamedLazyMemberLoading to - // false, and we fall back to loading all members during the retry. - auto &Table = *LookupTable.getPointer(); - if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table, - name.getBaseName(), this)) { - useNamedLazyMemberLoading = false; - } else { - populateLookupTableEntryFromExtensions(ctx, Table, this, - name.getBaseName()); - } + for (auto E : getExtensions()) + (void)E->getMembers(); + + LookupTable->updateLookupTable(this); + }; + + if (!useNamedLazyMemberLoading) { + updateLookupTable(LookupTable); } - // None of our attempts found anything. - return { }; + // Look for a declaration with this name. + if (auto lookup = tryCacheLookup(LookupTable, name)) + return lookup.getValue(); + + if (!useNamedLazyMemberLoading) { + return { }; + } + + // If we get here, we had a cache-miss and _are_ using + // NamedLazyMemberLoading. Try to populate a _single_ entry in the + // MemberLookupTable from both this nominal and all of its extensions, and + // retry. + auto &Table = *LookupTable; + if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table, + name.getBaseName(), this)) { + updateLookupTable(LookupTable); + } else { + populateLookupTableEntryFromExtensions(ctx, Table, this, + name.getBaseName()); + } + + return tryCacheLookup(LookupTable, name) + .getValueOr(TinyPtrVector()); } void ClassDecl::createObjCMethodLookup() { From 8b926af49a2aad883cb358812aa67137e8199222 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 16 Dec 2019 16:04:09 -0800 Subject: [PATCH 128/478] EscapeAnalysis: Add PointerKind and interior/reference flags Categorize three kinds of pointers: NoPointer (don't create a node) ReferenceOnly (safe to make normal assumptions) AnyPointer (may have addresses, rawpointers, or any mix of thoses with references) Flag ConnectionGraph nodes as - hasReferenceOnly - isInterior An interior node always has an additional content node. All sorts of arbitrary node merging is supported. Nodes with totally different properties can be safely merged. Interior nodes can safely be merged with their field content (which does happen surprisingly often). Alias analysis will use these flags to safely make assumptions about properties of the connection graph. --- .../SILOptimizer/Analysis/EscapeAnalysis.h | 319 +++++--- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 759 ++++++++++-------- 2 files changed, 646 insertions(+), 432 deletions(-) diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index 9c4b364383d11..0c68b94f9984e 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -43,18 +43,20 @@ /// return a /// /// Generates the following connection graph, where 'a' is in the SILValue %0: -/// Val %0 Esc: R, Succ: (%0.1) // Represents 'a', and points to 'a's content -/// Con %0.1 Esc: G, Succ: // Represents the content of 'a' -/// Ret Esc: R, Succ: %0 // The returned value, aliased with 'a' +/// Val [ref] %1 Esc: R, Succ: (%1) // Reference 'a'; points to 'a's object +/// Con [int] %2 Esc: R, Succ: (%2.1) // Object pointed to by 'a' +/// Con %2.1 Esc: G, Succ: // Fields in the object +/// Ret Esc: R, Succ: %0 // The returned value, aliased with 'a' /// /// Each node has an escaping state: None, (R)eturn, (A)rguments, or (G)lobal. /// These states form a lattice in which None is the most refined, or top, state /// and Global is the least refined, or bottom, state. Merging nodes performs a -/// meet operation on their escaping states. At a call site, the callee graph is -/// merged with the callee graph by merging the respective call argument -/// nodes. A node has a "Return" escaping state if it only escapes by being -/// returned from the current function. A node has an "Argument" escaping state -/// if only escapes by being passed as an incoming argument to this function. +/// meet operation on their escaping states, moving the state down in the +/// lattice. At a call site, the callee graph is merged with the caller graph by +/// merging the respective call argument nodes. A node has a "Return" escaping +/// state if it only escapes by being returned from the current function. A node +/// has an "Argument" escaping state if only escapes by being passed as an +/// incoming argument to this function. /// /// A directed edge between two connection graph nodes indicates that the memory /// represented by the destination node memory is reachable via an address @@ -81,30 +83,37 @@ /// /// Generates the following connection graph, where the alloc_stack for variable /// 'a' is in the SILValue %0 and class allocation returns SILValue %3. -/// Val %0 Esc: G, Succ: (%0.1) -/// Con %0.1 Esc: G, Succ: %3 -/// Val %3 Esc: G, Succ: (%3.1) -/// Con %3.1 Esc: G, Succ: -/// Ret Esc: R, Succ: %3 +/// +/// Val %0 Esc: , Succ: (%0.1) // Stack address of 'a' +/// Con [ref] %0.1 Esc: , Succ: %3 // Local reference 'a', aliased with %3 +/// Val [ref] %3 Esc: , Succ: (%4) // Local instance 'a', stored in %0.1 +/// Con [int] %4 Esc: G, Succ: (%4.1) // Object, escapes +/// Con %4.1 Esc: G, Succ: // Fields, escapes +/// Ret Esc: , Succ: (%4), %3 /// /// The value node for variable 'a' now points to local variable storage /// (%0.1). That local variable storage contains a reference. Assignment into /// that reference creates a defer edge to the allocated reference (%3). The -/// allocated reference in turn points to the object storage (%3.1). +/// allocated reference in turn points to the object storage (%4). /// -/// Note that a variable holding a single class reference and a variable -/// holding a non-trivial struct has the same graph representation. The -/// variable's content node only represents the value of the references, not the -/// memory pointed-to by the reference. +/// Note that a variable holding a single class reference and a variable holding +/// a non-trivial struct will have the same graph representation. A '[ref]' flag +/// on a node indicates that the all pointer-type fields that may be stored +/// inside the memory represented by that node are references. This allows alias +/// analysis to assume the object the node points to will not be released when +/// the node's memory is released as long as there are subsequent accesses to +/// the object accessed via a different path in the connection graph. /// /// A pointsTo edge does not necessarily indicate pointer indirection. It may -/// simply represent a derived address within the same object. This allows -/// escape analysis to view an object's memory in layers, each with separate -/// escaping properties. For example, a class object's first-level content node -/// represents the object header including the metadata pointer and reference -/// count. An object's second level content node only represents the -/// reference-holding fields within that object. Consider the connection graph -/// for a class with properties: +/// simply represent a derived address within the same object. A node that +/// points to the same logical object must be flagged as an interior node +/// ('[int]'). Interior nodes always have pointsTo content representing the rest +/// of the object. This allows escape analysis to view an object's memory in +/// layers, each with separate escaping properties. For example, a class +/// object's first-level content node represents the object header including the +/// metadata pointer and reference count. An object's second level content node +/// only represents the reference-holding fields within that object. Consider +/// the connection graph for a class with properties: /// /// class HasObj { /// var obj: AnyObject @@ -114,35 +123,37 @@ /// } /// /// Which generates this graph where the argument 'h' is %0, and 'o' is %1: -/// Arg %0 Esc: A, Succ: (%0.1) -/// Con %0.1 Esc: A, Succ: (%0.2) -/// Con %0.2 Esc: A, Succ: %1 -/// Arg %1 Esc: A, Succ: (%1.1) -/// Con %1.1 Esc: A, Succ: (%1.2) -/// Con %1.2 Esc: G, Succ: /// -/// Node %0.1 represents the header of 'h', including reference count and -/// metadata pointer. This node points to %0.2 which represents the 'obj' -/// property. The assignment 'h.obj = o' creates a defer edge from %0.2 to -/// %1. Similarly, %1.1 represents the header of 'o', and %1.2 represents any -/// potential nontrivial properties in 'o' which may have escaped globally when -/// 'o' was released. +/// Arg [ref] %0 Esc: A, Succ: (%6) // 'h' +/// Arg [ref] %1 Esc: A, Succ: (%1.1) // 'o' +/// Con [int] %1.1 Esc: A, Succ: (%1.2) // 'o' object +/// Con %1.2 Esc: A, Succ: (%1.3) // 'o' fields +/// Con %1.3 Esc: G, Succ: // memory 'h.obj' may point to +/// Con [int] %6 Esc: A, Succ: (%7) // 'h' object +/// Con %7 Esc: A, Succ: %1 // 'h.obj' +/// +/// Node %1.1 represents the header of 'o', including reference count and +/// metadata pointer. This node points to %1.2 which represents the 'obj' +/// property. '%1.3' represents any potential nontrivial properties in 'o' which +/// may have escaped globally when 'o' was released. '%6' is a ref_element_addr +/// accessing 'h.obj'. '%7' is a load of 'h.obj'. The assignment 'h.obj = o' +/// creates a defer edge from '%7' to '%1'. /// /// The connection graph is constructed by summarizing all memory operations in /// a flow-insensitive way. Hint: ConGraph->viewCG() displays the Dot-formatted /// connection graph. /// -/// In addition to the connection graph, EscapeAnalysis stores information about -/// "use points". Each release operation is a use points. These instructions are -/// recorded in a table and given an ID. Each connection graph node stores a -/// bitset indicating the use points reachable via the CFG by that node. This -/// provides some flow-sensitive information on top of the otherwise flow -/// insensitive connection graph. -/// -/// Note: storing bitsets in each node may be unnecessary overhead since the -/// same information can be obtained with a graph traversal, typically of only -/// 1-3 hops. -// ===---------------------------------------------------------------------===// +/// In addition to the connection graph, EscapeAnalysis caches information about +/// "use points". Each release operation in which the released reference can be +/// identified is a considered a use point. The use point instructions are +/// recorded in a table and given an ID. Each connection graph content node +/// stores a bitset indicating the use points that may release references that +/// point to by that content node. Correctness relies on an invariant: for each +/// reference-type value in the function, all points in the function which may +/// release the reference must be identified as use points of the node that the +/// value points to. If the reference-type value may be released any other +/// way, then its content node must be marked escaping. +/// ===---------------------------------------------------------------------===// #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ESCAPEANALYSIS_H_ #define SWIFT_SILOPTIMIZER_ANALYSIS_ESCAPEANALYSIS_H_ @@ -234,6 +245,13 @@ class EscapeAnalysis : public BottomUpIPAnalysis { Global }; + // Must be ordered from most precise to least precise. A meet across memory + // locations (such as aggregate fields) always moves down. + enum PointerKind { NoPointer, ReferenceOnly, AnyPointer }; + static bool canOnlyContainReferences(PointerKind pointerKind) { + return pointerKind <= ReferenceOnly; + } + public: class CGNode; class ConnectionGraph; @@ -320,25 +338,52 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// is completely unlinked from the graph, bool isMerged = false; - /// True if this is a content node that owns a reference count. Such a - /// content node necessarilly keeps alive all content it points to until it - /// is released. This can be conservatively false. - bool hasRC = false; + /// True if this node's pointsTo content is part of the same object with + /// respect to SIL memory access. When this is true, then this node must + /// have a content node. + /// + /// When this flag is false, it provides a "separate access guarantee" for + /// stronger analysis. If the base object for a SIL memory access is mapped + /// to this node, then the accessed memory must have the same escaping + /// properties as the base object. In contrast, when this is true, the + /// connection graph may model the escaping properties of the base object + /// separately from the accessed memory. + bool isInteriorFlag; + + /// True if this node can only point to other nodes via a reference, not an + /// address or raw pointer. + /// + /// When this flag is true, it provides a "separate lifetime guarantee". Any + /// reference modeled by this node keeps alive all pointsTo content until + /// it is released. e.g. Given two nodes that both pointTo the same + /// content, releasing the value associated one node cannot free that + /// content as long as the released node is reference-only and the value + /// associated with the other node is accessed later. + /// + /// Note that an object field may contain a raw pointer which could point + /// anywhere, even back into the same object. In that case + /// hasReferenceOnlyFlag must be false. + /// + /// Typically, when this is true, the node represents at least one + /// reference, however, a return node may be created even when the return + /// type is NoPointer. + bool hasReferenceOnlyFlag; /// The type of the node (mainly distinguishes between content and value /// nodes). NodeType Type; /// The constructor. - CGNode(ValueBase *V, NodeType Type, bool hasRC) - : mappedValue(V), UsePoints(0), hasRC(hasRC), Type(Type) { + CGNode(ValueBase *V, NodeType Type, bool isInterior, bool hasReferenceOnly) + : mappedValue(V), UsePoints(0), isInteriorFlag(isInterior), + hasReferenceOnlyFlag(hasReferenceOnly), Type(Type) { switch (Type) { case NodeType::Argument: case NodeType::Value: - assert(V); + assert(V && !isInteriorFlag); break; case NodeType::Return: - assert(!V); + assert(!V && !isInteriorFlag); break; case NodeType::Content: // A content node representing the returned value has no associated @@ -360,6 +405,8 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return Changed; } + void mergeFlags(bool isInterior, bool hasReferenceOnly); + // Merge the properties of \p fromNode into this node. void mergeProperties(CGNode *fromNode); @@ -454,10 +501,18 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Return true if this node represents content. bool isContent() const { return Type == NodeType::Content; } - /// Return true if this node represents an entire reference counted object. - bool hasRefCount() const { return hasRC; } + /// Return true if this node pointsTo the same object. + bool isInterior() const { return isInteriorFlag; } - void setRefCount(bool rc) { hasRC = rc; } + void setInterior(bool isInterior) { isInteriorFlag = isInterior; } + + /// Return true if this node can only point to another node via a reference, + /// as opposed to an address or raw pointer. + bool hasReferenceOnly() const { return hasReferenceOnlyFlag; } + + void setHasReferenceOnly(bool hasReferenceOnly) { + hasReferenceOnlyFlag = hasReferenceOnly; + } /// Returns the escape state. EscapeState getEscapeState() const { return State; } @@ -614,11 +669,13 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Allocates a node of a given type. /// - /// hasRC is set for Content nodes based on the type and origin of - /// the pointer. - CGNode *allocNode(ValueBase *V, NodeType Type, bool hasRC = false) { - assert(Type == NodeType::Content || !hasRC); - CGNode *Node = new (NodeAllocator.Allocate()) CGNode(V, Type, hasRC); + /// isInterior is always false for non-content nodes and is set for content + /// nodes based on the type and origin of the pointer. + CGNode *allocNode(ValueBase *V, NodeType Type, bool isInterior, + bool isReference) { + assert((Type == NodeType::Content) || !isInterior); + CGNode *Node = new (NodeAllocator.Allocate()) + CGNode(V, Type, isInterior, isReference); Nodes.push_back(Node); return Node; } @@ -665,6 +722,9 @@ class EscapeAnalysis : public BottomUpIPAnalysis { } } + // Helper for getNode and getValueContent. + CGNode *getOrCreateNode(ValueBase *V, PointerKind pointerKind); + /// Gets or creates a node for a value \p V. /// If V is a projection(-path) then the base of the projection(-path) is /// taken. This means the node is always created for the "outermost" value @@ -672,9 +732,14 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Returns null, if V is not a "pointer". CGNode *getNode(ValueBase *V, bool createIfNeeded = true); - /// Helper to create and return a content node with the given \p hasRC - /// flag. \p addrNode will gain a points-to edge to the new content node. - CGNode *createContentNode(CGNode *addrNode, bool hasRC); + // Helper for getValueContent to create and return a content node with the + // given \p isInterior and \p hasReferenceOnly flags. \p addrNode + // will gain a points-to edge to the new content node. + CGNode *createContentNode(CGNode *addrNode, bool isInterior, + bool hasReferenceOnly); + + CGNode *getOrCreateContentNode(CGNode *addrNode, bool isInterior, + bool hasReferenceOnly); /// Create a new content node based on an existing content node to support /// graph merging. @@ -683,18 +748,33 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// state will be initialized based on the \p srcContent node. CGNode *createMergedContent(CGNode *destAddrNode, CGNode *srcContent); - /// Get a node represnting the field data within the given RC node. - CGNode *getFieldContent(CGNode *rcNode); + // Helper for getValueContent to get the content node for an address, which + // may be variable content or field content. + CGNode *getOrCreateAddressContent(SILValue addrVal, CGNode *addrNode); + + // Helper for getValueContent to get the content representing a referenced + // object. \p refVal's type may or may not have reference semantics. The + // caller must knows based on the operand using \p refVal that it contains a + // reference. + CGNode *getOrCreateReferenceContent(SILValue refVal, CGNode *refNode); + + // Helper for getValueContent to get the content node for an unknown pointer + // value. This is useful to determine whether multiple nodes are in the same + // defer web, but is otherwise conservative. + CGNode *getOrCreateUnknownContent(CGNode *addrNode); + + /// Get a node representing the field data within the given object node. + /// If objNode was a recognized reference-only value, then it's content node + /// will already be initialized according to the reference type. Otherwise, + /// return null. + CGNode *getFieldContent(CGNode *objNode) { + if (!objNode->isInterior()) + return nullptr; + return objNode->getContentNodeOrNull(); + } /// Get or creates a pseudo node for the function return value. - CGNode *getReturnNode() { - if (!ReturnNode) { - ReturnNode = allocNode(nullptr, NodeType::Return); - if (!isSummaryGraph) - ReturnNode->mergeEscapeState(EscapeState::Return); - } - return ReturnNode; - } + CGNode *getReturnNode(); /// Returns the node for the function return value if present. CGNode *getReturnNodeOrNull() const { @@ -731,6 +811,21 @@ class EscapeAnalysis : public BottomUpIPAnalysis { assert(UsePoints.size() == UsePointTable.size()); Node->setUsePointBit(Idx); return Idx; + /// If \p pointer is a pointer, set it's content to global escaping. + /// + /// Only mark the content node as escaping. Marking a pointer node as + /// escaping would generally be meaningless because it may have aliases or + /// defer edges. Marking the pointer node as escaping would also be too + /// conservative because, when the pointer is mapped to a content node, it + /// would behave as if the memory containing the pointer also escaped. + /// + /// If the pointer is mapped to a node, then calling setEscapesGlobal must + /// ensure that it points to a content node. Even if we could mark the + /// pointer node as escaping, it would be insufficient because only content + /// nodes are merged into the caller graph. + void setEscapesGlobal(SILValue pointer) { + if (CGNode *content = getValueContent(pointer)) + content->markEscaping(); } void escapeContentsOf(CGNode *Node) { @@ -806,6 +901,14 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Returns null, if V is not a "pointer". CGNode *getNodeOrNull(ValueBase *V) { return getNode(V, false); } + /// Get the content node pointed to by \p ptrVal. + /// + /// If \p ptrVal cannot be mapped to a node, return nullptr. + /// + /// If \p ptrVal is mapped to a node, return a non-null node representing + /// the content that \p ptrVal points to. + CGNode *getValueContent(SILValue ptrVal); + /// Returns the number of use-points of a node. int getNumUsePoints(CGNode *Node) { assert(!Node->escapes() && @@ -901,6 +1004,8 @@ class EscapeAnalysis : public BottomUpIPAnalysis { MaxGraphMerges = 4 }; + using PointerKindCache = llvm::DenseMap; + /// The connection graphs for all functions (does not include external /// functions). llvm::DenseMap Function2Info; @@ -909,7 +1014,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { llvm::SpecificBumpPtrAllocator Allocator; /// Cache for isPointer(). - llvm::DenseMap isPointerCache; + PointerKindCache pointerKindCache; SILModule *M; @@ -919,10 +1024,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Callee analysis, used for determining the callees at call sites. BasicCalleeAnalysis *BCA; - /// Returns true if \p V may encapsulate a "pointer" value. - /// See EscapeAnalysis::NodeType::Value. - bool isPointer(ValueBase *V) const; - /// If EscapeAnalysis should consider the given value to be a derived address /// or pointer based on one of its address or pointer operands, then return /// that operand value. Otherwise, return an invalid value. @@ -933,23 +1034,14 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// value. SILValue getPointerRoot(SILValue value) const; - /// If \p pointer is a pointer, set it to global escaping. - void setEscapesGlobal(ConnectionGraph *conGraph, ValueBase *pointer) { - CGNode *Node = conGraph->getNode(pointer); - if (!Node) - return; + PointerKind findRecursivePointerKind(SILType Ty, const SILFunction &F) const; - if (Node->isContent()) { - Node->markEscaping(); - return; - } - Node->mergeEscapeState(EscapeState::Global); + PointerKind findCachedPointerKind(SILType Ty, const SILFunction &F) const; - // Make sure to have a content node. Otherwise we may end up not merging - // the global-escape state into a caller graph (only content nodes are - // merged). Either the node itself is a content node or we let the node - // point to one. - conGraph->escapeContentsOf(Node); + // Returns true if the type \p Ty must be a reference or must transitively + // contain a reference and no other pointer or address type. + bool hasReferenceOnly(SILType Ty, const SILFunction &F) const { + return findCachedPointerKind(Ty, F) <= ReferenceOnly; } /// Gets or creates FunctionEffects for \p F. @@ -960,15 +1052,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return FInfo; } - /// Get or create the node representing the memory pointed to by \p - /// addrVal. If \p addrVal is an address, then return the content node for the - /// variable's memory. Otherwise, \p addrVal may contain a reference, so - /// return the content node for the referenced heap object. - /// - /// Note that \p addrVal cannot be an address within a heap object, such as - /// an address from ref_element_addr or project_box. - CGNode *getValueContent(ConnectionGraph *conGraph, SILValue addrVal); - /// Build a connection graph for reach callee from the callee list. bool buildConnectionGraphForCallees(SILInstruction *Caller, CalleeList Callees, @@ -990,6 +1073,9 @@ class EscapeAnalysis : public BottomUpIPAnalysis { void buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth); + bool createArrayUninitializedSubgraph(FullApplySite apply, + ConnectionGraph *conGraph); + /// Updates the graph by analyzing instruction \p I. /// Visited callees are added to \p BottomUpOrder until \p RecursionDepth /// reaches MaxRecursionDepth. @@ -1046,6 +1132,19 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return &FInfo->Graph; } + /// Return \p value's PointerKind. + PointerKind getPointerKind(ValueBase *value) const { + auto *f = value->getFunction(); + // The function can be null, e.g. if V is an undef. + if (!f) + return NoPointer; + + return findCachedPointerKind(value->getType(), *f); + } + /// Returns true if \p V may encapsulate a "pointer" value. + /// See EscapeAnalysis::NodeType::Value. + bool isPointer(ValueBase *V) const { return getPointerKind(V) > NoPointer; } + /// Returns true if the value \p V can escape to the function call \p FAS. /// This means that the called function may access the value \p V. /// If \p V has reference semantics, this function returns false if only the @@ -1063,14 +1162,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// to \p V. bool canEscapeToValue(SILValue V, SILValue To); - /// Returns true if the parameter with index \p ParamIdx can escape in the - /// called function of apply site \p FAS. - /// If it is an indirect parameter and \p checkContentOfIndirectParam is true - /// then the escape status is not checked for the address itself but for the - /// referenced pointer (if the referenced type is a pointer). - bool canParameterEscape(FullApplySite FAS, int ParamIdx, - bool checkContentOfIndirectParam); - /// Returns true if the pointers \p V1 and \p V2 can possibly point to the /// same memory. /// diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 99e3fae1dbf49..363c1bc1eb195 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -30,120 +30,91 @@ static llvm::cl::opt EnableInternalVerify( llvm::cl::desc("Enable internal verification of escape analysis"), llvm::cl::init(false)); -// Returns true if \p Ty recursively contains a reference. If \p mustBeRef is -// true, only return true if the type is guaranteed to hold a reference. If \p -// mustBeRef is false, only return false if the type is guaranteed not to hold a -// reference. -// -// If \p Ty is itself an address, return false. -static bool findRecursiveRefType(SILType Ty, const SILFunction &F, - bool mustBeRef) { - if (mustBeRef) { - // An address *may* be converted into a reference via something like - // raw_pointer_to_ref. However, addresses don't normally refer to the head - // of a reference counted object. - // - // The check for trivial types catches types that have AST "reference - // semantics", but are determined by type lowering to be trivial, such as - // noescape function types. - if (Ty.isAddress() || Ty.isTrivial(F)) - return false; - } +// Returns the kind of pointer that \p Ty recursively contains. +EscapeAnalysis::PointerKind +EscapeAnalysis::findRecursivePointerKind(SILType Ty, + const SILFunction &F) const { + // An address may be converted into a reference via something like + // raw_pointer_to_ref, but in general we don't know what kind of pointer it + // is. + if (Ty.isAddress()) + return EscapeAnalysis::AnyPointer; - if (!mustBeRef) { - // Opaque types may contain a reference. Speculatively track them too. - // - // 1. It may be possible to optimize opaque values based on known mutation - // points. - // - // 2. A specialized function may call a generic function passing a concrete - // reference type via incomplete specialization. - // - // 3. A generic function may call a specialized function taking a concrete - // reference type via devirtualization. - if (Ty.isAddressOnly(F)) - return true; + // Opaque types may contain a reference. Speculatively track them too. + // + // 1. It may be possible to optimize opaque values based on known mutation + // points. + // + // 2. A specialized function may call a generic function passing a concrete + // reference type via incomplete specialization. + // + // 3. A generic function may call a specialized function taking a concrete + // reference type via devirtualization. + if (Ty.isAddressOnly(F)) + return EscapeAnalysis::AnyPointer; - if (Ty.getASTType() == F.getModule().getASTContext().TheRawPointerType) - return true; - } + // A raw pointer definitely does not have a reference, but could point + // anywhere. We do track these because critical stdlib data structures often + // use raw pointers under the hood. + if (Ty.getASTType() == F.getModule().getASTContext().TheRawPointerType) + return EscapeAnalysis::AnyPointer; if (Ty.hasReferenceSemantics()) - return true; + return EscapeAnalysis::ReferenceOnly; - auto &Mod = F.getModule(); + auto &M = F.getModule(); + // Start with the most precise pointer kind + PointerKind aggregateKind = NoPointer; + auto meetAggregateKind = [&](PointerKind otherKind) { + if (otherKind > aggregateKind) + aggregateKind = otherKind; + }; if (auto *Str = Ty.getStructOrBoundGenericStruct()) { for (auto *Field : Str->getStoredProperties()) { - if (findRecursiveRefType( - Ty.getFieldType(Field, Mod, F.getTypeExpansionContext()), F, - mustBeRef)) - return true; + SILType fieldTy = Ty.getFieldType(Field, M, F.getTypeExpansionContext()); + meetAggregateKind(findCachedPointerKind(fieldTy, F)); } - return false; + return aggregateKind; } if (auto TT = Ty.getAs()) { for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) { - if (findRecursiveRefType(Ty.getTupleElementType(i), F, mustBeRef)) - return true; + meetAggregateKind(findCachedPointerKind(Ty.getTupleElementType(i), F)); } - return false; + return aggregateKind; } if (auto En = Ty.getEnumOrBoundGenericEnum()) { for (auto *ElemDecl : En->getAllElements()) { - if (ElemDecl->hasAssociatedValues() && - findRecursiveRefType( - Ty.getEnumElementType(ElemDecl, Mod, F.getTypeExpansionContext()), - F, mustBeRef)) - return true; + if (!ElemDecl->hasAssociatedValues()) + continue; + SILType eltTy = + Ty.getEnumElementType(ElemDecl, M, F.getTypeExpansionContext()); + meetAggregateKind(findCachedPointerKind(eltTy, F)); } - return false; + return aggregateKind; } - // FIXME: without a covered switch, this is not robust for mayContainReference - // in the event that new reference-holding AST types are invented. - return false; + // FIXME: without a covered switch, this is not robust in the event that new + // reference-holding AST types are invented. + return NoPointer; } -// Returns true if the type \p Ty is a reference or may transitively contain -// a reference. If \p Ty is itself an address, return false. -// -// An address may contain a reference because addresses can be cast into -// reference types. -static bool mayContainReference(SILType Ty, const SILFunction &F) { - if (Ty.isAddress()) - return true; - return findRecursiveRefType(Ty, F, false); -} +// Returns the kind of pointer that \p Ty recursively contains. +EscapeAnalysis::PointerKind +EscapeAnalysis::findCachedPointerKind(SILType Ty, const SILFunction &F) const { + auto iter = pointerKindCache.find(Ty); + if (iter != pointerKindCache.end()) + return iter->second; -// Returns true if the type \p Ty must be a reference or must transitively -// contain a reference. If \p Ty is itself an address, return false. -static bool mustContainReference(SILType Ty, const SILFunction &F) { - return findRecursiveRefType(Ty, F, true); -} - -bool EscapeAnalysis::isPointer(ValueBase *V) const { - auto *F = V->getFunction(); - - // The function can be null, e.g. if V is an undef. - if (!F) - return false; - - SILType Ty = V->getType(); - auto Iter = isPointerCache.find(Ty); - if (Iter != isPointerCache.end()) - return Iter->second; - - bool IP = mayContainReference(Ty, *F); - const_cast(this)->isPointerCache[Ty] = IP; - return IP; + PointerKind pointerKind = findRecursivePointerKind(Ty, F); + const_cast(this)->pointerKindCache[Ty] = pointerKind; + return pointerKind; } static bool isExtractOfArrayUninitializedPointer(TupleExtractInst *TEI) { - if (TEI->getFieldNo() == 1) { - if (auto apply = dyn_cast(TEI->getOperand())) - if (ArraySemanticsCall(apply, "array.uninitialized", false)) - return true; - } + if (auto apply = dyn_cast(TEI->getOperand())) + if (ArraySemanticsCall(apply, "array.uninitialized", false)) + return true; + return false; } @@ -362,15 +333,26 @@ EscapeAnalysis::CGNode::RepValue EscapeAnalysis::CGNode::getRepValue() const { depth}; } +void EscapeAnalysis::CGNode::mergeFlags(bool isInterior, + bool hasReferenceOnly) { + // isInterior is conservatively preserved from either node unless two content + // nodes are being merged and one is the interior node's content. + isInteriorFlag |= isInterior; + + // hasReferenceOnly is always conservatively merged. + hasReferenceOnlyFlag &= hasReferenceOnly; +} + void EscapeAnalysis::CGNode::mergeProperties(CGNode *fromNode) { - // TODO: Optimistically merge hasRC. 'this' node can only be merged with - // `fromNode` if their pointer values are compatible. If `fromNode->hasRC` is - // true, then it is guaranteed to represent the head of a heap object. Thus, - // it can only be merged with 'this' when the pointer values that access - // 'this' are also references. - // - // For now, this is pessimistic until we understand performance implications. - hasRC &= fromNode->hasRC; + // isInterior is conservatively preserved from either node unless the other + // node is the interior node's content. + bool isInterior = fromNode->isInteriorFlag; + if (fromNode == pointsTo) + this->isInteriorFlag = isInterior; + else if (this == fromNode->pointsTo) + isInterior = this->isInteriorFlag; + + mergeFlags(isInterior, fromNode->hasReferenceOnlyFlag); } template @@ -413,38 +395,68 @@ void EscapeAnalysis::ConnectionGraph::clear() { } EscapeAnalysis::CGNode * -EscapeAnalysis::ConnectionGraph::getNode(ValueBase *V, bool createIfNeeded) { +EscapeAnalysis::ConnectionGraph::getOrCreateNode(ValueBase *V, + PointerKind pointerKind) { + assert(pointerKind != EscapeAnalysis::NoPointer); + if (isa(V) || isa(V) || isa(V)) return nullptr; - // In the case of a struct or tuple extract, 'V' may not be a pointer - // even if it's pointer root is a pointer. Bail first because we only expect - // graph nodes for pointer values. - if (!EA->isPointer(V)) - return nullptr; - - // Look past address projections, pointer casts, and the like within the same - // object. Does not look past a dereference such as ref_element_addr, or - // project_box. - V = EA->getPointerRoot(V); - - if (!createIfNeeded) - return lookupNode(V); - CGNode * &Node = Values2Nodes[V]; + // Nodes mapped to values must have an indirect pointsTo. Nodes that don't + // have an indirect pointsTo are imaginary nodes that don't directly represnt + // a SIL value. + bool hasReferenceOnly = canOnlyContainReferences(pointerKind); if (!Node) { if (isa(V)) { - Node = allocNode(V, NodeType::Argument); + Node = allocNode(V, NodeType::Argument, false, hasReferenceOnly); if (!isSummaryGraph) Node->mergeEscapeState(EscapeState::Arguments); } else { - Node = allocNode(V, NodeType::Value); + Node = allocNode(V, NodeType::Value, false, hasReferenceOnly); } } return Node->getMergeTarget(); } +EscapeAnalysis::CGNode * +EscapeAnalysis::ConnectionGraph::getNode(ValueBase *V, bool createIfNeeded) { + PointerKind pointerKind = EA->getPointerKind(V); + if (pointerKind == EscapeAnalysis::NoPointer) + return nullptr; + + // Look past address projections, pointer casts, and the like within the same + // object. Does not look past a dereference such as ref_element_addr, or + // project_box. + V = EA->getPointerRoot(V); + + if (!createIfNeeded) + return lookupNode(V); + + return getOrCreateNode(V, pointerKind); +} + +/// Adds an argument/instruction in which the node's memory is released. +int EscapeAnalysis::ConnectionGraph::addUsePoint(CGNode *Node, + SILInstruction *User) { + // Use points are never consulted for escaping nodes, but still need to + // propagate to other nodes in a defer web. Even if this node is escaping, + // some defer predecessors may not be escaping. Only checking if this node has + // defer predecessors is insufficient because a defer successor of this node + // may have defer predecessors. + if (Node->getEscapeState() >= EscapeState::Global) + return -1; + + int Idx = (int)UsePoints.size(); + assert(UsePoints.count(User) == 0 && "value is already a use-point"); + UsePoints[User] = Idx; + UsePointTable.push_back(User); + assert(UsePoints.size() == UsePointTable.size()); + Node->setUsePointBit(Idx); + return Idx; +} + CGNode *EscapeAnalysis::ConnectionGraph::defer(CGNode *From, CGNode *To, bool &Changed) { if (!From->canAddDeferred(To)) @@ -640,9 +652,9 @@ void EscapeAnalysis::ConnectionGraph::mergeAllScheduledNodes() { assert(To->pointsTo != From); } else { // If 'To' has no pointsTo at all, initialize its defer web. - if (!To->pointsTo) { + if (!To->pointsTo) initializePointsToEdge(To, redirectPointsTo(From->pointsTo)); - } else { + else { // Upgrade 'To's pointsTo to an edge to preserve the fact that 'From' // had a pointsTo edge. To->pointsToIsEdge = true; @@ -864,15 +876,30 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { } while (Changed); } -CGNode *EscapeAnalysis::ConnectionGraph::createContentNode(CGNode *addrNode, - bool hasRC) { - CGNode *newContent = allocNode(nullptr, NodeType::Content, hasRC); +CGNode *EscapeAnalysis::ConnectionGraph::createContentNode( + CGNode *addrNode, bool isInterior, bool hasReferenceOnly) { + CGNode *newContent = + allocNode(nullptr, NodeType::Content, isInterior, hasReferenceOnly); initializePointsToEdge(addrNode, newContent); assert(ToMerge.empty() && "Initially setting pointsTo should not require any node merges"); return newContent; } +CGNode *EscapeAnalysis::ConnectionGraph::getOrCreateContentNode( + CGNode *addrNode, bool isInterior, bool hasReferenceOnly) { + if (CGNode *content = addrNode->getContentNodeOrNull()) { + content->mergeFlags(isInterior, hasReferenceOnly); + return content; + } + CGNode *content = createContentNode(addrNode, isInterior, hasReferenceOnly); + // getValueContent may be called after the graph is built and escape states + // are propagated. Keep the escape state and use points consistent here. + content->mergeEscapeState(addrNode->State); + content->mergeUsePoints(addrNode); + return content; +} + // Create a content node for merging based on an address node in the destination // graph and a content node in the source graph. CGNode * @@ -881,19 +908,103 @@ EscapeAnalysis::ConnectionGraph::createMergedContent(CGNode *destAddrNode, // destAddrNode may itself be a content node, so its value may be null. Since // we don't have the original pointer value, build a new content node based // on the source content. - return createContentNode(destAddrNode, srcContent->hasRC); + CGNode *mergedContent = createContentNode( + destAddrNode, srcContent->isInterior(), srcContent->hasReferenceOnly()); + return mergedContent; } -// Get a node representing the field data within the given reference-counted -// node. The caller has already determined that rcNode represents the head of a -// heap object rather than field content or the address of a local variable or -// argument. -CGNode *EscapeAnalysis::ConnectionGraph::getFieldContent(CGNode *rcNode) { - assert(rcNode->isContent()); - if (rcNode->pointsTo) - return rcNode->pointsTo; +CGNode * +EscapeAnalysis::ConnectionGraph::getOrCreateAddressContent(SILValue addrVal, + CGNode *addrNode) { + assert(addrVal->getType().isAddress()); + + bool contentHasReferenceOnly = + EA->hasReferenceOnly(addrVal->getType().getObjectType(), *F); + // Address content always has an indirect pointsTo (only reference content can + // have a non-indirect pointsTo). + return getOrCreateContentNode(addrNode, false, contentHasReferenceOnly); +} + +// refVal is allowed to be invalid so we can model escaping content for +// secondary deinitializers of released objects. +CGNode * +EscapeAnalysis::ConnectionGraph::getOrCreateReferenceContent(SILValue refVal, + CGNode *refNode) { + // The object node points to internal fields. It neither has indirect pointsTo + // nor reference-only pointsTo. + CGNode *objNode = getOrCreateContentNode(refNode, true, false); + if (!objNode->isInterior()) + return objNode; + + bool contentHasReferenceOnly = false; + if (refVal) { + SILType refType = refVal->getType(); + if (auto *C = refType.getClassOrBoundGenericClass()) { + PointerKind aggregateKind = NoPointer; + for (auto *field : C->getStoredProperties()) { + SILType fieldType = refType.getFieldType(field, F->getModule(), + F->getTypeExpansionContext()); + PointerKind fieldKind = EA->findCachedPointerKind(fieldType, *F); + if (fieldKind > aggregateKind) + aggregateKind = fieldKind; + } + contentHasReferenceOnly = canOnlyContainReferences(aggregateKind); + } + } + getOrCreateContentNode(objNode, false, contentHasReferenceOnly); + return objNode; +} + +CGNode * +EscapeAnalysis::ConnectionGraph::getOrCreateUnknownContent(CGNode *addrNode) { + // We don't know if addrVal has been cast from a reference or raw + // pointer. More importantly, we don't know what memory contents it may + // point to. There's no need to consider it an "interior" node initially. If + // it's ever merged with another interior node (from ref_element_addr), then + // it will conservatively take on the interior flag at that time. + return getOrCreateContentNode(addrNode, false, false); +} + +// If ptrVal is itself mapped to a node, then this must return a non-null +// contentnode. Otherwise, setEscapesGlobal won't be able to represent escaping +// memory. +// +// This may be called after the graph is built and all escape states and use +// points are propagate. If a new content node is created, update its state +// on-the-fly. +EscapeAnalysis::CGNode * +EscapeAnalysis::ConnectionGraph::getValueContent(SILValue ptrVal) { + // Look past address projections, pointer casts, and the like within the same + // object. Does not look past a dereference such as ref_element_addr, or + // project_box. + SILValue ptrBase = EA->getPointerRoot(ptrVal); + + PointerKind pointerKind = EA->getPointerKind(ptrBase); + if (pointerKind == EscapeAnalysis::NoPointer) + return nullptr; + + CGNode *addrNode = getOrCreateNode(ptrBase, pointerKind); + if (!addrNode) + return nullptr; + + if (ptrBase->getType().isAddress()) + return getOrCreateAddressContent(ptrBase, addrNode); + + if (canOnlyContainReferences(pointerKind)) + return getOrCreateReferenceContent(ptrBase, addrNode); - return createContentNode(rcNode, /*hasRC=*/false); + // The pointer value may contain raw pointers. + return getOrCreateUnknownContent(addrNode); +} + +CGNode *EscapeAnalysis::ConnectionGraph::getReturnNode() { + if (!ReturnNode) { + SILType resultTy = + F->mapTypeIntoContext(F->getConventions().getSILResultType()); + bool hasReferenceOnly = EA->hasReferenceOnly(resultTy, *F); + ReturnNode = allocNode(nullptr, NodeType::Return, false, hasReferenceOnly); + } + return ReturnNode; } bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, @@ -918,7 +1029,9 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, // global escaping state set. // Just set global escaping in the caller node and that's it. Changed |= DestNd->mergeEscapeState(EscapeState::Global); - continue; + // If DestNd is an interior node, its content still needs to be created. + if (!DestNd->isInterior()) + continue; } CGNode *SourcePT = SourceNd->pointsTo; @@ -1208,7 +1321,7 @@ std::string CGForDotView::getNodeAttributes(const Node *Node) const { switch (Orig->Type) { case EscapeAnalysis::NodeType::Content: attr = "style=\"rounded"; - if (Orig->hasRefCount()) { + if (Orig->isInterior()) { attr += ",filled"; } attr += "\""; @@ -1330,8 +1443,10 @@ void EscapeAnalysis::ConnectionGraph::dumpCG() const { void EscapeAnalysis::CGNode::dump() const { llvm::errs() << getTypeStr(); - if (hasRefCount()) - llvm::errs() << " [rc]"; + if (isInterior()) + llvm::errs() << " [int]"; + if (hasReferenceOnly()) + llvm::errs() << " [ref]"; auto rep = getRepValue(); if (rep.depth > 0) @@ -1391,16 +1506,6 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { }); }; - auto nodeStr = [&](CGNode *Nd) -> std::string { - std::string Str; - llvm::raw_string_ostream OS(Str); - if (Nd->hasRefCount()) - OS << "[rc] "; - Nd->getRepValue().print(OS, InstToIDMap); - OS.flush(); - return Str; - }; - llvm::SmallVector SortedNodes; for (CGNode *Nd : Nodes) { if (!Nd->isMerged) @@ -1409,7 +1514,13 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { sortNodes(SortedNodes); for (CGNode *Nd : SortedNodes) { - OS << " " << Nd->getTypeStr() << ' ' << nodeStr(Nd) << " Esc: "; + OS << " " << Nd->getTypeStr() << ' '; + if (Nd->isInterior()) + OS << "[int] "; + if (Nd->hasReferenceOnly()) + OS << "[ref] "; + Nd->getRepValue().print(OS, InstToIDMap); + OS << " Esc: "; switch (Nd->getEscapeState()) { case EscapeState::None: { const char *Separator = ""; @@ -1434,13 +1545,16 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { OS << ", Succ: "; const char *Separator = ""; if (CGNode *PT = Nd->getPointsToEdge()) { - OS << '(' << nodeStr(PT) << ')'; + OS << '('; + PT->getRepValue().print(OS, InstToIDMap); + OS << ')'; Separator = ", "; } llvm::SmallVector SortedDefers = Nd->defersTo; sortNodes(SortedDefers); for (CGNode *Def : SortedDefers) { - OS << Separator << nodeStr(Def); + OS << Separator; + Def->getRepValue().print(OS, InstToIDMap); Separator = ", "; } OS << '\n'; @@ -1476,10 +1590,11 @@ void EscapeAnalysis::ConnectionGraph::verify(bool allowMerge) const { // which consist of only defer-edges and a single trailing points-to edge // must lead to the same assert(Nd->matchPointToOfDefers(allowMerge)); - if (Nd->hasRefCount()) { - SILValue v = Nd->getRepValue().getValue(); - (void)v; - assert(!v || mayContainReference(v->getType(), *F)); + if (Nd->mappedValue && !(allowMerge && Nd->isMerged)) { + assert(Nd == Values2Nodes.lookup(Nd->mappedValue)); + assert(EA->isPointer(Nd->mappedValue)); + // Nodes must always be mapped from the pointer root value. + assert(Nd->mappedValue == EA->getPointerRoot(Nd->mappedValue)); } } #endif @@ -1488,9 +1603,6 @@ void EscapeAnalysis::ConnectionGraph::verify(bool allowMerge) const { void EscapeAnalysis::ConnectionGraph::verifyStructure(bool allowMerge) const { #ifndef NDEBUG for (CGNode *Nd : Nodes) { - if (Nd->mappedValue && !(allowMerge && Nd->mergeTo)) - assert(Nd == Values2Nodes.lookup(Nd->mappedValue)); - if (Nd->isMerged) { assert(Nd->mergeTo); assert(!Nd->pointsTo); @@ -1517,6 +1629,8 @@ void EscapeAnalysis::ConnectionGraph::verifyStructure(bool allowMerge) const { assert(PT->Type == NodeType::Content); assert(PT->findPred(Predecessor(Nd, EdgeType::PointsTo)) != PT->Preds.end()); } + if (Nd->isInterior()) + assert(Nd->pointsTo && "Interior content node requires a pointsTo node"); } #endif } @@ -1548,45 +1662,6 @@ static bool linkBBArgs(SILBasicBlock *BB) { return true; } -EscapeAnalysis::CGNode * -EscapeAnalysis::getValueContent(ConnectionGraph *conGraph, SILValue addrVal) { - CGNode *addrNode = conGraph->getNode(addrVal); - if (!addrNode) - return nullptr; - - if (CGNode *content = addrNode->getPointsToEdge()) - return content; - -#ifndef NDEBUG - if (!addrNode->isContent()) { - if (SILValue addrNodeValue = addrNode->getRepValue().getValue()) { - assert(isPointer(addrNodeValue)); - assert(addrNodeValue == getPointerRoot(addrVal)); - } - } -#endif - SILValue baseAddr = getPointerRoot(addrVal); - auto *F = addrVal->getFunction(); - auto hasRC = [&](){ - return mustContainReference(baseAddr->getType(), *F) - || mustContainReference(addrVal->getType(), *F); - }; - // Have we already merged a content node for this address? - if (CGNode *content = addrNode->getContentNodeOrNull()) { - // TODO: Optimistically merge hasRC content. The original content might not - // have an RC if one of the values pointing to this content was cast to an - // unknown type. If any of the types must contain a reference, then the - // content should contain a reference. - // - // For now, conservatively merge the RC flag instead. - if (content->hasRefCount() && !hasRC()) - content->setRefCount(false); - - return content; - } - return conGraph->createContentNode(addrNode, hasRC()); -} - void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth) { @@ -1632,7 +1707,7 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, if (!BBArg->getSingleTerminatorOperands(Incoming)) { // We don't know where the block argument comes from -> treat it // conservatively. - setEscapesGlobal(ConGraph, BBArg); + ConGraph->setEscapesGlobal(BBArg); continue; } CGNode *ArgNode = ConGraph->getNode(BBArg); @@ -1644,7 +1719,7 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, if (SrcArg) { ArgNode = ConGraph->defer(ArgNode, SrcArg); } else { - setEscapesGlobal(ConGraph, BBArg); + ConGraph->setEscapesGlobal(BBArg); break; } } @@ -1654,13 +1729,26 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, << FInfo->Graph.F->getName() << '\n'); } -/// Returns true if all uses of \p I are tuple_extract instructions. -static bool onlyUsedInTupleExtract(SILValue V) { +/// Returns the tuple extract for the first two fields if all uses of \p I are +/// tuple_extract instructions. +static std::pair +onlyUsedInTupleExtract(SILValue V) { + TupleExtractInst *field0 = nullptr; + TupleExtractInst *field1 = nullptr; for (Operand *Use : getNonDebugUses(V)) { - if (!isa(Use->getUser())) - return false; + if (auto *TEI = dyn_cast(Use->getUser())) { + if (TEI->getFieldNo() == 0) { + field0 = TEI; + continue; + } + if (TEI->getFieldNo() == 1) { + field1 = TEI; + continue; + } + } + return std::make_pair(nullptr, nullptr); } - return true; + return std::make_pair(field0, field1); } bool EscapeAnalysis::buildConnectionGraphForCallees( @@ -1719,6 +1807,40 @@ bool EscapeAnalysis::buildConnectionGraphForDestructor( RecursionDepth); } +// Handle array.uninitialized +bool EscapeAnalysis::createArrayUninitializedSubgraph( + FullApplySite apply, ConnectionGraph *conGraph) { + + // Check if the result is used in the usual way: extracting the + // array and the element pointer with tuple_extract. + TupleExtractInst *arrayStruct; + TupleExtractInst *arrayElementPtr; + std::tie(arrayStruct, arrayElementPtr) = + onlyUsedInTupleExtract(cast(apply.getInstruction())); + if (!arrayStruct || !arrayElementPtr) + return false; + + // array.uninitialized may have a first argument which is the + // allocated array buffer. The call is like a struct(buffer) + // instruction. + CGNode *arrayRefNode = conGraph->getNode(apply.getArgument(0)); + if (!arrayRefNode) + return false; + + CGNode *arrayStructNode = conGraph->getNode(arrayStruct); + assert(arrayStructNode && "Array struct must have a node"); + + CGNode *arrayObjNode = conGraph->getValueContent(apply.getArgument(0)); + + // The reference argument is effectively stored inside the returned + // array struct. + conGraph->defer(arrayStructNode, arrayRefNode); + + // Map the returned element pointer to the array object's field pointer. + conGraph->setNode(arrayElementPtr, arrayObjNode); + return true; +} + void EscapeAnalysis::analyzeInstruction(SILInstruction *I, FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, @@ -1739,74 +1861,71 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // These array semantics calls do not capture anything. return; case ArrayCallKind::kArrayUninitialized: - // Check if the result is used in the usual way: extracting the - // array and the element pointer with tuple_extract. - if (onlyUsedInTupleExtract(ASC.getCallResult())) { - // array.uninitialized may have a first argument which is the - // allocated array buffer. The call is like a struct(buffer) - // instruction. - if (CGNode *BufferNode = ConGraph->getNode(FAS.getArgument(0))) { - SILValue ArrayBase = ASC.getCallResult(); - CGNode *ArrayContent = getValueContent(ConGraph, ArrayBase); - assert(ArrayContent && "Array base must have a node"); - ConGraph->defer(ArrayContent, BufferNode); - } + if (createArrayUninitializedSubgraph(FAS, ConGraph)) return; - } break; case ArrayCallKind::kGetElement: - if (CGNode *ArrayRefNode = getValueContent(ConGraph, ASC.getSelf())) { + if (CGNode *ArrayObjNode = ConGraph->getValueContent(ASC.getSelf())) { CGNode *LoadedElement = nullptr; // This is like a load from a ref_element_addr. if (ASC.hasGetElementDirectResult()) { LoadedElement = ConGraph->getNode(ASC.getCallResult()); } else { // The content of the destination address. - LoadedElement = getValueContent(ConGraph, FAS.getArgument(0)); + LoadedElement = ConGraph->getValueContent(FAS.getArgument(0)); assert(LoadedElement && "indirect result must have node"); } if (LoadedElement) { - CGNode *ArrayElementStorage = - ConGraph->getFieldContent(ArrayRefNode); - ConGraph->defer(LoadedElement, ArrayElementStorage); - return; + if (CGNode *arrayElementStorage = + ConGraph->getFieldContent(ArrayObjNode)) { + ConGraph->defer(LoadedElement, arrayElementStorage); + return; + } } } break; case ArrayCallKind::kGetElementAddress: - // This is like a ref_element_addr. - if (CGNode *ArrayRefNode = getValueContent(ConGraph, ASC.getSelf())) { - ConGraph->defer(ConGraph->getNode(ASC.getCallResult()), ArrayRefNode); + // This is like a ref_element_addr. Both the object node and the + // returned address point to the same element storage. + if (CGNode *ArrayObjNode = ConGraph->getValueContent(ASC.getSelf())) { + CGNode *arrayElementAddress = ConGraph->getNode(ASC.getCallResult()); + ConGraph->defer(arrayElementAddress, ArrayObjNode); + return; } - return; + break; case ArrayCallKind::kWithUnsafeMutableBufferPointer: // Model this like an escape of the elements of the array and a capture // of anything captured by the closure. // Self is passed inout. - if (CGNode *ArrayStructValue = - getValueContent(ConGraph, ASC.getSelf())) { + if (CGNode *ArrayStructNode = + ConGraph->getValueContent(ASC.getSelf())) { + // The first non indirect result is the closure. + auto Args = FAS.getArgumentsWithoutIndirectResults(); + ConGraph->setEscapesGlobal(Args[0]); // One content node for going from the array buffer pointer to // the element address (like ref_element_addr). - CGNode *ArrayRefNode = ArrayStructValue->getContentNodeOrNull(); - // TODO: If ArrayRefNode already exists, optimistically do - // ArrayRefNode->setRefCount(true). - if (!ArrayRefNode) { - ArrayRefNode = ConGraph->createContentNode( - ArrayStructValue, /*hasRC=*/true); + CGNode *ArrayObjNode = + ConGraph->getOrCreateContentNode(ArrayStructNode, + /*isInterior*/ true, + /*hasRefOnly*/ false); + // If ArrayObjNode was already potentially merged with its pointsTo, + // then conservatively mark the whole thing as escaping. + if (!ArrayObjNode->isInterior()) { + ArrayObjNode->markEscaping(); + return; } - // Another content node for the element storage. - CGNode *ArrayElementStorage = ConGraph->getFieldContent(ArrayRefNode); + // Otherwise, create the content node for the element storage. + CGNode *ArrayElementStorage = ConGraph->getOrCreateContentNode( + ArrayObjNode, /*isInterior*/ false, + /*hasRefOnly*/ true); ArrayElementStorage->markEscaping(); - // The first non indirect result is the closure. - auto Args = FAS.getArgumentsWithoutIndirectResults(); - setEscapesGlobal(ConGraph, Args[0]); return; } break; default: break; - } + } if (FAS.getReferencedFunctionOrNull() && FAS.getReferencedFunctionOrNull()->hasSemanticsAttr( @@ -1819,7 +1938,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // from the pointer. auto Args = FAS.getArgumentsWithoutIndirectResults(); // The first not indirect result argument is the closure. - setEscapesGlobal(ConGraph, Args[0]); + ConGraph->setEscapesGlobal(Args[0]); return; } @@ -1834,7 +1953,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // from the pointer. auto Args = FAS.getArgumentsWithoutIndirectResults(); // The second not indirect result argument is the closure. - setEscapesGlobal(ConGraph, Args[1]); + ConGraph->setEscapesGlobal(Args[1]); return; } @@ -1916,86 +2035,117 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::StrongReleaseInst: case SILInstructionKind::ReleaseValueInst: { // A release instruction may deallocate the pointer operand. This may - // capture anything pointed to by the released object, but not the pointer - // to the object itself (because it will be a dangling pointer after - // deallocation). + // capture anything pointed to by the released object, but not the object + // itself (because it will be a dangling pointer after deallocation). SILValue OpV = I->getOperand(0); - CGNode *rcContent = getValueContent(ConGraph, OpV); - if (!rcContent) + CGNode *objNode = ConGraph->getValueContent(OpV); + if (!objNode) return; - // rcContent->hasRefCount() may or may not be true depending on whether - // the type could be analyzed. Either way, treat it structurally like a - // refcounted object. - CGNode *fieldContent = ConGraph->getFieldContent(rcContent); + CGNode *fieldNode = ConGraph->getFieldContent(objNode); + if (!fieldNode) { + // In the unexpected case that the object has no field content, create + // escaping unknown content. + ConGraph->getOrCreateUnknownContent(objNode)->markEscaping(); + return; + } if (!deinitIsKnownToNotCapture(OpV)) { - fieldContent->markEscaping(); + ConGraph->getOrCreateUnknownContent(fieldNode)->markEscaping(); + return; + } + // This deinit is known to not directly capture it's own field content; + // however, other secondary deinitializers could still capture anything + // pointed to by references within those fields. Since secondary + // deinitializers only apply to reference-type fields, not pointer-type + // fields, the "field" content can initially be considered an indirect + // reference. Unfortunately, we can't know all possible reference types + // that may eventually be associated with 'fieldContent', so we must + // assume here that 'fieldContent2' could hold raw pointers. This is + // implied by passing in invalid SILValue. + CGNode *objNode2 = + ConGraph->getOrCreateReferenceContent(SILValue(), fieldNode); + CGNode *fieldNode2 = objNode2->getContentNodeOrNull(); + ConGraph->getOrCreateUnknownContent(fieldNode2)->markEscaping(); + return; + } + case SILInstructionKind::DestroyAddrInst: { + SILValue addressVal = I->getOperand(0); + CGNode *valueNode = ConGraph->getValueContent(addressVal); + if (!valueNode) + return; + + // The value's destructor may escape anything the value points to. + // This could be an object referenced by the value or the contents of an + // existential box. + if (CGNode *fieldNode = ConGraph->getFieldContent(valueNode)) { + ConGraph->getOrCreateUnknownContent(fieldNode)->markEscaping(); return; } - // This deinit is known to not directly capture it's own field content, - // however, indirect deinitializers could still capture anything pointed - // to by those fields. - ConGraph->escapeContentsOf(fieldContent); + ConGraph->getOrCreateUnknownContent(valueNode)->markEscaping(); return; } #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Load##Name##Inst: #include "swift/AST/ReferenceStorage.def" - case SILInstructionKind::LoadInst: + case SILInstructionKind::LoadInst: { assert(!cast(I)->getType().isAddress()); - LLVM_FALLTHROUGH; - case SILInstructionKind::RefElementAddrInst: - case SILInstructionKind::RefTailAddrInst: - case SILInstructionKind::ProjectBoxInst: - case SILInstructionKind::InitExistentialAddrInst: - case SILInstructionKind::OpenExistentialAddrInst: { - // Loads and projections into RC objects have a similar pattern: - // - // For RC object projections, get the non-address reference operand and - // return an RC content node that the reference directly points to. It is - // as-if the RC content node holds the pointer to the object fields. - // // For loads, get the address-type operand and return the content node // that the address directly points to. The load's address may itself come // from a ref_element_addr, project_box or open_existential, in which - // case, the loaded content will be the field content, not the RC content. + // case, the loaded content will be the field content, not the RC + // content. auto SVI = cast(I); if (!isPointer(SVI)) return; - SILValue pointerVal = SVI->getOperand(0); - if (CGNode *PointsTo = getValueContent(ConGraph, pointerVal)) { + if (CGNode *PointsTo = ConGraph->getValueContent(SVI->getOperand(0))) { + ConGraph->setNode(SVI, PointsTo); + return; + } + // A load from an address we don't handle -> be conservative. + ConGraph->setEscapesGlobal(SVI); + break; + } + case SILInstructionKind::RefElementAddrInst: + case SILInstructionKind::RefTailAddrInst: + case SILInstructionKind::ProjectBoxInst: + case SILInstructionKind::InitExistentialAddrInst: + case SILInstructionKind::OpenExistentialAddrInst: { + // For projections into objects, get the non-address reference operand and + // return an interior content node that the reference points to. + auto SVI = cast(I); + if (CGNode *PointsTo = ConGraph->getValueContent(SVI->getOperand(0))) { ConGraph->setNode(SVI, PointsTo); return; } // A load or projection from an address we don't handle -> be // conservative. - setEscapesGlobal(ConGraph, SVI); + ConGraph->setEscapesGlobal(SVI); return; } case SILInstructionKind::CopyAddrInst: { // Be conservative if the dest may be the final release. if (!cast(I)->isInitializationOfDest()) { setAllEscaping(I, ConGraph); - break; + return; } // A copy_addr is like a 'store (load src) to dest'. SILValue srcAddr = I->getOperand(CopyAddrInst::Src); - CGNode *loadedContent = getValueContent(ConGraph, srcAddr); + CGNode *loadedContent = ConGraph->getValueContent(srcAddr); if (!loadedContent) { setAllEscaping(I, ConGraph); break; } SILValue destAddr = I->getOperand(CopyAddrInst::Dest); // Create a defer-edge from the store location to the loaded content. - if (CGNode *destContent = getValueContent(ConGraph, destAddr)) { + if (CGNode *destContent = ConGraph->getValueContent(destAddr)) { ConGraph->defer(destContent, loadedContent); return; } // A store to an address we don't handle -> be conservative. - setEscapesGlobal(ConGraph, srcAddr); + ConGraph->setEscapesGlobal(srcAddr); return; } @@ -2016,13 +2166,13 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // (via ref_element_addr, project_box, or open_existential_addr) where the // stored field content is chained one level below the RC content. SILValue destAddr = I->getOperand(StoreInst::Dest); - if (CGNode *pointsTo = getValueContent(ConGraph, destAddr)) { + if (CGNode *pointsTo = ConGraph->getValueContent(destAddr)) { // Create a defer-edge from the content to the stored value. ConGraph->defer(pointsTo, valueNode); return; } // A store to an address we don't handle -> be conservative. - setEscapesGlobal(ConGraph, srcVal); + ConGraph->setEscapesGlobal(srcVal); return; } case SILInstructionKind::PartialApplyInst: { @@ -2062,15 +2212,14 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::TupleExtractInst: { // This is a tuple_extract which extracts the second result of an // array.uninitialized call (otherwise getPointerBase should have already - // looked through it). The first result is the array itself. - // The second result (which is a pointer to the array elements) must be - // the content node of the first result. It's just like a ref_element_addr - // instruction. + // looked through it). The first result is the array itself. The second + // result (which is a pointer to the array elements) must be the content + // node of the first result. It's just like a ref_element_addr + // instruction. It is mapped to a node when processing + // array.uninitialized. auto *TEI = cast(I); assert(isExtractOfArrayUninitializedPointer(TEI) && "tuple_extract should be handled as projection"); - if (CGNode *ArrayElements = getValueContent(ConGraph, TEI->getOperand())) - ConGraph->setNode(TEI, ArrayElements); return; } case SILInstructionKind::UncheckedRefCastAddrInst: { @@ -2081,12 +2230,15 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, ConGraph->defer(DestNode, SrcNode); return; } - case SILInstructionKind::ReturnInst: - if (CGNode *ValueNd = - ConGraph->getNode(cast(I)->getOperand())) { + case SILInstructionKind::ReturnInst: { + SILValue returnVal = cast(I)->getOperand(); + if (CGNode *ValueNd = ConGraph->getNode(returnVal)) { ConGraph->defer(ConGraph->getReturnNode(), ValueNd); + ConGraph->getValueContent(returnVal)->mergeEscapeState( + EscapeState::Return); } return; + } default: // We handle all other instructions conservatively. setAllEscaping(I, ConGraph); @@ -2147,8 +2299,8 @@ bool EscapeAnalysis::deinitIsKnownToNotCapture(SILValue V) { void EscapeAnalysis::setAllEscaping(SILInstruction *I, ConnectionGraph *ConGraph) { if (auto *TAI = dyn_cast(I)) { - setEscapesGlobal(ConGraph, TAI->getNormalBB()->getArgument(0)); - setEscapesGlobal(ConGraph, TAI->getErrorBB()->getArgument(0)); + ConGraph->setEscapesGlobal(TAI->getNormalBB()->getArgument(0)); + ConGraph->setEscapesGlobal(TAI->getErrorBB()->getArgument(0)); } // Even if the instruction does not write memory we conservatively set all // operands to escaping, because they may "escape" to the result value in @@ -2157,12 +2309,12 @@ void EscapeAnalysis::setAllEscaping(SILInstruction *I, for (const Operand &Op : I->getAllOperands()) { SILValue OpVal = Op.get(); if (!isNonWritableMemoryAddress(OpVal)) - setEscapesGlobal(ConGraph, OpVal); + ConGraph->setEscapesGlobal(OpVal); } // Even if the instruction does not write memory it could e.g. return the // address of global memory. Therefore we have to define it as escaping. for (auto result : I->getResults()) - setEscapesGlobal(ConGraph, result); + ConGraph->setEscapesGlobal(result); } void EscapeAnalysis::recompute(FunctionInfo *Initial) { @@ -2483,35 +2635,6 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { return true; } -bool EscapeAnalysis::canParameterEscape(FullApplySite FAS, int ParamIdx, - bool checkContentOfIndirectParam) { - CalleeList Callees = BCA->getCalleeList(FAS); - if (!Callees.allCalleesVisible()) - return true; - - // Derive the connection graph of the apply from the known callees. - for (SILFunction *Callee : Callees) { - FunctionInfo *FInfo = getFunctionInfo(Callee); - if (!FInfo->isValid()) - recompute(FInfo); - - CGNode *Node = - FInfo->SummaryGraph.getNodeOrNull(Callee->getArgument(ParamIdx)); - if (!Node) - return true; - - if (checkContentOfIndirectParam) { - Node = Node->getContentNodeOrNull(); - if (!Node) - continue; - } - - if (Node->escapes()) - return true; - } - return false; -} - void EscapeAnalysis::invalidate() { Function2Info.clear(); Allocator.DestroyAll(); From 7fb4e21bc0ebdd5c05b37abf88b3d73084a4ade7 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 16 Dec 2019 16:10:57 -0800 Subject: [PATCH 129/478] EscapeAnalysis: Make EscapeState and UsePoints a property of the content node only. For alias analysis query to be generally correct, we need to effectively merge the escape state and use points for everything in a defer web. It was unclear from the current design whether the "escaping" property applied to the pointer value or its content. The implementation is inconsistent in how it was treated. It appears that some bugs have been worked around by propagating forward through defer edges, some have been worked around by querying the content instead of the pointer, and others have been worked around be creating fake use points at block arguments. If we always simply query the content for escape state and use points, then we never need to propagate along defer edges. The current code that propagates escape state along defer edges in one direction is simply incorrect from the perspective of alias analysis. One very attractive solution is to merge nodes eagerly without creating any defer edges, but that would be a much more radical change even than what I've done here. It would also pose some new issues: how to resolve the current "node types" when merging and how to deal with missing content nodes. This solution of applying escape state to content nodes solves all these problems without too radical of a change at the expense of eagerly creating content nodes. (The potential graph memory usage is not really an issue because it's possible to drastically shrink the size of the graph anyway in a future commit--I've been able to fit a node within one cache line). This solution nicely preserves graph structure which makes it easy to debug and relate to the IR. Eagerly creating content nodes also solves the missing content node problem. For example, when querying canEscapeTo, we need to know whether to look at the escape state for just the pointer value itself, or also for its content. It may be possible the its content node is actually part of the same object at the IR level. If the content node is missing, then we don't know if the object's interior address is not recognizable/representable or whether we simply never saw an access to the interior address. We can't simply look at whether the current IR value happens to be a reference, because that doesn't tell us whether the graph node may have been merged with a non-reference node or even with it's own content node. To be correct in general, this query would need to be extremely conservative. However, if content nodes are always created for references, then we only need to query the escape state of a pointer's content node. The content node's flag tells us if it's an interior node, in which case it will always point to another content node which also needs to be queried. --- .../SILOptimizer/Analysis/EscapeAnalysis.h | 39 ++++------- lib/SILOptimizer/Analysis/AliasAnalysis.cpp | 4 +- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 64 +++++++++---------- .../Transforms/StackPromotion.cpp | 20 ++---- .../UtilityPasses/EscapeAnalysisDumper.cpp | 44 +++++++++++++ 5 files changed, 93 insertions(+), 78 deletions(-) diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index 0c68b94f9984e..d6ee88c0a36a8 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -641,10 +641,10 @@ class EscapeAnalysis : public BottomUpIPAnalysis { CGNode *ReturnNode = nullptr; /// The list of use points. - llvm::SmallVector UsePointTable; + llvm::SmallVector UsePointTable; /// Mapping of use points to bit indices in CGNode::UsePoints. - llvm::DenseMap UsePoints; + llvm::DenseMap UsePoints; /// The allocator for nodes. llvm::SpecificBumpPtrAllocator NodeAllocator; @@ -798,19 +798,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { Node->mappedValue = V; } - /// Adds an argument/instruction in which the node's value is used. - int addUsePoint(CGNode *Node, SILNode *User) { - if (Node->getEscapeState() >= EscapeState::Global) - return -1; - - User = User->getRepresentativeSILNodeInObject(); - int Idx = (int)UsePoints.size(); - assert(UsePoints.count(User) == 0 && "value is already a use-point"); - UsePoints[User] = Idx; - UsePointTable.push_back(User); - assert(UsePoints.size() == UsePointTable.size()); - Node->setUsePointBit(Idx); - return Idx; /// If \p pointer is a pointer, set it's content to global escaping. /// /// Only mark the content node as escaping. Marking a pointer node as @@ -828,13 +815,8 @@ class EscapeAnalysis : public BottomUpIPAnalysis { content->markEscaping(); } - void escapeContentsOf(CGNode *Node) { - CGNode *escapedContent = Node->getContentNodeOrNull(); - if (!escapedContent) { - escapedContent = createContentNode(Node, /*hasRC=*/false); - } - escapedContent->markEscaping(); - } + /// Adds an argument/instruction in which the node's value is used. + int addUsePoint(CGNode *Node, SILInstruction *User); /// Creates a defer-edge between \p From and \p To. /// This may trigger node merges to keep the graph invariance 4). @@ -920,11 +902,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// (indirectly) somehow refer to the Node's value. /// Use-points are only values which are relevant for lifeness computation, /// e.g. release or apply instructions. - bool isUsePoint(SILNode *UsePoint, CGNode *Node); + bool isUsePoint(SILInstruction *UsePoint, CGNode *Node); /// Returns all use points of \p Node in \p UsePoints. void getUsePoints(CGNode *Node, - llvm::SmallVectorImpl &UsePoints); + llvm::SmallVectorImpl &UsePoints); /// Computes the use point information. void computeUsePoints(); @@ -1108,10 +1090,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis { bool mergeSummaryGraph(ConnectionGraph *SummaryGraph, ConnectionGraph *Graph); - /// Returns true if the value \p V can escape to the \p UsePoint, where - /// \p UsePoint is either a release-instruction or a function call. - bool canEscapeToUsePoint(SILValue V, SILNode *UsePoint, - ConnectionGraph *ConGraph); + /// Returns true if the value \p value or any address within that value can + /// escape to the \p usePoint, where \p usePoint is either a + /// release-instruction or a function call. + bool canEscapeToUsePoint(SILValue value, SILInstruction *usePoint, + ConnectionGraph *conGraph); friend struct ::CGForDotView; diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index 90e304d0a5084..a6ab4c4ea36a5 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -737,8 +737,8 @@ bool AliasAnalysis::mayValueReleaseInterfereWithInstruction(SILInstruction *User // The most important check: does the object escape the current function? auto LO = getUnderlyingObject(V); auto *ConGraph = EA->getConnectionGraph(User->getFunction()); - auto *Node = ConGraph->getNodeOrNull(LO); - if (Node && !Node->escapes()) + auto *Content = ConGraph->getValueContent(LO); + if (Content && !Content->escapes()) return false; // This is either a non-local allocation or a scoped allocation that escapes. diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 363c1bc1eb195..54d72876e5501 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -804,10 +804,14 @@ void EscapeAnalysis::ConnectionGraph::propagateEscapeStates() { Changed = false; for (CGNode *Node : Nodes) { - // Propagate the state to all successor nodes. + // Propagate the state to all pointsTo nodes. It would be sufficient to + // only follow proper pointsTo edges, since this loop also follows defer + // edges, but this may converge faster. if (Node->pointsTo) { Changed |= Node->pointsTo->mergeEscapeState(Node->State); } + // Note: Propagating along defer edges may be interesting from an SSA + // standpoint, but it is entirely irrelevant alias analysis. for (CGNode *Def : Node->defersTo) { Changed |= Def->mergeEscapeState(Node->State); } @@ -845,14 +849,13 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { /// liferange. And that must be a releasing instruction. int ValueIdx = -1; for (const Operand &Op : I.getAllOperands()) { - ValueBase *OpV = Op.get(); - if (CGNode *OpNd = lookupNode(EA->getPointerRoot(OpV))) { - if (ValueIdx < 0) { - ValueIdx = addUsePoint(OpNd, &I); - } else { - OpNd->setUsePointBit(ValueIdx); - } - } + CGNode *content = getValueContent(Op.get()); + if (!content) + continue; + if (ValueIdx < 0) + ValueIdx = addUsePoint(content, &I); + else + content->setUsePointBit(ValueIdx); } break; } @@ -867,11 +870,10 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { do { Changed = false; for (CGNode *Node : Nodes) { - // Propagate the bits to all successor nodes. - Node->visitSuccessors([&Changed, Node](CGNode *succ) { - Changed |= succ->mergeUsePoints(Node); - return true; - }); + // Propagate the bits to pointsTo. A release of a node may also release + // any content pointed to be the node. + if (Node->pointsTo) + Changed |= Node->pointsTo->mergeUsePoints(Node); } } while (Changed); } @@ -1097,11 +1099,10 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, /// somehow refer to the Node's value. /// Use-points are only values which are relevant for lifeness computation, /// e.g. release or apply instructions. -bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILNode *UsePoint, +bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILInstruction *UsePoint, CGNode *Node) { assert(Node->getEscapeState() < EscapeState::Global && "Use points are only valid for non-escaping nodes"); - UsePoint = UsePoint->getRepresentativeSILNodeInObject(); auto Iter = UsePoints.find(UsePoint); if (Iter == UsePoints.end()) return false; @@ -1111,8 +1112,8 @@ bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILNode *UsePoint, return Node->UsePoints.test(Idx); } -void EscapeAnalysis::ConnectionGraph:: -getUsePoints(CGNode *Node, llvm::SmallVectorImpl &UsePoints) { +void EscapeAnalysis::ConnectionGraph::getUsePoints( + CGNode *Node, llvm::SmallVectorImpl &UsePoints) { assert(Node->getEscapeState() < EscapeState::Global && "Use points are only valid for non-escaping nodes"); for (int Idx = Node->UsePoints.find_first(); Idx >= 0; @@ -2571,13 +2572,13 @@ bool EscapeAnalysis::canEscapeToValue(SILValue V, SILValue To) { return true; auto *ConGraph = getConnectionGraph(F); - CGNode *Node = ConGraph->getNodeOrNull(V); - if (!Node) + CGNode *valueContent = ConGraph->getValueContent(V); + if (!valueContent) return true; - CGNode *ToNode = ConGraph->getNodeOrNull(To); - if (!ToNode) + CGNode *userContent = ConGraph->getValueContent(To); + if (!userContent) return true; - return ConGraph->mayReach(ToNode, Node); + return ConGraph->mayReach(userContent, valueContent); } bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { @@ -2592,27 +2593,26 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { return true; auto *ConGraph = getConnectionGraph(F); - CGNode *Node1 = ConGraph->getNodeOrNull(V1); - if (!Node1) + CGNode *Content1 = ConGraph->getValueContent(V1); + if (!Content1) return true; - CGNode *Node2 = ConGraph->getNodeOrNull(V2); - if (!Node2) + + CGNode *Content2 = ConGraph->getValueContent(V2); + if (!Content2) return true; // Finish the check for one value being a non-escaping local object. - if (isUniq1 && Node1->valueEscapesInsideFunction(V1)) + if (isUniq1 && Content1->valueEscapesInsideFunction(V1)) isUniq1 = false; - if (isUniq2 && Node2->valueEscapesInsideFunction(V2)) + if (isUniq2 && Content2->valueEscapesInsideFunction(V2)) isUniq2 = false; if (!isUniq1 && !isUniq2) return true; // Check if both nodes may point to the same content. - CGNode *Content1 = getValueContent(ConGraph, V1); - CGNode *Content2 = getValueContent(ConGraph, V2); - + // FIXME!!!: This will be rewritten to use node flags in the next commit. SILType T1 = V1->getType(); SILType T2 = V2->getType(); if (T1.isAddress() && T2.isAddress()) { diff --git a/lib/SILOptimizer/Transforms/StackPromotion.cpp b/lib/SILOptimizer/Transforms/StackPromotion.cpp index 9c769f5197da9..a7579edc39094 100644 --- a/lib/SILOptimizer/Transforms/StackPromotion.cpp +++ b/lib/SILOptimizer/Transforms/StackPromotion.cpp @@ -108,32 +108,20 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, return false; auto *ConGraph = EA->getConnectionGraph(ARI->getFunction()); - auto *Node = ConGraph->getNodeOrNull(ARI); - if (!Node) + auto *contentNode = ConGraph->getValueContent(ARI); + if (!contentNode) return false; // The most important check: does the object escape the current function? - if (Node->escapes()) + if (contentNode->escapes()) return false; LLVM_DEBUG(llvm::dbgs() << "Promote " << *ARI); // Collect all use-points of the allocation. These are refcount instructions // and apply instructions. - llvm::SmallVector BaseUsePoints; llvm::SmallVector UsePoints; - ConGraph->getUsePoints(Node, BaseUsePoints); - for (SILNode *UsePoint : BaseUsePoints) { - if (SILInstruction *I = dyn_cast(UsePoint)) { - UsePoints.push_back(I); - } else { - // Also block arguments can be use points. - SILBasicBlock *UseBB = cast(UsePoint)->getParent(); - // For simplicity we just add the first instruction of the block as use - // point. - UsePoints.push_back(&UseBB->front()); - } - } + ConGraph->getUsePoints(contentNode, UsePoints); ValueLifetimeAnalysis VLA(ARI, UsePoints); // Check if there is a use point before the allocation (this can happen e.g. diff --git a/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp b/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp index 3d1cf9331b697..da470ec37d705 100644 --- a/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp @@ -25,6 +25,23 @@ llvm::cl::opt EnableGraphWriter( namespace { +static bool gatherValues(EscapeAnalysis *EA, SILFunction &Fn, + std::vector &Values) { + for (auto &BB : Fn) { + for (auto *Arg : BB.getArguments()) { + if (EA->isPointer(Arg)) + Values.push_back(SILValue(Arg)); + } + for (auto &II : BB) { + for (auto result : II.getResults()) { + if (EA->isPointer(result)) + Values.push_back(result); + } + } + } + return Values.size() > 1; +} + /// Dumps the escape information of all functions in the module. /// Only dumps if the compiler is built with assertions. /// For details see EscapeAnalysis. @@ -43,6 +60,33 @@ class EscapeAnalysisDumper : public SILModuleTransform { ConnectionGraph->print(llvm::outs()); if (EnableGraphWriter) ConnectionGraph->dumpCG(); + + // Gather up all Values in Fn. + std::vector Values; + if (!gatherValues(EA, F, Values)) + continue; + + // Emit the N^2 escape analysis evaluation of the values. + for (auto &bb : F) { + for (auto &ii : bb) { + if (auto fas = FullApplySite::isa(&ii)) { + for (unsigned i = 0, e = Values.size(); i != e; ++i) { + SILValue val = Values[i]; + bool escape = EA->canEscapeTo(val, fas); + llvm::outs() << (escape ? "May" : "No") << "Escape: " << val + << " to " << ii; + } + } + if (RefCountingInst *rci = dyn_cast(&ii)) { + for (unsigned i = 0, e = Values.size(); i != e; ++i) { + SILValue val = Values[i]; + bool escape = EA->canEscapeTo(val, rci); + llvm::outs() << (escape ? "May" : "No") << "Escape: " << val + << " to " << ii; + } + } + } + } } } #endif From 17ab0ad5b5b627b889ae77180fde213f34c69617 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 16 Dec 2019 16:12:17 -0800 Subject: [PATCH 130/478] EscapeAnalysis: Do not create defer edges for block arguemnts. That appears to have been a partial workaround for the real problem that usepoints need to be propagated across the entire defer web. This is now solved by considering use points on the reference node's content, not the reference node itself. --- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 54d72876e5501..58499f9a61ad5 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -826,15 +826,6 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { #endif // First scan the whole function and add relevant instructions as use-points. for (auto &BB : *F) { - for (SILArgument *BBArg : BB.getArguments()) { - /// In addition to releasing instructions (see below) we also add block - /// arguments as use points. In case of loops, block arguments can - /// "extend" the liferange of a reference in upward direction. - if (CGNode *ArgNode = lookupNode(BBArg)) { - addUsePoint(ArgNode, BBArg); - } - } - for (auto &I : BB) { switch (I.getKind()) { #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ From 3782d67cda8789b3ffc9cbb48baaf11b87b53358 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 16 Dec 2019 16:12:58 -0800 Subject: [PATCH 131/478] EscapeAnalysis: rewrite canEscapeToUsePoint. Correctness: do not make any unenforced assumptions about how the connection graph is built (I don't think the previous assumption about the structure of the graph node mapped to a reference-type value would always hold if content nodes can be arbitrarily merged). Only make one assumption about the client code: the access being checked must be to some address within the provided value, not another object indirectly reachable from that value. Optimization: Allow escape analysis to prove that an addressable object does not escape even when one of its reference-type fields escapes. --- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 66 +++++---- .../escape_analysis_dead_store.sil | 138 ++++++++++++++++++ 2 files changed, 174 insertions(+), 30 deletions(-) create mode 100644 test/SILOptimizer/escape_analysis_dead_store.sil diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 58499f9a61ad5..bfa437bf439a9 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -2479,44 +2479,50 @@ bool EscapeAnalysis::mergeSummaryGraph(ConnectionGraph *SummaryGraph, return SummaryGraph->mergeFrom(Graph, Mapping); } -bool EscapeAnalysis::canEscapeToUsePoint(SILValue V, SILNode *UsePoint, - ConnectionGraph *ConGraph) { +// Return true if any content within the logical object pointed to by \p value +// escapes. +// +// Get the value's content node and check the escaping flag on all nodes within +// that object. An interior CG node points to content within the same object. +bool EscapeAnalysis::canEscapeToUsePoint(SILValue value, + SILInstruction *usePoint, + ConnectionGraph *conGraph) { - assert((FullApplySite::isa(UsePoint) || isa(UsePoint)) && - "use points are only created for calls and refcount instructions"); + assert((FullApplySite::isa(usePoint) || isa(usePoint)) + && "use points are only created for calls and refcount instructions"); - CGNode *Node = ConGraph->getNodeOrNull(V); - if (!Node) + CGNode *node = conGraph->getValueContent(value); + if (!node) return true; - // First check if there are escape paths which we don't explicitly see - // in the graph. - if (Node->valueEscapesInsideFunction(V)) - return true; + // Follow points-to edges and return true if the current 'node' may escape at + // 'usePoint'. + CGNodeWorklist worklist(conGraph); + while (node) { + // Merging arbitrary nodes is supported, which may lead to cycles of + // interior nodes. End the search. + if (!worklist.tryPush(node)) + break; - // No hidden escapes: check if the Node is reachable from the UsePoint. - // Check if the object itself can escape to the called function. - if (ConGraph->isUsePoint(UsePoint, Node)) - return true; + // First check if 'node' may escape in a way not represented by the + // connection graph, assuming that it may represent part of the object + // pointed to by 'value'. If 'node' happens to represent another object + // indirectly reachabe from 'value', then it cannot actually escape to this + // usePoint, so passing the original value is still conservatively correct. + if (node->valueEscapesInsideFunction(value)) + return true; - assert(isPointer(V) && "should not have a node for a non-pointer"); - - // Check if the object "content" can escape to the called function. - // This will catch cases where V is a reference and a pointer to a stored - // property escapes. - // It's also important in case of a pointer assignment, e.g. - // V = V1 - // apply(V1) - // In this case the apply is only a use-point for V1 and V1's content node. - // As V1's content node is the same as V's content node, we also make the - // check for the content node. - CGNode *ContentNode = getValueContent(ConGraph, V); - if (ContentNode->valueEscapesInsideFunction(V)) - return true; + // No hidden escapes; check if 'usePoint' may access memory at 'node'. + if (conGraph->isUsePoint(usePoint, node)) + return true; - if (ConGraph->isUsePoint(UsePoint, ContentNode)) - return true; + if (!node->isInterior()) + break; + // Continue to check for escaping content whenever 'content' may point to + // the same object as 'node'. + node = node->getContentNodeOrNull(); + } return false; } diff --git a/test/SILOptimizer/escape_analysis_dead_store.sil b/test/SILOptimizer/escape_analysis_dead_store.sil new file mode 100644 index 0000000000000..9a649975407cb --- /dev/null +++ b/test/SILOptimizer/escape_analysis_dead_store.sil @@ -0,0 +1,138 @@ +// RUN: %target-sil-opt %s -dead-store-elim -enable-sil-verify-all | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +final class X { + init() +} + +public struct S { + @_hasStorage var x: X { get set } + @_hasStorage var i: Int { get set } + init(x: X, i: Int) +} + +@_hasStorage @_hasInitialValue var gg: X { get set } + +@inline(never) func takex(_ x: X) + +sil [noinline] @takeX : $@convention(thin) (@guaranteed X) -> () + +// Test that escape analysis does not consider an inout argument to +// escape at a call site even though it's reference-type field does +// escape. Dead store elimination asks MemoryBehaviorVisitor whether +// the apply may read from the inout argument. This call into +// canEscapeToUsePoint, which should return false because the inout +// structure itself is not exposed to the call, only it's +// reference-type field is. +// +// CHECK-LABEL: sil @testInoutNoEscape +// CHECK-NOT: store +// CHECK: apply +// CHECK: store +// CHECK-NOT: store +// CHECK: } // end sil function 'testInoutNoEscape' +sil @testInoutNoEscape : $@convention(thin) (@inout S, @guaranteed S) -> () { +bb0(%0 : $*S, %1 : $S): + %4 = struct_extract %1 : $S, #S.x + %5 = struct_element_addr %0 : $*S, #S.x + %6 = load %5 : $*X + strong_retain %4 : $X + strong_release %6 : $X + store %1 to %0 : $*S + %10 = function_ref @takeX : $@convention(thin) (@guaranteed X) -> () + strong_retain %4 : $X + %12 = apply %10(%4) : $@convention(thin) (@guaranteed X) -> () + release_value %1 : $S + store %1 to %0 : $*S + %15 = tuple () + return %15 : $() +} + +// ============================================================================= +// Test that a store writing back into a container is not eliminated +// when the container's interior pointer later escapes into a function +// that reads from the pointer. + +final internal class TestArrayContainer { + @_hasStorage @_hasInitialValue internal final var pointer: UnsafeMutablePointer { get set } + @_hasStorage @_hasInitialValue internal final var storage: ContiguousArray { get set } + @_optimize(none) @inline(never) internal final func append(_ arg: Int32) + internal final func va_list() -> UnsafeMutableRawPointer + init() +} + +sil @UnsafeMutablePointer_load_Int64 : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> // user: %5 +// ContiguousArray.append(_:) +sil @$ss15ContiguousArrayV6appendyyxnF : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () + + +// Helper that reads from a raw pointer. +sil hidden [noinline] @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool { +bb0(%0 : $UnsafeMutableRawPointer): + %1 = integer_literal $Builtin.Int64, 0 + %2 = struct $Int64 (%1 : $Builtin.Int64) + %3 = function_ref @UnsafeMutablePointer_load_Int64 : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> + %4 = apply %3(%2, %0) : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> // users: %18, %6 + %5 = integer_literal $Builtin.Int1, -1 + %6 = struct $Bool (%5 : $Builtin.Int1) + return %6 : $Bool +} + +// TestArrayContainer.append(_:) +// Helper that produces a nonempty array. +sil hidden [noinline] [Onone] @TestArrayContainer_append : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () { +bb0(%0 : $Int32, %1 : $TestArrayContainer): + %2 = alloc_stack $Int32 + store %0 to %2 : $*Int32 + %4 = ref_element_addr %1 : $TestArrayContainer, #TestArrayContainer.storage + %5 = function_ref @$ss15ContiguousArrayV6appendyyxnF : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () + %6 = apply %5(%2, %4) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () + dealloc_stack %2 : $*Int32 + %8 = tuple () + return %8 : $() +} + +// CHECK-LABEL: sil [noinline] @testContainerPointer : $@convention(thin) () -> Bool { +// CHECK: [[ALLOC:%.*]] = alloc_ref [stack] $TestArrayContainer +// CHECK: [[PTR:%.*]] = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.pointer +// CHECK: [[LOAD:%.*]] = load %{{.*}} : $*__ContiguousArrayStorageBase +// CHECK: [[ELTS:%.*]] = ref_tail_addr [[LOAD]] : $__ContiguousArrayStorageBase, $Int32 +// CHECK: [[ELTPTR:%.*]] = address_to_pointer [[ELTS]] : $*Int32 to $Builtin.RawPointer +// CHECK: [[UMP:%.*]] = struct $UnsafeMutablePointer ([[ELTPTR]] : $Builtin.RawPointer) +// CHECK: store [[UMP]] to [[PTR]] : $*UnsafeMutablePointer +// CHECK: [[F:%.*]] = function_ref @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool +// CHECK: apply [[F]](%{{.*}}) : $@convention(thin) (UnsafeMutableRawPointer) -> Bool +// CHECK: fix_lifetime %0 : $TestArrayContainer +// CHECK-LABEL: } // end sil function 'testContainerPointer' +sil [noinline] @testContainerPointer : $@convention(thin) () -> Bool { +bb0: + %0 = alloc_ref [stack] $TestArrayContainer + %1 = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.pointer + %2 = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.storage + %3 = integer_literal $Builtin.Int32, 42 + %4 = struct $Int32 (%3 : $Builtin.Int32) + %5 = function_ref @TestArrayContainer_append : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () + %6 = apply %5(%4, %0) : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () + %7 = struct_element_addr %2 : $*ContiguousArray, #ContiguousArray._buffer + %8 = struct_element_addr %7 : $*_ContiguousArrayBuffer, #_ContiguousArrayBuffer._storage + %9 = load %8 : $*__ContiguousArrayStorageBase + %10 = ref_tail_addr %9 : $__ContiguousArrayStorageBase, $Int32 + %11 = address_to_pointer %10 : $*Int32 to $Builtin.RawPointer + %12 = struct $UnsafeMutablePointer (%11 : $Builtin.RawPointer) + store %12 to %1 : $*UnsafeMutablePointer + %14 = address_to_pointer %1 : $*UnsafeMutablePointer to $Builtin.RawPointer + %15 = struct $UnsafeMutableRawPointer (%14 : $Builtin.RawPointer) + %16 = function_ref @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool + %17 = apply %16(%15) : $@convention(thin) (UnsafeMutableRawPointer) -> Bool + fix_lifetime %0 : $TestArrayContainer + set_deallocating %0 : $TestArrayContainer + strong_release %9 : $__ContiguousArrayStorageBase + dealloc_ref %0 : $TestArrayContainer + dealloc_ref [stack] %0 : $TestArrayContainer + return %17 : $Bool +} From 6dfbafb1ec7022e3a88d0ae2aad12f3786345114 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 16 Dec 2019 16:15:03 -0800 Subject: [PATCH 132/478] EscapeAnalysis: Add and update tests. Fix tests for changes to EscapeAnalysis output - New node flags - Only content nodes are marked escaping - Content nodes are eagerly created - No defer edges for block args --- test/SILOptimizer/escape_analysis.sil | 746 +++++++++++------- .../sil_combine_concrete_existential.swift | 2 +- 2 files changed, 455 insertions(+), 293 deletions(-) diff --git a/test/SILOptimizer/escape_analysis.sil b/test/SILOptimizer/escape_analysis.sil index 25085c9609fbc..1b248fbf25642 100644 --- a/test/SILOptimizer/escape_analysis.sil +++ b/test/SILOptimizer/escape_analysis.sil @@ -77,8 +77,8 @@ struct FourFields { sil @take_indirect_tuple : $@convention(method) (@in (Int, ())) -> () // CHECK-LABEL: CG of handle_undef -// CHECK: Val %2 Esc: G, Succ: (%2.1) -// CHECK: Con %2.1 Esc: G, Succ: +// CHECK: Val %2 Esc: , Succ: (%2.1) +// CHECK: Con [ref] %2.1 Esc: G, Succ: // CHECK: End sil @handle_undef : $@convention(thin) (Int) -> () { bb0(%0 : $Int): @@ -96,11 +96,12 @@ bb0(%0 : $Int): // CHECK-LABEL: CG of test_simple // CHECK-NEXT: Arg %0 Esc: A, Succ: (%5) -// CHECK-NEXT: Arg %1 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: %1 -// CHECK-NEXT: Con %5 Esc: A, Succ: %2 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Con %3.1 Esc: A, Succ: %1 +// CHECK-NEXT: Con [ref] %5 Esc: A, Succ: %2 // CHECK-NEXT: End sil @test_simple : $@convention(thin) (@inout Y, @owned X) -> () { bb0(%0 : $*Y, %1 : $X): @@ -117,12 +118,12 @@ bb0(%0 : $*Y, %1 : $X): // Test if a deferring edge is created for a block argument. // CHECK-LABEL: CG of deferringEdge -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %4) -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: %3, Succ: ([rc] %4), %0 -// CHECK-NEXT: Con [rc] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%4) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%4), %0 +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) // CHECK-NEXT: Con %4.1 Esc: A, Succ: %1 -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @deferringEdge : $@convention(thin) (@owned LinkedNode, @owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode, %1 : $LinkedNode): @@ -137,8 +138,10 @@ bb1(%3 : $LinkedNode): // Test a local object just escaping via return. // CHECK-LABEL: CG of escapes_via_return -// CHECK-NEXT: Val %0 Esc: R, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: R, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: R, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @escapes_via_return : $@convention(thin) () -> @owned X { bb0: @@ -149,14 +152,14 @@ bb0: // A linear chain of assignments is collapsed to a single node. // CHECK-LABEL: CG of test_linked_list -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Val %1 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%13) -// CHECK-NEXT: Val %4 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Val %7 Esc: %11, Succ: ([rc] %2) -// CHECK-NEXT: Val %11 Esc: %11, Succ: ([rc] %2), %7, %13 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Val [ref] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%13) +// CHECK-NEXT: Val [ref] %4 Esc: A, Succ: (%2) +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%2) +// CHECK-NEXT: Val [ref] %11 Esc: , Succ: (%2), %7, %13 // CHECK-NEXT: Con %13 Esc: A, Succ: %0, %1, %4 -// CHECK-NEXT: Ret return Esc: R, Succ: %13 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %13 // CHECK-NEXT: End sil @test_linked_list : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -184,14 +187,14 @@ bb2: // The same example as above but distributed over two functions. // CHECK-LABEL: CG of create_chain -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %8) -// CHECK-NEXT: Val %1 Esc: A, Succ: ([rc] %8) -// CHECK-NEXT: Val %4 Esc: A, Succ: ([rc] %8) -// CHECK-NEXT: Val %7 Esc: %11, Succ: ([rc] %8) -// CHECK-NEXT: Con [rc] %8 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%8) +// CHECK-NEXT: Val [ref] %1 Esc: A, Succ: (%8) +// CHECK-NEXT: Val [ref] %4 Esc: A, Succ: (%8) +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%8) +// CHECK-NEXT: Con [int] %8 Esc: A, Succ: (%8.1) // CHECK-NEXT: Con %8.1 Esc: A, Succ: %0, %1, %4 -// CHECK-NEXT: Val %11 Esc: R, Succ: ([rc] %8), %8.1 -// CHECK-NEXT: Ret return Esc: R, Succ: %11 +// CHECK-NEXT: Val [ref] %11 Esc: , Succ: (%8), %8.1 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %11 // CHECK-NEXT: End sil @create_chain : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -211,11 +214,11 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of loadNext -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Val %2 Esc: %2, Succ: ([rc] %3), %0, %4 -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %4 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%3) +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%3), %0, %4 +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con %4 Esc: A, Succ: (%3) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @loadNext : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -233,15 +236,17 @@ bb2: // Content nodes in the callee are duplicated in the caller. // CHECK-LABEL: CG of call_load_next3 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: ([rc] %0.3) -// CHECK-NEXT: Con [rc] %0.3 Esc: A, Succ: (%0.4) -// CHECK-NEXT: Con %0.4 Esc: A, Succ: ([rc] %0.5) -// CHECK-NEXT: Con [rc] %0.5 Esc: A, Succ: (%0.6) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con [int] %0.3 Esc: A, Succ: (%0.4) +// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%0.5) +// CHECK-NEXT: Con [int] %0.5 Esc: A, Succ: (%0.6) // CHECK-NEXT: Con %0.6 Esc: A, Succ: -// CHECK-NEXT: Val %2 Esc: R, Succ: %0.6 -// CHECK-NEXT: Ret return Esc: R, Succ: %2 +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1), %0.6 +// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: (%2.2) +// CHECK-NEXT: Con %2.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %2 // CHECK-NEXT: End sil @call_load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -251,14 +256,16 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of load_next3 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %4 Esc: A, Succ: ([rc] %5) -// CHECK-NEXT: Con [rc] %5 Esc: A, Succ: (%6) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con %4 Esc: A, Succ: (%5) +// CHECK-NEXT: Con [int] %5 Esc: A, Succ: (%6) // CHECK-NEXT: Con %6 Esc: A, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %6 +// CHECK-NEXT: Con [int] %6.1 Esc: A, Succ: (%6.2) +// CHECK-NEXT: Con %6.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -277,10 +284,12 @@ sil_global @global_x : $X // The argument escapes because it is stored to a global variable in the callee. // CHECK-LABEL: CG of call_store_pointer -// CHECK-NEXT: Arg %0 Esc: G, Succ: ([rc] %5) -// CHECK-NEXT: Con [rc] %5 Esc: G, Succ: (%6) +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: (%5) +// CHECK-NEXT: Con [int] %5 Esc: G, Succ: (%6) // CHECK-NEXT: Con %6 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %6 +// CHECK-NEXT: Con [int] %6.1 Esc: G, Succ: (%6.2) +// CHECK-NEXT: Con [ref] %6.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @call_store_pointer : $@convention(thin) (@owned Pointer) -> @owned X { bb0(%0 : $Pointer): @@ -294,9 +303,9 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of store_pointer -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @store_pointer : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -310,10 +319,10 @@ bb0(%0 : $Pointer): // global variable in the callee. // CHECK-LABEL: CG of store_content -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %4 -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%3) +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %4 +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) // CHECK-NEXT: Con %4 Esc: G, Succ: // CHECK-NEXT: End sil @store_content : $@convention(thin) (@owned Pointer) -> () { @@ -328,10 +337,12 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of call_store_content -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %4) -// CHECK-NEXT: Con [rc] %4 Esc: A, Succ: (%5) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%5) // CHECK-NEXT: Con %5 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %5 +// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2) +// CHECK-NEXT: Con [ref] %5.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @call_store_content : $@convention(thin) (@owned Pointer) -> @owned X { bb0(%0 : $Pointer): @@ -345,9 +356,9 @@ bb0(%0 : $Pointer): // CHECK-LABEL: CG of copy_addr_content // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1 +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.1 // CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %1.1 Esc: A, Succ: // CHECK-NEXT: End sil @copy_addr_content : $@convention(thin) (@in_guaranteed Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -358,9 +369,9 @@ bb0(%0: $*Int32, %1: $*Int32): // CHECK-LABEL: CG of copy_addr_take_content // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1 +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.1 // CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %1.1 Esc: A, Succ: // CHECK-NEXT: End sil @copy_addr_take_content : $@convention(thin) (@in Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -370,10 +381,10 @@ bb0(%0: $*Int32, %1: $*Int32): } // CHECK-LABEL: CG of copy_addr_noinit_content -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: +// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: // CHECK-NEXT: End sil @copy_addr_noinit_content : $@convention(thin) (@in Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -383,10 +394,10 @@ bb0(%0: $*Int32, %1: $*Int32): } // CHECK-LABEL: CG of call_copy_addr_content -// CHECK-NEXT: Val %0 Esc: %3, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: %3, Succ: %1.1 -// CHECK-NEXT: Val %1 Esc: %3, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: %3, Succ: +// CHECK-NEXT: Val %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: %3, Succ: %1.1 +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: %3, Succ: // CHECK-NEXT: End sil @call_copy_addr_content : $@convention(thin) () -> () { %0 = alloc_stack $Int32 @@ -404,14 +415,17 @@ sil @call_copy_addr_content : $@convention(thin) () -> () { // of Y's box _could_ capture Y.x in Y's deinit. // CHECK-LABEL: CG of test_partial_apply -// CHECK-NEXT: Arg %1 Esc: G, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: %14,%15,%17, Succ: ([rc] %7) -// CHECK-NEXT: Val %6 Esc: %14,%15,%16, Succ: ([rc] %7) -// CHECK-NEXT: Con [rc] %7 Esc: %14,%15,%16,%17, Succ: (%7.1) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %1.2 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: +// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%1.1), %1 +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%7) +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: (%7) +// CHECK-NEXT: Con [int] %7 Esc: %14,%15,%16,%17, Succ: (%7.1) // CHECK-NEXT: Con %7.1 Esc: %14,%15,%16,%17, Succ: %2 -// CHECK-NEXT: Val %12 Esc: %14,%15, Succ: %3, %6 +// CHECK-NEXT: Val [ref] %12 Esc: , Succ: %3, %6 // CHECK-NEXT: End sil @test_partial_apply : $@convention(thin) (Int64, @owned X, @owned Y) -> Int64 { bb0(%0 : $Int64, %1 : $X, %2 : $Y): @@ -434,16 +448,21 @@ bb0(%0 : $Int64, %1 : $X, %2 : $Y): } // CHECK-LABEL: CG of closure1 -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Arg %2 Esc: A, Succ: ([rc] %4) -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%3) +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%3.1) // CHECK-NEXT: Con %3.1 Esc: A, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: G, Succ: -// CHECK-NEXT: Con [rc] %4 Esc: A, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: A, Succ: ([rc] %4.2) -// CHECK-NEXT: Con [rc] %4.2 Esc: G, Succ: -// CHECK-NEXT: Val %7 Esc: %8, Succ: %2 +// CHECK-NEXT: Con [int] %3.2 Esc: A, Succ: (%3.3) +// CHECK-NEXT: Con %3.3 Esc: A, Succ: (%3.4) +// CHECK-NEXT: Con %3.4 Esc: G, Succ: +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con %4.1 Esc: A, Succ: (%4.2) +// CHECK-NEXT: Con [int] %4.2 Esc: A, Succ: +// CHECK-NEXT: Con %4.3 Esc: A, Succ: (%0.1), %0 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: %2 // CHECK-NEXT: End sil @closure1 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } , @owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } , %2 : $<τ_0_0> { var τ_0_0 } ): @@ -458,12 +477,13 @@ bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } , %2 : $<τ_0_0> { var τ_0_0 } // CHECK-LABEL: CG of closure2 -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: A, Succ: ([rc] %4) -// CHECK-NEXT: Con [rc] %4 Esc: G, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con %0.1 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con %4.1 Esc: A, Succ: %0 // CHECK-NEXT: End sil @closure2 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } ) -> () { bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } ): @@ -479,11 +499,13 @@ bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } ): // Test partial_apply. The box escapes in the callee. // CHECK-LABEL: CG of test_escaped_box -// CHECK-NEXT: Val %1 Esc: G, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: G, Succ: (%2.1) +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: G, Succ: (%2.1) // CHECK-NEXT: Con %2.1 Esc: G, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: G, Succ: -// CHECK-NEXT: Val %6 Esc: G, Succ: %1 +// CHECK-NEXT: Con [int] %2.2 Esc: G, Succ: +// CHECK-NEXT: Con %2.3 Esc: G, Succ: +// CHECK-NEXT: Con %2.4 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1 // CHECK-NEXT: End sil @test_escaped_box : $@convention(thin) (Int64) -> Int64 { bb0(%0 : $Int64): @@ -502,10 +524,12 @@ bb0(%0 : $Int64): } // CHECK-LABEL: CG of let_box_escape -// CHECK-NEXT: Arg %0 Esc: G, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: G, Succ: (%1.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: G, Succ: (%1.1) // CHECK-NEXT: Con %1.1 Esc: G, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Con [int] %1.2 Esc: G, Succ: (%1.3) +// CHECK-NEXT: Con %1.3 Esc: G, Succ: (%1.4) +// CHECK-NEXT: Con %1.4 Esc: G, Succ: // CHECK-NEXT: End sil @let_box_escape : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $<τ_0_0> { var τ_0_0 } ): @@ -524,9 +548,10 @@ sil @takebox : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> ( // The partial_apply itself escapes and therefore also the box escapes. // CHECK-LABEL: CG of test_escaped_partial_apply -// CHECK-NEXT: Val %1 Esc: G, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: G, Succ: -// CHECK-NEXT: Val %6 Esc: G, Succ: %1 +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: G, Succ: +// CHECK-NEXT: Con %2.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1 // CHECK-NEXT: End sil @test_escaped_partial_apply : $@convention(thin) (Int64) -> () { bb0(%0 : $Int64): @@ -544,10 +569,12 @@ bb0(%0 : $Int64): } // CHECK-LABEL: CG of closure3 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%1.1) // CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Con [int] %1.2 Esc: A, Succ: +// CHECK-NEXT: Con %1.3 Esc: A, Succ: (%1.4) +// CHECK-NEXT: Con %1.4 Esc: G, Succ: // CHECK-NEXT: End sil @closure3 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $<τ_0_0> { var τ_0_0 } ): @@ -564,11 +591,13 @@ sil @take_partial_apply : $@convention(thin) (@owned @callee_owned () -> Int64) sil_global @global_ln : $LinkedNode // CHECK-LABEL: CG of load_next_recursive -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) // CHECK-NEXT: Con %2 Esc: G, Succ: -// CHECK-NEXT: Val %4 Esc: G, Succ: %2 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1), %2 +// CHECK-NEXT: Con [int] %4.1 Esc: G, Succ: (%4.2) +// CHECK-NEXT: Con %4.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @load_next_recursive : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -580,12 +609,13 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of let_escape -// CHECK-NEXT: Arg %0 Esc: G, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0 -// CHECK-NEXT: Val %4 Esc: G, Succ: %0 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %0 +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%0.1), %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @let_escape : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -597,12 +627,12 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of return_same -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %3.1) -// CHECK-NEXT: Val %3 Esc: G, Succ: ([rc] %3.1), %3.2 -// CHECK-NEXT: Con [rc] %3.1 Esc: G, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: G, Succ: ([rc] %3.1) -// CHECK-NEXT: Val %5 Esc: R, Succ: %0, %3 -// CHECK-NEXT: Ret return Esc: R, Succ: %5 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%5.1) +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%5.1), %5.2 +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%5.1), %0, %3 +// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2) +// CHECK-NEXT: Con %5.2 Esc: G, Succ: (%5.1) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @return_same : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -620,15 +650,15 @@ bb2(%5 : $LinkedNode): // Another recursion test. // CHECK-LABEL: CG of loadNext2 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: A, Succ: ([rc] %2.1) -// CHECK-NEXT: Con [rc] %2.1 Esc: A, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: A, Succ: ([rc] %2.3) -// CHECK-NEXT: Con [rc] %2.3 Esc: A, Succ: (%2.4) -// CHECK-NEXT: Con %2.4 Esc: A, Succ: ([rc] %2.3) -// CHECK-NEXT: Val %4 Esc: R, Succ: %2.2, %2.4 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: (%2.2) +// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1), %2.2, %4.2 +// CHECK-NEXT: Con [int] %4.1 Esc: A, Succ: (%4.2) +// CHECK-NEXT: Con %4.2 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @loadNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -640,14 +670,14 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of returnNext2 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %5) -// CHECK-NEXT: Val %3 Esc: R, Succ: ([rc] %3.1), %3.2 -// CHECK-NEXT: Con [rc] %3.1 Esc: A, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: A, Succ: ([rc] %3.1) -// CHECK-NEXT: Con [rc] %5 Esc: A, Succ: (%6) -// CHECK-NEXT: Con %6 Esc: A, Succ: ([rc] %3.1) -// CHECK-NEXT: Val %8 Esc: R, Succ: %3, %6 -// CHECK-NEXT: Ret return Esc: R, Succ: %8 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%5) +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%8.1), %8.2 +// CHECK-NEXT: Con [int] %5 Esc: A, Succ: (%6) +// CHECK-NEXT: Con %6 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Val [ref] %8 Esc: , Succ: (%8.1), %3, %6 +// CHECK-NEXT: Con [int] %8.1 Esc: A, Succ: (%8.2) +// CHECK-NEXT: Con %8.2 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %8 // CHECK-NEXT: End sil @returnNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -670,12 +700,12 @@ bb3(%8 : $LinkedNode): // A single-cycle recursion test. // CHECK-LABEL: CG of single_cycle_recursion -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Val %5 Esc: R, Succ: ([rc] %2), %3 -// CHECK-NEXT: Val %7 Esc: R, Succ: %0, %5 -// CHECK-NEXT: Ret return Esc: R, Succ: %7 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: A, Succ: (%2) +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%2), %3 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%2), %0, %5 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %7 // CHECK-NEXT: End sil @single_cycle_recursion : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -695,9 +725,11 @@ bb2(%5 : $LinkedNode): // Test if a try_apply is represented correctly in the connection graph. // CHECK-LABEL: CG of call_throwing_func -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: R, Succ: %0 -// CHECK-NEXT: Ret return Esc: R, Succ: %3 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%0.1), %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @call_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -712,10 +744,12 @@ bb2(%5 : $Error): } // CHECK-LABEL: CG of throwing_func -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -735,11 +769,13 @@ bb2: // Test if a try_apply to an unknown function is handled correctly. // CHECK-LABEL: CG of call_unknown_throwing_func -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %3 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [int] %3.1 Esc: G, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @call_unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -758,12 +794,14 @@ sil @unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error // Test that the deinit of a box itself does not capture anything. // CHECK-LABEL: CG of test_release_of_partial_apply_with_box -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: %6, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: %6, Succ: (%2.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con %0.3 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: %6, Succ: (%2.1) // CHECK-NEXT: Con %2.1 Esc: %6, Succ: %0 -// CHECK-NEXT: Val %5 Esc: %6, Succ: %1 +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: %1 // CHECK-NEXT: End sil @test_release_of_partial_apply_with_box : $@convention(thin) (@owned Y) -> () { bb0(%0 : $Y): @@ -782,9 +820,9 @@ sil @take_y_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () // Test is an unknown value is merged correctly into the caller graph. // CHECK-LABEL: CG of store_to_unknown_reference -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: ([rc] %3) -// CHECK-NEXT: Con [rc] %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: G, Succ: (%3.1) // CHECK-NEXT: Con %3.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @store_to_unknown_reference : $@convention(thin) (@owned X) -> () { @@ -798,9 +836,10 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of get_reference -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %1 +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: G, Succ: + // CHECK-NEXT: Ret [ref] return Esc: , Succ: %1 // CHECK-NEXT: End sil @get_reference : $@convention(thin) () -> @owned Y { bb0: @@ -816,9 +855,11 @@ sil @unknown_get_reference : $@convention(thin) () -> @owned Y sil @unknown_set_y : $@convention(thin) () -> @out Y // CHECK-LABEL: CG of get_y -// CHECK-NEXT: Val %0 Esc: G, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %3 +// CHECK-NEXT: Val %0 Esc: , Succ: (%3) +// CHECK-NEXT: Con [ref] %3 Esc: G, Succ: +// CHECK-NEXT: Con [int] %3.1 Esc: G, Succ: (%3.2) +// CHECK-NEXT: Con %3.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @get_y : $@convention(thin) () -> @owned Y { bb0: @@ -831,9 +872,9 @@ bb0: } // CHECK-LABEL: CG of create_and_store_x -// CHECK-NEXT: Val %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: ([rc] %3) -// CHECK-NEXT: Con [rc] %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Val [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: G, Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: G, Succ: (%3.1) // CHECK-NEXT: Con %3.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @create_and_store_x : $@convention(thin) () -> () { @@ -850,11 +891,13 @@ bb0: // Test types which are considered as pointers. // CHECK-LABEL: CG of pointer_types -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: R, Succ: %0, %1 -// CHECK-NEXT: Val %7 Esc: R, Succ: %4 -// CHECK-NEXT: Ret return Esc: R, Succ: %7 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %0, %1 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%7.1), %4 +// CHECK-NEXT: Con [int] %7.1 Esc: A, Succ: (%7.2) +// CHECK-NEXT: Con %7.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %7 // CHECK-NEXT: End sil @pointer_types : $@convention(thin) (@owned Y, @owned Y) -> @owned Y { bb0(%0 : $Y, %1 : $Y): @@ -873,9 +916,9 @@ bb1(%7 : $(Pointer, Pointer)): // CHECK-LABEL: CG of defer_edge_cycle // CHECK-NEXT: Arg %0 Esc: A, Succ: (%2) // CHECK-NEXT: Arg %1 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %2 Esc: A, Succ: ([rc] %6), %4 -// CHECK-NEXT: Con %4 Esc: A, Succ: %2 -// CHECK-NEXT: Con [rc] %6 Esc: A, Succ: (%7) +// CHECK-NEXT: Con [ref] %2 Esc: A, Succ: (%6), %4 +// CHECK-NEXT: Con [ref] %4 Esc: A, Succ: %2 +// CHECK-NEXT: Con [int] %6 Esc: A, Succ: (%7) // CHECK-NEXT: Con %7 Esc: A, Succ: // CHECK-NEXT: End sil @defer_edge_cycle : $@convention(thin) (@inout Y, @inout Y) -> () { @@ -891,9 +934,10 @@ entry(%0 : $*Y, %1 : $*Y): } // CHECK-LABEL: CG of take_c_func -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @take_c_func : $@convention(thin) (@convention(c) () -> ()) -> @convention(c) () -> () { bb0(%0 : $@convention(c) () -> ()): @@ -910,8 +954,9 @@ bb0: } // CHECK-LABEL: CG of pass_c_func -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: +// CHECK-NEXT: Con %2.2 Esc: G, Succ: // CHECK-NEXT: End sil @pass_c_func : $@convention(thin) () -> () { bb0: @@ -924,12 +969,14 @@ bb0: // CHECK-LABEL: CG of test_select_enum -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: -// CHECK-NEXT: Arg %3 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: R, Succ: %1, %2, %3 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: +// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %1, %2, %3 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @test_select_enum : $@convention(thin) (PointerEnum2, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $PointerEnum2, %1 : $X, %2 : $X, %3 : $X): @@ -939,11 +986,13 @@ bb0(%0 : $PointerEnum2, %1 : $X, %2 : $X, %3 : $X): // CHECK-LABEL: CG of test_select_enum_addr // CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: -// CHECK-NEXT: Arg %3 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: R, Succ: %1, %2, %3 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %1, %2, %3 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @test_select_enum_addr : $@convention(thin) (@in PointerEnum2, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $*PointerEnum2, %1 : $X, %2 : $X, %3 : $X): @@ -952,11 +1001,13 @@ bb0(%0 : $*PointerEnum2, %1 : $X, %2 : $X, %3 : $X): } // CHECK-LABEL: CG of test_select_value -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: -// CHECK-NEXT: Arg %3 Esc: A, Succ: -// CHECK-NEXT: Val %6 Esc: R, Succ: %1, %2, %3 -// CHECK-NEXT: Ret return Esc: R, Succ: %6 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: +// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1, %2, %3 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @test_select_value : $@convention(thin) (Builtin.Int64, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X): @@ -967,10 +1018,10 @@ bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X): } // CHECK-LABEL: CG of test_existential_addr -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: Val %1 Esc: , Succ: (%2) // CHECK-NEXT: Con %2 Esc: , Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: , Succ: %0 +// CHECK-NEXT: Con [ref] %2.1 Esc: , Succ: %0 // CHECK-NEXT: End sil @test_existential_addr : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -985,7 +1036,7 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of test_existential_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @test_existential_ref : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1000,9 +1051,9 @@ bb0(%0 : $X): // Check that we don't crash on this. // CHECK-LABEL: CG of test_unknown_store -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [ref] %2.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @test_unknown_store : $@convention(thin) (@owned ErrorClass) -> () { bb0(%0 : $ErrorClass): @@ -1014,8 +1065,10 @@ bb0(%0 : $ErrorClass): } // CHECK-LABEL: CG of test_raw_pointer_to_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @test_raw_pointer_to_ref : $@convention(thin) (@owned X) -> @owned X { bb0(%0 : $X): @@ -1025,8 +1078,10 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of test_bridge_object_to_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @test_bridge_object_to_ref : $@convention(thin) (@owned X, Builtin.Word) -> @owned X { bb0(%0 : $X, %1 : $Builtin.Word): @@ -1037,8 +1092,8 @@ bb0(%0 : $X, %1 : $Builtin.Word): // CHECK-LABEL: CG of test_address_to_pointer // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1 -// CHECK-NEXT: Arg %1 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: // CHECK-NEXT: End sil @test_address_to_pointer : $@convention(thin) (@owned X) -> @out X { bb0(%0 : $*X, %1 : $X): @@ -1050,8 +1105,10 @@ bb0(%0 : $*X, %1 : $X): } // CHECK-LABEL: CG of test_casts -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @test_casts : $@convention(thin) (@owned AnyObject) -> @owned X { bb0(%0 : $AnyObject): @@ -1074,10 +1131,10 @@ bb0(%0 : $*U, %1 : $*T, %2 : $@thick U.Type): sil_global @global_y : $SomeData // CHECK-LABEL: CG of test_node_merge_during_struct_inst -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%8) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%8) // CHECK-NEXT: Val %1 Esc: G, Succ: (%8) // CHECK-NEXT: Val %4 Esc: , Succ: (%8) -// CHECK-NEXT: Con %8 Esc: G, Succ: (%8), %1 +// CHECK-NEXT: Con [ref] %8 Esc: G, Succ: (%8), %1 // CHECK-NEXT: Val %10 Esc: , Succ: %0, %4, %8 // CHECK-NEXT: End sil @test_node_merge_during_struct_inst : $@convention(thin) (Y) -> () { @@ -1104,7 +1161,9 @@ bb0(%0 : $Y): } // CHECK-LABEL: CG of arraysemantics_is_native_no_typecheck -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_is_native_no_typecheck : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1116,7 +1175,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_check_subscript -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_check_subscript : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1133,7 +1194,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_check_index -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_check_index : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1149,9 +1212,9 @@ bb0(%0 : $Array): // CHECK-LABEL: CG of arraysemantics_get_element // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.2 -// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %1.1) -// CHECK-NEXT: Con [rc] %1.1 Esc: A, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.2 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: A, Succ: (%1.2) // CHECK-NEXT: Con %1.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_element : $@convention(thin) (Array) -> @out X { @@ -1170,6 +1233,7 @@ bb0(%io : $*X, %1 : $Array): // CHECK-LABEL: CG of arraysemantics_make_mutable // CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_make_mutable : $@convention(thin) (@inout Array) -> () { bb0(%0 : $*Array): @@ -1181,9 +1245,10 @@ bb0(%0 : $*Array): } // CHECK-LABEL: CG of arraysemantics_get_element_address -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: , Succ: [rc] %0.1 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: +// CHECK-NEXT: Con %0.2 Esc: A, Succ: +// CHECK-NEXT: Val %4 Esc: , Succ: %0.1 // CHECK-NEXT: End sil @arraysemantics_get_element_address : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1198,7 +1263,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_get_count -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_count : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1211,7 +1278,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_get_capacity -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_capacity : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1225,12 +1294,14 @@ bb0(%0 : $Array): // CHECK-LABEL: CG of arraysemantics_withUnsafeMutableBufferPointer // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: ([rc] %0.2) -// CHECK-NEXT: Con [rc] %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: %4, Succ: +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [int] %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con [ref] %0.3 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: %4, Succ: // CHECK-NEXT: End sil @arraysemantics_withUnsafeMutableBufferPointer : $@convention(thin) (@inout Array, @owned @callee_owned (@inout X) -> (@out (), @error Error)) -> () { bb(%0 : $*Array, %1 : $@callee_owned (@inout X) -> (@out (), @error Error)): @@ -1245,12 +1316,12 @@ bb(%0 : $*Array, %1 : $@callee_owned (@inout X) -> (@out (), @error Error)): } // CHECK-LABEL: CG of arraysemantics_createUninitialized -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %2 Esc: R, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: R, Succ: %0 -// CHECK-NEXT: Val %4 Esc: R, Succ: ([rc] %6) -// CHECK-NEXT: Con [rc] %6 Esc: R, Succ: %2 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%6) +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: %2 +// CHECK-NEXT: Con [int] %6 Esc: R, Succ: (%6.1) +// CHECK-NEXT: Con %6.1 Esc: R, Succ: %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @arraysemantics_createUninitialized : $@convention(thin) (@owned X) -> @owned Array { bb0(%0 : $X): @@ -1284,11 +1355,17 @@ sil [_semantics "array.uninitialized"] @createUninitialized : $@convention(metho sil @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject // CHECK-LABEL: CG of semantics_pair_no_escaping_closure -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: -// CHECK-NEXT: Val %4 Esc: %5, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Con [int] %1.1 Esc: A, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %1.2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: (%2.2) +// CHECK-NEXT: Con %2.2 Esc: G, Succ: +// CHECK-NEXT: Val %4 Esc: , Succ: (%4.1) +// CHECK-NEXT: Con [ref] %4.1 Esc: %5, Succ: // CHECK-NEXT: End sil @semantics_pair_no_escaping_closure : $@convention(thin) (@owned X, @guaranteed X, @owned @callee_owned (X, X) -> (@out X, @error Error)) -> () { bb(%0 : $X, %1 : $X, %2: $@callee_owned (X, X) -> (@out X, @error Error)): @@ -1301,10 +1378,14 @@ bb(%0 : $X, %1 : $X, %2: $@callee_owned (X, X) -> (@out X, @error Error)): } // CHECK-LABEL: CG of semantics_self_no_escaping_closure -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: %4, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: +// CHECK-NEXT: Con [ref] %3.1 Esc: %4, Succ: // CHECK-NEXT: End sil @semantics_self_no_escaping_closure : $@convention(thin) (@guaranteed X, @owned @callee_owned (X, X) -> (@out X, @error Error)) -> () { bb(%0 : $X, %1: $@callee_owned (X, X) -> (@out X, @error Error)): @@ -1317,7 +1398,7 @@ bb(%0 : $X, %1: $@callee_owned (X, X) -> (@out X, @error Error)): } // CHECK-LABEL: CG of check_dealloc_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @check_dealloc_ref : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1327,7 +1408,7 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of check_set_deallocating -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @check_set_deallocating : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1340,7 +1421,9 @@ bb0(%0 : $X): // happen due to strong_release and figure out that the object will not // globally escape. // CHECK-LABEL: CG of check_non_escaping_implicit_destructor_invocation_via_strong_release -// CHECK-NEXT: Val %0 Esc: %1, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: %1, Succ: // CHECK-NEXT: End sil @check_non_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () { bb0: @@ -1355,7 +1438,9 @@ bb0: // happen due to strong_release and figure out that the object will not // globally escape. // CHECK-LABEL: CG of check_non_escaping_implicit_destructor_invocation_via_release_value -// CHECK-NEXT: Val %0 Esc: %1, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: %1, Succ: // CHECK-NEXT: End sil @check_non_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () { bb0: @@ -1370,8 +1455,8 @@ bb0: // happen due to strong_release and figure out that the object will // globally escape. // CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_strong_release -// CHECK-NEXT: Val %0 Esc: %1, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) // CHECK-NEXT: Con %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @check_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () { @@ -1387,8 +1472,8 @@ bb0: // happen due to strong_release and figure out that the object will // globally escape. // CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_release_value -// CHECK-NEXT: Val %0 Esc: %1, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) // CHECK-NEXT: Con %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @check_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () { @@ -1406,9 +1491,11 @@ bb0: // invoked by strong_release it would also determine that these objects // do not escape from the function. // CHECK-LABEL: CG of check_is_local_object_through_bb_args -// CHECK-LABEL: Val %2 Esc: %6,%7, Succ: -// CHECK-LABEL: Val %4 Esc: %6,%7, Succ: -// CHECK-LABEL: Val %6 Esc: %6,%7, Succ: %2, %4 +// CHECK-LABEL: Val [ref] %2 Esc: , Succ: (%2.1) +// CHECK-LABEL: Con [int] %2.1 Esc: %7, Succ: (%2.2) +// CHECK-LABEL: Con [ref] %2.2 Esc: %7, Succ: +// CHECK-LABEL: Val [ref] %4 Esc: , Succ: (%2.1) +// CHECK-LABEL: Val [ref] %6 Esc: , Succ: %2, %4 // CHECK-LABEL: End sil @check_is_local_object_through_bb_args: $@convention(thin) (Builtin.Int1) -> () { bb0(%0 : $Builtin.Int1): @@ -1429,7 +1516,10 @@ bb3(%6: $X): } // CHECK-LABEL: CG of check_look_through_thin_to_thick -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: %2, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: %2, Succ: // CHECK-NEXT: End sil @check_look_through_thin_to_thick: $(@convention(thin) () -> ()) -> () { bb0(%0 : $@convention(thin) () -> ()): @@ -1441,7 +1531,7 @@ bb0(%0 : $@convention(thin) () -> ()): // X.deinit // CHECK-LABEL: CG of $s4main1XCfD -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @$s4main1XCfD: $@convention(method) (@owned X) -> () { bb0(%0 : $X): @@ -1452,11 +1542,11 @@ bb0(%0 : $X): // Z.deinit // CHECK-LABEL: CG of $s4main1ZCfD -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) // CHECK-NEXT: Con %2 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: %2 +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: %2 // CHECK-NEXT: End sil @$s4main1ZCfD: $@convention(method) (@owned Z) -> () { bb0(%0 : $Z): @@ -1485,8 +1575,9 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of call_public_external_func -// CHECK-NEXT: Val %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @call_public_external_func : $@convention(thin) () -> () { bb0: @@ -1515,12 +1606,13 @@ bb2: // begin_apply result) is just marked as escaping. // CHECK-LABEL: CG of call_coroutine -// CHECK-NEXT: Val %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) // CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: %4 -// CHECK-NEXT: Val %4 Esc: G, Succ: +// CHECK-NEXT: Con %0.3 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [ref] %2.1 Esc: G, Succ: %4 +// CHECK-NEXT: Val [ref] %4 Esc: G, Succ: // CHECK-NEXT: End sil @call_coroutine : $@convention(thin) () -> () { bb0: @@ -1537,14 +1629,14 @@ bb0: // Test the absence of redundant pointsTo edges // CHECK-LABEL: CG of testInitializePointsToLeaf -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: ([rc] %13) -// CHECK-NEXT: Val %2 Esc: %4, Succ: %0.2 -// CHECK-NEXT: Val %4 Esc: %4, Succ: %2 -// CHECK-NEXT: Val %7 Esc: %12, Succ: ([rc] %13), %0.2 -// CHECK-NEXT: Val %12 Esc: %12, Succ: ([rc] %13), %7 -// CHECK-NEXT: Con [rc] %13 Esc: A, Succ: (%14) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%13) +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%13), %0.2 +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %2 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%13), %0.2 +// CHECK-NEXT: Val [ref] %12 Esc: , Succ: (%13), %7 +// CHECK-NEXT: Con [int] %13 Esc: A, Succ: (%14) // CHECK-NEXT: Con %14 Esc: A, Succ: // CHECK-LABEL: End class C { @@ -1593,14 +1685,14 @@ bb10(%arg2 : $LinkedNode): // a defer edge from a node with uninitialized pointsTo to a node with // already-initialized pointsTo. // CHECK-LABEL: CG of testInitializePointsToRedundant -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) // CHECK-NEXT: Con %3 Esc: A, Succ: -// CHECK-NEXT: Val %7 Esc: %7,%18, Succ: %0 -// CHECK-NEXT: Val %12 Esc: %12,%14,%18, Succ: %1 -// CHECK-NEXT: Val %14 Esc: %18, Succ: ([rc] %2), %1, %12 -// CHECK-NEXT: Val %18 Esc: %18, Succ: %7, %14 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: %0 +// CHECK-NEXT: Val [ref] %12 Esc: , Succ: %1 +// CHECK-NEXT: Val [ref] %14 Esc: , Succ: (%2), %1, %12 +// CHECK-NEXT: Val [ref] %18 Esc: , Succ: %7, %14 // CHECK-LABEL: End sil @testInitializePointsToMerge : $@convention(method) (@guaranteed C, @guaranteed C) -> C { bb0(%0: $C, %1 : $C): @@ -1655,3 +1747,73 @@ bb10(%arg3 : $C): %66 = tuple () return %66 : $() } + +// Test canEscapeToUsePoint with defer edges between non-reference-type nodes. +// +// Unfortunately, the only way I can think of to create defer edges +// between non-reference nodes is with address-type block arguments. +// This will be banned soon, so the test will need to be disabled, but +// this is still an important corner case in the EscapeAnalysis +// logic. To keep the test working, and be able to test many more +// important corner cases, we should introduce testing modes that +// introduce spurious but predictable defer edges and node merges. +// +// Here, canEscapeTo is called on %bbarg (%5) whose node is not +// marked escaping. But it does have a defer edge to the local address +// (%0) which does cause the address to escape via the call site. +// +// CHECK-LABEL: CG of testDeferPointer +// CHECK-NEXT: Val %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%0.1) +// CHECK-NEXT: Val %5 Esc: , Succ: %0, %1 +// CHECK-LABEL: End +// CHECK: MayEscape: %5 = argument of bb3 : $*Builtin.Int32 +// CHECK-NEXT: to %{{.*}} = apply %{{.*}}(%0) : $@convention(thin) (@inout Builtin.Int32) -> () +sil @takeAdr : $@convention(thin) (@inout Builtin.Int32) -> () + +sil hidden @testDeferPointer : $@convention(thin) () -> () { +bb0: + %iadr1 = alloc_stack $Builtin.Int32 + %iadr2 = alloc_stack $Builtin.Int32 + cond_br undef, bb1, bb2 + +bb1: + br bb3(%iadr1: $*Builtin.Int32) + +bb2: + br bb3(%iadr2: $*Builtin.Int32) + +bb3(%bbarg: $*Builtin.Int32): + %val = integer_literal $Builtin.Int32, 1 + store %val to %bbarg : $*Builtin.Int32 + %ftake = function_ref @takeAdr : $@convention(thin) (@inout Builtin.Int32) -> () + %call = apply %ftake(%iadr1) : $@convention(thin) (@inout Builtin.Int32) -> () + dealloc_stack %iadr2 : $*Builtin.Int32 + dealloc_stack %iadr1 : $*Builtin.Int32 + %z = tuple () + return %z : $() +} + +// Test interior node (self) cycles. + +class IntWrapper { + var property: Int64 +} + +// CHECK-LABEL: CG of testInteriorCycle +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: G, Succ: (%2) +// CHECK-NEXT: Val %6 Esc: , Succ: %0, %2 +// CHECK-NEXT: Ret return Esc: , Succ: %6 +// CHECK-LABEL: End +sil @testInteriorCycle : $@convention(thin) (@owned IntWrapper) -> (Builtin.BridgeObject, UnsafeMutablePointer) { +bb0(%0 : $IntWrapper): + %bridge = unchecked_ref_cast %0 : $IntWrapper to $Builtin.BridgeObject + %adr = ref_tail_addr %0 : $IntWrapper, $Int64 + %ptr = address_to_pointer %adr : $*Int64 to $Builtin.RawPointer + %ump = struct $UnsafeMutablePointer (%ptr : $Builtin.RawPointer) + strong_release %0 : $IntWrapper + %tuple = tuple (%bridge : $Builtin.BridgeObject, %ump : $UnsafeMutablePointer) + return %tuple : $(Builtin.BridgeObject, UnsafeMutablePointer) +} diff --git a/test/SILOptimizer/sil_combine_concrete_existential.swift b/test/SILOptimizer/sil_combine_concrete_existential.swift index f7e49003c9333..b202d5fbd0f63 100644 --- a/test/SILOptimizer/sil_combine_concrete_existential.swift +++ b/test/SILOptimizer/sil_combine_concrete_existential.swift @@ -101,7 +101,7 @@ struct SS: PPP { // CHECK-LABEL: } // end sil function '$s32sil_combine_concrete_existential37testWitnessReturnOptionalIndirectSelfyyF' public func testWitnessReturnOptionalIndirectSelf() { let p: PPP = S() - p.returnsOptionalIndirect()?.returnsOptionalIndirect() + _ = p.returnsOptionalIndirect()?.returnsOptionalIndirect() } //===----------------------------------------------------------------------===// From 4a590cea5af8c01068c05f13998c9bf6eac6c4d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Faic=CC=A7al=20Tchirou?= Date: Thu, 19 Dec 2019 10:26:31 +0100 Subject: [PATCH 133/478] =?UTF-8?q?Add=20missing=20=3F=20in=20=E2=80=9Cdid?= =?UTF-8?q?=20you=20mean=20=E2=80=A6=E2=80=9D=20diagnostics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/swift/AST/DiagnosticsSema.def | 4 ++-- test/Constraints/diagnostics.swift | 4 ++-- test/Constraints/enum_cases.swift | 2 +- test/Constraints/keyword_arguments.swift | 4 ++-- test/NameBinding/name_lookup.swift | 10 +++++----- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 36b0e01566ac8..da6304b39d408 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -107,7 +107,7 @@ ERROR(could_not_find_subscript_member_did_you_mean,none, (Type)) ERROR(could_not_find_enum_case,none, - "enum type %0 has no case %1; did you mean %2", + "enum type %0 has no case %1; did you mean %2?", (Type, DeclNameRef, DeclName)) NOTE(did_you_mean_raw_type,none, @@ -951,7 +951,7 @@ NOTE(in_cast_expr_types,none, (Type, Type)) ERROR(types_not_convertible_use_bool_value,none, - "%0 is not convertible to %1; did you mean %0.boolValue", (Type, Type)) + "%0 is not convertible to %1; did you mean %0.boolValue?", (Type, Type)) ERROR(tuple_types_not_convertible_nelts,none, "%0 is not convertible to %1, " diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 6c98ae232b116..077bb346bd685 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -529,8 +529,8 @@ let _: Color = .frob(1, b: i) // expected-error {{passing value of type 'Int' t let _: Color = .frob(1, &d) // expected-error {{missing argument label 'b:' in call}} // expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}} let _: Color = .frob(1, b: &d) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} -var someColor : Color = .red // expected-error {{enum type 'Color' has no case 'red'; did you mean 'Red'}} -someColor = .red // expected-error {{enum type 'Color' has no case 'red'; did you mean 'Red'}} +var someColor : Color = .red // expected-error {{enum type 'Color' has no case 'red'; did you mean 'Red'?}} +someColor = .red // expected-error {{enum type 'Color' has no case 'red'; did you mean 'Red'?}} someColor = .svar() // expected-error {{cannot call value of non-function type 'Color'}} someColor = .svar(1) // expected-error {{cannot call value of non-function type 'Color'}} diff --git a/test/Constraints/enum_cases.swift b/test/Constraints/enum_cases.swift index 2b162eb03d9db..5a918dacd6fa1 100644 --- a/test/Constraints/enum_cases.swift +++ b/test/Constraints/enum_cases.swift @@ -121,7 +121,7 @@ func rdar34583132() { func bar(_ s: S) { guard s.foo(1 + 2) == .timeout else { - // expected-error@-1 {{enum type 'E' has no case 'timeout'; did you mean 'timeOut'}} + // expected-error@-1 {{enum type 'E' has no case 'timeout'; did you mean 'timeOut'?}} fatalError() } } diff --git a/test/Constraints/keyword_arguments.swift b/test/Constraints/keyword_arguments.swift index 977943e116529..6458fd4c36a66 100644 --- a/test/Constraints/keyword_arguments.swift +++ b/test/Constraints/keyword_arguments.swift @@ -349,14 +349,14 @@ func trailingClosure6(value: Int, expression: () -> T?) { } trailingClosure5(file: "hello", line: 17) { // expected-error{{extraneous argument label 'file:' in call}}{{18-24=}} // expected-error@-1 {{generic parameter 'T' could not be inferred}} return Optional.Some(5) - // expected-error@-1 {{enum type 'Optional' has no case 'Some'; did you mean 'some'}} {{19-23=some}} + // expected-error@-1 {{enum type 'Optional' has no case 'Some'; did you mean 'some'?}} {{19-23=some}} // expected-error@-2 {{generic parameter 'Wrapped' could not be inferred}} // expected-note@-3 {{explicitly specify the generic arguments to fix this issue}} } trailingClosure6(5) { // expected-error{{missing argument label 'value:' in call}}{{18-18=value: }} // expected-error@-1 {{generic parameter 'T' could not be inferred}} return Optional.Some(5) - // expected-error@-1 {{enum type 'Optional' has no case 'Some'; did you mean 'some'}} {{19-23=some}} + // expected-error@-1 {{enum type 'Optional' has no case 'Some'; did you mean 'some'?}} {{19-23=some}} // expected-error@-2 {{generic parameter 'Wrapped' could not be inferred}} // expected-note@-3 {{explicitly specify the generic arguments to fix this issue}} } diff --git a/test/NameBinding/name_lookup.swift b/test/NameBinding/name_lookup.swift index 0e443d4e6d87c..7fabd37ba89a4 100644 --- a/test/NameBinding/name_lookup.swift +++ b/test/NameBinding/name_lookup.swift @@ -553,9 +553,9 @@ default: } func foo() { - _ = MyEnum.One // expected-error {{enum type 'MyEnum' has no case 'One'; did you mean 'one'}}{{14-17=one}} - _ = MyEnum.Two // expected-error {{enum type 'MyEnum' has no case 'Two'; did you mean 'two'}}{{14-17=two}} - _ = MyEnum.OneTwoThree // expected-error {{enum type 'MyEnum' has no case 'OneTwoThree'; did you mean 'oneTwoThree'}}{{14-25=oneTwoThree}} + _ = MyEnum.One // expected-error {{enum type 'MyEnum' has no case 'One'; did you mean 'one'?}}{{14-17=one}} + _ = MyEnum.Two // expected-error {{enum type 'MyEnum' has no case 'Two'; did you mean 'two'?}}{{14-17=two}} + _ = MyEnum.OneTwoThree // expected-error {{enum type 'MyEnum' has no case 'OneTwoThree'; did you mean 'oneTwoThree'?}}{{14-25=oneTwoThree}} } enum MyGenericEnum { @@ -564,8 +564,8 @@ enum MyGenericEnum { } func foo1() { - _ = MyGenericEnum.One // expected-error {{enum type 'MyGenericEnum' has no case 'One'; did you mean 'one'}}{{26-29=one}} - _ = MyGenericEnum.OneTwo // expected-error {{enum type 'MyGenericEnum' has no case 'OneTwo'; did you mean 'oneTwo'}}{{26-32=oneTwo}} + _ = MyGenericEnum.One // expected-error {{enum type 'MyGenericEnum' has no case 'One'; did you mean 'one'?}}{{26-29=one}} + _ = MyGenericEnum.OneTwo // expected-error {{enum type 'MyGenericEnum' has no case 'OneTwo'; did you mean 'oneTwo'?}}{{26-32=oneTwo}} } // SR-4082 From 78c7eecd9a2c1344a8ff96f5d22b4e73d171862e Mon Sep 17 00:00:00 2001 From: David Ungar Date: Wed, 18 Dec 2019 20:04:27 -0800 Subject: [PATCH 134/478] off-by-default & fix tests --- test/Driver/advanced_output_file_map.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Driver/advanced_output_file_map.swift b/test/Driver/advanced_output_file_map.swift index 7af1af35918ba..8de5a610934fd 100644 --- a/test/Driver/advanced_output_file_map.swift +++ b/test/Driver/advanced_output_file_map.swift @@ -82,7 +82,7 @@ // BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "ld{{(.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o", "./OutputFileMap.swiftmodule"], output: {image: "./advanced_output_file_map.out"} // BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "dsymutil{{(\.exe)?}}", inputs: ["./advanced_output_file_map.out"], output: {dSYM: "./advanced_output_file_map.out.dSYM"} -// Defaulting to: -disable-only-one-dependency-file +// Defaulting to: -enable-only-one-dependency-file // RUN: %swiftc_driver -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-DIS @@ -91,5 +91,5 @@ // RUN: %swiftc_driver -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS-DIS // Should be no dummy files: // RUN: test ! -e %t/d/advanced_output_file_map.d -// RUN: test ! -e %t/d/main.d -// RUN: test ! -e %t/d/lib.d +// RUN: test -e %t/d/main.d -a ! -s %t/d/main.d +// RUN: test -e %t/d/lib.d -a ! -s %t/d/lib.d From b1f6a8941c22ca8aa3e0b713f056a72b0dd70e44 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Wed, 18 Dec 2019 21:25:58 -0800 Subject: [PATCH 135/478] Change to defaulting on --- lib/Driver/Driver.cpp | 2 +- test/Driver/advanced_output_file_map.swift | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 69d5b0b1bfced..4eee305e71807 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -954,7 +954,7 @@ Driver::buildCompilation(const ToolChain &TC, const bool OnlyOneDependencyFile = ArgList->hasFlag(options::OPT_enable_only_one_dependency_file, - options::OPT_disable_only_one_dependency_file, false); + options::OPT_disable_only_one_dependency_file, true); // relies on the new dependency graph const bool EnableFineGrainedDependencies = diff --git a/test/Driver/advanced_output_file_map.swift b/test/Driver/advanced_output_file_map.swift index 8de5a610934fd..0e1216db48a36 100644 --- a/test/Driver/advanced_output_file_map.swift +++ b/test/Driver/advanced_output_file_map.swift @@ -84,12 +84,11 @@ // Defaulting to: -enable-only-one-dependency-file -// RUN: %swiftc_driver -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-DIS +// RUN: %swiftc_driver -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-ENA // RUN: %empty-directory(%t/d) -// RUN: %swiftc_driver -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS-DIS -// Should be no dummy files: +// RUN: %swiftc_driver -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | tee /tmp/out | %FileCheck %/s -check-prefix=BINDINGS-ENA // RUN: test ! -e %t/d/advanced_output_file_map.d // RUN: test -e %t/d/main.d -a ! -s %t/d/main.d // RUN: test -e %t/d/lib.d -a ! -s %t/d/lib.d From 3a9b8bc658caf5c6cfb03e5fad4e089de7115c04 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 19 Dec 2019 09:10:58 -0800 Subject: [PATCH 136/478] [Driver] Add unversioned triple to the output of -print-target-info. SwiftPM wants to use the unversioned triple to know where to put results. Vend this as part of -print-target-info. --- include/swift/Basic/Platform.h | 4 +++- lib/Basic/Platform.cpp | 14 ++++++++++++++ lib/FrontendTool/FrontendTool.cpp | 4 ++++ test/Driver/print_target_info.swift | 1 + test/Driver/print_target_info_macos.swift | 3 ++- 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/include/swift/Basic/Platform.h b/include/swift/Basic/Platform.h index 04ee3253990e9..cfd9abebd4a00 100644 --- a/include/swift/Basic/Platform.h +++ b/include/swift/Basic/Platform.h @@ -91,7 +91,9 @@ namespace swift { /// llvm::Triple::normalize() would not affect it. llvm::Triple getTargetSpecificModuleTriple(const llvm::Triple &triple); - + /// Computes the target triple without version information. + llvm::Triple getUnversionedTriple(const llvm::Triple &triple); + /// Get the Swift runtime version to deploy back to, given a deployment target expressed as an /// LLVM target triple. Optional diff --git a/lib/Basic/Platform.cpp b/lib/Basic/Platform.cpp index 06bc996822217..85abc0ee97e43 100644 --- a/lib/Basic/Platform.cpp +++ b/lib/Basic/Platform.cpp @@ -342,6 +342,20 @@ llvm::Triple swift::getTargetSpecificModuleTriple(const llvm::Triple &triple) { return triple; } +llvm::Triple swift::getUnversionedTriple(const llvm::Triple &triple) { + StringRef unversionedOSName = triple.getOSName().take_until(llvm::isDigit); + if (triple.getEnvironment()) { + StringRef environment = + llvm::Triple::getEnvironmentTypeName(triple.getEnvironment()); + + return llvm::Triple(triple.getArchName(), triple.getVendorName(), + unversionedOSName, environment); + } + + return llvm::Triple(triple.getArchName(), triple.getVendorName(), + unversionedOSName); +} + Optional swift::getSwiftRuntimeCompatibilityVersionForTarget( const llvm::Triple &Triple) { diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 1427e7197a524..874136009a70e 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1880,6 +1880,10 @@ static void printTargetInfo(CompilerInvocation &invocation, out.write_escaped(langOpts.Target.getTriple()); out << "\",\n"; + out << " \"unversionedTriple\": \""; + out.write_escaped(getUnversionedTriple(langOpts.Target).getTriple()); + out << "\",\n"; + out << " \"moduleTriple\": \""; out.write_escaped(getTargetSpecificModuleTriple(langOpts.Target).getTriple()); out << "\",\n"; diff --git a/test/Driver/print_target_info.swift b/test/Driver/print_target_info.swift index cd2bd5c7460cd..28a65dea78d06 100644 --- a/test/Driver/print_target_info.swift +++ b/test/Driver/print_target_info.swift @@ -6,6 +6,7 @@ // CHECK-IOS: "target": { // CHECK-IOS: "triple": "arm64-apple-ios12.0", +// CHECK-IOS: "unversionedTriple": "arm64-apple-ios", // CHECK-IOS: "moduleTriple": "arm64-apple-ios", // CHECK-IOS: "swiftRuntimeCompatibilityVersion": "5.0", // CHECK-IOS: "librariesRequireRPath": true diff --git a/test/Driver/print_target_info_macos.swift b/test/Driver/print_target_info_macos.swift index 4121ab3810a4a..14074424cf484 100644 --- a/test/Driver/print_target_info_macos.swift +++ b/test/Driver/print_target_info_macos.swift @@ -7,4 +7,5 @@ // REQUIRES: OS=macosx -// CHECK: "triple": "x86_64-apple-macosx +// CHECK: "triple": "x86_64-apple-macosx10 +// CHECK: "unversionedTriple": "x86_64-apple-macosx" From aebfb539664c8e2e1c4fd1028d5561e1ecaaedbb Mon Sep 17 00:00:00 2001 From: Vinicius Vendramini Date: Thu, 19 Dec 2019 16:47:47 -0300 Subject: [PATCH 137/478] Ast dump ignore wmo (#20596) * Ignore -wmo when passing -dump-ast * Cleanup on driver diagnostics * Remove the FIXME. * Add support for ignoring `-index-file` * Revert unrelated formatting changes * Revert back to only ignoring `-wmo` Ignoring both `-wmo` and `-index-file` will be harder than just `-wmo`. This is because when calling the compiler and passing `-index-file` after `-dump-ast`, the option gets un-ignored by `Driver::buildOutputInfo`. Therefore, we will just ignore `-wmo` for now. * Add tests, inspired by `Driver/batch_mode_with_WMO_or_index.swift` --- include/swift/AST/DiagnosticsDriver.def | 3 ++ lib/Driver/Driver.cpp | 11 +++++++ lib/FrontendTool/FrontendTool.cpp | 4 --- test/Driver/ast_dump_with_WMO.swift | 38 +++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 test/Driver/ast_dump_with_WMO.swift diff --git a/include/swift/AST/DiagnosticsDriver.def b/include/swift/AST/DiagnosticsDriver.def index 1664d394dfe1b..e301e51f09daf 100644 --- a/include/swift/AST/DiagnosticsDriver.def +++ b/include/swift/AST/DiagnosticsDriver.def @@ -166,6 +166,9 @@ WARNING(warn_opt_remark_disabled, none, WARNING(warn_ignoring_batch_mode,none, "ignoring '-enable-batch-mode' because '%0' was also specified", (StringRef)) +WARNING(warn_ignoring_wmo, none, + "ignoring '-wmo' because '-dump-ast' was also specified", ()) + WARNING(warn_ignoring_source_range_dependencies,none, "ignoring '-enable-source-range-dependencies' because '%0' was also specified", (StringRef)) diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 4eee305e71807..e3a6e2a3361cf 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1764,6 +1764,17 @@ Driver::computeCompilerMode(const DerivedArgList &Args, const Arg *ArgRequiringSinglePrimaryCompile = Args.getLastArg(options::OPT_enable_source_range_dependencies); + // AST dump doesn't work with `-wmo`. Since it's not common to want to dump + // the AST, we assume that's the priority and ignore `-wmo`, but we warn the + // user about this decision. + // FIXME: AST dump also doesn't work with `-index-file`, but that fix is a bit + // more complicated than this. + if (Args.hasArg(options::OPT_whole_module_optimization) && + Args.hasArg(options::OPT_dump_ast)) { + Diags.diagnose(SourceLoc(), diag::warn_ignoring_wmo); + return OutputInfo::Mode::StandardCompile; + } + // Override batch mode if given -wmo or -index-file. if (ArgRequiringSingleCompile) { if (BatchModeOut) { diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 5fad0e61f50f9..fd01c1131faf5 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -894,10 +894,6 @@ static SourceFile *getPrimaryOrMainSourceFile(CompilerInvocation &Invocation, /// files were specified, use them; otherwise, dump the AST to stdout. static void dumpAST(CompilerInvocation &Invocation, CompilerInstance &Instance) { - // FIXME: WMO doesn't use the notion of primary files, so this doesn't do the - // right thing. Perhaps it'd be best to ignore WMO when dumping the AST, just - // like WMO ignores `-incremental`. - auto primaryFiles = Instance.getPrimarySourceFiles(); if (!primaryFiles.empty()) { for (SourceFile *sourceFile: primaryFiles) { diff --git a/test/Driver/ast_dump_with_WMO.swift b/test/Driver/ast_dump_with_WMO.swift new file mode 100644 index 0000000000000..2b2a142f730c8 --- /dev/null +++ b/test/Driver/ast_dump_with_WMO.swift @@ -0,0 +1,38 @@ +// RUN: %empty-directory(%t) + + +// Check that -wmo is ignored when -dump-ast is present and that the AST gets +// dumped to stdout, no matter the order of the options + +// RUN: %swiftc_driver -whole-module-optimization -dump-ast -module-name=main %S/../Inputs/empty.swift -### 2>%t/stderr_WMO_dump | %FileCheck -check-prefix CHECK-STDOUT %s +// RUN: %swiftc_driver -dump-ast -whole-module-optimization -module-name=main %S/../Inputs/empty.swift -### 2>%t/stderr_dump_WMO | %FileCheck -check-prefix CHECK-STDOUT %s +// RUN: %FileCheck -check-prefix CHECK-WMO %s <%t/stderr_WMO_dump +// RUN: %FileCheck -check-prefix CHECK-WMO %s <%t/stderr_dump_WMO + + +// Check that ignoring -wmo doesn't affect the output file paths for the AST +// dumps + +// RUN: cd %t +// RUN: echo ' ' > a.swift +// RUN: echo ' ' > main.swift +// RUN: echo '{ "a.swift": { "ast-dump": "a.ast" }, "main.swift": { "ast-dump": "main.ast" }}' > outputFileMap.json + +// RUN: %swiftc_driver -whole-module-optimization -dump-ast -module-name=main -output-file-map=outputFileMap.json main.swift a.swift -### 2>%t/stderr_WMO_OFM | %FileCheck -check-prefix CHECK-OFM %s +// RUN: %FileCheck -check-prefix CHECK-WMO %s <%t/stderr_WMO_OFM + + + +// CHECK-WMO: warning: ignoring '-wmo' because '-dump-ast' was also specified + +// CHECK-STDOUT-NOT: -whole-module-optimization +// CHECK-STDOUT-NOT: -wmo +// CHECK-STDOUT: -dump-ast +// CHECK-STDOUT: -o - + +// CHECK-OFM-NOT: -whole-module-optimization +// CHECK-OFM-NOT: -wmo +// CHECK-OFM: -dump-ast +// CHECK-OFM-SAME: -o main.ast +// CHECK-OFM-NEXT: -dump-ast +// CHECK-OFM-SAME: -o a.ast From 8bcc192591f8a5e9cdbe221757d538600b4d4379 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 19 Dec 2019 11:46:09 -0800 Subject: [PATCH 138/478] [Diagnostics] Diagnose inability to infer (complex) closure return type --- include/swift/AST/DiagnosticsSema.def | 10 +++---- lib/Sema/CSBindings.cpp | 5 ++++ lib/Sema/CSDiagnostics.cpp | 28 +++++++++++++++++++ lib/Sema/CSDiagnostics.h | 9 ++++++ lib/Sema/CSFix.cpp | 12 ++++++++ lib/Sema/CSFix.h | 19 +++++++++++++ lib/Sema/CSGen.cpp | 6 ++-- lib/Sema/CSSimplify.cpp | 1 + lib/Sema/ConstraintSystem.h | 3 ++ lib/Sema/TypeCheckConstraints.cpp | 8 ++++++ test/Constraints/closures.swift | 12 ++++---- test/Constraints/diagnostics.swift | 4 +-- test/Constraints/patterns.swift | 5 ++-- test/expr/closure/closures.swift | 4 +-- .../Sema/SwiftUI/rdar57201781.swift | 2 +- .../0119-rdar33613329.swift | 3 +- 16 files changed, 108 insertions(+), 23 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 36b0e01566ac8..e9ededa3065f9 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -302,11 +302,11 @@ ERROR(cannot_invoke_closure_type,none, ERROR(cannot_infer_closure_type,none, "unable to infer closure type in the current context", ()) ERROR(cannot_infer_closure_result_type,none, - "unable to infer complex closure return type; " - "add explicit type to disambiguate", ()) -FIXIT(insert_closure_return_type, - "%select{| () }1-> %0 %select{|in }1", - (Type, bool)) + "unable to infer%select{ complex|}0 closure return type; " + "add explicit type to disambiguate", (bool)) +FIXIT(insert_closure_return_type_placeholder, + "%select{| () }0-> <#Result#> %select{|in }0", + (bool)) ERROR(incorrect_explicit_closure_result,none, "declared closure result %0 is incompatible with contextual type %1", diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index a129fc838c681..741fb3cd4acef 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -999,6 +999,11 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { cs.getConstraintLocator(dstLocator->getAnchor(), path.drop_back())); if (cs.recordFix(fix)) return true; + } else if (TypeVar->getImpl().isClosureResultType()) { + auto *fix = SpecifyClosureReturnType::create( + cs, TypeVar->getImpl().getLocator()); + if (cs.recordFix(fix)) + return true; } } } diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 25f1113a40d12..d01349c410e5c 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -5915,3 +5915,31 @@ bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() { .highlight(anchor->getSourceRange()); return true; } + +bool UnableToInferClosureReturnType::diagnoseAsError() { + auto *closure = cast(getRawAnchor()); + + auto diagnostic = + emitDiagnostic(closure->getLoc(), + diag::cannot_infer_closure_result_type, + closure->hasSingleExpressionBody()); + + // If there is a location for an 'in' token, then the argument list was + // specified somehow but no return type was. Insert a "-> ReturnType " + // before the in token. + if (closure->getInLoc().isValid()) { + diagnostic.fixItInsert(closure->getInLoc(), + diag::insert_closure_return_type_placeholder, + /*argListSpecified=*/false); + } else if (closure->getParameters()->size() == 0) { + // Otherwise, the closure must take zero arguments. + // + // As such, we insert " () -> ReturnType in " right after the '{' that + // starts the closure body. + diagnostic.fixItInsertAfter(closure->getBody()->getLBraceLoc(), + diag::insert_closure_return_type_placeholder, + /*argListSpecified=*/true); + } + + return true; +} diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 54cdc3f2b9d70..c8493ff289b19 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1912,6 +1912,15 @@ class MissingContextualBaseInMemberRefFailure final : public FailureDiagnostic { bool diagnoseAsError(); }; +class UnableToInferClosureReturnType final : public FailureDiagnostic { +public: + UnableToInferClosureReturnType(ConstraintSystem &cs, + ConstraintLocator *locator) + : FailureDiagnostic(cs, locator) {} + + bool diagnoseAsError(); +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 2788265e4d769..a8ccc6e7e63c1 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1155,3 +1155,15 @@ SpecifyBaseTypeForContextualMember *SpecifyBaseTypeForContextualMember::create( return new (cs.getAllocator()) SpecifyBaseTypeForContextualMember(cs, member, locator); } + +bool SpecifyClosureReturnType::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + UnableToInferClosureReturnType failure(cs, getLocator()); + return failure.diagnose(asNote); +} + +SpecifyClosureReturnType * +SpecifyClosureReturnType::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) SpecifyClosureReturnType(cs, locator); +} diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 3bb7a2e0ac3a8..305e2f488cc15 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -231,6 +231,10 @@ enum class FixKind : uint8_t { /// Base type in reference to the contextual member e.g. `.foo` couldn't be /// inferred and has to be specified explicitly. SpecifyBaseTypeForContextualMember, + + /// Closure return type has to be explicitly specified because it can't be + /// inferred in current context e.g. because it's a multi-statement closure. + SpecifyClosureReturnType, }; class ConstraintFix { @@ -1605,6 +1609,21 @@ class SpecifyBaseTypeForContextualMember final : public ConstraintFix { create(ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator); }; +class SpecifyClosureReturnType final : public ConstraintFix { + SpecifyClosureReturnType(ConstraintSystem &cs, ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::SpecifyClosureReturnType, locator) {} + +public: + std::string getName() const { + return "specify closure return type"; + } + + bool diagnose(bool asNote = false) const; + + static SpecifyClosureReturnType *create(ConstraintSystem &cs, + ConstraintLocator *locator); +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 05a16678c527e..117a304bb6c94 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2430,7 +2430,8 @@ namespace { CS.getConstraintLocator(expr, ConstraintLocator::ClosureResult); if (expr->hasEmptyBody()) { - resultTy = CS.createTypeVariable(locator, 0); + resultTy = CS.createTypeVariable( + locator, expr->hasSingleExpressionBody() ? 0 : TVO_CanBindToHole); // Closures with empty bodies should be inferred to return // (). @@ -2442,7 +2443,8 @@ namespace { } else { // If no return type was specified, create a fresh type // variable for it. - resultTy = CS.createTypeVariable(locator, 0); + resultTy = CS.createTypeVariable( + locator, expr->hasSingleExpressionBody() ? 0 : TVO_CanBindToHole); if (closureHasNoResult(expr)) { // Allow it to default to () if there are no return statements. diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 63003c4e61ac1..e0aba3953cc81 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -8596,6 +8596,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::AllowMutatingMemberOnRValueBase: case FixKind::AllowTupleSplatForSingleParameter: case FixKind::AllowInvalidUseOfTrailingClosure: + case FixKind::SpecifyClosureReturnType: llvm_unreachable("handled elsewhere"); } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index bcbefc2315b20..4dae739eca75b 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -297,6 +297,9 @@ class TypeVariableType::Implementation { /// Retrieve the generic parameter opened by this type variable. GenericTypeParamType *getGenericParameter() const; + /// Determine whether this type variable represents a closure result type. + bool isClosureResultType() const; + /// Retrieve the representative of the equivalence class to which this /// type variable belongs. /// diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 8567666f5a354..f02cd1770abd5 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -83,6 +83,14 @@ TypeVariableType::Implementation::getGenericParameter() const { return locator ? locator->getGenericParameter() : nullptr; } +bool TypeVariableType::Implementation::isClosureResultType() const { + if (!(locator && locator->getAnchor())) + return false; + + return isa(locator->getAnchor()) && + locator->isLastElement(); +} + void *operator new(size_t bytes, ConstraintSystem& cs, size_t alignment) { return cs.getAllocator().Allocate(bytes, alignment); diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 59a990f33a8d1..4d6c31cd6f1bf 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -249,10 +249,10 @@ var _: (Int,Int) -> Int = {$0+$1+$2} // expected-error {{contextual closure typ // Crash when re-typechecking bodies of non-single expression closures struct CC {} -func callCC(_ f: (CC) -> U) -> () {} // expected-note {{in call to function 'callCC'}} +func callCC(_ f: (CC) -> U) -> () {} func typeCheckMultiStmtClosureCrash() { - callCC { // expected-error {{generic parameter 'U' could not be inferred}} + callCC { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{none}} _ = $0 return 1 } @@ -313,7 +313,7 @@ struct Thing { init?() {} } // This throws a compiler error -let things = Thing().map { thing in // expected-error {{generic parameter 'U' could not be inferred}} +let things = Thing().map { thing in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{34-34=-> <#Result#> }} // Commenting out this makes it compile _ = thing return thing @@ -322,7 +322,7 @@ let things = Thing().map { thing in // expected-error {{generic parameter 'U' c // QoI: [Closure return type inference] Swift cannot find members for the result of inlined lambdas with branches func r21675896(file : String) { - let x: String = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{20-20= () -> String in }} + let x: String = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{20-20= () -> <#Result#> in }} if true { return "foo" } @@ -360,7 +360,7 @@ func someGeneric19997471(_ x: T) { // Swift fails to compile: [0].map() { _ in let r = (1,2).0; return r } -[0].map { // expected-error {{generic parameter 'T' could not be inferred}} +[0].map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{5-5=-> <#Result#> }} _ in let r = (1,2).0 return r @@ -408,7 +408,7 @@ func r20789423() { print(p.f(p)()) // expected-error {{cannot convert value of type 'C' to expected argument type 'Int'}} // expected-error@-1:11 {{cannot call value of non-function type '()'}} - let _f = { (v: Int) in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{23-23=-> String }} + let _f = { (v: Int) in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{23-23=-> <#Result#> }} print("a") return "hi" } diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 6c98ae232b116..437720d580ef2 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -146,7 +146,7 @@ func ***~(_: Int, _: String) { } i ***~ i // expected-error{{cannot convert value of type 'Int' to expected argument type 'String'}} @available(*, unavailable, message: "call the 'map()' method on the sequence") -public func myMap( // expected-note {{in call to function 'myMap'}} +public func myMap( _ source: C, _ transform: (C.Iterator.Element) -> T ) -> [T] { fatalError("unavailable function can't be called") @@ -159,7 +159,7 @@ public func myMap(_ x: T?, _ f: (T) -> U) -> U? { // func rdar20142523() { - myMap(0..<10, { x in // expected-error{{generic parameter 'T' could not be inferred}} + myMap(0..<10, { x in // expected-error{{unable to infer complex closure return type; add explicit type to disambiguate}} {{21-21=-> <#Result#> }} () return x }) diff --git a/test/Constraints/patterns.swift b/test/Constraints/patterns.swift index d391c73e48f0e..ef03365f1ce4a 100644 --- a/test/Constraints/patterns.swift +++ b/test/Constraints/patterns.swift @@ -211,7 +211,6 @@ struct A : PP { extension PP { func map(_ f: (Self.E) -> T) -> T {} - // expected-note@-1 2 {{in call to function 'map'}} } enum EE { @@ -231,14 +230,14 @@ func good(_ a: A) -> Int { } func bad(_ a: A) { - a.map { // expected-error {{generic parameter 'T' could not be inferred}} + a.map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{none}} let _: EE = $0 return 1 } } func ugly(_ a: A) { - a.map { // expected-error {{generic parameter 'T' could not be inferred}} + a.map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{none}} switch $0 { case .A: return 1 diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index 845e5bc69ab25..ca1a0156ad9ed 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -276,7 +276,7 @@ Void(0) // expected-error{{argument passed to call that takes no arguments}} _ = {0} // "multi-statement closures require an explicit return type" should be an error not a note -let samples = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> Bool in }} +let samples = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} if (i > 10) { return true } else { return false } }() @@ -358,7 +358,7 @@ func lvalueCapture(c: GenericClass) { } // Don't expose @lvalue-ness in diagnostics. -let closure = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> Bool in }} +let closure = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} var helper = true return helper } diff --git a/validation-test/Sema/SwiftUI/rdar57201781.swift b/validation-test/Sema/SwiftUI/rdar57201781.swift index 5591619ced6cc..be35137e218a8 100644 --- a/validation-test/Sema/SwiftUI/rdar57201781.swift +++ b/validation-test/Sema/SwiftUI/rdar57201781.swift @@ -8,7 +8,7 @@ struct ContentView : View { @State var foo: [String] = Array(repeating: "", count: 5) var body: some View { - VStack { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} + VStack { // expected-error {{expression type 'VStack<_>' is ambiguous without more context}} HStack { Text("") TextFi // expected-error {{use of unresolved identifier 'TextFi'}} diff --git a/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift b/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift index 2e6dfe51e708f..d29e72ce83713 100644 --- a/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift +++ b/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift @@ -28,7 +28,6 @@ protocol P { extension P { static func ≈> (f: Self, b: @escaping (inout B) -> R) -> M {} - // expected-note@-1 {{in call to operator '≈>'}} } extension WritableKeyPath : P { @@ -43,5 +42,5 @@ extension WritableKeyPath : P { struct X { var y: Int = 0 } var x = X() x ~> \X.y ≈> { a in a += 1; return 3 } -// expected-error@-1 {{generic parameter 'R' could not be inferred}} +// expected-error@-1 {{unable to infer complex closure return type; add explicit type to disambiguate}} // expected-error@-2 {{cannot convert value of type 'M, R>' to expected argument type 'WritableKeyPath<_, _>'}} From a7bb827b6d79d05d9a23119ef8e5f87fa14a9e26 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 19 Dec 2019 12:17:23 -0800 Subject: [PATCH 139/478] [CSDiag] NFC: Remove obsolete `diagnoseAmbiguousMultiStatementClosure` --- lib/Sema/CSDiag.cpp | 158 -------------------------------------------- 1 file changed, 158 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 0ac2fd371b8e5..ae8464e9eee9d 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -220,11 +220,6 @@ class FailureDiagnosis :public ASTVisitor{ std::pair validateContextualType(Type contextualType, ContextualTypePurpose CTP); - /// Check the specified closure to see if it is a multi-statement closure with - /// an uninferred type. If so, diagnose the problem with an error and return - /// true. - bool diagnoseAmbiguousMultiStatementClosure(ClosureExpr *closure); - /// Given a result of name lookup that had no viable results, diagnose the /// unviable ones. void diagnoseUnviableLookupResults(MemberLookupResult &lookupResults, @@ -2694,138 +2689,6 @@ FailureDiagnosis::validateContextualType(Type contextualType, return {contextualType, CTP}; } -/// Check the specified closure to see if it is a multi-statement closure with -/// an uninferred type. If so, diagnose the problem with an error and return -/// true. -bool FailureDiagnosis:: -diagnoseAmbiguousMultiStatementClosure(ClosureExpr *closure) { - if (closure->hasSingleExpressionBody() || - closure->hasExplicitResultType()) - return false; - - auto closureType = CS.getType(closure)->getAs(); - if (!closureType || - !(closureType->getResult()->hasUnresolvedType() || - closureType->getResult()->hasTypeVariable())) - return false; - - // Okay, we have a multi-statement closure expr that has no inferred result, - // type, in the context of a larger expression. The user probably expected - // the compiler to infer the result type of the closure from the body of the - // closure, which Swift doesn't do for multi-statement closures. Try to be - // helpful by digging into the body of the closure, looking for a return - // statement, and inferring the result type from it. If we can figure that - // out, we can produce a fixit hint. - class ReturnStmtFinder : public ASTWalker { - SmallVectorImpl &returnStmts; - public: - ReturnStmtFinder(SmallVectorImpl &returnStmts) - : returnStmts(returnStmts) {} - - // Walk through statements, so we find returns hiding in if/else blocks etc. - std::pair walkToStmtPre(Stmt *S) override { - // Keep track of any return statements we find. - if (auto RS = dyn_cast(S)) - returnStmts.push_back(RS); - return { true, S }; - } - - // Don't walk into anything else, since they cannot contain statements - // that can return from the current closure. - std::pair walkToExprPre(Expr *E) override { - return { false, E }; - } - std::pair walkToPatternPre(Pattern *P) override { - return { false, P }; - } - bool walkToDeclPre(Decl *D) override { return false; } - bool walkToTypeLocPre(TypeLoc &TL) override { return false; } - bool walkToTypeReprPre(TypeRepr *T) override { return false; } - bool walkToParameterListPre(ParameterList *PL) override { return false; } - }; - - SmallVector Returns; - closure->getBody()->walk(ReturnStmtFinder(Returns)); - - // If we found a return statement inside of the closure expression, then go - // ahead and type check the body to see if we can determine a type. - for (auto RS : Returns) { - llvm::SaveAndRestore SavedDC(CS.DC, closure); - - // Otherwise, we're ok to type check the subexpr. - Type resultType; - if (RS->hasResult()) { - auto resultExpr = RS->getResult(); - ConcreteDeclRef decl = nullptr; - - // If return expression uses closure parameters, which have/are - // type variables, such means that we won't be able to - // type-check result correctly and, unfortunately, - // we are going to leak type variables from the parent - // constraint system through declaration types. - bool hasUnresolvedParams = false; - resultExpr->forEachChildExpr([&](Expr *childExpr) -> Expr *{ - if (auto DRE = dyn_cast(childExpr)) { - if (auto param = dyn_cast(DRE->getDecl())) { - auto paramType = - param->hasInterfaceType() ? param->getType() : Type(); - if (!paramType || paramType->hasTypeVariable()) { - hasUnresolvedParams = true; - return nullptr; - } - } - } - return childExpr; - }); - - if (hasUnresolvedParams) - continue; - - ConstraintSystem::preCheckExpression(resultExpr, CS.DC, &CS); - - // Obtain type of the result expression without applying solutions, - // because otherwise this might result in leaking of type variables, - // since we are not resetting result statement and if expression is - // successfully type-checked its type cleanup is going to be disabled - // (we are allowing unresolved types), and as a side-effect it might - // also be transformed e.g. OverloadedDeclRefExpr -> DeclRefExpr. - auto type = TypeChecker::getTypeOfExpressionWithoutApplying( - resultExpr, CS.DC, decl, FreeTypeVariableBinding::UnresolvedType); - if (type) - resultType = type->getRValueType(); - } - - // If we found a type, presuppose it was the intended result and insert a - // fixit hint. - if (resultType && !isUnresolvedOrTypeVarType(resultType)) { - // If there is a location for an 'in' token, then the argument list was - // specified somehow but no return type was. Insert a "-> ReturnType " - // before the in token. - if (closure->getInLoc().isValid()) { - diagnose(closure->getLoc(), diag::cannot_infer_closure_result_type) - .fixItInsert(closure->getInLoc(), diag::insert_closure_return_type, - resultType, /*argListSpecified*/ false); - return true; - } - - // Otherwise, the closure must take zero arguments. We know this - // because the if one or more argument is specified, a multi-statement - // closure *must* name them, or explicitly ignore them with "_ in". - // - // As such, we insert " () -> ReturnType in " right after the '{' that - // starts the closure body. - diagnose(closure->getLoc(), diag::cannot_infer_closure_result_type) - .fixItInsertAfter(closure->getBody()->getLBraceLoc(), - diag::insert_closure_return_type, resultType, - /*argListSpecified*/ true); - return true; - } - } - - diagnose(closure->getLoc(), diag::cannot_infer_closure_result_type); - return true; -} - /// Emit an ambiguity diagnostic about the specified expression. void FailureDiagnosis::diagnoseAmbiguity(Expr *E) { if (auto *assignment = dyn_cast(E)) { @@ -2855,11 +2718,6 @@ void FailureDiagnosis::diagnoseAmbiguity(Expr *E) { // Unresolved/Anonymous ClosureExprs are common enough that we should give // them tailored diagnostics. if (auto CE = dyn_cast(E->getValueProvidingExpr())) { - // If this is a multi-statement closure with no explicit result type, emit - // a note to clue the developer in. - if (diagnoseAmbiguousMultiStatementClosure(CE)) - return; - diagnose(E->getLoc(), diag::cannot_infer_closure_type) .highlight(E->getSourceRange()); return; @@ -2901,22 +2759,6 @@ void FailureDiagnosis::diagnoseAmbiguity(Expr *E) { return; } - // A very common cause of this diagnostic is a situation where a closure expr - // has no inferred type, due to being a multiline closure. Check to see if - // this is the case and (if so), speculatively diagnose that as the problem. - bool didDiagnose = false; - E->forEachChildExpr([&](Expr *subExpr) -> Expr*{ - auto closure = dyn_cast(subExpr); - if (!didDiagnose && closure) - didDiagnose = diagnoseAmbiguousMultiStatementClosure(closure); - - return subExpr; - }); - - if (didDiagnose) return; - - - // Attempt to re-type-check the entire expression, allowing ambiguity, but // ignoring a contextual type. if (expr == E) { From eebcbf6564696d3a36e59c8048e6c318b59e8a5d Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 19 Dec 2019 12:16:24 -0800 Subject: [PATCH 140/478] [SourceKit] Pass 'EnableASTCaching' flag as an argument so that it is associated with a specific completion. --- include/swift/IDE/CompletionInstance.h | 28 ++++----- lib/IDE/CompletionInstance.cpp | 59 ++++++++++--------- .../lib/SwiftLang/SwiftCompletion.cpp | 17 +++--- .../SwiftLang/SwiftConformingMethodList.cpp | 6 +- .../lib/SwiftLang/SwiftLangSupport.cpp | 6 +- .../lib/SwiftLang/SwiftLangSupport.h | 2 +- .../lib/SwiftLang/SwiftTypeContextInfo.cpp | 7 ++- tools/swift-ide-test/swift-ide-test.cpp | 11 ++-- 8 files changed, 68 insertions(+), 68 deletions(-) diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h index f60bcd399418a..0eb0de5eeb005 100644 --- a/include/swift/IDE/CompletionInstance.h +++ b/include/swift/IDE/CompletionInstance.h @@ -38,20 +38,18 @@ makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, /// Manages \c CompilerInstance for completion like operations. class CompletionInstance { unsigned MaxASTReuseCount = 100; - bool EnableASTCaching = false; std::mutex mtx; std::unique_ptr CachedCI; llvm::hash_code CachedArgHash; - unsigned CachedReuseCound = 0; + unsigned CachedReuseCount = 0; /// Calls \p Callback with cached \c CompilerInstance if it's usable for the /// specified completion request. - /// Returns \c if the callback was called. Returns \c false if the - /// functionality is disabled, compiler argument has - /// changed, primary file is not the same, the \c Offset is not in function - /// bodies, or the interface hash of the file has changed. + /// Returns \c if the callback was called. Returns \c false if the compiler + /// argument has changed, primary file is not the same, the \c Offset is not + /// in function bodies, or the interface hash of the file has changed. bool performCachedOperaitonIfPossible( const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, @@ -63,15 +61,14 @@ class CompletionInstance { /// the first pass. /// Returns \c false if it fails to setup the \c CompilerInstance. bool performNewOperation( - swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, + llvm::Optional ArgsHash, + swift::CompilerInvocation &Invocation, llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, std::string &Error, DiagnosticConsumer *DiagC, llvm::function_ref Callback); public: - void setEnableASTCaching(bool Flag) { EnableASTCaching = Flag; } - /// Calls \p Callback with a \c CompilerInstance which is prepared for the /// second pass. \p Callback is resposible to perform the second pass on it. /// The \c CompilerInstance may be reused from the previous completions, @@ -82,13 +79,12 @@ class CompletionInstance { /// NOTE: \p Args is only used for checking the equaity of the invocation. /// Since this function assumes that it is already normalized, exact the same /// arguments including their order is considered as the same invocation. - bool - performOperation(swift::CompilerInvocation &Invocation, - llvm::ArrayRef Args, - llvm::IntrusiveRefCntPtr FileSystem, - llvm::MemoryBuffer *completionBuffer, unsigned int Offset, - std::string &Error, DiagnosticConsumer *DiagC, - llvm::function_ref Callback); + bool performOperation( + swift::CompilerInvocation &Invocation, llvm::ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + bool EnableASTCaching, std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback); }; } // namespace ide diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index bcb915a9fd7dd..ab3c289019116 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -135,13 +135,9 @@ bool CompletionInstance::performCachedOperaitonIfPossible( DiagnosticConsumer *DiagC, llvm::function_ref Callback) { - if (!EnableASTCaching) - return false; - if (!CachedCI) return false; - - if (CachedReuseCound >= MaxASTReuseCount) + if (CachedReuseCount >= MaxASTReuseCount) return false; if (CachedArgHash != ArgsHash) return false; @@ -239,13 +235,13 @@ bool CompletionInstance::performCachedOperaitonIfPossible( if (DiagC) CI.removeDiagnosticConsumer(DiagC); - CachedReuseCound += 1; + CachedReuseCount += 1; return true; } bool CompletionInstance::performNewOperation( - swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, + Optional ArgsHash, swift::CompilerInvocation &Invocation, llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, std::string &Error, DiagnosticConsumer *DiagC, @@ -273,10 +269,10 @@ bool CompletionInstance::performNewOperation( if (DiagC) CI.removeDiagnosticConsumer(DiagC); - if (EnableASTCaching) { + if (ArgsHash.hasValue()) { CachedCI = std::move(TheInstance); - CachedArgHash = ArgsHash; - CachedReuseCound = 0; + CachedArgHash = *ArgsHash; + CachedReuseCount = 0; } return true; @@ -286,7 +282,7 @@ bool swift::ide::CompletionInstance::performOperation( swift::CompilerInvocation &Invocation, llvm::ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, - std::string &Error, DiagnosticConsumer *DiagC, + bool EnableASTCaching, std::string &Error, DiagnosticConsumer *DiagC, llvm::function_ref Callback) { // Always disable source location resolutions from .swiftsourceinfo file @@ -300,24 +296,29 @@ bool swift::ide::CompletionInstance::performOperation( // FIXME: ASTScopeLookup doesn't support code completion yet. Invocation.disableASTScopeLookup(); - // Compute the signature of the invocation. - llvm::hash_code ArgsHash(0); - for (auto arg : Args) - ArgsHash = llvm::hash_combine(ArgsHash, StringRef(arg)); - - // If AST caching is enabled, block completions in other threads. So they - // have higher chance to use the cached completion instance. - Optional> lock; - if (EnableASTCaching) - lock.emplace(mtx); - - if (performCachedOperaitonIfPossible(Invocation, ArgsHash, completionBuffer, - Offset, DiagC, Callback)) - return true; - - if (performNewOperation(Invocation, ArgsHash, FileSystem, completionBuffer, - Offset, Error, DiagC, Callback)) - return true; + if (EnableASTCaching) { + // Compute the signature of the invocation. + llvm::hash_code ArgsHash(0); + for (auto arg : Args) + ArgsHash = llvm::hash_combine(ArgsHash, StringRef(arg)); + + // Concurrent completions will block so that they have higher chance to use + // the cached completion instance. + std::lock_guard lock(mtx); + + if (performCachedOperaitonIfPossible(Invocation, ArgsHash, completionBuffer, + Offset, DiagC, Callback)) + return true; + + if (performNewOperation(ArgsHash, Invocation, FileSystem, completionBuffer, + Offset, Error, DiagC, Callback)) + return true; + } else { + // Concurrent completions may happen in parallel when caching is disabled. + if (performNewOperation(None, Invocation, FileSystem, completionBuffer, + Offset, Error, DiagC, Callback)) + return true; + } assert(!Error.empty()); return false; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 2d18f0f8318fc..2ca913da9b417 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -123,9 +123,9 @@ static bool swiftCodeCompleteImpl( unsigned Offset, SwiftCodeCompletionConsumer &SwiftConsumer, ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, - std::string &Error) { + bool EnableASTCaching, std::string &Error) { return Lang.performCompletionLikeOperation( - UnresolvedInputFile, Offset, Args, FileSystem, Error, + UnresolvedInputFile, Offset, Args, FileSystem, EnableASTCaching, Error, [&](CompilerInstance &CI) { // Create a factory for code completion callbacks that will feed the // Consumer. @@ -155,7 +155,6 @@ void SwiftLangSupport::codeComplete( SourceKit::CodeCompletionConsumer &SKConsumer, ArrayRef Args, Optional vfsOptions) { - CodeCompletion::Options CCOpts; if (options) { StringRef filterText; @@ -164,7 +163,6 @@ void SwiftLangSupport::codeComplete( translateCodeCompletionOptions(*options, CCOpts, filterText, resultOffset, maxResults); } - CompletionInst->setEnableASTCaching(CCOpts.reuseASTContextIfPossible); std::string error; // FIXME: the use of None as primary file is to match the fact we do not read @@ -210,7 +208,8 @@ void SwiftLangSupport::codeComplete( std::string Error; if (!swiftCodeCompleteImpl(*this, UnresolvedInputFile, Offset, SwiftConsumer, - Args, fileSystem, Error)) { + Args, fileSystem, + CCOpts.reuseASTContextIfPossible, Error)) { SKConsumer.failed(Error); } } @@ -1080,7 +1079,8 @@ static void transformAndForwardResults( cargs.push_back(arg.c_str()); std::string error; if (!swiftCodeCompleteImpl(lang, buffer.get(), str.size(), swiftConsumer, - cargs, session->getFileSystem(), error)) { + cargs, session->getFileSystem(), + options.reuseASTContextIfPossible, error)) { consumer.failed(error); return; } @@ -1128,7 +1128,6 @@ void SwiftLangSupport::codeCompleteOpen( if (options) translateCodeCompletionOptions(*options, CCOpts, filterText, resultOffset, maxResults); - CompletionInst->setEnableASTCaching(CCOpts.reuseASTContextIfPossible); std::string error; // FIXME: the use of None as primary file is to match the fact we do not read @@ -1179,7 +1178,8 @@ void SwiftLangSupport::codeCompleteOpen( // Invoke completion. if (!swiftCodeCompleteImpl(*this, inputBuf, offset, swiftConsumer, - extendedArgs, fileSystem, error)) { + extendedArgs, fileSystem, + CCOpts.reuseASTContextIfPossible, error)) { consumer.failed(error); return; } @@ -1249,7 +1249,6 @@ void SwiftLangSupport::codeCompleteUpdate( if (options) translateCodeCompletionOptions(*options, CCOpts, filterText, resultOffset, maxResults); - CompletionInst->setEnableASTCaching(CCOpts.reuseASTContextIfPossible); NameToPopularityMap *nameToPopularity = nullptr; // This reference must outlive the uses of nameToPopularity. diff --git a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp index f567c8b9a183f..eeeb0bc3b76df 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp @@ -32,9 +32,9 @@ static bool swiftConformingMethodListImpl( ArrayRef ExpectedTypeNames, ide::ConformingMethodListConsumer &Consumer, llvm::IntrusiveRefCntPtr FileSystem, - std::string &Error) { + bool EnableASTCaching, std::string &Error) { return Lang.performCompletionLikeOperation( - UnresolvedInputFile, Offset, Args, FileSystem, Error, + UnresolvedInputFile, Offset, Args, FileSystem, EnableASTCaching, Error, [&](CompilerInstance &CI) { // Create a factory for code completion callbacks that will feed the // Consumer. @@ -176,7 +176,7 @@ void SwiftLangSupport::getConformingMethodList( if (!swiftConformingMethodListImpl(*this, UnresolvedInputFile, Offset, Args, ExpectedTypeNames, Consumer, fileSystem, - error)) { + /*EnableASTCaching=*/false, error)) { SKConsumer.failed(error); } } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index e2b1901596c0f..fe547e0ec693d 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -957,7 +957,8 @@ bool SwiftLangSupport::performCompletionLikeOperation( llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, - std::string &Error, llvm::function_ref Callback) { + bool EnableASTCaching, std::string &Error, + llvm::function_ref Callback) { assert(FileSystem); // Resolve symlinks for the input file; we resolve them for the input files @@ -1011,7 +1012,8 @@ bool SwiftLangSupport::performCompletionLikeOperation( auto CompletionInst = getCompletionInstance(); return CompletionInst->performOperation(Invocation, Args, FileSystem, - newBuffer.get(), Offset, Error, + newBuffer.get(), Offset, + EnableASTCaching, Error, &CIDiags, Callback); } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index a0ad2358d458b..2c37911224119 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -443,7 +443,7 @@ class SwiftLangSupport : public LangSupport { llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, - std::string &Error, + bool EnableASTCaching, std::string &Error, llvm::function_ref Callback); //==========================================================================// diff --git a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp index 0134b818b8ff7..17ae2701f3451 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp @@ -30,9 +30,9 @@ static bool swiftTypeContextInfoImpl( unsigned Offset, ide::TypeContextInfoConsumer &Consumer, ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, - std::string &Error) { + bool EnableASTCaching, std::string &Error) { return Lang.performCompletionLikeOperation( - UnresolvedInputFile, Offset, Args, FileSystem, Error, + UnresolvedInputFile, Offset, Args, FileSystem, EnableASTCaching, Error, [&](CompilerInstance &CI) { // Create a factory for code completion callbacks that will feed the // Consumer. @@ -151,7 +151,8 @@ void SwiftLangSupport::getExpressionContextInfo( } Consumer(SKConsumer); if (!swiftTypeContextInfoImpl(*this, UnresolvedInputFile, Offset, Consumer, - Args, fileSystem, error)) { + Args, fileSystem, /*EnableASTCaching=*/false, + error)) { SKConsumer.failed(error); } } diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 85b17c114294b..7d452e729a55f 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -759,11 +759,12 @@ static bool doCodeCompletionImpl( CompletionInstance CompletionInst; auto isSuccess = CompletionInst.performOperation( Invocation, /*Args=*/{}, llvm::vfs::getRealFileSystem(), CleanFile.get(), - Offset, Error, CodeCompletionDiagnostics ? &PrintDiags : nullptr, - [&](CompilerInstance &CI) { - performCodeCompletionSecondPass(CI.getPersistentParserState(), - *callbacksFactory); - }); + Offset, /*EnableASTCaching=*/false, Error, + CodeCompletionDiagnostics ? &PrintDiags : nullptr, + [&](CompilerInstance &CI) { + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + }); return isSuccess ? 0 : 1; } From 8e7c6a5c1421746b2a837b7db3d7f3130385a210 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Thu, 19 Dec 2019 15:41:29 -0500 Subject: [PATCH 141/478] [Test] Disable reflect_Enum_(254|Two)CaseNoPayloads.swift when testing the OS stdlib. rdar://problem/58087442 --- validation-test/Reflection/reflect_Enum_254CaseNoPayloads.swift | 1 + validation-test/Reflection/reflect_Enum_TwoCaseNoPayload.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/validation-test/Reflection/reflect_Enum_254CaseNoPayloads.swift b/validation-test/Reflection/reflect_Enum_254CaseNoPayloads.swift index 03e18c29c2c7b..9a6faf06360c2 100644 --- a/validation-test/Reflection/reflect_Enum_254CaseNoPayloads.swift +++ b/validation-test/Reflection/reflect_Enum_254CaseNoPayloads.swift @@ -6,6 +6,7 @@ // REQUIRES: objc_interop // REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib import SwiftReflectionTest diff --git a/validation-test/Reflection/reflect_Enum_TwoCaseNoPayload.swift b/validation-test/Reflection/reflect_Enum_TwoCaseNoPayload.swift index a35d2fb3ed0d9..219eeee88fda1 100644 --- a/validation-test/Reflection/reflect_Enum_TwoCaseNoPayload.swift +++ b/validation-test/Reflection/reflect_Enum_TwoCaseNoPayload.swift @@ -6,6 +6,7 @@ // REQUIRES: objc_interop // REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib import SwiftReflectionTest From 216d02f84d009a94981bbb98e17d3dcc5bb2ce22 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Thu, 19 Dec 2019 12:53:23 -0800 Subject: [PATCH 142/478] [test] Mark _Differentiable tests as unsupported in Swift-in-the-OS configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new _Differentiable module is not available in any shipping OS release, but its public API currently doesn’t have availability, either. Temporarily disable tests that import it when we’re testing with OS-provided libraries. The typecheck test test/AutoDiff/stdlib/differentiable_protocol.swift imports _Differentable but still somehow succeeds in these configs, so leave that one enabled. rdar://57975086 --- test/AutoDiff/Sema/derivative_attr_type_checking.swift | 3 +++ test/AutoDiff/Serialization/derivative_attr.swift | 3 +++ test/AutoDiff/Serialization/differentiable_attr.swift | 3 +++ test/AutoDiff/Serialization/transpose_attr.swift | 3 +++ test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift | 3 +++ 5 files changed, 15 insertions(+) diff --git a/test/AutoDiff/Sema/derivative_attr_type_checking.swift b/test/AutoDiff/Sema/derivative_attr_type_checking.swift index bd1b055f12615..8aab201c49e0d 100644 --- a/test/AutoDiff/Sema/derivative_attr_type_checking.swift +++ b/test/AutoDiff/Sema/derivative_attr_type_checking.swift @@ -1,6 +1,9 @@ // RUN: %target-swift-frontend-typecheck -enable-experimental-differentiable-programming -verify %s // REQUIRES: differentiable_programming +// We currently lack availability information (rdar://57975086) +// UNSUPPORTED: use_os_stdlib + import _Differentiation // Test top-level functions. diff --git a/test/AutoDiff/Serialization/derivative_attr.swift b/test/AutoDiff/Serialization/derivative_attr.swift index 8b306740424c1..43318bf055fbe 100644 --- a/test/AutoDiff/Serialization/derivative_attr.swift +++ b/test/AutoDiff/Serialization/derivative_attr.swift @@ -7,6 +7,9 @@ // REQUIRES: differentiable_programming +// We currently lack availability information (rdar://57975086) +// UNSUPPORTED: use_os_stdlib + import _Differentiation // Dummy `Differentiable`-conforming type. diff --git a/test/AutoDiff/Serialization/differentiable_attr.swift b/test/AutoDiff/Serialization/differentiable_attr.swift index c1dcdfacfc5b2..7da58171a4593 100644 --- a/test/AutoDiff/Serialization/differentiable_attr.swift +++ b/test/AutoDiff/Serialization/differentiable_attr.swift @@ -4,6 +4,9 @@ // RUN: %target-sil-opt -disable-sil-linking -enable-sil-verify-all %t/differentiable_attr.swiftmodule -o - | %FileCheck %s // REQUIRES: differentiable_programming +// We currently lack availability information (rdar://57975086) +// UNSUPPORTED: use_os_stdlib + // TODO(TF-836): Enable this test. // Blocked by TF-828: `@differentiable` attribute type-checking. // XFAIL: * diff --git a/test/AutoDiff/Serialization/transpose_attr.swift b/test/AutoDiff/Serialization/transpose_attr.swift index 80bc7a220586b..c2d04614d5f96 100644 --- a/test/AutoDiff/Serialization/transpose_attr.swift +++ b/test/AutoDiff/Serialization/transpose_attr.swift @@ -6,6 +6,9 @@ // BCANALYZER-NOT: UnknownCode // REQUIRES: differentiable_programming +// We currently lack availability information (rdar://57975086) +// UNSUPPORTED: use_os_stdlib + // TODO(TF-838): Enable this test. // Blocked by TF-830: `@transpose` attribute type-checking. // XFAIL: * diff --git a/test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift b/test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift index 977072bac6318..79e8bac8f379a 100644 --- a/test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift +++ b/test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift @@ -2,6 +2,9 @@ // REQUIRES: executable_test // REQUIRES: differentiable_programming +// We currently lack availability information (rdar://57975086) +// UNSUPPORTED: use_os_stdlib + import _Differentiation // Test `Differentiable` protocol conformances for stdlib types. From 980b1f0561b5b2c5ac4b7d25cf876af70a5d3d04 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 19 Dec 2019 15:00:22 -0800 Subject: [PATCH 143/478] [di] Hide some internal state and give some methods better names given current usage. NFC. --- .../Mandatory/DIMemoryUseCollector.cpp | 12 +++---- .../Mandatory/DIMemoryUseCollector.h | 32 +++++++++++++------ .../Mandatory/DefiniteInitialization.cpp | 17 +++++----- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 5188411efe102..bfb32ac76f27a 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -558,7 +558,7 @@ class ElementUseCollector { // If this is a delegating initializer, collect uses specially. if (IsSelfOfNonDelegatingInitializer && - TheMemory.getType()->getClassOrBoundGenericClass() != nullptr) { + TheMemory.getASTType()->getClassOrBoundGenericClass() != nullptr) { assert(!TheMemory.isDerivedClassSelfOnly() && "Should have been handled outside of here"); // If this is a class pointer, we need to look through ref_element_addrs. @@ -1125,14 +1125,14 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { /// constructor. The memory object has class type. void ElementUseCollector::collectClassSelfUses() { assert(IsSelfOfNonDelegatingInitializer && - TheMemory.getType()->getClassOrBoundGenericClass() != nullptr); + TheMemory.getASTType()->getClassOrBoundGenericClass() != nullptr); // For efficiency of lookup below, compute a mapping of the local ivars in the // class to their element number. llvm::SmallDenseMap EltNumbering; { - SILType T = TheMemory.MemorySILType; + SILType T = TheMemory.getType(); auto *NTD = T.getNominalOrBoundGenericNominal(); unsigned NumElements = 0; for (auto *VD : NTD->getStoredProperties()) { @@ -1149,7 +1149,7 @@ void ElementUseCollector::collectClassSelfUses() { // MUI use-def chain directly to find our uses. auto *MemoryInst = TheMemory.MemoryInst; if (MemoryInst->getKind() == MarkUninitializedInst::RootSelf) { - collectClassSelfUses(MemoryInst, TheMemory.MemorySILType, EltNumbering); + collectClassSelfUses(MemoryInst, TheMemory.getType(), EltNumbering); return; } @@ -1217,7 +1217,7 @@ void ElementUseCollector::collectClassSelfUses() { // Loads of the box produce self, so collect uses from them. if (isa(User) || isa(User)) { auto load = cast(User); - collectClassSelfUses(load, TheMemory.MemorySILType, EltNumbering); + collectClassSelfUses(load, TheMemory.getType(), EltNumbering); continue; } @@ -1857,7 +1857,7 @@ static bool shouldPerformClassInitSelf(const DIMemoryObjectInfo &MemoryInfo) { return true; return MemoryInfo.isNonDelegatingInit() && - MemoryInfo.getType()->getClassOrBoundGenericClass() != nullptr && + MemoryInfo.getASTType()->getClassOrBoundGenericClass() != nullptr && MemoryInfo.isDerivedClassSelfOnly(); } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index cc9b8c56add95..01081dbaa82f5 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -56,14 +56,18 @@ class DIMemoryObjectInfo { /// The uninitialized memory that we are analyzing. MarkUninitializedInst *MemoryInst; +private: /// This is the base type of the memory allocation. SILType MemorySILType; +public: /// This is the count of elements being analyzed. For memory objects that are /// tuples, this is the flattened element count. For 'self' members in init - /// methods, this is the local field count (+1 for derive classes). + /// methods, this is the local field count (+1 for super/self classes were + /// initialized). unsigned NumElements; +private: /// True if the memory object being analyzed represents a 'let', which is /// initialize-only (reassignments are not allowed). bool IsLet = false; @@ -81,23 +85,31 @@ class DIMemoryObjectInfo { /// Return the first instruction of the function containing the memory object. SILInstruction *getFunctionEntryPoint() const; - CanType getType() const { return MemorySILType.getASTType(); } + CanType getASTType() const { return MemorySILType.getASTType(); } + SILType getType() const { return MemorySILType; } + + /// Returns true if this memory object is of trivial type. + bool hasTrivialType() const { return MemorySILType.isTrivial(getFunction()); } + + /// Returns true if NumElements has a dummy value in it to force a struct to + /// be non-empty. + bool hasDummyElement() const { return HasDummyElement; } SingleValueInstruction *getAddress() const { return MemoryInst; } - /// getNumMemoryElements - Return the number of elements, without the extra - /// "super.init" tracker in initializers of derived classes. + /// Return the number of elements, without the extra "super.init" tracker in + /// initializers of derived classes. unsigned getNumMemoryElements() const { return NumElements - (unsigned)isDerivedClassSelf(); } - /// isAnyInitSelf - Return true if this is 'self' in any kind of initializer. + /// Return true if this is 'self' in any kind of initializer. bool isAnyInitSelf() const { return !MemoryInst->isVar(); } /// True if the memory object is the 'self' argument of a struct initializer. bool isStructInitSelf() const { if (MemoryInst->isRootSelf() || MemoryInst->isCrossModuleRootSelf()) { - if (auto decl = getType()->getAnyNominal()) { + if (auto decl = getASTType()->getAnyNominal()) { if (isa(decl)) { return true; } @@ -123,7 +135,7 @@ class DIMemoryObjectInfo { return false; if (!MemoryInst->isVar()) { - if (auto decl = getType()->getAnyNominal()) { + if (auto decl = getASTType()->getAnyNominal()) { if (isa(decl)) { return true; } @@ -176,14 +188,14 @@ class DIMemoryObjectInfo { return false; } - /// emitElementAddress - Given an element number (in the flattened sense) - /// return a pointer to a leaf element of the specified number. + /// Given an element number (in the flattened sense) return a pointer to a + /// leaf element of the specified number. SILValue emitElementAddress(unsigned TupleEltNo, SILLocation Loc, SILBuilder &B, llvm::SmallVectorImpl> &EndBorrowList) const; - /// getElementType - Return the swift type of the specified element. + /// Return the swift type of the specified element. SILType getElementType(unsigned EltNo) const; /// Push the symbolic path name to the specified element number onto the diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 29f8bc8785d1c..f38626d68a82e 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -792,11 +792,11 @@ void LifetimeChecker::doIt() { // memory object will destruct the memory. If the memory (or some element // thereof) is not initialized on some path, the bad things happen. Process // releases to adjust for this. - if (!TheMemory.MemorySILType.isTrivial(F)) { + if (!TheMemory.hasTrivialType()) { for (unsigned i = 0, e = Destroys.size(); i != e; ++i) processNonTrivialRelease(i); } - + // If the memory object had any non-trivial stores that are init or assign // based on the control flow path reaching them, then insert dynamic control // logic and CFG diamonds to handle this. @@ -1020,7 +1020,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { if (auto *ctor = fnLoc.getAsASTNode()) selfTy = ctor->getImplicitSelfDecl()->getType(); else - selfTy = TheMemory.getType(); + selfTy = TheMemory.getASTType(); StructDecl *theStruct = selfTy->getStructOrBoundGenericStruct(); assert(theStruct); @@ -1284,7 +1284,7 @@ void LifetimeChecker::handleEscapeUse(const DIMemoryUse &Use) { diagnose(Module, Inst->getLoc(), diag::self_before_selfinit_value_type); if (!HasSuggestedNoArgSelfInit && FullyUninitialized) { auto *maybeStruct = - TheMemory.getType().getStructOrBoundGenericStruct(); + TheMemory.getASTType().getStructOrBoundGenericStruct(); maybeSuggestNoArgSelfInit(Module, Inst->getLoc(), maybeStruct); HasSuggestedNoArgSelfInit = true; } @@ -1618,9 +1618,8 @@ bool LifetimeChecker::diagnoseReturnWithoutInitializingStoredProperties( if (!shouldEmitError(Inst)) return true; - if (TheMemory.isCrossModuleStructInitSelf() && - TheMemory.HasDummyElement) { - Type selfTy = TheMemory.getType(); + if (TheMemory.isCrossModuleStructInitSelf() && TheMemory.hasDummyElement()) { + Type selfTy = TheMemory.getASTType(); const StructDecl *theStruct = selfTy->getStructOrBoundGenericStruct(); assert(theStruct); @@ -1813,7 +1812,7 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { assert(TheMemory.isAnyInitSelf()); assert(!TheMemory.isClassInitSelf() || TheMemory.isNonRootClassSelf()); - assert(TheMemory.getType()->hasReferenceSemantics()); + assert(TheMemory.getASTType()->hasReferenceSemantics()); // Determine the liveness states of the memory object, including the // self/super.init state. @@ -1981,7 +1980,7 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, if (Pointer->getType().isAddress()) Pointer = B.createLoad(Loc, Pointer, LoadOwnershipQualifier::Take); - auto MetatypeTy = CanMetatypeType::get(TheMemory.MemorySILType.getASTType(), + auto MetatypeTy = CanMetatypeType::get(TheMemory.getASTType(), MetatypeRepresentation::Thick); auto SILMetatypeTy = SILType::getPrimitiveObjectType(MetatypeTy); SILValue Metatype; From b26869825323629bd9a30b6df4e26d867b592329 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 19 Dec 2019 10:28:18 -0800 Subject: [PATCH 144/478] [ownership] Allow mark_uninitialized to only take owned/none ownership parameters. More explicitly, this disallows guaranteed values to be passed to mark_uninitialized. From the perspective of OSSA, it only makes sense for mark_uninitialized to consume its incoming parameter since we want the underlying allocated value to have its "entire" lifetime "funnel" through the mark_uninitialized. Since if the input value is none, we still accept it, all mark_uninitialized on pointers will not be affected by this. NOTE: Today, mark_uninitialized can not even accept a borrow parameter (we severely restrict what parameters it can take). So I can not actually even write a test for this today since the verifier will run after parsing and assert. But from a modeling perspective and from the perspective of not creating confusion, specifying the ownership of mark_uninitialized more explicitly is good. --- lib/SIL/OperandOwnership.cpp | 3 ++- lib/SIL/ValueOwnership.cpp | 10 ++++++++-- test/SIL/ownership-verifier/undef.sil | 12 ++++++------ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/SIL/OperandOwnership.cpp b/lib/SIL/OperandOwnership.cpp index 9f9a79ae6fa77..a08e2bd634ab9 100644 --- a/lib/SIL/OperandOwnership.cpp +++ b/lib/SIL/OperandOwnership.cpp @@ -333,7 +333,6 @@ FORWARD_ANY_OWNERSHIP_INST(ConvertFunction) FORWARD_ANY_OWNERSHIP_INST(RefToBridgeObject) FORWARD_ANY_OWNERSHIP_INST(BridgeObjectToRef) FORWARD_ANY_OWNERSHIP_INST(UnconditionalCheckedCast) -FORWARD_ANY_OWNERSHIP_INST(MarkUninitialized) FORWARD_ANY_OWNERSHIP_INST(UncheckedEnumData) FORWARD_ANY_OWNERSHIP_INST(DestructureStruct) FORWARD_ANY_OWNERSHIP_INST(DestructureTuple) @@ -355,6 +354,8 @@ FORWARD_ANY_OWNERSHIP_INST(DestructureTuple) } FORWARD_CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, MustBeLive, TupleExtract) FORWARD_CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, MustBeLive, StructExtract) +FORWARD_CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, MustBeInvalidated, + MarkUninitialized) #undef CONSTANT_OR_NONE_OWNERSHIP_INST OperandOwnershipKindMap diff --git a/lib/SIL/ValueOwnership.cpp b/lib/SIL/ValueOwnership.cpp index 44fa9a81c4a16..94ebb2555fdd6 100644 --- a/lib/SIL/ValueOwnership.cpp +++ b/lib/SIL/ValueOwnership.cpp @@ -157,7 +157,8 @@ CONSTANT_OWNERSHIP_INST(Unowned, ValueToBridgeObject) #define CONSTANT_OR_NONE_OWNERSHIP_INST(OWNERSHIP, INST) \ ValueOwnershipKind ValueOwnershipKindClassifier::visit##INST##Inst( \ INST##Inst *I) { \ - if (I->getType().isTrivial(*I->getFunction())) { \ + if (I->getType().isTrivial(*I->getFunction()) || \ + I->getType().isAddress()) { \ return ValueOwnershipKind::None; \ } \ return ValueOwnershipKind::OWNERSHIP; \ @@ -174,6 +175,12 @@ CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, OpenExistentialValue) CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, OpenExistentialBoxValue) CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, UnconditionalCheckedCastValue) +// Given an owned value, mark_uninitialized always forwards an owned value since +// we want to make sure that all destroys of that value must come through the +// mark_uninitialized (which will happen due to mark_uninitialized consuming the +// value). +CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, MarkUninitialized) + // unchecked_bitwise_cast is a bitwise copy. It produces a trivial or unowned // result. // @@ -249,7 +256,6 @@ FORWARDING_OWNERSHIP_INST(Tuple) FORWARDING_OWNERSHIP_INST(UncheckedRefCast) FORWARDING_OWNERSHIP_INST(UnconditionalCheckedCast) FORWARDING_OWNERSHIP_INST(Upcast) -FORWARDING_OWNERSHIP_INST(MarkUninitialized) FORWARDING_OWNERSHIP_INST(UncheckedEnumData) FORWARDING_OWNERSHIP_INST(SelectEnum) FORWARDING_OWNERSHIP_INST(Enum) diff --git a/test/SIL/ownership-verifier/undef.sil b/test/SIL/ownership-verifier/undef.sil index 74d53def70f34..ebf5f138969ab 100644 --- a/test/SIL/ownership-verifier/undef.sil +++ b/test/SIL/ownership-verifier/undef.sil @@ -20,9 +20,9 @@ struct MyInt { // CHECK-NEXT: Operand Ownership Map: // CHECK-NEXT: Op #: 0 // CHECK-NEXT: Ownership Map: -- OperandOwnershipKindMap -- -// CHECK-NEXT: unowned: Yes. Liveness: MustBeLive -// CHECK-NEXT: owned: Yes. Liveness: MustBeLive -// CHECK-NEXT: guaranteed: Yes. Liveness: MustBeLive +// CHECK-NEXT: unowned: No. +// CHECK-NEXT: owned: Yes. Liveness: MustBeInvalidated +// CHECK-NEXT: guaranteed: No. // CHECK-NEXT: any: Yes. Liveness: MustBeLive // CHECK: Visiting: {{.*}}%1 = mark_uninitialized [var] undef : $Klass // CHECK-NEXT: Operand Ownership Map: @@ -36,9 +36,9 @@ struct MyInt { // CHECK-NEXT: Operand Ownership Map: // CHECK-NEXT: Op #: 0 // CHECK-NEXT: Ownership Map: -- OperandOwnershipKindMap -- -// CHECK-NEXT: unowned: Yes. Liveness: MustBeLive -// CHECK-NEXT: owned: Yes. Liveness: MustBeLive -// CHECK-NEXT: guaranteed: Yes. Liveness: MustBeLive +// CHECK-NEXT: unowned: No. +// CHECK-NEXT: owned: Yes. Liveness: MustBeInvalidated +// CHECK-NEXT: guaranteed: No. // CHECK-NEXT: any: Yes. Liveness: MustBeLive // CHECK: Visiting: {{.*}}%4 = struct $MyInt (undef : $Builtin.Int32) // CHECK-NEXT: Operand Ownership Map: From 35a1906717ead7ac5026786a706700ad92efdb8f Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 19 Dec 2019 15:18:52 -0800 Subject: [PATCH 145/478] [Clang importer] objc_direct methods/properties are unavailable in Swift. objc_direct methods and properties cannot be used from Swift, so mark them as unavailable in the importer. Fixes rdar://problem/57287895 --- lib/ClangImporter/ImportDecl.cpp | 9 +++++++++ test/ClangImporter/Inputs/objc_direct.h | 12 ++++++++++++ test/ClangImporter/objc_direct.swift | 14 ++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 test/ClangImporter/Inputs/objc_direct.h create mode 100644 test/ClangImporter/objc_direct.swift diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 46748e909aac0..6459d0e9cbf04 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -7555,6 +7555,15 @@ void ClangImporter::Implementation::importAttributes( } } + if (auto method = dyn_cast(ClangDecl)) { + if (method->isDirectMethod() && !AnyUnavailable) { + auto attr = AvailableAttr::createPlatformAgnostic( + C, "", "", PlatformAgnosticAvailabilityKind::UnavailableInSwift); + MappedDecl->getAttrs().add(attr); + AnyUnavailable = true; + } + } + // If the declaration is unavailable, we're done. if (AnyUnavailable) return; diff --git a/test/ClangImporter/Inputs/objc_direct.h b/test/ClangImporter/Inputs/objc_direct.h new file mode 100644 index 0000000000000..d283e587c45f1 --- /dev/null +++ b/test/ClangImporter/Inputs/objc_direct.h @@ -0,0 +1,12 @@ +@interface Bar +@property (readonly, direct, nonatomic) int directProperty; +- (void)directMethod __attribute__((objc_direct)); ++ (void)directClassMethod __attribute__((objc_direct)); +@end + +__attribute__((objc_direct_members)) +@interface Bar () +@property (readonly, nonatomic) int directProperty2; +- (void)directMethod2; ++ (void)directClassMethod2; +@end diff --git a/test/ClangImporter/objc_direct.swift b/test/ClangImporter/objc_direct.swift new file mode 100644 index 0000000000000..494e002b8d46e --- /dev/null +++ b/test/ClangImporter/objc_direct.swift @@ -0,0 +1,14 @@ +// RUN: %target-swift-frontend-verify -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/objc_direct.h -verify-ignore-unknown + +// REQUIRES: objc_interop + +func callThingsOnBar(_ x: Bar) { + let _ = x.directProperty // expected-error {{getter for 'directProperty' is unavailable in Swift}} + let _ = x.directProperty2 // expected-error {{getter for 'directProperty2' is unavailable in Swift}} + + x.directMethod() // expected-error {{'directMethod()' is unavailable in Swift}} + x.directMethod2() // expected-error {{'directMethod2()' is unavailable in Swift}} + + Bar.directClassMethod() // expected-error {{'directClassMethod()' is unavailable in Swift}} + Bar.directClassMethod2() // expected-error {{'directClassMethod2()' is unavailable in Swift}} +} From 637314aeefd398065145521109322dddd11ab413 Mon Sep 17 00:00:00 2001 From: Marc Rasi Date: Wed, 18 Dec 2019 19:55:40 -0800 Subject: [PATCH 146/478] [AutoDiff] factor derivative typechecking helper out of AttributeChecker --- include/swift/AST/ASTContext.h | 2 +- lib/Sema/TypeCheckAttr.cpp | 155 +++++++++++++++++++-------------- 2 files changed, 90 insertions(+), 67 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index d54fd98c1ac0c..51e85ae49a550 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -290,7 +290,7 @@ class ASTContext final { // derivative function configurations per original `AbstractFunctionDecl`. llvm::DenseMap< std::tuple, - DerivativeAttr *> + llvm::SmallPtrSet> DerivativeAttrs; private: diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 74c4dfc09165e..31029fab1c704 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3391,14 +3391,28 @@ getAutoDiffOriginalFunctionType(AnyFunctionType *derivativeFnTy) { return originalType; } -void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { +/// Typechecks the given derivative attribute `attr` on decl `D`. +/// +/// Effects are: +/// - Sets the original function and parameter indices on `attr`. +/// - Diagnoses errors. +/// - Stores the attribute in `ASTContext::DerivativeAttrs`. +/// +/// \returns true on error, false on success. +static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, + DerivativeAttr *attr) { + // Note: Implementation must be idempotent because it can get called multiple + // times for the same attribute. + + auto &diags = Ctx.Diags; + // `@derivative` attribute requires experimental differentiable programming // to be enabled. auto &ctx = D->getASTContext(); if (!ctx.LangOpts.EnableExperimentalDifferentiableProgramming) { - diagnoseAndRemoveAttr( - attr, diag::experimental_differentiable_programming_disabled); - return; + diags.diagnose(attr->getLocation(), + diag::experimental_differentiable_programming_disabled); + return true; } auto *derivative = cast(D); auto lookupConformance = @@ -3418,26 +3432,27 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { auto derivativeResultTupleType = derivativeResultType->getAs(); if (!derivativeResultTupleType || derivativeResultTupleType->getNumElements() != 2) { - diagnoseAndRemoveAttr(attr, diag::derivative_attr_expected_result_tuple); - return; + diags.diagnose(attr->getLocation(), + diag::derivative_attr_expected_result_tuple); + return true; } auto valueResultElt = derivativeResultTupleType->getElement(0); auto funcResultElt = derivativeResultTupleType->getElement(1); // Get derivative kind and derivative function identifier. AutoDiffDerivativeFunctionKind kind; if (valueResultElt.getName().str() != "value") { - diagnoseAndRemoveAttr( - attr, diag::derivative_attr_invalid_result_tuple_value_label); - return; + diags.diagnose(attr->getLocation(), + diag::derivative_attr_invalid_result_tuple_value_label); + return true; } if (funcResultElt.getName().str() == "differential") { kind = AutoDiffDerivativeFunctionKind::JVP; } else if (funcResultElt.getName().str() == "pullback") { kind = AutoDiffDerivativeFunctionKind::VJP; } else { - diagnoseAndRemoveAttr( - attr, diag::derivative_attr_invalid_result_tuple_func_label); - return; + diags.diagnose(attr->getLocation(), + diag::derivative_attr_invalid_result_tuple_func_label); + return true; } attr->setDerivativeKind(kind); // `value: R` result tuple element must conform to `Differentiable`. @@ -3448,10 +3463,10 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { auto valueResultConf = TypeChecker::conformsToProtocol( valueResultType, diffableProto, derivative->getDeclContext(), None); if (!valueResultConf) { - diagnoseAndRemoveAttr(attr, - diag::derivative_attr_result_value_not_differentiable, - valueResultElt.getType()); - return; + diags.diagnose(attr->getLocation(), + diag::derivative_attr_result_value_not_differentiable, + valueResultElt.getType()); + return true; } // Compute expected original function type and look up original function. @@ -3495,22 +3510,23 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { }; auto noneValidDiagnostic = [&]() { - diagnose(originalName.Loc, - diag::autodiff_attr_original_decl_none_valid_found, - originalName.Name, originalFnType); + diags.diagnose(originalName.Loc, + diag::autodiff_attr_original_decl_none_valid_found, + originalName.Name, originalFnType); }; auto ambiguousDiagnostic = [&]() { - diagnose(originalName.Loc, diag::attr_ambiguous_reference_to_decl, - originalName.Name, attr->getAttrName()); + diags.diagnose(originalName.Loc, diag::attr_ambiguous_reference_to_decl, + originalName.Name, attr->getAttrName()); }; auto notFunctionDiagnostic = [&]() { - diagnose(originalName.Loc, diag::autodiff_attr_original_decl_invalid_kind, - originalName.Name); + diags.diagnose(originalName.Loc, + diag::autodiff_attr_original_decl_invalid_kind, + originalName.Name); }; std::function invalidTypeContextDiagnostic = [&]() { - diagnose(originalName.Loc, - diag::autodiff_attr_original_decl_not_same_type_context, - originalName.Name); + diags.diagnose(originalName.Loc, + diag::autodiff_attr_original_decl_not_same_type_context, + originalName.Name); }; // Returns true if the derivative function and original function candidate are @@ -3544,22 +3560,19 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { derivativeTypeCtx, isValidOriginal, noneValidDiagnostic, ambiguousDiagnostic, notFunctionDiagnostic, lookupOptions, hasValidTypeContext, invalidTypeContextDiagnostic); - if (!originalAFD) { - attr->setInvalid(); - return; - } + if (!originalAFD) + return true; // Diagnose original stored properties. Stored properties cannot have custom // registered derivatives. if (auto *accessorDecl = dyn_cast(originalAFD)) { auto *asd = accessorDecl->getStorage(); if (asd->hasStorage()) { - diagnose(originalName.Loc, - diag::derivative_attr_original_stored_property_unsupported, - originalName.Name); - diagnose(originalAFD->getLoc(), diag::decl_declared_here, - asd->getFullName()); - attr->setInvalid(); - return; + diags.diagnose(originalName.Loc, + diag::derivative_attr_original_stored_property_unsupported, + originalName.Name); + diags.diagnose(originalAFD->getLoc(), diag::decl_declared_here, + asd->getFullName()); + return true; } } attr->setOriginalFunction(originalAFD); @@ -3576,19 +3589,15 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { resolvedWrtParamIndices = computeDifferentiationParameters( parsedWrtParams, derivative, derivative->getGenericEnvironment(), attr->getAttrName(), attr->getLocation()); - if (!resolvedWrtParamIndices) { - attr->setInvalid(); - return; - } + if (!resolvedWrtParamIndices) + return true; // Check if the `wrt:` parameter indices are valid. if (checkDifferentiationParameters( originalAFD, resolvedWrtParamIndices, originalFnType, derivative->getGenericEnvironment(), derivative->getModuleContext(), - parsedWrtParams, attr->getLocation())) { - attr->setInvalid(); - return; - } + parsedWrtParams, attr->getLocation())) + return true; // Set the resolved `wrt:` parameter indices in the attribute. attr->setParameterIndices(resolvedWrtParamIndices); @@ -3647,42 +3656,56 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { // Check if differential/pullback type matches expected type. if (!actualFuncEltType->isEqual(expectedFuncEltType)) { // Emit differential/pullback type mismatch error on attribute. - diagnoseAndRemoveAttr(attr, diag::derivative_attr_result_func_type_mismatch, - funcResultElt.getName(), originalAFD->getFullName()); + diags.diagnose(attr->getLocation(), + diag::derivative_attr_result_func_type_mismatch, + funcResultElt.getName(), originalAFD->getFullName()); // Emit note with expected differential/pullback type on actual type // location. auto *tupleReturnTypeRepr = cast(derivative->getBodyResultTypeLoc().getTypeRepr()); auto *funcEltTypeRepr = tupleReturnTypeRepr->getElementType(1); - diagnose(funcEltTypeRepr->getStartLoc(), - diag::derivative_attr_result_func_type_mismatch_note, - funcResultElt.getName(), expectedFuncEltType) + diags + .diagnose(funcEltTypeRepr->getStartLoc(), + diag::derivative_attr_result_func_type_mismatch_note, + funcResultElt.getName(), expectedFuncEltType) .highlight(funcEltTypeRepr->getSourceRange()); // Emit note showing original function location, if possible. if (originalAFD->getLoc().isValid()) - diagnose(originalAFD->getLoc(), - diag::derivative_attr_result_func_original_note, - originalAFD->getFullName()); - return; + diags.diagnose(originalAFD->getLoc(), + diag::derivative_attr_result_func_original_note, + originalAFD->getFullName()); + return true; } // Reject different-file derivative registration. // TODO(TF-1021): Lift this restriction. if (originalAFD->getParentSourceFile() != derivative->getParentSourceFile()) { - diagnoseAndRemoveAttr(attr, - diag::derivative_attr_not_in_same_file_as_original); - return; + diags.diagnose(attr->getLocation(), + diag::derivative_attr_not_in_same_file_as_original); + return true; } // Reject duplicate `@derivative` attributes. - auto insertion = Ctx.DerivativeAttrs.try_emplace( - std::make_tuple(originalAFD, resolvedWrtParamIndices, kind), attr); - if (!insertion.second) { - diagnoseAndRemoveAttr(attr, - diag::derivative_attr_original_already_has_derivative, - originalAFD->getFullName()); - diagnose(insertion.first->getSecond()->getLocation(), - diag::derivative_attr_duplicate_note); - return; + auto &derivativeAttrs = Ctx.DerivativeAttrs[std::make_tuple( + originalAFD, resolvedWrtParamIndices, kind)]; + derivativeAttrs.insert(attr); + if (derivativeAttrs.size() > 1) { + diags.diagnose(attr->getLocation(), + diag::derivative_attr_original_already_has_derivative, + originalAFD->getFullName()); + for (auto *duplicateAttr : derivativeAttrs) { + if (duplicateAttr == attr) + continue; + diags.diagnose(duplicateAttr->getLocation(), + diag::derivative_attr_duplicate_note); + } + return true; } + + return false; +} + +void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { + if (typeCheckDerivativeAttr(Ctx, D, attr)) + attr->setInvalid(); } From 71697c37ca79898083058c2293cc362af495c9d1 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 19 Dec 2019 21:38:41 -0500 Subject: [PATCH 147/478] Allow implicit self in escaping closures when self usage is unlikely to cause cycle (#23934) * WIP implementation * Cleanup implementation * Install backedge rather than storing array reference * Add diagnostics * Add missing parameter to ResultFinderForTypeContext constructor * Fix tests for correct fix-it language * Change to solution without backedge, change lookup behavior * Improve diagnostics for weak captures and captures under different names * Remove ghosts of implementations past * Address review comments * Reorder member variable initialization * Fix typos * Exclude value types from explicit self requirements * Add tests * Add implementation for AST lookup * Add tests * Begin addressing review comments * Re-enable AST scope lookup * Add fixme * Pull fix-its into a separate function * Remove capturedSelfContext tracking from type property initializers * Add const specifiers to arguments * Address review comments * Fix string literals * Refactor implicit self diagnostics * Add comment * Remove trailing whitespace * Add tests for capture list across multiple lines * Add additional test * Fix typo * Remove use of ?: to fix linux build * Remove second use of ?: * Rework logic for finding nested self contexts --- include/swift/AST/ASTScope.h | 20 +++ include/swift/AST/Decl.h | 11 +- include/swift/AST/DiagnosticsSema.def | 15 +- include/swift/AST/Expr.h | 33 ++++- include/swift/AST/NameLookup.h | 16 +- include/swift/Parse/Lexer.h | 12 +- include/swift/Parse/Parser.h | 16 +- lib/AST/ASTScopeLookup.cpp | 68 ++++++++- lib/AST/Decl.cpp | 1 + lib/AST/Expr.cpp | 17 +++ lib/AST/NameLookup.cpp | 7 + lib/AST/UnqualifiedLookup.cpp | 175 +++++++++++++++------- lib/Parse/Lexer.cpp | 5 +- lib/Parse/ParseExpr.cpp | 42 ++++-- lib/Sema/DebuggerTestingTransform.cpp | 5 +- lib/Sema/MiscDiagnostics.cpp | 205 +++++++++++++++++++++----- lib/Sema/TypeCheckNameLookup.cpp | 18 ++- lib/Sema/TypeCheckREPL.cpp | 5 +- test/Constraints/closures.swift | 2 +- test/attr/attr_autoclosure.swift | 6 +- test/attr/attr_noescape.swift | 97 ++++++------ test/expr/closure/closures.swift | 137 ++++++++++++++++- 22 files changed, 720 insertions(+), 193 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 02db70aba959b..abf03f84eb2e0 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -479,6 +479,10 @@ class ASTScopeImpl { /// asked. virtual Optional> computeSelfDCForParent() const; + /// Returns the context that should be used when a nested scope (e.g. a + /// closure) captures self explicitly. + virtual NullablePtr capturedSelfDC() const; + protected: /// Find either locals or members (no scope has both) /// \param history The scopes visited since the start of lookup (including @@ -692,6 +696,15 @@ class Portion { /// to compute the selfDC from the history. static NullablePtr computeSelfDC(ArrayRef history); + + /// If we find a lookup result that requires the dynamic implict self value, + /// we need to check the nested scopes to see if any closures explicitly + /// captured \c self. In that case, the appropriate selfDC is that of the + /// innermost closure which captures a \c self value from one of this type's + /// methods. + static NullablePtr + checkNestedScopesForSelfCapture(ArrayRef history, + size_t start); }; /// Behavior specific to representing the trailing where clause of a @@ -1449,6 +1462,13 @@ class ClosureParametersScope final : public AbstractClosureScope { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; + + /// Since explicit captures of \c self by closures enable the use of implicit + /// \c self, we need to make sure that the appropriate \c self is used as the + /// base decl for these uses (otherwise, the capture would be marked as + /// unused. \c ClosureParametersScope::capturedSelfDC() checks if we have such + /// a capture of self. + NullablePtr capturedSelfDC() const override; protected: ASTScopeImpl *expandSpecifically(ScopeCreator &) override; diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 9f007fa1c9001..110072d14bb93 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -334,12 +334,15 @@ class alignas(1 << DeclAlignInBits) Decl { IsStatic : 1 ); - SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1+1, /// Encodes whether this is a 'let' binding. Introducer : 1, /// Whether this declaration was an element of a capture list. IsCaptureList : 1, + + /// Whether this declaration captures the 'self' param under the same name. + IsSelfParamCapture : 1, /// Whether this vardecl has an initial value bound to it in a way /// that isn't represented in the AST with an initializer in the pattern @@ -5029,6 +5032,12 @@ class VarDecl : public AbstractStorageDecl { /// Is this an element in a capture list? bool isCaptureList() const { return Bits.VarDecl.IsCaptureList; } + + /// Is this a capture of the self param? + bool isSelfParamCapture() const { return Bits.VarDecl.IsSelfParamCapture; } + void setIsSelfParamCapture(bool IsSelfParamCapture = true) { + Bits.VarDecl.IsSelfParamCapture = IsSelfParamCapture; + } /// Return true if this vardecl has an initial value bound to it in a way /// that isn't represented in the AST with an initializer in the pattern diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index b6c87490c14c8..cb6e9aa11f745 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3244,11 +3244,20 @@ ERROR(self_assignment_var,none, ERROR(self_assignment_prop,none, "assigning a property to itself", ()) ERROR(property_use_in_closure_without_explicit_self,none, - "reference to property %0 in closure requires explicit 'self.' to make" - " capture semantics explicit", (Identifier)) + "reference to property %0 in closure requires explicit use of 'self' to" + " make capture semantics explicit", (Identifier)) ERROR(method_call_in_closure_without_explicit_self,none, - "call to method %0 in closure requires explicit 'self.' to make" + "call to method %0 in closure requires explicit use of 'self' to make" " capture semantics explicit", (Identifier)) +NOTE(note_capture_self_explicitly,none, + "capture 'self' explicitly to enable implicit 'self' in this closure", ()) +NOTE(note_reference_self_explicitly,none, + "reference 'self.' explicitly", ()) +NOTE(note_other_self_capture,none, + "variable other than 'self' captured here under the name 'self' does not " + "enable implicit 'self'", ()) +NOTE(note_self_captured_weakly,none, + "weak capture of 'self' here does not enable implicit 'self'", ()) ERROR(implicit_use_of_self_in_closure,none, "implicit use of 'self' in closure; use 'self.' to make" " capture semantics explicit", ()) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 7afd8e75db84b..77cdc587f7129 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -65,6 +65,7 @@ namespace swift { class EnumElementDecl; class CallExpr; class KeyPathExpr; + class CaptureListExpr; enum class ExprKind : uint8_t { #define EXPR(Id, Parent) Id, @@ -3582,6 +3583,18 @@ class SerializedAbstractClosureExpr : public SerializedLocalDeclContext { /// \endcode class ClosureExpr : public AbstractClosureExpr { + /// The range of the brackets of the capture list, if present. + SourceRange BracketRange; + + /// The (possibly null) VarDecl captured by this closure with the literal name + /// "self". In order to recover this information at the time of name lookup, + /// we must be able to access it from the associated DeclContext. + /// Because the DeclContext inside a closure is the closure itself (and not + /// the CaptureListExpr which would normally maintain this sort of + /// information about captured variables), we need to have some way to access + /// this information directly on the ClosureExpr. + VarDecl *CapturedSelfDecl; + /// The location of the "throws", if present. SourceLoc ThrowsLoc; @@ -3598,16 +3611,16 @@ class ClosureExpr : public AbstractClosureExpr { /// The body of the closure, along with a bit indicating whether it /// was originally just a single expression. llvm::PointerIntPair Body; - public: - ClosureExpr(ParameterList *params, SourceLoc throwsLoc, SourceLoc arrowLoc, + ClosureExpr(SourceRange bracketRange, VarDecl *capturedSelfDecl, + ParameterList *params, SourceLoc throwsLoc, SourceLoc arrowLoc, SourceLoc inLoc, TypeLoc explicitResultType, unsigned discriminator, DeclContext *parent) : AbstractClosureExpr(ExprKind::Closure, Type(), /*Implicit=*/false, discriminator, parent), + BracketRange(bracketRange), CapturedSelfDecl(capturedSelfDecl), ThrowsLoc(throwsLoc), ArrowLoc(arrowLoc), InLoc(inLoc), - ExplicitResultType(explicitResultType), - Body(nullptr) { + ExplicitResultType(explicitResultType), Body(nullptr) { setParameterList(params); Bits.ClosureExpr.HasAnonymousClosureVars = false; } @@ -3639,6 +3652,8 @@ class ClosureExpr : public AbstractClosureExpr { /// explicitly-specified result type. bool hasExplicitResultType() const { return ArrowLoc.isValid(); } + /// Retrieve the range of the \c '[' and \c ']' that enclose the capture list. + SourceRange getBracketRange() const { return BracketRange; } /// Retrieve the location of the \c '->' for closures with an /// explicit result type. @@ -3704,6 +3719,14 @@ class ClosureExpr : public AbstractClosureExpr { /// Is this a completely empty closure? bool hasEmptyBody() const; + /// VarDecl captured by this closure under the literal name \c self , if any. + VarDecl *getCapturedSelfDecl() const { return CapturedSelfDecl; } + + /// Whether this closure captures the \c self param in its body in such a + /// way that implicit \c self is enabled within its body (i.e. \c self is + /// captured non-weakly). + bool capturesSelfEnablingImplictSelf() const; + static bool classof(const Expr *E) { return E->getKind() == ExprKind::Closure; } @@ -3778,6 +3801,8 @@ struct CaptureListEntry { CaptureListEntry(VarDecl *Var, PatternBindingDecl *Init) : Var(Var), Init(Init) { } + + bool isSimpleSelfCapture() const; }; /// CaptureListExpr - This expression represents the capture list on an explicit diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 552a4be7f5e27..374d8b4fcd310 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -70,13 +70,15 @@ struct LookupResultEntry { /// When finding \c bar() from the function body of \c foo(), \c BaseDC is /// the method \c foo(). /// - /// \c BaseDC will be the method if \c self is needed for the lookup, - /// and will be the type if not. - /// In other words: If \c baseDC is a method, it means you found an instance - /// member and you should add an implicit 'self.' (Each method has its own - /// implicit self decl.) There's one other kind of non-method context that - /// has a 'self.' -- a lazy property initializer, which unlike a non-lazy - /// property can reference \c self) Hence: \code + /// \c BaseDC will be the type if \c self is not needed for the lookup. If + /// \c self is needed, \c baseDC will be either the method or a closure + /// which explicitly captured \c self. + /// In other words: If \c baseDC is a method or a closure, it means you + /// found an instance member and you should add an implicit 'self.' (Each + /// method has its own implicit self decl.) There's one other kind of + /// non-method, non-closure context that has a 'self.' -- a lazy property + /// initializer, which unlike a non-lazy property can reference \c self. + /// \code /// class Outer { ///   static func s() ///   func i() diff --git a/include/swift/Parse/Lexer.h b/include/swift/Parse/Lexer.h index 8441a12dc17d4..8cd9b75a415c7 100644 --- a/include/swift/Parse/Lexer.h +++ b/include/swift/Parse/Lexer.h @@ -293,7 +293,17 @@ class Lexer { /// resides. /// /// \param Loc The source location of the beginning of a token. - static Token getTokenAtLocation(const SourceManager &SM, SourceLoc Loc); + /// + /// \param CRM How comments should be treated by the lexer. Default is to + /// return the comments as tokens. This is needed in situations where + /// detecting the next semantically meaningful token is required, such as + /// the 'implicit self' diagnostic determining whether a capture list is + /// empty (i.e., the opening bracket is immediately followed by a closing + /// bracket, possibly with comments in between) in order to insert the + /// appropriate fix-it. + static Token getTokenAtLocation( + const SourceManager &SM, SourceLoc Loc, + CommentRetentionMode CRM = CommentRetentionMode::ReturnAsTokens); /// Retrieve the source location that points just past the diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index e99b82f7bbd79..ff70de7a7674e 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1514,6 +1514,8 @@ class Parser { /// identifier (',' identifier)* func-signature-result? 'in' /// \endverbatim /// + /// \param bracketRange The range of the brackets enclosing a capture list, if + /// present. Needed to offer fix-its for inserting 'self' into a capture list. /// \param captureList The entries in the capture list. /// \param params The parsed parameter list, or null if none was provided. /// \param arrowLoc The location of the arrow, if present. @@ -1522,12 +1524,14 @@ class Parser { /// /// \returns true if an error occurred, false otherwise. bool parseClosureSignatureIfPresent( - SmallVectorImpl &captureList, - ParameterList *¶ms, - SourceLoc &throwsLoc, - SourceLoc &arrowLoc, - TypeRepr *&explicitResultType, - SourceLoc &inLoc); + SourceRange &bracketRange, + SmallVectorImpl &captureList, + VarDecl *&capturedSelfParamDecl, + ParameterList *¶ms, + SourceLoc &throwsLoc, + SourceLoc &arrowLoc, + TypeRepr *&explicitResultType, + SourceLoc &inLoc); Expr *parseExprAnonClosureArg(); ParserResult parseExprList(tok LeftTok, tok RightTok, diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 07d6d7d40921b..be34aafee6885 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -517,12 +517,59 @@ GenericTypeOrExtensionWhereOrBodyPortion::computeSelfDC( while (i != 0) { Optional> maybeSelfDC = history[--i]->computeSelfDCForParent(); - if (maybeSelfDC) - return *maybeSelfDC; + if (maybeSelfDC) { + // If we've found a selfDC, we'll definitely be returning something. + // However, we may have captured 'self' somewhere down the tree, so we + // can't return outright without checking the nested scopes. + NullablePtr nestedCapturedSelfDC = + checkNestedScopesForSelfCapture(history, i); + return nestedCapturedSelfDC ? nestedCapturedSelfDC : *maybeSelfDC; + } } return nullptr; } +#pragma mark checkNestedScopesForSelfCapture + +NullablePtr +GenericTypeOrExtensionWhereOrBodyPortion::checkNestedScopesForSelfCapture( + ArrayRef history, size_t start) { + NullablePtr innerCapturedSelfDC; + // Start with the next scope down the tree. + size_t j = start; + + // Note: even though having this loop nested inside the while loop from + // GenericTypeOrExtensionWhereOrBodyPortion::computeSelfDC may appear to + // result in quadratic blowup, complexity actually remains linear with respect + // to the size of history. This relies on the fact that + // GenericTypeOrExtensionScope::computeSelfDCForParent returns a null pointer, + // which will cause this method to bail out of the search early. Thus, this + // method is called once per type body in the lookup history, and will not + // end up re-checking the bodies of nested types that have already been + // covered by earlier calls, so the total impact of this method across all + // calls in a single lookup is O(n). + while (j != 0) { + auto *entry = history[--j]; + Optional> selfDCForParent = + entry->computeSelfDCForParent(); + + // If we encounter a scope that should cause us to forget the self + // context (such as a nested type), bail out and use whatever the + // the last inner captured context was. + if (selfDCForParent && (*selfDCForParent).isNull()) + break; + + // Otherwise, if we have a captured self context for this scope, then + // remember it since it is now the innermost scope we have encountered. + NullablePtr capturedSelfDC = entry->capturedSelfDC(); + if (!capturedSelfDC.isNull()) + innerCapturedSelfDC = entry->capturedSelfDC(); + + // Continue searching in the next scope down. + } + return innerCapturedSelfDC; +} + #pragma mark compute isCascadingUse Optional ASTScopeImpl::computeIsCascadingUse( @@ -631,6 +678,23 @@ MethodBodyScope::computeSelfDCForParent() const { return NullablePtr(decl); } +#pragma mark capturedSelfDC + +// Closures may explicitly capture the self param, in which case the lookup +// should use the closure as the context for implicit self lookups. + +// By default, there is no such context to return. +NullablePtr ASTScopeImpl::capturedSelfDC() const { + return NullablePtr(); +} + +// Closures track this information explicitly. +NullablePtr ClosureParametersScope::capturedSelfDC() const { + if (closureExpr->capturesSelfEnablingImplictSelf()) + return NullablePtr(closureExpr); + return NullablePtr(); +} + #pragma mark ifUnknownIsCascadingUseAccordingTo static bool isCascadingUseAccordingTo(const DeclContext *const dc) { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 9ac57850ecf11..1f1daae54a411 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5249,6 +5249,7 @@ VarDecl::VarDecl(DeclKind kind, bool isStatic, VarDecl::Introducer introducer, { Bits.VarDecl.Introducer = unsigned(introducer); Bits.VarDecl.IsCaptureList = isCaptureList; + Bits.VarDecl.IsSelfParamCapture = false; Bits.VarDecl.IsDebuggerVar = false; Bits.VarDecl.IsLazyStorageProperty = false; Bits.VarDecl.HasNonPatternBindingInit = false; diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 9296e947989cb..f6b41f4ade2a4 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1179,6 +1179,17 @@ UnresolvedSpecializeExpr *UnresolvedSpecializeExpr::create(ASTContext &ctx, UnresolvedParams, RAngleLoc); } +bool CaptureListEntry::isSimpleSelfCapture() const { + if (Init->getPatternList().size() != 1) + return false; + if (auto *DRE = dyn_cast(Init->getInit(0))) + if (auto *VD = dyn_cast(DRE->getDecl())) { + return (VD->isSelfParameter() || VD->isSelfParamCapture()) + && VD->getName() == Var->getName(); + } + return false; +} + CaptureListExpr *CaptureListExpr::create(ASTContext &ctx, ArrayRef captureList, ClosureExpr *closureBody) { @@ -1813,6 +1824,12 @@ bool ClosureExpr::hasEmptyBody() const { return getBody()->empty(); } +bool ClosureExpr::capturesSelfEnablingImplictSelf() const { + if (auto *VD = getCapturedSelfDecl()) + return VD->isSelfParamCapture() && !VD->getType()->is(); + return false; +} + FORWARD_SOURCE_LOCS_TO(AutoClosureExpr, Body) void AutoClosureExpr::setBody(Expr *E) { diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 02a3e9cd46a95..5f62012520646 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -60,6 +60,13 @@ ValueDecl *LookupResultEntry::getBaseDecl() const { return selfDecl; } + if (auto *CE = dyn_cast(BaseDC)) { + auto *selfDecl = CE->getCapturedSelfDecl(); + assert(selfDecl); + assert(selfDecl->isSelfParamCapture()); + return selfDecl; + } + auto *nominalDecl = BaseDC->getSelfNominalTypeDecl(); assert(nominalDecl); return nominalDecl; diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 4e62f84f4cf9a..3c7a152b92d59 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -196,7 +196,7 @@ namespace { SourceFile const *recordedSF = nullptr; bool recordedIsCascadingUse = false; unsigned resultsSizeBeforeLocalsPass = ~0; - + public: // clang-format off UnqualifiedLookupFactory(DeclNameRef Name, @@ -252,52 +252,70 @@ namespace { void lookupOperatorInDeclContexts(ContextAndUnresolvedIsCascadingUse); - void lookupNamesIntroducedBy(const ContextAndUnresolvedIsCascadingUse); + /// When performing a lookup, we may come across a capture of 'self'. We + /// will need to remember the DeclContext of the innermost captured self so + /// that it can be used as the base DeclContext if we find a lookup result + /// in the enclosing type. \c capturedSelfContext tracks this. + void lookupNamesIntroducedBy(const ContextAndUnresolvedIsCascadingUse, + DeclContext *capturedSelfContext); void finishLookingInContext( AddGenericParameters addGenericParameters, DeclContext *lookupContextForThisContext, Optional &&resultFinderForTypeContext, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupInModuleScopeContext(DeclContext *, Optional isCascadingUse); void lookupNamesIntroducedByPatternBindingInitializer( - PatternBindingInitializer *PBI, Optional isCascadingUse); + PatternBindingInitializer *PBI, + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByLazyVariableInitializer(PatternBindingInitializer *PBI, ParamDecl *selfParam, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByInitializerOfStoredPropertyOfAType( PatternBindingInitializer *PBI, Optional isCascadingUse); /// An initializer of a global name, or a function-likelocal name. void lookupNamesIntroducedByInitializerOfGlobalOrLocal( - PatternBindingInitializer *PBI, Optional isCascadingUse); + PatternBindingInitializer *PBI, + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByFunctionDecl(AbstractFunctionDecl *AFD, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByMemberFunction(AbstractFunctionDecl *AFD, - bool isCascadingUse); + bool isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByPureFunction(AbstractFunctionDecl *AFD, - bool isCascadingUse); + bool isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByClosure(AbstractClosureExpr *ACE, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); template void lookupNamesIntroducedByNominalTypeOrExtension( NominalTypeDeclOrExtensionDecl *D, Optional isCascadingUse); void lookupNamesIntroducedByDefaultArgumentInitializer( - DefaultArgumentInitializer *I, Optional isCascadingUse); + DefaultArgumentInitializer *I, + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByMiscContext(DeclContext *dc, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookForLocalVariablesIn(AbstractFunctionDecl *AFD, Optional isCascadingUse); @@ -481,7 +499,7 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() { if (Name.isOperator()) lookupOperatorInDeclContexts(contextAndIsCascadingUse); else - lookupNamesIntroducedBy(contextAndIsCascadingUse); + lookupNamesIntroducedBy(contextAndIsCascadingUse, NULL); } if (crosscheckUnqualifiedLookup && @@ -495,7 +513,7 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() { else if (Name.isOperator()) altLookup.lookupOperatorInDeclContexts(contextAndIsCascadingUse); else - altLookup.lookupNamesIntroducedBy(contextAndIsCascadingUse); + altLookup.lookupNamesIntroducedBy(contextAndIsCascadingUse, NULL); const auto *ASTScopeLabel = "ASTScope lookup"; const auto *contextLabel = "context-bsed lookup"; @@ -556,28 +574,43 @@ void UnqualifiedLookupFactory::lookupOperatorInDeclContexts( // TODO: Unify with LookupVisibleDecls.cpp::lookupVisibleDeclsImpl void UnqualifiedLookupFactory::lookupNamesIntroducedBy( - const ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUseArg) { + const ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUseArg, + DeclContext *capturedSelfContext) { #ifndef NDEBUG stopForDebuggingIfDuringTargetLookup(false); #endif DeclContext *const dc = contextAndIsCascadingUseArg.whereToLook; const auto isCascadingUseSoFar = contextAndIsCascadingUseArg.isCascadingUse; - if (dc->isModuleScopeContext()) + if (dc->isModuleScopeContext()) { + assert(capturedSelfContext == NULL && "By the time we reach module scope," + " there should be no 'self'."); lookupInModuleScopeContext(dc, isCascadingUseSoFar); + } else if (auto *PBI = dyn_cast(dc)) - lookupNamesIntroducedByPatternBindingInitializer(PBI, isCascadingUseSoFar); + lookupNamesIntroducedByPatternBindingInitializer(PBI, isCascadingUseSoFar, + capturedSelfContext); else if (auto *AFD = dyn_cast(dc)) - lookupNamesIntroducedByFunctionDecl(AFD, isCascadingUseSoFar); + lookupNamesIntroducedByFunctionDecl(AFD, isCascadingUseSoFar, + capturedSelfContext); else if (auto *ACE = dyn_cast(dc)) - lookupNamesIntroducedByClosure(ACE, isCascadingUseSoFar); - else if (auto *ED = dyn_cast(dc)) + lookupNamesIntroducedByClosure(ACE, isCascadingUseSoFar, + capturedSelfContext); + else if (auto *ED = dyn_cast(dc)) { + assert(capturedSelfContext == NULL && "When we recurse into type context," + " 'self' should be forgotten."); lookupNamesIntroducedByNominalTypeOrExtension(ED, isCascadingUseSoFar); - else if (auto *ND = dyn_cast(dc)) + } + else if (auto *ND = dyn_cast(dc)) { + assert(capturedSelfContext == NULL && "When we recurse into type context," + " 'self' should be forgotten."); lookupNamesIntroducedByNominalTypeOrExtension(ND, isCascadingUseSoFar); + } else if (auto I = dyn_cast(dc)) - lookupNamesIntroducedByDefaultArgumentInitializer(I, isCascadingUseSoFar); + lookupNamesIntroducedByDefaultArgumentInitializer(I, isCascadingUseSoFar, + capturedSelfContext); else - lookupNamesIntroducedByMiscContext(dc, isCascadingUseSoFar); + lookupNamesIntroducedByMiscContext(dc, isCascadingUseSoFar, + capturedSelfContext); } void UnqualifiedLookupFactory::lookupInModuleScopeContext( @@ -595,22 +628,29 @@ void UnqualifiedLookupFactory::lookupInModuleScopeContext( } void UnqualifiedLookupFactory::lookupNamesIntroducedByPatternBindingInitializer( - PatternBindingInitializer *PBI, Optional isCascadingUse) { + PatternBindingInitializer *PBI, Optional isCascadingUse, + DeclContext *capturedSelfContext) { // Lazy variable initializer contexts have a 'self' parameter for // instance member lookup. if (auto *selfParam = PBI->getImplicitSelfDecl()) lookupNamesIntroducedByLazyVariableInitializer(PBI, selfParam, - isCascadingUse); - else if (PBI->getParent()->isTypeContext()) - lookupNamesIntroducedByInitializerOfStoredPropertyOfAType(PBI, + isCascadingUse, + capturedSelfContext); + else if (PBI->getParent()->isTypeContext()) { + assert(capturedSelfContext == NULL && "If we were in a type's property" + " initializer, there should be no 'self' to have been captured."); + lookupNamesIntroducedByInitializerOfStoredPropertyOfAType( + PBI, isCascadingUse); + } else - lookupNamesIntroducedByInitializerOfGlobalOrLocal(PBI, isCascadingUse); + lookupNamesIntroducedByInitializerOfGlobalOrLocal(PBI, isCascadingUse, + capturedSelfContext); } void UnqualifiedLookupFactory::lookupNamesIntroducedByLazyVariableInitializer( PatternBindingInitializer *PBI, ParamDecl *selfParam, - Optional isCascadingUse) { + Optional isCascadingUse, DeclContext *capturedSelfContext) { Consumer.foundDecl(selfParam, DeclVisibilityKind::FunctionParameter); ifNotDoneYet([&] { DeclContext *const patternContainer = PBI->getParent(); @@ -620,7 +660,8 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByPatternBindingInitializer( patternContainer, ResultFinderForTypeContext(this, PBI, patternContainer), resolveIsCascadingUse(PBI, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + capturedSelfContext); // clang-format on }); } @@ -638,13 +679,15 @@ void UnqualifiedLookupFactory:: ResultFinderForTypeContext( this, storedPropertyContainer, storedPropertyContainer), resolveIsCascadingUse(storedPropertyContainer, None, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + /*capturedSelfContext=*/NULL); // clang-format on } void UnqualifiedLookupFactory:: lookupNamesIntroducedByInitializerOfGlobalOrLocal( - PatternBindingInitializer *PBI, Optional isCascadingUse) { + PatternBindingInitializer *PBI, Optional isCascadingUse, + DeclContext *capturedSelfContext) { // There's not much to find here, we'll keep going up to a parent // context. // clang-format off @@ -653,12 +696,14 @@ void UnqualifiedLookupFactory:: PBI, None, // not looking in the partic type resolveIsCascadingUse(PBI, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + capturedSelfContext); // clang-format on } void UnqualifiedLookupFactory::lookupNamesIntroducedByFunctionDecl( - AbstractFunctionDecl *AFD, Optional isCascadingUseArg) { + AbstractFunctionDecl *AFD, Optional isCascadingUseArg, + DeclContext *capturedSelfContext) { // DOUG: how does this differ from isOutsideBodyOfFunction below? const bool isCascadingUse = @@ -668,13 +713,16 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByFunctionDecl( !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc))); if (AFD->getDeclContext()->isTypeContext()) - lookupNamesIntroducedByMemberFunction(AFD, isCascadingUse); + lookupNamesIntroducedByMemberFunction(AFD, isCascadingUse, + capturedSelfContext); else - lookupNamesIntroducedByPureFunction(AFD, isCascadingUse); + lookupNamesIntroducedByPureFunction(AFD, isCascadingUse, + capturedSelfContext); } void UnqualifiedLookupFactory::lookupNamesIntroducedByMemberFunction( - AbstractFunctionDecl *AFD, bool isCascadingUse) { + AbstractFunctionDecl *AFD, bool isCascadingUse, + DeclContext *capturedSelfContext) { lookForLocalVariablesIn(AFD, isCascadingUse); ifNotDoneYet( [&] { @@ -690,9 +738,13 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByMemberFunction( // If we're not in the body of the function (for example, we // might be type checking a default argument expression and // performing name lookup from there), the base declaration - // is the nominal type, not 'self'. + // is the nominal type, not 'self'. If we've captured self + // somewhere down the tree, we should use that as the context + // for lookup. DeclContext *const BaseDC = - isOutsideBodyOfFunction(AFD) ? fnDeclContext : AFD; + isOutsideBodyOfFunction(AFD) ? fnDeclContext + : capturedSelfContext ? capturedSelfContext + : AFD; // If we are inside of a method, check to see if there are any ivars in // scope, and if so, whether this is a reference to one of them. // FIXME: We should persist this information between lookups. @@ -701,13 +753,15 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByMemberFunction( AddGenericParameters::Yes, AFD->getParent(), ResultFinderForTypeContext(this, BaseDC, fnDeclContext), - isCascadingUse); + isCascadingUse, + NULL); // clang-format on }); } void UnqualifiedLookupFactory::lookupNamesIntroducedByPureFunction( - AbstractFunctionDecl *AFD, bool isCascadingUse) { + AbstractFunctionDecl *AFD, bool isCascadingUse, + DeclContext *capturedSelfContext) { lookForLocalVariablesIn(AFD, isCascadingUse); ifNotDoneYet([&] { // clang-format off @@ -715,15 +769,24 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByPureFunction( AddGenericParameters::Yes, AFD, None, - isCascadingUse); + isCascadingUse, + capturedSelfContext); }); } void UnqualifiedLookupFactory::lookupNamesIntroducedByClosure( - AbstractClosureExpr *ACE, Optional isCascadingUse) { - if (auto *CE = dyn_cast(ACE)) + AbstractClosureExpr *ACE, Optional isCascadingUse, + DeclContext *capturedSelfContext) { + if (auto *CE = dyn_cast(ACE)) { lookForLocalVariablesIn(CE); + // If we don't already have a captured self context, and this closure + // captures the self param (not weakly, so that implicit self is available), + // remember that. + if (capturedSelfContext == nullptr) + if (CE->capturesSelfEnablingImplictSelf()) + capturedSelfContext = CE; + } ifNotDoneYet([&] { // clang-format off finishLookingInContext( @@ -731,7 +794,8 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByClosure( ACE, None, resolveIsCascadingUse(ACE, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + capturedSelfContext); // clang-format on }); } @@ -748,21 +812,25 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByNominalTypeOrExtension( ResultFinderForTypeContext(this, D, D)) : None, resolveIsCascadingUse(D, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + /*capturedSelfContext=*/NULL); // clang-format on } void UnqualifiedLookupFactory:: lookupNamesIntroducedByDefaultArgumentInitializer( - DefaultArgumentInitializer *I, Optional isCascadingUse) { + DefaultArgumentInitializer *I, Optional isCascadingUse, + DeclContext *capturedSelfContext) { // In a default argument, skip immediately out of both the // initializer and the function. - finishLookingInContext(AddGenericParameters::No, I->getParent(), None, false); + finishLookingInContext(AddGenericParameters::No, I->getParent(), None, false, + capturedSelfContext); } void UnqualifiedLookupFactory::lookupNamesIntroducedByMiscContext( - DeclContext *dc, Optional isCascadingUse) { + DeclContext *dc, Optional isCascadingUse, + DeclContext *capturedSelfContext) { // clang-format off assert(isa(dc) || isa(dc) || @@ -774,7 +842,8 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByMiscContext( dc, None, resolveIsCascadingUse(DC, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + capturedSelfContext); // clang-format on } @@ -783,7 +852,8 @@ void UnqualifiedLookupFactory::finishLookingInContext( const AddGenericParameters addGenericParameters, DeclContext *const lookupContextForThisContext, Optional &&resultFinderForTypeContext, - const Optional isCascadingUse) { + const Optional isCascadingUse, + DeclContext *capturedSelfContext) { #ifndef NDEBUG stopForDebuggingIfDuringTargetLookup(false); #endif @@ -804,7 +874,8 @@ void UnqualifiedLookupFactory::finishLookingInContext( // Recurse into the next context. [&] { lookupNamesIntroducedBy(ContextAndUnresolvedIsCascadingUse{ - lookupContextForThisContext->getParentForLookup(), isCascadingUse}); + lookupContextForThisContext->getParentForLookup(), isCascadingUse}, + capturedSelfContext); }); } diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp index 0c7a51f7e7ff4..7fd7e5915725d 100644 --- a/lib/Parse/Lexer.cpp +++ b/lib/Parse/Lexer.cpp @@ -2482,7 +2482,8 @@ void Lexer::lexImpl() { } } -Token Lexer::getTokenAtLocation(const SourceManager &SM, SourceLoc Loc) { +Token Lexer::getTokenAtLocation(const SourceManager &SM, SourceLoc Loc, + CommentRetentionMode CRM) { // Don't try to do anything with an invalid location. if (!Loc.isValid()) return Token(); @@ -2501,7 +2502,7 @@ Token Lexer::getTokenAtLocation(const SourceManager &SM, SourceLoc Loc) { // (making this option irrelevant), or the caller lexed comments and // we need to lex just the comment token. Lexer L(FakeLangOpts, SM, BufferID, nullptr, LexerMode::Swift, - HashbangMode::Allowed, CommentRetentionMode::ReturnAsTokens); + HashbangMode::Allowed, CRM); L.restoreState(State(Loc)); return L.peekNextToken(); } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index e093eac765b0a..5963e405678a8 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2403,11 +2403,15 @@ static void printTupleNames(const TypeRepr *typeRepr, llvm::raw_ostream &OS) { } bool Parser:: -parseClosureSignatureIfPresent(SmallVectorImpl &captureList, +parseClosureSignatureIfPresent(SourceRange &bracketRange, + SmallVectorImpl &captureList, + VarDecl *&capturedSelfDecl, ParameterList *¶ms, SourceLoc &throwsLoc, SourceLoc &arrowLoc, TypeRepr *&explicitResultType, SourceLoc &inLoc){ // Clear out result parameters. + bracketRange = SourceRange(); + capturedSelfDecl = nullptr; params = nullptr; throwsLoc = SourceLoc(); arrowLoc = SourceLoc(); @@ -2476,14 +2480,15 @@ parseClosureSignatureIfPresent(SmallVectorImpl &captureList, } SyntaxParsingContext ClosureSigCtx(SyntaxContext, SyntaxKind::ClosureSignature); if (Tok.is(tok::l_square) && peekToken().is(tok::r_square)) { + SyntaxParsingContext CaptureCtx(SyntaxContext, SyntaxKind::ClosureCaptureSignature); - consumeToken(tok::l_square); - consumeToken(tok::r_square); + bracketRange = SourceRange(consumeToken(tok::l_square), + consumeToken(tok::r_square)); } else if (Tok.is(tok::l_square) && !peekToken().is(tok::r_square)) { SyntaxParsingContext CaptureCtx(SyntaxContext, SyntaxKind::ClosureCaptureSignature); - consumeToken(tok::l_square); + SourceLoc lBracketLoc = consumeToken(tok::l_square); // At this point, we know we have a closure signature. Parse the capture list // and parameters. bool HasNext; @@ -2573,6 +2578,10 @@ parseClosureSignatureIfPresent(SmallVectorImpl &captureList, auto *VD = new (Context) VarDecl(/*isStatic*/false, introducer, /*isCaptureList*/true, nameLoc, name, CurDeclContext); + + // If we captured something under the name "self", remember that. + if (name == Context.Id_self) + capturedSelfDecl = VD; // Attributes. if (ownershipKind != ReferenceOwnership::Strong) @@ -2586,17 +2595,22 @@ parseClosureSignatureIfPresent(SmallVectorImpl &captureList, /*VarLoc*/ nameLoc, pattern, /*EqualLoc*/ equalLoc, initializer, CurDeclContext); - captureList.push_back(CaptureListEntry(VD, PBD)); + auto CLE = CaptureListEntry(VD, PBD); + if (CLE.isSimpleSelfCapture()) + VD->setIsSelfParamCapture(); + captureList.push_back(CLE); } while (HasNext); SyntaxContext->collectNodesInPlace(SyntaxKind::ClosureCaptureItemList); // The capture list needs to be closed off with a ']'. + SourceLoc rBracketLoc = Tok.getLoc(); if (!consumeIf(tok::r_square)) { diagnose(Tok, diag::expected_capture_list_end_rsquare); skipUntil(tok::r_square); if (Tok.is(tok::r_square)) - consumeToken(tok::r_square); + rBracketLoc = consumeToken(tok::r_square); } + bracketRange = SourceRange(lBracketLoc, rBracketLoc); } bool invalid = false; @@ -2754,14 +2768,17 @@ ParserResult Parser::parseExprClosure() { SourceLoc leftBrace = consumeToken(); // Parse the closure-signature, if present. + SourceRange bracketRange; + SmallVector captureList; + VarDecl *capturedSelfDecl; ParameterList *params = nullptr; SourceLoc throwsLoc; SourceLoc arrowLoc; TypeRepr *explicitResultType; SourceLoc inLoc; - SmallVector captureList; - parseClosureSignatureIfPresent(captureList, params, throwsLoc, arrowLoc, - explicitResultType, inLoc); + parseClosureSignatureIfPresent(bracketRange, captureList, + capturedSelfDecl, params, throwsLoc, + arrowLoc, explicitResultType, inLoc); // If the closure was created in the context of an array type signature's // size expression, there will not be a local context. A parse error will @@ -2776,9 +2793,10 @@ ParserResult Parser::parseExprClosure() { unsigned discriminator = CurLocalContext->claimNextClosureDiscriminator(); // Create the closure expression and enter its context. - auto *closure = new (Context) ClosureExpr(params, throwsLoc, arrowLoc, inLoc, - explicitResultType, - discriminator, CurDeclContext); + auto *closure = new (Context) ClosureExpr(bracketRange, capturedSelfDecl, + params, throwsLoc, arrowLoc, inLoc, + explicitResultType, discriminator, + CurDeclContext); // The arguments to the func are defined in their own scope. Scope S(this, ScopeKind::ClosureParams); ParseFunctionBody cc(*this, closure); diff --git a/lib/Sema/DebuggerTestingTransform.cpp b/lib/Sema/DebuggerTestingTransform.cpp index e85b332962953..0a4ad23767bfc 100644 --- a/lib/Sema/DebuggerTestingTransform.cpp +++ b/lib/Sema/DebuggerTestingTransform.cpp @@ -225,8 +225,9 @@ class DebuggerTestingTransform : public ASTWalker { // Create the closure. auto *Params = ParameterList::createEmpty(Ctx); auto *Closure = new (Ctx) - ClosureExpr(Params, SourceLoc(), SourceLoc(), SourceLoc(), TypeLoc(), - DF.getNextDiscriminator(), getCurrentDeclContext()); + ClosureExpr(SourceRange(), nullptr, Params, SourceLoc(), SourceLoc(), + SourceLoc(), TypeLoc(), DF.getNextDiscriminator(), + getCurrentDeclContext()); Closure->setImplicit(true); // TODO: Save and return the value of $OriginalExpr. diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 38d0202d4e7e9..eeab6b6062f12 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -1316,21 +1316,27 @@ static void diagRecursivePropertyAccess(const Expr *E, const DeclContext *DC) { const_cast(E)->walk(walker); } -/// Look for any property references in closures that lack a "self." qualifier. -/// Within a closure, we require that the source code contain "self." explicitly -/// because 'self' is captured, not the property value. This is a common source -/// of confusion, so we force an explicit self. +/// Look for any property references in closures that lack a 'self.' qualifier. +/// Within a closure, we require that the source code contain 'self.' explicitly +/// (or that the closure explicitly capture 'self' in the capture list) because +/// 'self' is captured, not the property value. This is a common source of +/// confusion, so we force an explicit self. static void diagnoseImplicitSelfUseInClosure(const Expr *E, const DeclContext *DC) { class DiagnoseWalker : public ASTWalker { ASTContext &Ctx; - unsigned InClosure; + SmallVector Closures; public: - explicit DiagnoseWalker(ASTContext &ctx, bool isAlreadyInClosure) - : Ctx(ctx), InClosure(isAlreadyInClosure) {} + explicit DiagnoseWalker(ASTContext &ctx, AbstractClosureExpr *ACE) + : Ctx(ctx), Closures() { + if (ACE) + Closures.push_back(ACE); + } - /// Return true if this is an implicit reference to self. - static bool isImplicitSelfUse(Expr *E) { + /// Return true if this is an implicit reference to self which is required + /// to be explicit in an escaping closure. Metatype references and value + /// type references are excluded. + static bool isImplicitSelfParamUseLikelyToCauseCycle(Expr *E) { auto *DRE = dyn_cast(E); if (!DRE || !DRE->isImplicit() || !isa(DRE->getDecl()) || @@ -1345,11 +1351,19 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, return false; // Metatype self captures don't extend the lifetime of an object. - return !ty->is(); + if (ty->is()) + return false; + + // If self does not have reference semantics, it is very unlikely that + // capturing it will create a reference cycle. + if (!ty->hasReferenceSemantics()) + return false; + + return true; } - /// Return true if this is a closure expression that will require "self." - /// qualification of member references. + /// Return true if this is a closure expression that will require explicit + /// use or capture of "self." for qualification of member references. static bool isClosureRequiringSelfQualification( const AbstractClosureExpr *CE) { // If the closure's type was inferred to be noescape, then it doesn't @@ -1371,43 +1385,48 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, // If this is a potentially-escaping closure expression, start looking // for references to self if we aren't already. if (isClosureRequiringSelfQualification(CE)) - ++InClosure; + Closures.push_back(CE); } // If we aren't in a closure, no diagnostics will be produced. - if (!InClosure) + if (Closures.size() == 0) return { true, E }; - // If we see a property reference with an implicit base from within a - // closure, then reject it as requiring an explicit "self." qualifier. We - // do this in explicit closures, not autoclosures, because otherwise the - // transparence of autoclosures is lost. auto &Diags = Ctx.Diags; + + // Diagnostics should correct the innermost closure + auto *ACE = Closures[Closures.size() - 1]; + assert(ACE); + + SourceLoc memberLoc = SourceLoc(); if (auto *MRE = dyn_cast(E)) - if (isImplicitSelfUse(MRE->getBase())) { + if (isImplicitSelfParamUseLikelyToCauseCycle(MRE->getBase())) { auto baseName = MRE->getMember().getDecl()->getBaseName(); - Diags.diagnose(MRE->getLoc(), + memberLoc = MRE->getLoc(); + Diags.diagnose(memberLoc, diag::property_use_in_closure_without_explicit_self, - baseName.getIdentifier()) - .fixItInsert(MRE->getLoc(), "self."); - return { false, E }; + baseName.getIdentifier()); } // Handle method calls with a specific diagnostic + fixit. if (auto *DSCE = dyn_cast(E)) - if (isImplicitSelfUse(DSCE->getBase()) && + if (isImplicitSelfParamUseLikelyToCauseCycle(DSCE->getBase()) && isa(DSCE->getFn())) { auto MethodExpr = cast(DSCE->getFn()); + memberLoc = DSCE->getLoc(); Diags.diagnose(DSCE->getLoc(), diag::method_call_in_closure_without_explicit_self, - MethodExpr->getDecl()->getBaseName().getIdentifier()) - .fixItInsert(DSCE->getLoc(), "self."); - return { false, E }; + MethodExpr->getDecl()->getBaseName().getIdentifier()); } + if (memberLoc.isValid()) { + emitFixIts(Diags, memberLoc, ACE); + return { false, E }; + } + // Catch any other implicit uses of self with a generic diagnostic. - if (isImplicitSelfUse(E)) + if (isImplicitSelfParamUseLikelyToCauseCycle(E)) Diags.diagnose(E->getLoc(), diag::implicit_use_of_self_in_closure); return { true, E }; @@ -1416,26 +1435,144 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, Expr *walkToExprPost(Expr *E) override { if (auto *CE = dyn_cast(E)) { if (isClosureRequiringSelfQualification(CE)) { - assert(InClosure); - --InClosure; + assert(Closures.size() > 0); + Closures.pop_back(); } } return E; } + + /// Emit any fix-its for this error. + void emitFixIts(DiagnosticEngine &Diags, + SourceLoc memberLoc, + const AbstractClosureExpr *ACE) { + // This error can be fixed by either capturing self explicitly (if in an + // explicit closure), or referencing self explicitly. + if (auto *CE = dyn_cast(ACE)) { + if (diagnoseAlmostMatchingCaptures(Diags, memberLoc, CE)) { + // Bail on the rest of the diagnostics. Offering the option to + // capture 'self' explicitly will result in an error, and using + // 'self.' explicitly will be accessing something other than the + // self param. + // FIXME: We could offer a special fixit in the [weak self] case to insert 'self?.'... + return; + } + emitFixItsForExplicitClosure(Diags, memberLoc, CE); + } else { + // If this wasn't an explicit closure, just offer the fix-it to + // reference self explicitly. + Diags.diagnose(memberLoc, diag::note_reference_self_explicitly) + .fixItInsert(memberLoc, "self."); + } + } + + /// Diagnose any captures which might have been an attempt to capture + /// \c self strongly, but do not actually enable implicit \c self. Returns + /// whether there were any such captures to diagnose. + bool diagnoseAlmostMatchingCaptures(DiagnosticEngine &Diags, + SourceLoc memberLoc, + const ClosureExpr *closureExpr) { + // If we've already captured something with the name "self" other than + // the actual self param, offer special diagnostics. + if (auto *VD = closureExpr->getCapturedSelfDecl()) { + // Either this is a weak capture of self... + if (VD->getType()->is()) { + Diags.diagnose(VD->getLoc(), diag::note_self_captured_weakly); + // ...or something completely different. + } else { + Diags.diagnose(VD->getLoc(), diag::note_other_self_capture); + } + + return true; + } + return false; + } + + /// Emit fix-its for invalid use of implicit \c self in an explicit closure. + /// The error can be solved by capturing self explicitly, + /// or by using \c self. explicitly. + void emitFixItsForExplicitClosure(DiagnosticEngine &Diags, + SourceLoc memberLoc, + const ClosureExpr *closureExpr) { + Diags.diagnose(memberLoc, diag::note_reference_self_explicitly) + .fixItInsert(memberLoc, "self."); + auto diag = Diags.diagnose(closureExpr->getLoc(), + diag::note_capture_self_explicitly); + // There are four different potential fix-its to offer based on the + // closure signature: + // 1. There is an existing capture list which already has some + // entries. We need to insert 'self' into the capture list along + // with a separating comma. + // 2. There is an existing capture list, but it is empty (jusr '[]'). + // We can just insert 'self'. + // 3. Arguments or types are already specified in the signature, + // but there is no existing capture list. We will need to insert + // the capture list, but 'in' will already be present. + // 4. The signature empty so far. We must insert the full capture + // list as well as 'in'. + const auto brackets = closureExpr->getBracketRange(); + if (brackets.isValid()) { + emitInsertSelfIntoCaptureListFixIt(brackets, diag); + } + else { + emitInsertNewCaptureListFixIt(closureExpr, diag); + } + } + + /// Emit a fix-it for inserting \c self into in existing capture list, along + /// with a trailing comma if needed. The fix-it will be attached to the + /// provided diagnostic \c diag. + void emitInsertSelfIntoCaptureListFixIt(SourceRange brackets, + InFlightDiagnostic &diag) { + // Look for any non-comment token. If there's anything before the + // closing bracket, we assume that it is a valid capture list entry and + // insert 'self,'. If it wasn't a valid entry, then we will at least not + // be introducing any new errors/warnings... + const auto locAfterBracket = brackets.Start.getAdvancedLoc(1); + const auto nextAfterBracket = + Lexer::getTokenAtLocation(Ctx.SourceMgr, locAfterBracket, + CommentRetentionMode::None); + if (nextAfterBracket.getLoc() != brackets.End) + diag.fixItInsertAfter(brackets.Start, "self, "); + else + diag.fixItInsertAfter(brackets.Start, "self"); + } + + /// Emit a fix-it for inserting a capture list into a closure that does not + /// already have one, along with a trailing \c in if necessary. The fix-it + /// will be attached to the provided diagnostic \c diag. + void emitInsertNewCaptureListFixIt(const ClosureExpr *closureExpr, + InFlightDiagnostic &diag) { + if (closureExpr->getInLoc().isValid()) { + diag.fixItInsertAfter(closureExpr->getLoc(), " [self]"); + return; + } + + // If there's a (non-comment) token immediately following the + // opening brace of the closure, we may need to pad the fix-it + // with a space. + const auto nextLoc = closureExpr->getLoc().getAdvancedLoc(1); + const auto next = + Lexer::getTokenAtLocation(Ctx.SourceMgr, nextLoc, + CommentRetentionMode::None); + std::string trailing = next.getLoc() == nextLoc ? " " : ""; + + diag.fixItInsertAfter(closureExpr->getLoc(), " [self] in" + trailing); + } }; - bool isAlreadyInClosure = false; + AbstractClosureExpr *ACE = nullptr; if (DC->isLocalContext()) { - while (DC->getParent()->isLocalContext() && !isAlreadyInClosure) { + while (DC->getParent()->isLocalContext() && !ACE) { if (auto *closure = dyn_cast(DC)) if (DiagnoseWalker::isClosureRequiringSelfQualification(closure)) - isAlreadyInClosure = true; + ACE = const_cast(closure); DC = DC->getParent(); } } auto &ctx = DC->getASTContext(); - const_cast(E)->walk(DiagnoseWalker(ctx, isAlreadyInClosure)); + const_cast(E)->walk(DiagnoseWalker(ctx, ACE)); } bool TypeChecker::getDefaultGenericArgumentsString( diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index de310517555cb..7ad524f45df3f 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -277,13 +277,21 @@ LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclNameRef name, // Determine which type we looked through to find this result. Type foundInType; - if (auto *baseDC = found.getDeclContext()) { - if (!baseDC->isTypeContext()) { - baseDC = baseDC->getParent(); - assert(baseDC->isTypeContext()); + if (auto *typeDC = found.getDeclContext()) { + if (!typeDC->isTypeContext()) { + // If we don't have a type context this is an implicit 'self' reference. + if (auto *CE = dyn_cast(typeDC)) { + // If we found the result in a self capture, look through the capture. + assert(CE->getCapturedSelfDecl()); + typeDC = found.getValueDecl()->getDeclContext(); + } else { + // Otherwise, we must have the method context. + typeDC = typeDC->getParent(); + } + assert(typeDC->isTypeContext()); } foundInType = dc->mapTypeIntoContext( - baseDC->getDeclaredInterfaceType()); + typeDC->getDeclaredInterfaceType()); assert(foundInType && "bogus base declaration?"); } diff --git a/lib/Sema/TypeCheckREPL.cpp b/lib/Sema/TypeCheckREPL.cpp index 2226037b40418..59c3cf05d14f1 100644 --- a/lib/Sema/TypeCheckREPL.cpp +++ b/lib/Sema/TypeCheckREPL.cpp @@ -256,8 +256,9 @@ void REPLChecker::generatePrintOfExpression(StringRef NameStr, Expr *E) { unsigned discriminator = DF.getNextDiscriminator(); ClosureExpr *CE = - new (Context) ClosureExpr(params, SourceLoc(), SourceLoc(), SourceLoc(), - TypeLoc(), discriminator, newTopLevel); + new (Context) ClosureExpr(SourceRange(), nullptr, params, SourceLoc(), + SourceLoc(), SourceLoc(), TypeLoc(), + discriminator, newTopLevel); SmallVector args; params->getParams(args); diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 4d6c31cd6f1bf..869096937ff8d 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -443,7 +443,7 @@ class C_SR_2505 : P_SR_2505 { func call(_ c: C_SR_2505) -> Bool { // Note: the diagnostic about capturing 'self', indicates that we have // selected test(_) rather than test(it:) - return c.test { o in test(o) } // expected-error{{call to method 'test' in closure requires explicit 'self.' to make capture semantics explicit}} + return c.test { o in test(o) } // expected-error{{call to method 'test' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} expected-note{{reference 'self.' explicitly}} } } diff --git a/test/attr/attr_autoclosure.swift b/test/attr/attr_autoclosure.swift index 6b6d689dd08ee..d2cc8b53fdaaa 100644 --- a/test/attr/attr_autoclosure.swift +++ b/test/attr/attr_autoclosure.swift @@ -98,9 +98,9 @@ class TestFunc12 { func test() { func12a(x + foo()) // okay - func12c(x + foo()) - // expected-error@-1{{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{13-13=self.}} - // expected-error@-2{{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{17-17=self.}} + func12c(x + foo()) + // expected-error@-1{{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note@-1{{reference 'self.' explicitly}} {{13-13=self.}} + // expected-error@-2{{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note@-2{{reference 'self.' explicitly}} {{17-17=self.}} } } diff --git a/test/attr/attr_noescape.swift b/test/attr/attr_noescape.swift index d22bccde0f388..98b2992dd885a 100644 --- a/test/attr/attr_noescape.swift +++ b/test/attr/attr_noescape.swift @@ -52,7 +52,7 @@ class SomeClass { func test() { // This should require "self." - doesEscape { x } // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{18-18=self.}} + doesEscape { x } // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{reference 'self.' explicitly}} {{18-18=self.}} // Since 'takesNoEscapeClosure' doesn't escape its closure, it doesn't // require "self." qualification of member references. @@ -64,88 +64,89 @@ class SomeClass { foo() func plain() { foo() } - let plain2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + let plain2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} _ = plain2 func multi() -> Int { foo(); return 0 } - let mulit2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{31-31=self.}} - _ = mulit2 + let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{30-30= [self] in}} expected-note{{reference 'self.' explicitly}} {{31-31=self.}} + _ = multi2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{18-18=self.}} + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{reference 'self.' explicitly}} {{18-18=self.}} takesNoEscapeClosure { foo() } // okay - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{18-18=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{reference 'self.' explicitly}} {{18-18=self.}} takesNoEscapeClosure { foo(); return 0 } // okay func outer() { func inner() { foo() } - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} + let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} _ = inner2 func multi() -> Int { foo(); return 0 } - let _: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{28-28=self.}} - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + let _: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{27-27= [self] in}} expected-note{{reference 'self.' explicitly}} {{28-28=self.}} + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo() } - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo(); return 0 } } - let _: () -> Void = { - func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - _ = inner2 - func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{29-29=self.}} - let mulit2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{33-33=self.}} - _ = mulit2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} - takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} - takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} + let _: () -> Void = { // expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} + func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + let _ = inner2 + func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + let _ = multi2 + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} } - doesEscape { - func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} + doesEscape { //expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} + func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} _ = inner2 - func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{29-29=self.}} - let mulit2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{33-33=self.}} - _ = mulit2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} - takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} - takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} + func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + _ = multi2 + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} return 0 } takesNoEscapeClosure { func inner() { foo() } - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} + let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} _ = inner2 func multi() -> Int { foo(); return 0 } - let mulit2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{33-33=self.}} - _ = mulit2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + _ = multi2 + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo() } // okay - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo(); return 0 } // okay return 0 } + // Implicit 'self' should be accepted when 'self' has value semantics. struct Outer { @discardableResult func bar() -> Int { bar() func plain() { bar() } - let plain2 = { bar() } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{24-24=self.}} + let plain2 = { bar() } _ = plain2 func multi() -> Int { bar(); return 0 } - let _: () -> Int = { bar(); return 0 } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} + let _: () -> Int = { bar(); return 0 } - doesEscape { bar() } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - takesNoEscapeClosure { bar() } // okay + doesEscape { bar() } + takesNoEscapeClosure { bar() } - doesEscape { bar(); return 0 } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - takesNoEscapeClosure { bar(); return 0 } // okay + doesEscape { bar(); return 0 } + takesNoEscapeClosure { bar(); return 0 } return 0 } @@ -158,17 +159,17 @@ class SomeClass { bar() // no-warning func plain() { bar() } - let plain2 = { bar() } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{26-26=self.}} + let plain2 = { bar() } _ = plain2 func multi() -> Int { bar(); return 0 } - let _: () -> Int = { bar(); return 0 } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{32-32=self.}} + let _: () -> Int = { bar(); return 0 } - doesEscape { bar() } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{24-24=self.}} - takesNoEscapeClosure { bar() } // okay + doesEscape { bar() } + takesNoEscapeClosure { bar() } - doesEscape { bar(); return 0 } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{24-24=self.}} - takesNoEscapeClosure { bar(); return 0 } // okay + doesEscape { bar(); return 0 } + takesNoEscapeClosure { bar(); return 0 } return 0 } diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index ca1a0156ad9ed..b7621be387d0d 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -151,28 +151,148 @@ func doVoidStuff(_ fn : @escaping () -> ()) {} class ExplicitSelfRequiredTest { var x = 42 func method() -> Int { - // explicit closure requires an explicit "self." base. + // explicit closure requires an explicit "self." base or an explicit capture. doVoidStuff({ self.x += 1 }) - doStuff({ x+1 }) // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{15-15=self.}} - doVoidStuff({ x += 1 }) // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{19-19=self.}} + doVoidStuff({ [self] in x += 1 }) + doVoidStuff({ [self = self] in x += 1 }) + doVoidStuff({ [unowned self] in x += 1 }) + doVoidStuff({ [unowned(unsafe) self] in x += 1 }) + doVoidStuff({ [unowned self = self] in x += 1 }) + + doStuff({ [self] in x+1 }) + doStuff({ [self = self] in x+1 }) + doStuff({ self.x+1 }) + doStuff({ [unowned self] in x+1 }) + doStuff({ [unowned(unsafe) self] in x+1 }) + doStuff({ [unowned self = self] in x+1 }) + doStuff({ x+1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} expected-note{{reference 'self.' explicitly}} {{15-15=self.}} + doVoidStuff({ doStuff({ x+1 })}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{28-28= [self] in}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ x += 1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{19-19=self.}} + doVoidStuff({ _ = "\(x)"}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} + doVoidStuff({ [y = self] in x += 1 }) // expected-warning {{capture 'y' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + doStuff({ [y = self] in x+1 }) // expected-warning {{capture 'y' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ [weak self] in x += 1 }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [weak self] in x+1 }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff({ [self = self.x] in x += 1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [self = self.x] in x+1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + + // Methods follow the same rules as properties, uses of 'self' without capturing must be marked with "self." + doStuff { method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} expected-note{{reference 'self.' explicitly}} {{15-15=self.}} + doVoidStuff { _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{23-23=self.}} + doVoidStuff { _ = "\(method())" } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} + doVoidStuff { () -> () in _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self]}} expected-note{{reference 'self.' explicitly}} {{35-35=self.}} + doVoidStuff { [y = self] in _ = method() } // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{37-37=self.}} + doStuff({ [y = self] in method() }) // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ [weak self] in _ = method() }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [weak self] in method() }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff({ [self = self.x] in _ = method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [self = self.x] in method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff { _ = self.method() } + doVoidStuff { [self] in _ = method() } + doVoidStuff { [self = self] in _ = method() } + doVoidStuff({ [unowned self] in _ = method() }) + doVoidStuff({ [unowned(unsafe) self] in _ = method() }) + doVoidStuff({ [unowned self = self] in _ = method() }) - // Methods follow the same rules as properties, uses of 'self' must be marked with "self." - doStuff { method() } // expected-error {{call to method 'method' in closure requires explicit 'self.' to make capture semantics explicit}} {{15-15=self.}} - doVoidStuff { _ = method() } // expected-error {{call to method 'method' in closure requires explicit 'self.' to make capture semantics explicit}} {{23-23=self.}} doStuff { self.method() } + doStuff { [self] in method() } + doStuff({ [self = self] in method() }) + doStuff({ [unowned self] in method() }) + doStuff({ [unowned(unsafe) self] in method() }) + doStuff({ [unowned self = self] in method() }) + + // When there's no space between the opening brace and the first expression, insert it + doStuff {method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in }} expected-note{{reference 'self.' explicitly}} {{14-14=self.}} + doVoidStuff {_ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in }} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + doVoidStuff {() -> () in _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self]}} expected-note{{reference 'self.' explicitly}} {{34-34=self.}} + // With an empty capture list, insertion should should be suggested without a comma + doStuff { [] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} expected-note{{reference 'self.' explicitly}} {{21-21=self.}} + doStuff { [ ] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} expected-note{{reference 'self.' explicitly}} {{23-23=self.}} + doStuff { [ /* This space intentionally left blank. */ ] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} expected-note{{reference 'self.' explicitly}} {{65-65=self.}} + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} + doStuff { [ // Nothing in this capture list! + ] + in + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{9-9=self.}} + } + // An inserted capture list should be on the same line as the opening brace, immediately following it. + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + doStuff { + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + // expected-note@+2 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + // Note: Trailing whitespace on the following line is intentional and should not be removed! + doStuff { + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + doStuff { // We have stuff to do. + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + doStuff {// We have stuff to do. + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + + // String interpolation should offer the diagnosis and fix-its at the expected locations + doVoidStuff { _ = "\(method())" } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} + doVoidStuff { _ = "\(x+1)" } // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} + + // If we already have a capture list, self should be added to the list + let y = 1 + doStuff { [y] in method() } // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + doStuff { [ // expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} + y // expected-warning {{capture 'y' was never used}} + ] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{14-14=self.}} // "self." shouldn't be required in the initializer expression in a capture list // This should not produce an error, "x" isn't being captured by the closure. doStuff({ [myX = x] in myX }) // This should produce an error, since x is used within the inner closure. - doStuff({ [myX = {x}] in 4 }) // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{23-23=self.}} + doStuff({ [myX = {x}] in 4 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{23-23= [self] in }} expected-note{{reference 'self.' explicitly}} {{23-23=self.}} // expected-warning @-1 {{capture 'myX' was never used}} return 42 } } +// If the implicit self is of value type, no diagnostic should be produced. +struct ImplicitSelfAllowedInStruct { + var x = 42 + mutating func method() -> Int { + doStuff({ x+1 }) + doVoidStuff({ x += 1 }) + doStuff({ method() }) + doVoidStuff({ _ = method() }) + } + + func method2() -> Int { + doStuff({ x+1 }) + doVoidStuff({ _ = x+1 }) + doStuff({ method2() }) + doVoidStuff({ _ = method2() }) + } +} + +enum ImplicitSelfAllowedInEnum { + case foo + var x: Int { 42 } + mutating func method() -> Int { + doStuff({ x+1 }) + doVoidStuff({ _ = x+1 }) + doStuff({ method() }) + doVoidStuff({ _ = method() }) + } + + func method2() -> Int { + doStuff({ x+1 }) + doVoidStuff({ _ = x+1 }) + doStuff({ method2() }) + doVoidStuff({ _ = method2() }) + } +} + class SomeClass { var field : SomeClass? @@ -216,7 +336,8 @@ extension SomeClass { doStuff { [weak xyz = self.field] in xyz!.foo() } // rdar://16889886 - Assert when trying to weak capture a property of self in a lazy closure - doStuff { [weak self.field] in field!.foo() } // expected-error {{fields may only be captured by assigning to a specific name}} expected-error {{reference to property 'field' in closure requires explicit 'self.' to make capture semantics explicit}} {{36-36=self.}} + // FIXME: We should probably offer a fix-it to the field capture error and suppress the 'implicit self' error. https://bugs.swift.org/browse/SR-11634 + doStuff { [weak self.field] in field!.foo() } // expected-error {{fields may only be captured by assigning to a specific name}} expected-error {{reference to property 'field' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}} {{36-36=self.}} expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} // expected-warning @+1 {{variable 'self' was written to, but never read}} doStuff { [weak self&field] in 42 } // expected-error {{expected ']' at end of capture list}} From f2ed81b763f81f231cfe5439bd775cc60930b507 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Thu, 19 Dec 2019 19:16:00 -0800 Subject: [PATCH 148/478] Revert "[test] Mark _Differentiable tests as unsupported in Swift-in-the-OS configurations" --- test/AutoDiff/Sema/derivative_attr_type_checking.swift | 3 --- test/AutoDiff/Serialization/derivative_attr.swift | 3 --- test/AutoDiff/Serialization/differentiable_attr.swift | 3 --- test/AutoDiff/Serialization/transpose_attr.swift | 3 --- test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift | 3 --- 5 files changed, 15 deletions(-) diff --git a/test/AutoDiff/Sema/derivative_attr_type_checking.swift b/test/AutoDiff/Sema/derivative_attr_type_checking.swift index 8aab201c49e0d..bd1b055f12615 100644 --- a/test/AutoDiff/Sema/derivative_attr_type_checking.swift +++ b/test/AutoDiff/Sema/derivative_attr_type_checking.swift @@ -1,9 +1,6 @@ // RUN: %target-swift-frontend-typecheck -enable-experimental-differentiable-programming -verify %s // REQUIRES: differentiable_programming -// We currently lack availability information (rdar://57975086) -// UNSUPPORTED: use_os_stdlib - import _Differentiation // Test top-level functions. diff --git a/test/AutoDiff/Serialization/derivative_attr.swift b/test/AutoDiff/Serialization/derivative_attr.swift index 43318bf055fbe..8b306740424c1 100644 --- a/test/AutoDiff/Serialization/derivative_attr.swift +++ b/test/AutoDiff/Serialization/derivative_attr.swift @@ -7,9 +7,6 @@ // REQUIRES: differentiable_programming -// We currently lack availability information (rdar://57975086) -// UNSUPPORTED: use_os_stdlib - import _Differentiation // Dummy `Differentiable`-conforming type. diff --git a/test/AutoDiff/Serialization/differentiable_attr.swift b/test/AutoDiff/Serialization/differentiable_attr.swift index 7da58171a4593..c1dcdfacfc5b2 100644 --- a/test/AutoDiff/Serialization/differentiable_attr.swift +++ b/test/AutoDiff/Serialization/differentiable_attr.swift @@ -4,9 +4,6 @@ // RUN: %target-sil-opt -disable-sil-linking -enable-sil-verify-all %t/differentiable_attr.swiftmodule -o - | %FileCheck %s // REQUIRES: differentiable_programming -// We currently lack availability information (rdar://57975086) -// UNSUPPORTED: use_os_stdlib - // TODO(TF-836): Enable this test. // Blocked by TF-828: `@differentiable` attribute type-checking. // XFAIL: * diff --git a/test/AutoDiff/Serialization/transpose_attr.swift b/test/AutoDiff/Serialization/transpose_attr.swift index c2d04614d5f96..80bc7a220586b 100644 --- a/test/AutoDiff/Serialization/transpose_attr.swift +++ b/test/AutoDiff/Serialization/transpose_attr.swift @@ -6,9 +6,6 @@ // BCANALYZER-NOT: UnknownCode // REQUIRES: differentiable_programming -// We currently lack availability information (rdar://57975086) -// UNSUPPORTED: use_os_stdlib - // TODO(TF-838): Enable this test. // Blocked by TF-830: `@transpose` attribute type-checking. // XFAIL: * diff --git a/test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift b/test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift index 79e8bac8f379a..977072bac6318 100644 --- a/test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift +++ b/test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift @@ -2,9 +2,6 @@ // REQUIRES: executable_test // REQUIRES: differentiable_programming -// We currently lack availability information (rdar://57975086) -// UNSUPPORTED: use_os_stdlib - import _Differentiation // Test `Differentiable` protocol conformances for stdlib types. From 5ab6c46e556d524bf6bc7224c7e60116099385cb Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 19 Dec 2019 19:43:41 -0800 Subject: [PATCH 149/478] Driver: mark test as XFAIL on windows --- test/Driver/batch_mode_with_supplementary_filelist.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Driver/batch_mode_with_supplementary_filelist.swift b/test/Driver/batch_mode_with_supplementary_filelist.swift index 898942b105c99..4bb87a4afef35 100644 --- a/test/Driver/batch_mode_with_supplementary_filelist.swift +++ b/test/Driver/batch_mode_with_supplementary_filelist.swift @@ -7,3 +7,5 @@ // RUN: %swiftc_driver -enable-batch-mode -driver-filelist-threshold=0 -j2 %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift -o %t/file-01.o -o %t/file-02.o -o %t/file-03.o -### | %FileCheck %s -check-prefix=CHECK-SUPPLEMENTARY-OUTPUT-FILELIST // // CHECK-SUPPLEMENTARY-OUTPUT-FILELIST: -supplementary-output-file-map {{.*(/|\\)}}supplementaryOutputs- +// +// XFAIL: OS=windows-msvc From 72018dbe6a41ebeb41030c304c143a27a9e11263 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 18 Dec 2019 16:22:08 -0500 Subject: [PATCH 150/478] Parse: Remove some dead code --- include/swift/Parse/Parser.h | 2 +- include/swift/Subsystems.h | 8 ++------ lib/Immediate/REPL.cpp | 6 ++---- lib/Parse/ParseDecl.cpp | 15 +------------- lib/ParseSIL/ParseSIL.cpp | 38 ++++++++++++++++-------------------- 5 files changed, 23 insertions(+), 46 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index ff70de7a7674e..40e016fdcd19c 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -869,7 +869,7 @@ class Parser { /// Return true if parser is at the start of a decl or decl-import. bool isStartOfDecl(); - bool parseTopLevel(); + void parseTopLevel(); /// Flags that control the parsing of declarations. enum ParseDeclFlags { diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 3baef1bf14fa3..57b4fd6e31803 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -119,18 +119,14 @@ namespace swift { /// /// \param PersistentState if non-null the same PersistentState object can /// be used to resume parsing or parse delayed function bodies. - /// - /// \return true if the parser found code with side effects. - bool parseIntoSourceFile(SourceFile &SF, unsigned BufferID, bool *Done, + void parseIntoSourceFile(SourceFile &SF, unsigned BufferID, bool *Done, SILParserState *SIL = nullptr, PersistentParserState *PersistentState = nullptr, bool DelayBodyParsing = true); /// Parse a single buffer into the given source file, until the full source /// contents are parsed. - /// - /// \return true if the parser found code with side effects. - bool parseIntoSourceFileFull(SourceFile &SF, unsigned BufferID, + void parseIntoSourceFileFull(SourceFile &SF, unsigned BufferID, PersistentParserState *PersistentState = nullptr, bool DelayBodyParsing = true); diff --git a/lib/Immediate/REPL.cpp b/lib/Immediate/REPL.cpp index 4d8362d762138..ab8d6d0f9add4 100644 --- a/lib/Immediate/REPL.cpp +++ b/lib/Immediate/REPL.cpp @@ -187,12 +187,10 @@ typeCheckREPLInput(ModuleDecl *MostRecentModule, StringRef Name, REPLInputFile.addImports(ImportsWithOptions); } - bool FoundAnySideEffects = false; bool Done; do { - FoundAnySideEffects |= - parseIntoSourceFile(REPLInputFile, BufferID, &Done, nullptr, - &PersistentState); + parseIntoSourceFile(REPLInputFile, BufferID, &Done, nullptr, + &PersistentState); } while (!Done); performTypeChecking(REPLInputFile); return REPLModule; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 075fec86cdb37..2362d0767483e 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -189,7 +189,7 @@ namespace { /// decl-sil [[only in SIL mode] /// decl-sil-stage [[only in SIL mode] /// \endverbatim -bool Parser::parseTopLevel() { +void Parser::parseTopLevel() { SF.ASTStage = SourceFile::Parsing; // Prime the lexer. @@ -246,17 +246,6 @@ bool Parser::parseTopLevel() { consumeToken(); } - // If this is a Main source file, determine if we found code that needs to be - // executed (this is used by the repl to know whether to compile and run the - // newly parsed stuff). - bool FoundTopLevelCodeToExecute = false; - if (allowTopLevelCode()) { - for (auto V : Items) { - if (isa(V.get())) - FoundTopLevelCodeToExecute = true; - } - } - // Add newly parsed decls to the module. for (auto Item : Items) { if (auto *D = Item.dyn_cast()) { @@ -279,8 +268,6 @@ bool Parser::parseTopLevel() { SyntaxContext->addToken(Tok, LeadingTrivia, TrailingTrivia); TokReceiver->finalize(); } - - return FoundTopLevelCodeToExecute; } ParserResult Parser::parseExtendedAvailabilitySpecList( diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index f20d804df0060..755a9a10ecb6d 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -112,13 +112,13 @@ void PrettyStackTraceParser::print(llvm::raw_ostream &out) const { out << '\n'; } -static bool parseIntoSourceFileImpl(SourceFile &SF, - unsigned BufferID, - bool *Done, - SILParserState *SIL, - PersistentParserState *PersistentState, - bool FullParse, - bool DelayBodyParsing) { +static void parseIntoSourceFileImpl(SourceFile &SF, + unsigned BufferID, + bool *Done, + SILParserState *SIL, + PersistentParserState *PersistentState, + bool FullParse, + bool DelayBodyParsing) { assert((!FullParse || (SF.canBeParsedInFull() && !SIL)) && "cannot parse in full with the given parameters!"); @@ -149,10 +149,8 @@ static bool parseIntoSourceFileImpl(SourceFile &SF, llvm::SaveAndRestore S(P.IsParsingInterfaceTokens, SF.hasInterfaceHash()); - bool FoundSideEffects = false; do { - bool hasSideEffects = P.parseTopLevel(); - FoundSideEffects = FoundSideEffects || hasSideEffects; + P.parseTopLevel(); *Done = P.Tok.is(tok::eof); } while (FullParse && !*Done); @@ -160,29 +158,27 @@ static bool parseIntoSourceFileImpl(SourceFile &SF, auto rawNode = P.finalizeSyntaxTree(); STreeCreator->acceptSyntaxRoot(rawNode, SF); } - - return FoundSideEffects; } -bool swift::parseIntoSourceFile(SourceFile &SF, +void swift::parseIntoSourceFile(SourceFile &SF, unsigned BufferID, bool *Done, SILParserState *SIL, PersistentParserState *PersistentState, bool DelayBodyParsing) { - return parseIntoSourceFileImpl(SF, BufferID, Done, SIL, - PersistentState, - /*FullParse=*/SF.shouldBuildSyntaxTree(), - DelayBodyParsing); + parseIntoSourceFileImpl(SF, BufferID, Done, SIL, + PersistentState, + /*FullParse=*/SF.shouldBuildSyntaxTree(), + DelayBodyParsing); } -bool swift::parseIntoSourceFileFull(SourceFile &SF, unsigned BufferID, +void swift::parseIntoSourceFileFull(SourceFile &SF, unsigned BufferID, PersistentParserState *PersistentState, bool DelayBodyParsing) { bool Done = false; - return parseIntoSourceFileImpl(SF, BufferID, &Done, /*SIL=*/nullptr, - PersistentState, /*FullParse=*/true, - DelayBodyParsing); + parseIntoSourceFileImpl(SF, BufferID, &Done, /*SIL=*/nullptr, + PersistentState, /*FullParse=*/true, + DelayBodyParsing); } From 1c2ac8ee17aa5de6347685c58f6e1744cb701896 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 18 Dec 2019 19:13:34 -0500 Subject: [PATCH 151/478] Parse: Parse entire top level in one shot --- lib/Parse/ParseStmt.cpp | 22 ++-------------------- test/Parse/confusables.swift | 4 ++-- test/Parse/identifiers.swift | 2 +- test/Parse/recovery.swift | 2 +- 4 files changed, 6 insertions(+), 24 deletions(-) diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 57daf5ae77b02..19ba496f31d72 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -202,6 +202,8 @@ bool Parser::isTerminatorForBraceItemListKind(BraceItemListKind Kind, ArrayRef ParsedDecls) { switch (Kind) { case BraceItemListKind::Brace: + case BraceItemListKind::TopLevelCode: + case BraceItemListKind::TopLevelLibrary: return false; case BraceItemListKind::Case: { if (Tok.is(tok::pound_if)) { @@ -220,26 +222,6 @@ bool Parser::isTerminatorForBraceItemListKind(BraceItemListKind Kind, } return isAtStartOfSwitchCase(*this); } - case BraceItemListKind::TopLevelCode: - // When parsing the top level executable code for a module, if we parsed - // some executable code, then we're done. We want to process (name bind, - // type check, etc) decls one at a time to make sure that there are not - // forward type references, etc. There is an outer loop around the parser - // that will reinvoke the parser at the top level on each statement until - // EOF. In contrast, it is ok to have forward references between classes, - // functions, etc. - for (auto I : ParsedDecls) { - if (isa(I.get())) - // Only bail out if the next token is at the start of a line. If we - // don't, then we may accidentally allow things like "a = 1 b = 4". - // FIXME: This is really dubious. This will reject some things, but - // allow other things we don't want. - if (Tok.isAtStartOfLine()) - return true; - } - return false; - case BraceItemListKind::TopLevelLibrary: - return false; case BraceItemListKind::ActiveConditionalBlock: case BraceItemListKind::InactiveConditionalBlock: return Tok.isNot(tok::pound_else) && Tok.isNot(tok::pound_endif) && diff --git a/test/Parse/confusables.swift b/test/Parse/confusables.swift index 8952a19f29d59..14a72ad943034 100644 --- a/test/Parse/confusables.swift +++ b/test/Parse/confusables.swift @@ -7,9 +7,9 @@ let number⁚ Int // expected-note {{operator '⁚' contains possibly confused characters; did you mean to use ':'?}} {{11-14=:}} // expected-warning @+3 2 {{integer literal is unused}} -// expected-error @+2 2 {{invalid character in source file}} +// expected-error @+2 {{invalid character in source file}} // expected-error @+1 {{consecutive statements on a line must be separated by ';'}} -5 ‒ 5 // expected-note 2 {{unicode character '‒' looks similar to '-'; did you mean to use '-'?}} {{3-6=-}} +5 ‒ 5 // expected-note {{unicode character '‒' looks similar to '-'; did you mean to use '-'?}} {{3-6=-}} // expected-error @+2 {{use of unresolved identifier 'ꝸꝸꝸ'}} // expected-error @+1 {{expected ',' separator}} diff --git a/test/Parse/identifiers.swift b/test/Parse/identifiers.swift index 264b6243a6586..b0ad8fe398c50 100644 --- a/test/Parse/identifiers.swift +++ b/test/Parse/identifiers.swift @@ -26,7 +26,7 @@ func s̈pin̈al_tap̈() {} () // expected-error{{invalid character in source file}} {{1-4= }} // Placeholders are recognized as identifiers but with error. -func <#some name#>() {} // expected-error 2 {{editor placeholder in source file}} +func <#some name#>() {} // expected-error {{editor placeholder in source file}} // Keywords as identifiers class switch {} // expected-error {{keyword 'switch' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{7-13=`switch`}} diff --git a/test/Parse/recovery.swift b/test/Parse/recovery.swift index 13d911cd15536..6984b1bd85b05 100644 --- a/test/Parse/recovery.swift +++ b/test/Parse/recovery.swift @@ -754,7 +754,7 @@ let curlyQuotes2 = “hello world!" // compiler should recover better from "unicode Specials" characters -let tryx = 123 // expected-error 2 {{invalid character in source file}} {{5-8= }} +let tryx = 123 // expected-error {{invalid character in source file}} {{5-8= }} // Malformed Swift Enums crash playground service From 19049608299447c954ef8936a960e02b6fffcd3a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 4 Dec 2019 23:40:45 -0500 Subject: [PATCH 152/478] Sema: Fix a couple of problems with capture analysis and TopLevelCodeDecls A TopLevelCodeDecl is a local context and any declarations inside of one must be treated as captures. Furthermore, all the TopLevelCodeDecl children of a source file are peers, and can see each other's bindings, so don't perform an exact match on the DeclContext. Part of / . --- lib/Sema/TypeCheckCaptures.cpp | 86 +++++++++++++++++----------- test/expr/capture/nested_class.swift | 17 ++++++ 2 files changed, 69 insertions(+), 34 deletions(-) diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index 2bdd3ec0ac52b..754c5a3564b13 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -25,6 +25,7 @@ #include "swift/AST/GenericSignature.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/SourceFile.h" #include "swift/AST/TypeWalker.h" #include "swift/Basic/Defer.h" #include "llvm/ADT/SmallPtrSet.h" @@ -219,10 +220,14 @@ class FindCapturedVars : public ASTWalker { if (D->getBaseName() == Context.Id_dollarInterpolation) return { false, DRE }; + // DC is the DeclContext where D was defined + // CurDC is the DeclContext where D was referenced + auto DC = D->getDeclContext(); + // Capture the generic parameters of the decl, unless it's a // local declaration in which case we will pick up generic // parameter references transitively. - if (!D->getDeclContext()->isLocalContext()) { + if (!DC->isLocalContext()) { if (!ObjC || !D->isObjC() || isa(D)) { if (auto subMap = DRE->getDeclRef().getSubstitutions()) { for (auto type : subMap.getReplacementTypes()) { @@ -232,40 +237,57 @@ class FindCapturedVars : public ASTWalker { } } - // DC is the DeclContext where D was defined - // CurDC is the DeclContext where D was referenced - auto DC = D->getDeclContext(); + // Don't "capture" type definitions at all. + if (isa(D)) + return { false, DRE }; // A local reference is not a capture. - if (CurDC == DC) + if (CurDC == DC || isa(CurDC)) return { false, DRE }; auto TmpDC = CurDC; - - if (!isa(DC)) { - while (TmpDC != nullptr) { - if (TmpDC == DC) - break; - - // The initializer of a lazy property will eventually get - // recontextualized into it, so treat it as if it's already there. - if (auto init = dyn_cast(TmpDC)) { - if (auto lazyVar = init->getInitializedLazyVar()) { - // If we have a getter with a body, we're already re-parented - // everything so pretend we're inside the getter. - if (auto getter = lazyVar->getAccessor(AccessorKind::Get)) { - if (getter->getBody(/*canSynthesize=*/false)) { - TmpDC = getter; - continue; - } + while (TmpDC != nullptr) { + // Variables defined inside TopLevelCodeDecls are semantically + // local variables. If the reference is not from the top level, + // we have a capture. + if (isa(DC) && + (isa(TmpDC) || isa(TmpDC))) + break; + + if (TmpDC == DC) + break; + + // The initializer of a lazy property will eventually get + // recontextualized into it, so treat it as if it's already there. + if (auto init = dyn_cast(TmpDC)) { + if (auto lazyVar = init->getInitializedLazyVar()) { + // If we have a getter with a body, we're already re-parented + // everything so pretend we're inside the getter. + if (auto getter = lazyVar->getAccessor(AccessorKind::Get)) { + if (getter->getBody(/*canSynthesize=*/false)) { + TmpDC = getter; + continue; } } } + } - // We have an intervening nominal type context that is not the - // declaration context, and the declaration context is not global. - // This is not supported since nominal types cannot capture values. - if (auto NTD = dyn_cast(TmpDC)) { + // We have an intervening nominal type context that is not the + // declaration context, and the declaration context is not global. + // This is not supported since nominal types cannot capture values. + if (auto NTD = dyn_cast(TmpDC)) { + // Allow references to local functions from inside methods of a + // local type, because if the local function has captures, we'll + // diagnose them in SILGen. It's a bit unfortunate that we can't + // ban this outright, but people rely on code like this working: + // + // do { + // func local() {} + // class C { + // func method() { local() } + // } + // } + if (!isa(D)) { if (DC->isLocalContext()) { Context.Diags.diagnose(DRE->getLoc(), diag::capture_across_type_decl, NTD->getDescriptiveKind(), @@ -278,18 +300,14 @@ class FindCapturedVars : public ASTWalker { return { false, DRE }; } } - - TmpDC = TmpDC->getParent(); } - // We walked all the way up to the root without finding the declaration, - // so this is not a capture. - if (TmpDC == nullptr) - return { false, DRE }; + TmpDC = TmpDC->getParent(); } - // Don't "capture" type definitions at all. - if (isa(D)) + // We walked all the way up to the root without finding the declaration, + // so this is not a capture. + if (TmpDC == nullptr) return { false, DRE }; // Only capture var decls at global scope. Other things can be captured diff --git a/test/expr/capture/nested_class.swift b/test/expr/capture/nested_class.swift index 463e99fe7eaf9..2db3a0a154f36 100644 --- a/test/expr/capture/nested_class.swift +++ b/test/expr/capture/nested_class.swift @@ -36,3 +36,20 @@ struct StructWithInnerStruct { } } } + +// Types cannot close over top-level guard bindings +guard let x: Int = nil else { fatalError() } +// expected-note@-1 {{'x' declared here}} + +func getX() -> Int { return x } + +class ClosesOverGuard { // expected-note {{type declared here}} + func foo() { + _ = x + // expected-error@-1 {{class declaration cannot close over value 'x' defined in outer scope}} + } + + func bar() { + _ = getX() // This is diagnosed by SILGen. + } +} From 04bf4327746b9e7e0fac48cfd19a4b3052dbe325 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 5 Dec 2019 00:39:30 -0500 Subject: [PATCH 153/478] SILGen: Diagnose invalid captures of top-level bindings A method of a nominal type is not allowed to capture anything. If we did compute a capture list, clear it out to preserve downstream invariants. This can happen because not all invalid captures involving nominal types in local context can be diagnosed in Sema. In particular, we allow a method of a local type to reference a local function from an outer scope, as long as that local function does not have any (transitive) captures. Since Sema cannot validate the latter condition, it punts and allows any reference to a FuncDecl in local context. When emitting the captures in this situation, SILGen will diagnose the problem using the existing 'forward reference' logic. The diagnostic is sub-optimal -- it should talk about a reference from inside a local type, and not a 'forward reference'. But still better than crashing. --- lib/SIL/TypeLowering.cpp | 9 +++++++++ ...ed_types_referencing_nested_functions.swift | 18 +++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index 13e8d03cca814..4b486652e0d4d 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -2392,6 +2392,15 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { resultingCaptures.push_back(*selfCapture); } + // It is an error for a member of a nominal type to have any captures + // at all. If we just clear them out here, SILGen will diagnose the + // problem. + if (fn.hasDecl()) { + auto *dc = fn.getDecl()->getDeclContext(); + if (dc->isTypeContext()) + resultingCaptures.clear(); + } + // Cache the uniqued set of transitive captures. CaptureInfo info{Context, resultingCaptures, capturesDynamicSelf, capturesOpaqueValue, capturesGenericParams}; diff --git a/test/SILGen/nested_types_referencing_nested_functions.swift b/test/SILGen/nested_types_referencing_nested_functions.swift index 0e27e309a5dea..99ae2cfcbd571 100644 --- a/test/SILGen/nested_types_referencing_nested_functions.swift +++ b/test/SILGen/nested_types_referencing_nested_functions.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-emit-silgen -module-name nested_types_referencing_nested_functions %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -verify -module-name nested_types_referencing_nested_functions %s | %FileCheck %s do { func foo() { bar(2) } @@ -32,3 +32,19 @@ do { _ = x.zim _ = x.zang as (Int) -> () } + +// Invalid case +do { + var x = 123 // expected-note {{captured value declared here}} + + func local() { + // expected-error@-1 {{closure captures 'x' before it is declared}} + _ = x // expected-note {{captured here}} + } + + class Bar { + func zang() { + local() + } + } +} From 8e6dc39f14b9f1172b1b8b08a6f4352c880c5c2a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 19 Dec 2019 22:35:55 -0500 Subject: [PATCH 154/478] Parse: Keep track of top-level declarations that follow a 'guard' statement --- include/swift/AST/Decl.h | 16 ++++++++++++++-- include/swift/Parse/Parser.h | 10 +++++++++- lib/AST/Decl.cpp | 6 ++++++ lib/Parse/ParseStmt.cpp | 27 ++++++++++++++++++++++++--- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index f129b5086a36d..0dfdda6db46c3 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -399,7 +399,7 @@ class alignas(1 << DeclAlignInBits) Decl { HasSingleExpressionBody : 1 ); - SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl, 1+1+2+1+1+2, + SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl, 1+1+2+1+1+2+1, /// Whether we've computed the 'static' flag yet. IsStaticComputed : 1, @@ -416,7 +416,12 @@ class alignas(1 << DeclAlignInBits) Decl { SelfAccessComputed : 1, /// Backing bits for 'self' access kind. - SelfAccess : 2 + SelfAccess : 2, + + /// Whether this is a top-level function which should be treated + /// as if it were in local context for the purposes of capture + /// analysis. + HasTopLevelLocalContextCaptures : 1 ); SWIFT_INLINE_BITFIELD(AccessorDecl, FuncDecl, 4+1+1, @@ -6078,6 +6083,7 @@ class FuncDecl : public AbstractFunctionDecl { Bits.FuncDecl.SelfAccessComputed = false; Bits.FuncDecl.IsStaticComputed = false; Bits.FuncDecl.IsStatic = false; + Bits.FuncDecl.HasTopLevelLocalContextCaptures = false; } private: @@ -6235,6 +6241,12 @@ class FuncDecl : public AbstractFunctionDecl { /// Perform basic checking to determine whether the @IBAction or /// @IBSegueAction attribute can be applied to this function. bool isPotentialIBActionTarget() const; + + bool hasTopLevelLocalContextCaptures() const { + return Bits.FuncDecl.HasTopLevelLocalContextCaptures; + } + + void setHasTopLevelLocalContextCaptures(bool hasCaptures=true); }; /// This represents an accessor function, such as a getter or setter. diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 40e016fdcd19c..a1c507c5e7dba 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -856,11 +856,19 @@ class Parser { void consumeTopLevelDecl(ParserPosition BeginParserPosition, TopLevelCodeDecl *TLCD); + ParserStatus parseBraceItems(SmallVectorImpl &Decls, + BraceItemListKind Kind, + BraceItemListKind ConditionalBlockKind, + bool &IsFollowingGuard); ParserStatus parseBraceItems(SmallVectorImpl &Decls, BraceItemListKind Kind = BraceItemListKind::Brace, BraceItemListKind ConditionalBlockKind = - BraceItemListKind::Brace); + BraceItemListKind::Brace) { + bool IsFollowingGuard = false; + return parseBraceItems(Decls, Kind, ConditionalBlockKind, + IsFollowingGuard); + } ParserResult parseBraceItemList(Diag<> ID); //===--------------------------------------------------------------------===// diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a7ed7ccb7fd01..6126a1ded8565 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7669,6 +7669,12 @@ bool FuncDecl::isPotentialIBActionTarget() const { !isa(this); } +void FuncDecl::setHasTopLevelLocalContextCaptures(bool hasCaptures) { + assert(!hasCaptures || isa(getDeclContext())); + + Bits.FuncDecl.HasTopLevelLocalContextCaptures = hasCaptures; +} + Type TypeBase::getSwiftNewtypeUnderlyingType() { auto structDecl = getStructOrBoundGenericStruct(); if (!structDecl) diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 19ba496f31d72..9184b08022263 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -271,7 +271,8 @@ void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition, /// expr '=' expr ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, BraceItemListKind Kind, - BraceItemListKind ConditionalBlockKind) { + BraceItemListKind ConditionalBlockKind, + bool &IsFollowingGuard) { bool isRootCtx = SyntaxContext->isRoot(); SyntaxParsingContext ItemListContext(SyntaxContext, SyntaxKind::CodeBlockItemList); @@ -364,7 +365,8 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, [&](SmallVectorImpl &Elements, bool IsActive) { parseBraceItems(Elements, Kind, IsActive ? BraceItemListKind::ActiveConditionalBlock - : BraceItemListKind::InactiveConditionalBlock); + : BraceItemListKind::InactiveConditionalBlock, + IsFollowingGuard); }); if (IfConfigResult.hasCodeCompletion() && isCodeCompletionFirstPass()) { consumeDecl(BeginParserPosition, None, IsTopLevel); @@ -401,7 +403,18 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, ParserResult DeclResult = parseDecl(IsTopLevel ? PD_AllowTopLevel : PD_Default, IsAtStartOfLineOrPreviousHadSemi, - [&](Decl *D) {TmpDecls.push_back(D);}); + [&](Decl *D) { + TmpDecls.push_back(D); + + // Any function after a 'guard' statement is marked as + // possibly having local captures. This allows SILGen + // to correctly determine its capture list, since + // otherwise it would be skipped because it is not + // defined inside a local context. + if (IsFollowingGuard) + if (auto *FD = dyn_cast(D)) + FD->setHasTopLevelLocalContextCaptures(); + }); BraceItemsStatus |= DeclResult; if (DeclResult.isParseError()) { NeedParseErrorRecovery = true; @@ -451,6 +464,14 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, Result, Result.getEndLoc()); TLCD->setBody(Brace); Entries.push_back(TLCD); + + // A top-level 'guard' statement can introduce local bindings, so we + // must mark all functions following one. This makes them behave + // as if they were in local context for the purposes of capture + // emission in SILGen. + if (auto *stmt = Result.dyn_cast()) + if (isa(stmt)) + IsFollowingGuard = true; } } else if (Tok.is(tok::kw_init) && isa(CurDeclContext)) { SourceLoc StartLoc = Tok.getLoc(); From e4626263c321e7b843d2ba7195837faa7a44c331 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 19 Dec 2019 22:53:21 -0500 Subject: [PATCH 155/478] AST: More consistent definition of a 'local capture' This commit adds a new ValueDecl::isLocalCapture() predicate and uses it in the right places. The predicate is true if the declaration is in local context, *or* if its at the top level of the main source file and follows a 'guard' statement. Fixes / . --- include/swift/AST/Decl.h | 4 ++++ lib/AST/CaptureInfo.cpp | 4 ++-- lib/AST/Decl.cpp | 10 ++++++++++ lib/Sema/TypeCheckCaptures.cpp | 2 +- test/SILGen/top_level_captures.swift | 14 ++++++++++++++ 5 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 test/SILGen/top_level_captures.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 0dfdda6db46c3..46faf5bab401e 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2713,6 +2713,10 @@ class ValueDecl : public Decl { AccessSemantics getAccessSemanticsFromContext(const DeclContext *DC, bool isAccessOnSelf) const; + /// Determines if a reference to this declaration from a nested function + /// should be treated like a capture of a local value. + bool isLocalCapture() const; + /// Print a reference to the given declaration. std::string printRef() const; diff --git a/lib/AST/CaptureInfo.cpp b/lib/AST/CaptureInfo.cpp index 3ea6088d88f89..382db84fe7170 100644 --- a/lib/AST/CaptureInfo.cpp +++ b/lib/AST/CaptureInfo.cpp @@ -57,7 +57,7 @@ CaptureInfo CaptureInfo::empty() { bool CaptureInfo::hasLocalCaptures() const { for (auto capture : getCaptures()) - if (capture.getDecl()->getDeclContext()->isLocalContext()) + if (capture.getDecl()->isLocalCapture()) return true; return false; } @@ -71,7 +71,7 @@ getLocalCaptures(SmallVectorImpl &Result) const { // Filter out global variables. for (auto capture : getCaptures()) { - if (!capture.getDecl()->getDeclContext()->isLocalContext()) + if (!capture.getDecl()->isLocalCapture()) continue; Result.push_back(capture); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 6126a1ded8565..f99f7964eccba 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2866,6 +2866,16 @@ bool ValueDecl::isImplicitlyUnwrappedOptional() const { false); } +bool ValueDecl::isLocalCapture() const { + auto *dc = getDeclContext(); + + if (auto *fd = dyn_cast(this)) + if (isa(dc)) + return fd->hasTopLevelLocalContextCaptures(); + + return dc->isLocalContext(); +} + ArrayRef ValueDecl::getSatisfiedProtocolRequirements(bool Sorted) const { // Dig out the nominal type. diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index 754c5a3564b13..d9173b384bde1 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -312,7 +312,7 @@ class FindCapturedVars : public ASTWalker { // Only capture var decls at global scope. Other things can be captured // if they are local. - if (!isa(D) && !DC->isLocalContext()) + if (!isa(D) && !D->isLocalCapture()) return { false, DRE }; // We're going to capture this, compute flags for the capture. diff --git a/test/SILGen/top_level_captures.swift b/test/SILGen/top_level_captures.swift new file mode 100644 index 0000000000000..b8ae627ba08cc --- /dev/null +++ b/test/SILGen/top_level_captures.swift @@ -0,0 +1,14 @@ +// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s + +guard let x: Int = nil else { while true { } } + +// CHECK-LABEL: sil hidden [ossa] @$s18top_level_captures0C1XyyF : $@convention(thin) (Int) -> () { +func capturesX() { + _ = x +} + +// CHECK-LABEL: sil hidden [ossa] @$s18top_level_captures17transitiveCaptureyyF : $@convention(thin) (Int) -> () { +// CHECK: [[FUNC:%.*]] = function_ref @$s18top_level_captures0C1XyyF : $@convention(thin) (Int) -> () +func transitiveCapture() { + capturesX() +} From 8214e18850e80847eb28e04880ea689f8e076e99 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 5 Dec 2019 16:22:15 -0500 Subject: [PATCH 156/478] SILGen: Clean up invariants around capture lists --- include/swift/SIL/TypeLowering.h | 18 ----------------- lib/SIL/TypeLowering.cpp | 29 ++++++++++++++++----------- lib/SILGen/SILGenFunction.cpp | 34 +------------------------------- lib/SILGen/SILGenProlog.cpp | 3 +-- 4 files changed, 20 insertions(+), 64 deletions(-) diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index 2e2a06876687c..eeea1c847d8c6 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -946,24 +946,6 @@ class TypeConverter { CaptureInfo getLoweredLocalCaptures(SILDeclRef fn); bool hasLoweredLocalCaptures(SILDeclRef fn); -#ifndef NDEBUG - /// If \c false, \c childDC is in a context it cannot capture variables from, - /// so it is expected that Sema may not have computed its \c CaptureInfo. - /// - /// This call exists for use in assertions; do not use it to skip capture - /// processing. - static bool canCaptureFromParent(DeclContext *childDC) { - // This call was added because Sema leaves the captures of functions that - // cannot capture anything uncomputed. - // TODO: Make Sema set them to CaptureInfo::empty() instead. - - if (childDC) - if (auto decl = childDC->getAsDecl()) - return decl->getDeclContext()->isLocalContext(); - return true; - } -#endif - enum class ABIDifference : uint8_t { // Types have compatible calling conventions and representations, so can // be trivially bitcast. diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index 4b486652e0d4d..8489ae68ae6f3 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -2172,6 +2172,20 @@ CaptureInfo TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { PrettyStackTraceSILLocation stack("getting lowered local captures", fn.getAsRegularLocation(), Context); + // If we're guaranteed to never have local captures, bail out now. + switch (fn.kind) { + case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + return CaptureInfo::empty(); + + default: + if (fn.hasDecl()) { + if (!fn.getDecl()->isLocalCapture()) + return CaptureInfo::empty(); + } + + break; + } fn.isForeign = 0; fn.isCurried = 0; @@ -2199,8 +2213,7 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { std::function collectConstantCaptures; collectCaptures = [&](CaptureInfo captureInfo, DeclContext *dc) { - assert(captureInfo.hasBeenComputed() || - !TypeConverter::canCaptureFromParent(dc)); + assert(captureInfo.hasBeenComputed()); if (captureInfo.hasGenericParamCaptures()) capturesGenericParams = true; @@ -2325,6 +2338,9 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { }; collectFunctionCaptures = [&](AnyFunctionRef curFn) { + if (!curFn.getBody()) + return; + if (!visitedFunctions.insert(curFn).second) return; @@ -2392,15 +2408,6 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { resultingCaptures.push_back(*selfCapture); } - // It is an error for a member of a nominal type to have any captures - // at all. If we just clear them out here, SILGen will diagnose the - // problem. - if (fn.hasDecl()) { - auto *dc = fn.getDecl()->getDeclContext(); - if (dc->isTypeContext()) - resultingCaptures.clear(); - } - // Cache the uniqued set of transitive captures. CaptureInfo info{Context, resultingCaptures, capturesDynamicSelf, capturesOpaqueValue, capturesGenericParams}; diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 2548a1901de44..162661705c3b7 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -695,31 +695,6 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { } } -#ifndef NDEBUG -/// If \c false, \c function is either a declaration that inherently cannot -/// capture variables, or it is in a context it cannot capture variables from. -/// In either case, it is expected that Sema may not have computed its -/// \c CaptureInfo. -/// -/// This call exists for use in assertions; do not use it to skip capture -/// processing. -static bool canCaptureFromParent(SILDeclRef function) { - switch (function.kind) { - case SILDeclRef::Kind::StoredPropertyInitializer: - case SILDeclRef::Kind::PropertyWrapperBackingInitializer: - return false; - - default: - if (function.hasDecl()) { - if (auto dc = dyn_cast(function.getDecl())) { - return TypeConverter::canCaptureFromParent(dc); - } - } - return false; - } -} -#endif - void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, bool EmitProfilerIncrement) { auto *dc = function.getDecl()->getInnermostDeclContext(); @@ -757,14 +732,7 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, params = ParameterList::create(ctx, SourceLoc(), {param}, SourceLoc()); } - CaptureInfo captureInfo; - if (function.getAnyFunctionRef()) - captureInfo = SGM.M.Types.getLoweredLocalCaptures(function); - else { - assert(!canCaptureFromParent(function)); - captureInfo = CaptureInfo::empty(); - } - + auto captureInfo = SGM.M.Types.getLoweredLocalCaptures(function); auto interfaceType = value->getType()->mapTypeOutOfContext(); emitProlog(captureInfo, params, /*selfParam=*/nullptr, dc, interfaceType, /*throws=*/false, SourceLoc()); diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 5b39eec77c341..e4d94aae962c4 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -445,8 +445,7 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo, // Emit the capture argument variables. These are placed last because they // become the first curry level of the SIL function. - assert((captureInfo.hasBeenComputed() || - !TypeConverter::canCaptureFromParent(DC)) && + assert(captureInfo.hasBeenComputed() && "can't emit prolog of function with uncomputed captures"); for (auto capture : captureInfo.getCaptures()) { if (capture.isDynamicSelfMetadata()) { From bdbe062aa2e1cd5f3e0c605a0cff1b4a8d37472c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 13 Dec 2019 16:56:44 -0500 Subject: [PATCH 157/478] Sema: Tweak SanitizeExpr handling of AutoClosureExprs Curry thunks create AutoClosureExprs with parameters, and we can't just fold those away. Also, remove an older AutoClosureExpr cleanup that seems to be redundant. --- lib/Sema/CSGen.cpp | 7 +++++-- lib/Sema/TypeCheckConstraints.cpp | 7 ------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 117a304bb6c94..e6d899aef29b1 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3409,8 +3409,11 @@ namespace { // Restore '@autoclosure'd value. if (auto ACE = dyn_cast(expr)) { - expr = ACE->getSingleExpressionBody(); - continue; + // This is only valid if the closure doesn't have parameters. + if (ACE->getParameters()->size() == 0) { + expr = ACE->getSingleExpressionBody(); + continue; + } } // Remove any semantic expression injected by typechecking. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index f02cd1770abd5..8fa945f79d107 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -1229,13 +1229,6 @@ namespace { DC = ce->getParent(); } - // Strip off any AutoClosures that were produced by a previous type check - // so that we don't choke in CSGen. - // FIXME: we shouldn't double typecheck, but it looks like code completion - // may do so in some circumstances. rdar://21466394 - if (auto autoClosure = dyn_cast(expr)) - return autoClosure->getSingleExpressionBody(); - // A 'self.init' or 'super.init' application inside a constructor will // evaluate to void, with the initializer's result implicitly rebound // to 'self'. Recognize the unresolved constructor expression and From af07642c8f423c5955d78a1e80b8c436a90d9913 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sat, 14 Dec 2019 23:39:44 -0500 Subject: [PATCH 158/478] Sema: Remove TypeCheckExprFlags::SkipMultiStmtClosures Fold this into TypeCheckExprFlags::SubExpressionDiagnostics, which is itself going away soon, hopefully! --- lib/Sema/CSApply.cpp | 4 ++-- lib/Sema/CSDiag.cpp | 4 ---- lib/Sema/ConstraintSystem.h | 9 +++++---- lib/Sema/TypeCheckConstraints.cpp | 2 +- lib/Sema/TypeChecker.h | 4 ---- 5 files changed, 8 insertions(+), 15 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index b6640f9cd647c..2be455c2a317b 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7208,7 +7208,7 @@ bool ConstraintSystem::applySolutionFixes(const Solution &solution) { Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, Type convertType, bool discardedExpr, - bool skipClosures) { + bool performingDiagnostics) { // If any fixes needed to be applied to arrive at this solution, resolve // them to specific expressions. if (!solution.Fixes.empty()) { @@ -7242,7 +7242,7 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, // If we're re-typechecking an expression for diagnostics, don't // visit closures that have non-single expression bodies. - if (!skipClosures) { + if (!performingDiagnostics) { bool hadError = false; for (auto *closure : walker.getClosuresToTypeCheck()) hadError |= TypeChecker::typeCheckClosureBody(closure); diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index ae8464e9eee9d..fb0b45f6cc625 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -595,10 +595,6 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( // to diagnose a problem. TCEOptions |= TypeCheckExprFlags::SubExpressionDiagnostics; - // Don't walk into non-single expression closure bodies, because - // ExprTypeSaver and TypeNullifier skip them too. - TCEOptions |= TypeCheckExprFlags::SkipMultiStmtClosures; - // Claim that the result is discarded to preserve the lvalue type of // the expression. if (options.contains(TCC_AllowLValue)) diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 4dae739eca75b..ab68f261434e6 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -3898,11 +3898,12 @@ class ConstraintSystem { /// expression should be converted, if any. /// \param discardedExpr if true, the result of the expression /// is contextually ignored. - /// \param skipClosures if true, don't descend into bodies of - /// non-single expression closures. + /// \param performingDiagnostics if true, don't descend into bodies of + /// non-single expression closures, or build curry thunks. Expr *applySolution(Solution &solution, Expr *expr, - Type convertType, bool discardedExpr, - bool skipClosures); + Type convertType, + bool discardedExpr, + bool performingDiagnostics); /// Reorder the disjunctive clauses for a given expression to /// increase the likelihood that a favored constraint will be successfully diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 8fa945f79d107..306adc0edb490 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2259,7 +2259,7 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, result = cs.applySolution( solution, result, convertType.getType(), options.contains(TypeCheckExprFlags::IsDiscarded), - options.contains(TypeCheckExprFlags::SkipMultiStmtClosures)); + options.contains(TypeCheckExprFlags::SubExpressionDiagnostics)); if (!result) { listener.applySolutionFailed(solution, expr); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 0de240b468473..0fec4740294f4 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -182,10 +182,6 @@ enum class TypeCheckExprFlags { /// not affect type checking itself. IsExprStmt = 0x20, - /// If set, this expression is being re-type checked as part of diagnostics, - /// and so we should not visit bodies of non-single expression closures. - SkipMultiStmtClosures = 0x40, - /// This is an inout yield. IsInOutYield = 0x100, From 4017a1668457b5ad6d05b9fe6404572f03746062 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 16 Dec 2019 15:38:17 -0500 Subject: [PATCH 159/478] Revert "SILGen: Simplify SILGenFunction::emitClosure()" This change has a subtle impact on debug info emission and causes a regression with a subsequent patch I'm about to commit. This reverts commit 62d1adb409918c638b53187cfb6ae3f4380afbd6. --- lib/SILGen/SILGenFunction.cpp | 7 ++++++- lib/SILGen/SILGenStmt.cpp | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 2548a1901de44..7a8c6980d950f 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -513,7 +513,12 @@ void SILGenFunction::emitClosure(AbstractClosureExpr *ace) { emitStmt(ce->getBody()); } else { auto *autoclosure = cast(ace); - emitStmt(autoclosure->getBody()); + // Closure expressions implicitly return the result of their body + // expression. + if (B.hasValidInsertionPoint()) { + emitReturnExpr(ImplicitReturnLocation(ace), + autoclosure->getSingleExpressionBody()); + } } emitEpilog(ace); } diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 78dfdba0cfb85..7c33d612fdcc4 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -482,6 +482,7 @@ void SILGenFunction::emitReturnExpr(SILLocation branchLoc, RValue RV = emitRValue(ret).ensurePlusOne(*this, CleanupLocation(ret)); std::move(RV).forwardAll(*this, directResults); } + Cleanups.emitBranchAndCleanups(ReturnDest, branchLoc, directResults); } From f9943349120de0e2bd47889e2b67f2168c82077f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 16 Dec 2019 16:52:37 -0500 Subject: [PATCH 160/478] SILOptimizer: AllocBoxToStack preserves [transparent] bit in cloned function We need this to uphold the invariant that in the performance pipeline before SIL serialization occurs, a function has ownership iff it is transparent. --- lib/SILOptimizer/Transforms/AllocBoxToStack.cpp | 2 +- test/SILOptimizer/allocbox_to_stack_ownership.sil | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index ddf770d2a0874..d70e8c7305d79 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -634,7 +634,7 @@ SILFunction *PromotedParamCloner::initCloned(SILOptFunctionBuilder &FuncBuilder, assert(!Orig->isGlobalInit() && "Global initializer cannot be cloned"); auto *Fn = FuncBuilder.createFunction( SILLinkage::Shared, ClonedName, ClonedTy, Orig->getGenericEnvironment(), - Orig->getLocation(), Orig->isBare(), IsNotTransparent, Serialized, + Orig->getLocation(), Orig->isBare(), Orig->isTransparent(), Serialized, IsNotDynamic, Orig->getEntryCount(), Orig->isThunk(), Orig->getClassSubclassScope(), Orig->getInlineStrategy(), Orig->getEffectsKind(), Orig, Orig->getDebugScope()); diff --git a/test/SILOptimizer/allocbox_to_stack_ownership.sil b/test/SILOptimizer/allocbox_to_stack_ownership.sil index 73f403011570d..12cfada7e7288 100644 --- a/test/SILOptimizer/allocbox_to_stack_ownership.sil +++ b/test/SILOptimizer/allocbox_to_stack_ownership.sil @@ -391,10 +391,10 @@ bb0(%0 : $Int): return %10 : $() // id: %11 } -// CHECK-LABEL: sil shared [ossa] @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n -// CHECK-LABEL: sil private [ossa] @$s6struct8useStack1tySi_tFSiycfU_ +// CHECK-LABEL: sil shared [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n +// CHECK-LABEL: sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ // struct.(useStack (t : Swift.Int) -> ()).(closure #1) -sil private [ossa] @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int { +sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int { bb0(%0 : @owned $<τ_0_0> { var τ_0_0 } ): %1 = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 // function_ref Swift.++ @postfix (x : @inout A) -> A From 3149d6d62c0c550ebf300e151d277973962829ab Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 16 Dec 2019 21:40:15 -0500 Subject: [PATCH 161/478] IDE: Preparations for Sema building curry thunks --- include/swift/AST/Expr.h | 16 +++++++++ lib/IDE/CodeCompletion.cpp | 70 ++++++++++++++++++++++++++++---------- lib/Index/Index.cpp | 4 +-- 3 files changed, 70 insertions(+), 20 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 77cdc587f7129..78a4b0563cc40 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -284,6 +284,12 @@ class alignas(8) Expr { Discriminator : 16 ); + SWIFT_INLINE_BITFIELD(AutoClosureExpr, AbstractClosureExpr, 1, + /// True if this autoclosure was built for a function conversion, and + /// not an actual @autoclosure parameter. + IsThunk : 1 + ); + SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1, /// True if closure parameters were synthesized from anonymous closure /// variables. @@ -3762,6 +3768,16 @@ class AutoClosureExpr : public AbstractClosureExpr { Discriminator, Parent) { if (Body != nullptr) setBody(Body); + + Bits.AutoClosureExpr.IsThunk = false; + } + + bool isThunk() const { + return Bits.AutoClosureExpr.IsThunk; + } + + void setIsThunk(bool isThunk) { + Bits.AutoClosureExpr.IsThunk = isThunk; } SourceRange getSourceRange() const; diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 0c0ec66fb7fb8..fa85bec958bcd 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1264,6 +1264,57 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { /// to the \c Consumer. bool DeliveredResults = false; + std::pair getReferencedDecl(Expr *expr) { + auto exprTy = ParsedExpr->getType(); + + // Look through unbound instance member accesses. + if (auto *dotSyntaxExpr = dyn_cast(expr)) + expr = dotSyntaxExpr->getRHS(); + + // Look through the 'self' application. + if (auto *selfApplyExpr = dyn_cast(expr)) + expr = selfApplyExpr->getFn(); + + // Look through curry thunks. + if (auto *closure = dyn_cast(expr)) { + if (closure->isThunk()) { + auto *body = closure->getSingleExpressionBody(); + if (isa(body) && + closure->getParameters()->size() == 1) + expr = closure->getSingleExpressionBody(); + } + } + + if (auto *closure = dyn_cast(expr)) { + if (closure->isThunk()) { + auto *body = closure->getSingleExpressionBody(); + body = body->getSemanticsProvidingExpr(); + if (auto *outerCall = dyn_cast(body)) { + if (auto *innerCall = dyn_cast(outerCall->getFn())) { + if (auto *declRef = dyn_cast(innerCall->getFn())) { + expr = declRef; + } + } + } + } + } + + // If this is an IUO result, unwrap the optional type. + auto refDecl = expr->getReferencedDecl(); + if (!refDecl) { + if (auto *applyExpr = dyn_cast(expr)) { + auto fnDecl = applyExpr->getFn()->getReferencedDecl(); + if (auto *func = fnDecl.getDecl()) { + if (func->isImplicitlyUnwrappedOptional()) { + if (auto objectTy = exprTy->getOptionalObjectType()) + exprTy = objectTy; + } + } + } + } + + return std::make_pair(exprTy, refDecl); + } Optional> typeCheckParsedExpr() { assert(ParsedExpr && "should have an expression"); @@ -1279,24 +1330,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { // typecheck again. rdar://21466394 if (CheckKind == CompletionTypeCheckKind::Normal && ParsedExpr->getType() && !ParsedExpr->getType()->is()) { - auto refDecl = ParsedExpr->getReferencedDecl(); - auto exprTy = ParsedExpr->getType(); - if (!refDecl) { - // FIXME: do this in the not-already-type-checked branch too? - if (auto *apply = dyn_cast(ParsedExpr)) { - refDecl = apply->getFn()->getReferencedDecl(); - } else if (auto *apply = dyn_cast(ParsedExpr)) { - // If this is an IUO, use the underlying non-optional type instead - auto fnDecl = apply->getFn()->getReferencedDecl(); - if (auto FD = fnDecl.getDecl()) { - if (FD->isImplicitlyUnwrappedOptional()) { - if (auto OT = exprTy->getOptionalObjectType()) - exprTy = OT; - } - } - } - } - return std::make_pair(exprTy, refDecl); + return getReferencedDecl(ParsedExpr); } ConcreteDeclRef ReferencedDecl = nullptr; diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index ca90d075fde2e..ad7b9db5f296f 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -362,10 +362,10 @@ class IndexSwiftASTWalker : public SourceEntityWalker { } void handleMemberwiseInitRefs(Expr *E) { - if (!isa(E)) + if (!isa(E)) return; - auto *DeclRef = dyn_cast(cast(E)->getFn()); + auto *DeclRef = dyn_cast(cast(E)->getFn()); if (!DeclRef || !isMemberwiseInit(DeclRef->getDecl())) return; From 3604b21f2a4247d6aa3cc2073010dde14efe3837 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 16 Dec 2019 21:34:07 -0500 Subject: [PATCH 162/478] AST: Simplify Expr::getReferencedDecl() --- include/swift/IDE/Utils.h | 5 ++ lib/AST/Expr.cpp | 10 +-- lib/IDE/CodeCompletion.cpp | 52 -------------- lib/IDE/Utils.cpp | 52 ++++++++++++++ lib/Migrator/APIDiffMigratorPass.cpp | 101 ++++++++++----------------- 5 files changed, 95 insertions(+), 125 deletions(-) diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index d0a216a872405..45a991a31e049 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -584,6 +584,11 @@ ClangNode getEffectiveClangNode(const Decl *decl); /// Retrieve the Clang node for the given extension, if it has one. ClangNode extensionGetClangNode(const ExtensionDecl *ext); +/// Utility for finding the referenced declaration from a call, which might +/// include a second level of function application for a 'self.' expression, +/// or a curry thunk, etc. +std::pair getReferencedDecl(Expr *expr); + } // namespace ide } // namespace swift diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index f6b41f4ade2a4..72dde8c11666a 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -254,13 +254,7 @@ ConcreteDeclRef Expr::getReferencedDecl() const { SIMPLE_REFERENCE(DeclRef, getDeclRef); SIMPLE_REFERENCE(SuperRef, getSelf); - case ExprKind::Type: { - auto typeRepr = cast(this)->getTypeRepr(); - if (!typeRepr) return ConcreteDeclRef(); - auto ident = dyn_cast(typeRepr); - if (!ident) return ConcreteDeclRef(); - return ident->getComponentRange().back()->getBoundDecl(); - } + NO_REFERENCE(Type); SIMPLE_REFERENCE(OtherConstructorDeclRef, getDeclRef); @@ -324,8 +318,8 @@ ConcreteDeclRef Expr::getReferencedDecl() const { NO_REFERENCE(Binary); NO_REFERENCE(DotSyntaxCall); NO_REFERENCE(MakeTemporarilyEscapable); + NO_REFERENCE(ConstructorRefCall); - PASS_THROUGH_REFERENCE(ConstructorRefCall, getFn); PASS_THROUGH_REFERENCE(Load, getSubExpr); NO_REFERENCE(DestructureTuple); NO_REFERENCE(UnresolvedTypeConversion); diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index fa85bec958bcd..ce2171a6118c1 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1264,58 +1264,6 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { /// to the \c Consumer. bool DeliveredResults = false; - std::pair getReferencedDecl(Expr *expr) { - auto exprTy = ParsedExpr->getType(); - - // Look through unbound instance member accesses. - if (auto *dotSyntaxExpr = dyn_cast(expr)) - expr = dotSyntaxExpr->getRHS(); - - // Look through the 'self' application. - if (auto *selfApplyExpr = dyn_cast(expr)) - expr = selfApplyExpr->getFn(); - - // Look through curry thunks. - if (auto *closure = dyn_cast(expr)) { - if (closure->isThunk()) { - auto *body = closure->getSingleExpressionBody(); - if (isa(body) && - closure->getParameters()->size() == 1) - expr = closure->getSingleExpressionBody(); - } - } - - if (auto *closure = dyn_cast(expr)) { - if (closure->isThunk()) { - auto *body = closure->getSingleExpressionBody(); - body = body->getSemanticsProvidingExpr(); - if (auto *outerCall = dyn_cast(body)) { - if (auto *innerCall = dyn_cast(outerCall->getFn())) { - if (auto *declRef = dyn_cast(innerCall->getFn())) { - expr = declRef; - } - } - } - } - } - - // If this is an IUO result, unwrap the optional type. - auto refDecl = expr->getReferencedDecl(); - if (!refDecl) { - if (auto *applyExpr = dyn_cast(expr)) { - auto fnDecl = applyExpr->getFn()->getReferencedDecl(); - if (auto *func = fnDecl.getDecl()) { - if (func->isImplicitlyUnwrappedOptional()) { - if (auto objectTy = exprTy->getOptionalObjectType()) - exprTy = objectTy; - } - } - } - } - - return std::make_pair(exprTy, refDecl); - } - Optional> typeCheckParsedExpr() { assert(ParsedExpr && "should have an expression"); diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index ec7393c37633a..a52c6570e178f 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -976,3 +976,55 @@ ClangNode swift::ide::extensionGetClangNode(const ExtensionDecl *ext) { return ClangNode(); } + +std::pair swift::ide::getReferencedDecl(Expr *expr) { + auto exprTy = expr->getType(); + + // Look through unbound instance member accesses. + if (auto *dotSyntaxExpr = dyn_cast(expr)) + expr = dotSyntaxExpr->getRHS(); + + // Look through the 'self' application. + if (auto *selfApplyExpr = dyn_cast(expr)) + expr = selfApplyExpr->getFn(); + + // Look through curry thunks. + if (auto *closure = dyn_cast(expr)) { + if (closure->isThunk()) { + auto *body = closure->getSingleExpressionBody(); + if (isa(body) && + closure->getParameters()->size() == 1) + expr = closure->getSingleExpressionBody(); + } + } + + if (auto *closure = dyn_cast(expr)) { + if (closure->isThunk()) { + auto *body = closure->getSingleExpressionBody(); + body = body->getSemanticsProvidingExpr(); + if (auto *outerCall = dyn_cast(body)) { + if (auto *innerCall = dyn_cast(outerCall->getFn())) { + if (auto *declRef = dyn_cast(innerCall->getFn())) { + expr = declRef; + } + } + } + } + } + + // If this is an IUO result, unwrap the optional type. + auto refDecl = expr->getReferencedDecl(); + if (!refDecl) { + if (auto *applyExpr = dyn_cast(expr)) { + auto fnDecl = applyExpr->getFn()->getReferencedDecl(); + if (auto *func = fnDecl.getDecl()) { + if (func->isImplicitlyUnwrappedOptional()) { + if (auto objectTy = exprTy->getOptionalObjectType()) + exprTy = objectTy; + } + } + } + } + + return std::make_pair(exprTy, refDecl); +} diff --git a/lib/Migrator/APIDiffMigratorPass.cpp b/lib/Migrator/APIDiffMigratorPass.cpp index 38535f82610fc..0cc1870cbd9ff 100644 --- a/lib/Migrator/APIDiffMigratorPass.cpp +++ b/lib/Migrator/APIDiffMigratorPass.cpp @@ -231,21 +231,6 @@ class ChildIndexFinder : public TypeReprVisitor { } }; -static ValueDecl* getReferencedDecl(Expr *E) { - // Get the syntactic expression out of an implicit expression. - if (auto *ICE = dyn_cast(E)) - E = ICE->getSyntacticSubExpr(); - if (auto *DRE = dyn_cast(E)) { - return DRE->getDecl(); - } else if (auto *MRE = dyn_cast(E)) { - return MRE->getMember().getDecl(); - } else if (auto OtherCtorE = dyn_cast(E)) { - return OtherCtorE->getDecl(); - } else { - return nullptr; - } -} - struct ConversionFunctionInfo { Expr *ExpressionToWrap; SmallString<256> Buffer; @@ -582,15 +567,10 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { } return false; }; - if (auto *DSC = dyn_cast(Call)) { - if (auto FD = DSC->getFn()->getReferencedDecl().getDecl()) { - if (handleDecl(FD, Call->getSourceRange())) - return true; - } - } else if (auto MRE = dyn_cast(Call)) { - if (handleDecl(MRE->getReferencedDecl().getDecl(), MRE->getSourceRange())) + if (auto *VD = getReferencedDecl(Call).second.getDecl()) + if (handleDecl(VD, Call->getSourceRange())) return true; - } + return false; } @@ -858,11 +838,12 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { Lexer::getLocForEndOfToken(SM, E->getEndLoc())), Text); } - bool wrapAttributeReference(Expr* Reference, Expr* WrapperTarget, + bool wrapAttributeReference(Expr *Reference, Expr *WrapperTarget, bool FromString) { - auto *RD = getReferencedDecl(Reference); + auto *RD = Reference->getReferencedDecl().getDecl(); if (!RD) return false; + std::string Rename; Optional Kind; StringRef LeftComment; @@ -1110,7 +1091,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { // reference of the property. bool handlePropertyTypeChange(Expr *E) { if (auto MRE = dyn_cast(E)) { - if (auto *VD = MRE->getReferencedDecl().getDecl()) { + if (auto *VD = MRE->getMember().getDecl()) { for (auto *I: getRelatedDiffItems(VD)) { if (auto *Item = dyn_cast(I)) { if (Item->DiffKind == NodeAnnotation::WrapOptional && @@ -1143,46 +1124,36 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { if (auto *CE = dyn_cast(E)) { auto Fn = CE->getFn(); auto Args = CE->getArg(); - switch (Fn->getKind()) { - case ExprKind::DeclRef: { - if (auto FD = Fn->getReferencedDecl().getDecl()) { - handleFuncRename(FD, Fn, Args); - handleTypeHoist(FD, CE, Args); - handleSpecialCases(FD, CE, Args); - handleStringRepresentableArg(FD, Args, CE); - handleResultTypeChange(FD, CE); - } - break; - } - case ExprKind::DotSyntaxCall: { - auto DSC = cast(Fn); - if (auto FD = DSC->getFn()->getReferencedDecl().getDecl()) { - handleFuncRename(FD, DSC->getFn(), Args); - handleFunctionCallToPropertyChange(FD, DSC->getFn(), Args); - handleSpecialCases(FD, CE, Args); - handleStringRepresentableArg(FD, Args, CE); - handleResultTypeChange(FD, CE); + + if (auto *DRE = dyn_cast(Fn)) { + if (auto *VD = DRE->getDecl()) { + if (VD->getNumCurryLevels() == 1) { + handleFuncRename(VD, Fn, Args); + handleTypeHoist(VD, CE, Args); + handleSpecialCases(VD, CE, Args); + handleStringRepresentableArg(VD, Args, CE); + handleResultTypeChange(VD, CE); + } } - break; } - case ExprKind::ConstructorRefCall: { - auto CCE = cast(Fn); - if (auto FD = CCE->getFn()->getReferencedDecl().getDecl()) { - handleFuncRename(FD, CE, Args); - handleStringRepresentableArg(FD, Args, CE); - handleResultTypeChange(FD, CE); + + if (auto *SelfApply = dyn_cast(Fn)) { + if (auto VD = SelfApply->getFn()->getReferencedDecl().getDecl()) { + if (VD->getNumCurryLevels() == 2) { + handleFuncRename(VD, SelfApply->getFn(), Args); + handleFunctionCallToPropertyChange(VD, SelfApply->getFn(), Args); + handleSpecialCases(VD, CE, Args); + handleStringRepresentableArg(VD, Args, CE); + handleResultTypeChange(VD, CE); + } } - break; - } - default: - break; } } return true; } - static void collectParamters(AbstractFunctionDecl *AFD, - SmallVectorImpl &Results) { + static void collectParameters(AbstractFunctionDecl *AFD, + SmallVectorImpl &Results) { for (auto PD : *AFD->getParameters()) { Results.push_back(PD); } @@ -1197,7 +1168,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { Editor.replace(NameRange, View.base()); unsigned Index = 0; SmallVector Params; - collectParamters(AFD, Params); + collectParameters(AFD, Params); for (auto *PD: Params) { if (Index == View.argSize()) break; @@ -1322,7 +1293,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { return; Idx --; SmallVector Params; - collectParamters(AFD, Params); + collectParameters(AFD, Params); if (Params.size() <= Idx) return; @@ -1397,11 +1368,11 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { Editor(Editor), USRs(USRs) {} bool isSuperExpr(Expr *E) { if (E->isImplicit()) - return false; + return false; // Check if the expression is super.foo(). if (auto *CE = dyn_cast(E)) { if (auto *DSC = dyn_cast(CE->getFn())) { - if (DSC->getBase()->getKind() != ExprKind::SuperRef) + if (!isa(DSC->getBase())) return false; llvm::SmallString<64> Buffer; llvm::raw_svector_ostream OS(Buffer); @@ -1419,9 +1390,9 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { } std::pair walkToStmtPre(Stmt *S) override { if (auto *BS = dyn_cast(S)) { - for(auto Ele: BS->getElements()) { - if (Ele.is() && isSuperExpr(Ele.get())) { - Editor.remove(Ele.getSourceRange()); + for(auto Ele: BS->getElements()) { + if (Ele.is() && isSuperExpr(Ele.get())) { + Editor.remove(Ele.getSourceRange()); } } } From 14ee6c5d1ccdd3da0c32323db21d2b988ee22e18 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 19 Dec 2019 21:06:20 -0800 Subject: [PATCH 163/478] [AutoDiff] Enable `@derivative` attribute qualified declaration names. (#28892) Enable qualified declaration names in `@derivative` attribute, just like `@transpose` attribute. `DerivativeAttr` now stores a base type `TypeRepr *`, which is non-null for parsed attributes that reference a qualified original declaration. Add `TypeResolutionFlags::AllowModule` flag to enable module lookup via `TypeChecker::lookupMember` given a `ModuleType`. Add tests for type-qualified and module-qualified declaration names. Resolves TF-1058. --- include/swift/AST/Attr.h | 26 ++++++++++----- lib/AST/Attr.cpp | 33 ++++++++++--------- lib/Parse/ParseDecl.cpp | 16 ++++----- lib/Sema/TypeCheckAttr.cpp | 18 +++++++--- lib/Sema/TypeCheckType.cpp | 4 +++ lib/Sema/TypeCheckType.h | 3 ++ lib/Serialization/Deserialization.cpp | 11 ++++--- .../Sema/derivative_attr_type_checking.swift | 11 ++++++- .../round_trip_parse_gen.swift.withkinds | 4 +-- .../Syntax/round_trip_parse_gen.swift | 4 +-- 10 files changed, 85 insertions(+), 45 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 198b7eeffbebb..f3a7083b49aea 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1791,6 +1791,11 @@ class DerivativeAttr final private llvm::TrailingObjects { friend TrailingObjects; + /// The base type repr for the referenced original function. This field is + /// non-null only for parsed attributes that reference a qualified original + /// declaration. This field is not serialized; type-checking uses it to + /// resolve the original declaration, which is serialized. + TypeRepr *BaseTypeRepr; /// The original function name. DeclNameRefWithLoc OriginalFunctionName; /// The original function declaration, resolved by the type checker. @@ -1803,23 +1808,27 @@ class DerivativeAttr final Optional Kind = None; explicit DerivativeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, - DeclNameRefWithLoc original, + TypeRepr *baseTypeRepr, DeclNameRefWithLoc original, ArrayRef params); explicit DerivativeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, - DeclNameRefWithLoc original, IndexSubset *indices); + TypeRepr *baseTypeRepr, DeclNameRefWithLoc original, + IndexSubset *parameterIndices); public: static DerivativeAttr *create(ASTContext &context, bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseTypeRepr, DeclNameRefWithLoc original, ArrayRef params); static DerivativeAttr *create(ASTContext &context, bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseTypeRepr, DeclNameRefWithLoc original, - IndexSubset *indices); + IndexSubset *parameterIndices); + TypeRepr *getBaseTypeRepr() const { return BaseTypeRepr; } DeclNameRefWithLoc getOriginalFunctionName() const { return OriginalFunctionName; } @@ -1876,9 +1885,10 @@ class TransposeAttr final private llvm::TrailingObjects { friend TrailingObjects; - /// The base type of the original function. - /// This is non-null only when the original function is not top-level (i.e. it - /// is an instance/static method). + /// The base type repr for the referenced original function. This field is + /// non-null only for parsed attributes that reference a qualified original + /// declaration. This field is not serialized; type-checking uses it to + /// resolve the original declaration, which is serialized. TypeRepr *BaseTypeRepr; /// The original function name. DeclNameRefWithLoc OriginalFunctionName; @@ -1895,7 +1905,7 @@ class TransposeAttr final explicit TransposeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, TypeRepr *baseType, DeclNameRefWithLoc original, - IndexSubset *indices); + IndexSubset *parameterIndices); public: static TransposeAttr *create(ASTContext &context, bool implicit, @@ -1906,7 +1916,7 @@ class TransposeAttr final static TransposeAttr *create(ASTContext &context, bool implicit, SourceLoc atLoc, SourceRange baseRange, TypeRepr *baseType, DeclNameRefWithLoc original, - IndexSubset *indices); + IndexSubset *parameterIndices); TypeRepr *getBaseTypeRepr() const { return BaseTypeRepr; } DeclNameRefWithLoc getOriginalFunctionName() const { diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index bc96f9b29d253..65333c36d96de 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1518,41 +1518,43 @@ void DifferentiableAttr::print(llvm::raw_ostream &OS, const Decl *D, } DerivativeAttr::DerivativeAttr(bool implicit, SourceLoc atLoc, - SourceRange baseRange, + SourceRange baseRange, TypeRepr *baseTypeRepr, DeclNameRefWithLoc originalName, ArrayRef params) : DeclAttribute(DAK_Derivative, atLoc, baseRange, implicit), - OriginalFunctionName(std::move(originalName)), + BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), NumParsedParameters(params.size()) { std::copy(params.begin(), params.end(), getTrailingObjects()); } DerivativeAttr::DerivativeAttr(bool implicit, SourceLoc atLoc, - SourceRange baseRange, + SourceRange baseRange, TypeRepr *baseTypeRepr, DeclNameRefWithLoc originalName, - IndexSubset *indices) + IndexSubset *parameterIndices) : DeclAttribute(DAK_Derivative, atLoc, baseRange, implicit), - OriginalFunctionName(std::move(originalName)), ParameterIndices(indices) { -} + BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), + ParameterIndices(parameterIndices) {} DerivativeAttr * DerivativeAttr::create(ASTContext &context, bool implicit, SourceLoc atLoc, - SourceRange baseRange, DeclNameRefWithLoc originalName, + SourceRange baseRange, TypeRepr *baseTypeRepr, + DeclNameRefWithLoc originalName, ArrayRef params) { unsigned size = totalSizeToAlloc(params.size()); void *mem = context.Allocate(size, alignof(DerivativeAttr)); - return new (mem) DerivativeAttr(implicit, atLoc, baseRange, + return new (mem) DerivativeAttr(implicit, atLoc, baseRange, baseTypeRepr, std::move(originalName), params); } DerivativeAttr *DerivativeAttr::create(ASTContext &context, bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseTypeRepr, DeclNameRefWithLoc originalName, - IndexSubset *indices) { + IndexSubset *parameterIndices) { void *mem = context.Allocate(sizeof(DerivativeAttr), alignof(DerivativeAttr)); - return new (mem) DerivativeAttr(implicit, atLoc, baseRange, - std::move(originalName), indices); + return new (mem) DerivativeAttr(implicit, atLoc, baseRange, baseTypeRepr, + std::move(originalName), parameterIndices); } TransposeAttr::TransposeAttr(bool implicit, SourceLoc atLoc, @@ -1568,10 +1570,11 @@ TransposeAttr::TransposeAttr(bool implicit, SourceLoc atLoc, TransposeAttr::TransposeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, TypeRepr *baseTypeRepr, - DeclNameRefWithLoc originalName, IndexSubset *indices) + DeclNameRefWithLoc originalName, + IndexSubset *parameterIndices) : DeclAttribute(DAK_Transpose, atLoc, baseRange, implicit), BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), - ParameterIndices(indices) {} + ParameterIndices(parameterIndices) {} TransposeAttr *TransposeAttr::create(ASTContext &context, bool implicit, SourceLoc atLoc, SourceRange baseRange, @@ -1588,10 +1591,10 @@ TransposeAttr *TransposeAttr::create(ASTContext &context, bool implicit, SourceLoc atLoc, SourceRange baseRange, TypeRepr *baseType, DeclNameRefWithLoc originalName, - IndexSubset *indices) { + IndexSubset *parameterIndices) { void *mem = context.Allocate(sizeof(TransposeAttr), alignof(TransposeAttr)); return new (mem) TransposeAttr(implicit, atLoc, baseRange, baseType, - std::move(originalName), indices); + std::move(originalName), parameterIndices); } ImplementsAttr::ImplementsAttr(SourceLoc atLoc, SourceRange range, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 075fec86cdb37..849ccd8a4fa94 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1111,7 +1111,8 @@ static bool parseBaseTypeForQualifiedDeclName(Parser &P, TypeRepr *&baseType) { return false; } -/// parseQualifiedDeclName +/// Parses an optional base type, followed by a declaration name. +/// Returns true on error (if declaration name could not be parsed). /// /// \verbatim /// qualified-decl-name: @@ -1120,8 +1121,6 @@ static bool parseBaseTypeForQualifiedDeclName(Parser &P, TypeRepr *&baseType) { /// identifier generic-args? ('.' identifier generic-args?)* /// \endverbatim /// -/// Parses an optional base type, followed by a declaration name. -/// Returns true on error (if declaration name could not be parsed). // TODO(TF-1066): Use module qualified name syntax/parsing instead of custom // qualified name syntax/parsing. static bool parseQualifiedDeclName(Parser &P, Diag<> nameParseError, @@ -1147,7 +1146,8 @@ static bool parseQualifiedDeclName(Parser &P, Diag<> nameParseError, /// /// \verbatim /// derivative-attribute-arguments: -/// '(' 'of' ':' decl-name (',' differentiation-params-clause)? ')' +/// '(' 'of' ':' qualified-decl-name (',' differentiation-params-clause)? +/// ')' /// \endverbatim ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, SourceLoc loc) { @@ -1206,16 +1206,16 @@ ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, /*DeclModifier*/ false); return makeParserError(); } - return ParserResult( - DerivativeAttr::create(Context, /*implicit*/ false, atLoc, - SourceRange(loc, rParenLoc), original, params)); + return ParserResult(DerivativeAttr::create( + Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), baseType, + original, params)); } /// Parse a `@transpose(of:)` attribute, returning true on error. /// /// \verbatim /// transpose-attribute-arguments: -/// '(' 'of' ':' decl-name (',' transposed-params-clause)? ')' +/// '(' 'of' ':' qualified-decl-name (',' transposed-params-clause)? ')' /// \endverbatim ParserResult Parser::parseTransposeAttribute(SourceLoc atLoc, SourceLoc loc) { diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 31029fab1c704..c50917979ba3f 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3547,8 +3547,18 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, return derivative->getParent() == func->getParent(); }; - auto lookupOptions = - defaultMemberLookupOptions | NameLookupFlags::IgnoreAccessControl; + auto resolution = TypeResolution::forContextual(derivative->getDeclContext()); + Type baseType; + if (auto *baseTypeRepr = attr->getBaseTypeRepr()) { + TypeResolutionOptions options = None; + options |= TypeResolutionFlags::AllowModule; + baseType = resolution.resolveType(baseTypeRepr, options); + } + if (baseType && baseType->hasError()) + return; + auto lookupOptions = attr->getBaseTypeRepr() + ? defaultMemberLookupOptions + : defaultUnqualifiedLookupOptions; auto derivativeTypeCtx = derivative->getInnermostTypeContext(); if (!derivativeTypeCtx) derivativeTypeCtx = derivative->getParent(); @@ -3556,7 +3566,7 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, // Look up original function. auto *originalAFD = findAbstractFunctionDecl( - originalName.Name, originalName.Loc.getBaseNameLoc(), /*baseType*/ Type(), + originalName.Name, originalName.Loc.getBaseNameLoc(), baseType, derivativeTypeCtx, isValidOriginal, noneValidDiagnostic, ambiguousDiagnostic, notFunctionDiagnostic, lookupOptions, hasValidTypeContext, invalidTypeContextDiagnostic); @@ -3678,7 +3688,7 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, } // Reject different-file derivative registration. - // TODO(TF-1021): Lift this restriction. + // TODO(TF-1021): Lift same-file derivative registration restriction. if (originalAFD->getParentSourceFile() != derivative->getParentSourceFile()) { diags.diagnose(attr->getLocation(), diag::derivative_attr_not_in_same_file_as_original); diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 3158e3be8ef1c..aaa462cc2af5d 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1644,6 +1644,10 @@ Type TypeChecker::resolveIdentifierType( if (!result) return nullptr; if (auto moduleTy = result->getAs()) { + // Allow module types only if flag is specified. + if (options.contains(TypeResolutionFlags::AllowModule)) + return moduleTy; + // Otherwise, emit an error. if (!options.contains(TypeResolutionFlags::SilenceErrors)) { auto moduleName = moduleTy->getModule()->getName(); diags.diagnose(Components.back()->getNameLoc(), diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index e9c5c3162ba4e..bcd564c9b20ba 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -66,6 +66,9 @@ enum class TypeResolutionFlags : uint16_t { /// Whether we should not produce diagnostics if the type is invalid. SilenceErrors = 1 << 10, + + /// Whether to allow module declaration types. + AllowModule = 1 << 11 }; /// Type resolution contexts that require special handling. diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index f260a588bec93..d742bf418a61b 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4202,11 +4202,12 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { parametersBitVector[i] = parameters[i]; auto *indices = IndexSubset::get(ctx, parametersBitVector); - auto *derivAttr = DerivativeAttr::create( - ctx, isImplicit, SourceLoc(), SourceRange(), origName, indices); - derivAttr->setOriginalFunction(origDecl); - derivAttr->setDerivativeKind(*derivativeKind); - Attr = derivAttr; + auto *derivativeAttr = + DerivativeAttr::create(ctx, isImplicit, SourceLoc(), SourceRange(), + /*baseType*/ nullptr, origName, indices); + derivativeAttr->setOriginalFunction(origDecl); + derivativeAttr->setDerivativeKind(*derivativeKind); + Attr = derivativeAttr; break; } diff --git a/test/AutoDiff/Sema/derivative_attr_type_checking.swift b/test/AutoDiff/Sema/derivative_attr_type_checking.swift index 8aab201c49e0d..13e6260225d41 100644 --- a/test/AutoDiff/Sema/derivative_attr_type_checking.swift +++ b/test/AutoDiff/Sema/derivative_attr_type_checking.swift @@ -195,7 +195,8 @@ extension StaticMethod { return (x, { $0 }) } - @derivative(of: foo) + // Test qualified declaration name. + @derivative(of: StaticMethod.foo) static func vjpFoo(x: Float) -> (value: Float, pullback: (Float) -> Float) { return (x, { $0 }) } @@ -232,6 +233,14 @@ extension InstanceMethod { return (x, { $0 + $1 }) } + // Test qualified declaration name. + @derivative(of: InstanceMethod.foo, wrt: x) + func jvpFooWrtX(x: Self) -> ( + value: Self, differential: (TangentVector) -> (TangentVector) + ) { + return (x, { $0 }) + } + @derivative(of: generic) func vjpGeneric(_ x: T) -> ( value: Self, pullback: (TangentVector) -> (TangentVector, T.TangentVector) diff --git a/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds index ea9e5722954a2..c49bacacb7633 100644 --- a/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds +++ b/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds @@ -50,7 +50,7 @@ func bazDerivative(_ x: < return (x, { v in v }) } -@derivative(of: A.B.C.foo(label:_:), wrt: (x)) +@derivative(of: A<T>.B<U, V>.C.foo(label:_:), wrt: x) func qualifiedDerivative(_ x: Float, y: Float) -> (value: Float, pullback: (Float) -> Float) { return (x, { v in v }) @@ -71,7 +71,7 @@ func subtractTranspose(_ return (v, -v) } -@transpose(of: A.B.C.foo(label:_:), wrt: (0)) +@derivative(of: A<T>.B<U, V>.C.foo(label:_:), wrt: 0) func qualifiedTranspose(_ v: Float) -> (Float, Float) { return (v, -v) } diff --git a/test/AutoDiff/Syntax/round_trip_parse_gen.swift b/test/AutoDiff/Syntax/round_trip_parse_gen.swift index 2603a994a2e34..bfde3d5e04cfd 100644 --- a/test/AutoDiff/Syntax/round_trip_parse_gen.swift +++ b/test/AutoDiff/Syntax/round_trip_parse_gen.swift @@ -50,7 +50,7 @@ func bazDerivative(_ x: Float, y: Float) return (x, { v in v }) } -@derivative(of: A.B.C.foo(label:_:), wrt: (x)) +@derivative(of: A.B.C.foo(label:_:), wrt: x) func qualifiedDerivative(_ x: Float, y: Float) -> (value: Float, pullback: (Float) -> Float) { return (x, { v in v }) @@ -71,7 +71,7 @@ func subtractTranspose(_ v: Float) -> (Float, Float) { return (v, -v) } -@transpose(of: A.B.C.foo(label:_:), wrt: (0)) +@derivative(of: A.B.C.foo(label:_:), wrt: 0) func qualifiedTranspose(_ v: Float) -> (Float, Float) { return (v, -v) } From 2692abbe2ee08fc9569b9f7eb23442c322c1c4d9 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 19 Dec 2019 22:26:21 -0800 Subject: [PATCH 164/478] [AutoDiff] Fix build error. Fix build error introduced in https://github.com/apple/swift/pull/28892. Caused by not re-running CI after https://github.com/apple/swift/pull/28879 was merged. --- lib/Sema/TypeCheckAttr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index c50917979ba3f..3dae0bf29bf89 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3555,7 +3555,7 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, baseType = resolution.resolveType(baseTypeRepr, options); } if (baseType && baseType->hasError()) - return; + return true; auto lookupOptions = attr->getBaseTypeRepr() ? defaultMemberLookupOptions : defaultUnqualifiedLookupOptions; From b7cb3b67bf7827235854256dc489a5ac13bc327c Mon Sep 17 00:00:00 2001 From: "Kita, Maksim" Date: Sun, 8 Dec 2019 22:51:48 +0300 Subject: [PATCH 165/478] SR-11889: Using Located instead of std::pair --- include/swift/AST/ASTContext.h | 7 +-- include/swift/AST/Decl.h | 9 ++-- include/swift/AST/Module.h | 4 +- include/swift/AST/ModuleLoader.h | 5 ++- include/swift/Basic/Located.h | 39 ++++++++++++++++ include/swift/ClangImporter/ClangImporter.h | 6 +-- include/swift/Parse/CodeCompletionCallbacks.h | 2 +- include/swift/Sema/SourceLoader.h | 4 +- .../Serialization/SerializedModuleLoader.h | 10 ++--- lib/AST/ASTContext.cpp | 18 ++++---- lib/AST/ASTDumper.cpp | 2 +- lib/AST/ASTPrinter.cpp | 4 +- lib/AST/ImportCache.cpp | 2 +- lib/AST/Module.cpp | 14 +++--- lib/ClangImporter/ClangImporter.cpp | 28 ++++++------ lib/ClangImporter/DWARFImporter.cpp | 4 +- lib/ClangImporter/ImporterImpl.h | 4 +- lib/Frontend/Frontend.cpp | 4 +- lib/Frontend/ModuleInterfaceLoader.cpp | 6 +-- lib/Frontend/ModuleInterfaceSupport.cpp | 4 +- lib/FrontendTool/ImportedModules.cpp | 5 +-- lib/IDE/CodeCompletion.cpp | 15 +++---- lib/IDE/ModuleInterfacePrinting.cpp | 2 +- lib/IDE/SourceEntityWalker.cpp | 14 +++--- lib/IDE/SwiftSourceDocInfo.cpp | 2 +- lib/IRGen/GenDecl.cpp | 2 +- lib/Parse/ParseDecl.cpp | 12 ++--- lib/ParseSIL/ParseSIL.cpp | 18 ++++---- lib/SILGen/SILGenFunction.cpp | 2 +- lib/Sema/NameBinding.cpp | 44 +++++++++---------- lib/Sema/SourceLoader.cpp | 25 +++++------ lib/Serialization/ModuleFile.cpp | 16 +++---- lib/Serialization/Serialization.cpp | 2 +- lib/Serialization/SerializedModuleLoader.cpp | 26 +++++------ .../lib/SwiftLang/SwiftDocSupport.cpp | 4 +- .../lib/SwiftLang/SwiftEditorInterfaceGen.cpp | 4 +- .../lldb-moduleimport-test.cpp | 4 +- tools/swift-ide-test/swift-ide-test.cpp | 7 ++- 38 files changed, 209 insertions(+), 171 deletions(-) create mode 100644 include/swift/Basic/Located.h diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 51e85ae49a550..fd717bb31df10 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -26,6 +26,7 @@ #include "swift/AST/TypeAlignments.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/Malloc.h" +#include "swift/Basic/Located.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" @@ -718,12 +719,12 @@ class ASTContext final { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - bool canImportModule(std::pair ModulePath); + bool canImportModule(Located ModulePath); /// \returns a module with a given name that was already loaded. If the /// module was not loaded, returns nullptr. ModuleDecl *getLoadedModule( - ArrayRef> ModulePath) const; + ArrayRef> ModulePath) const; ModuleDecl *getLoadedModule(Identifier ModuleName) const; @@ -733,7 +734,7 @@ class ASTContext final { /// be returned. /// /// \returns The requested module, or NULL if the module cannot be found. - ModuleDecl *getModule(ArrayRef> ModulePath); + ModuleDecl *getModule(ArrayRef> ModulePath); ModuleDecl *getModuleByName(StringRef ModuleName); diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index f129b5086a36d..4d714102a351f 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -40,6 +40,7 @@ #include "swift/Basic/NullablePtr.h" #include "swift/Basic/OptionalEnum.h" #include "swift/Basic/Range.h" +#include "swift/Basic/Located.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/TrailingObjects.h" @@ -1503,11 +1504,11 @@ enum class ImportKind : uint8_t { /// import Swift /// import typealias Swift.Int class ImportDecl final : public Decl, - private llvm::TrailingObjects> { + private llvm::TrailingObjects> { friend TrailingObjects; friend class Decl; public: - typedef std::pair AccessPathElement; + typedef Located AccessPathElement; private: SourceLoc ImportLoc; @@ -1577,9 +1578,9 @@ class ImportDecl final : public Decl, } SourceLoc getStartLoc() const { return ImportLoc; } - SourceLoc getLocFromSource() const { return getFullAccessPath().front().second; } + SourceLoc getLocFromSource() const { return getFullAccessPath().front().loc; } SourceRange getSourceRange() const { - return SourceRange(ImportLoc, getFullAccessPath().back().second); + return SourceRange(ImportLoc, getFullAccessPath().back().loc); } SourceLoc getKindLoc() const { return KindLoc; } diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 8f4a28ab2da87..d364053d313eb 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -132,14 +132,14 @@ enum class ResilienceStrategy : unsigned { /// \sa FileUnit class ModuleDecl : public DeclContext, public TypeDecl { public: - typedef ArrayRef> AccessPathTy; + typedef ArrayRef> AccessPathTy; typedef std::pair ImportedModule; static bool matchesAccessPath(AccessPathTy AccessPath, DeclName Name) { assert(AccessPath.size() <= 1 && "can only refer to top-level decls"); return AccessPath.empty() - || DeclName(AccessPath.front().first).matchesRef(Name); + || DeclName(AccessPath.front().item).matchesRef(Name); } /// Arbitrarily orders ImportedModule records, for inclusion in sets and such. diff --git a/include/swift/AST/ModuleLoader.h b/include/swift/AST/ModuleLoader.h index af1d372659b69..92c05dc1f0630 100644 --- a/include/swift/AST/ModuleLoader.h +++ b/include/swift/AST/ModuleLoader.h @@ -23,6 +23,7 @@ #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/TinyPtrVector.h" +#include "swift/Basic/Located.h" namespace llvm { class FileCollector; @@ -100,7 +101,7 @@ class ModuleLoader { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - virtual bool canImportModule(std::pair named) = 0; + virtual bool canImportModule(Located named) = 0; /// Import a module with the given module path. /// @@ -113,7 +114,7 @@ class ModuleLoader { /// emits a diagnostic and returns NULL. virtual ModuleDecl *loadModule(SourceLoc importLoc, - ArrayRef> path) = 0; + ArrayRef> path) = 0; /// Load extensions to the given nominal type. /// diff --git a/include/swift/Basic/Located.h b/include/swift/Basic/Located.h new file mode 100644 index 0000000000000..34427aaf99ed2 --- /dev/null +++ b/include/swift/Basic/Located.h @@ -0,0 +1,39 @@ +//===--- Located.h - Source Location and Associated Value ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 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 forward declares and imports various common LLVM datatypes that +// swift wants to use unqualified. +// +//===----------------------------------------------------------------------===// + + +#ifndef SWIFT_BASIC_LOCATED_H +#define SWIFT_BASIC_LOCATED_H +#include "swift/Basic/SourceLoc.h" + +namespace swift { + +template +struct Located { + + T item; + + SourceLoc loc; + + template + friend bool operator ==(const Located lhs, const Located rhs) { + return lhs.item == rhs.item && lhs.loc == rhs.loc; + } +}; +} + +#endif // SWIFT_BASIC_LOCATED_H diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 9150852c0088a..9cace98692d33 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -173,7 +173,7 @@ class ClangImporter final : public ClangModuleLoader { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - virtual bool canImportModule(std::pair named) override; + virtual bool canImportModule(Located named) override; /// Import a module with the given module path. /// @@ -189,7 +189,7 @@ class ClangImporter final : public ClangModuleLoader { /// emits a diagnostic and returns NULL. virtual ModuleDecl *loadModule( SourceLoc importLoc, - ArrayRef> path) + ArrayRef> path) override; /// Determine whether \c overlayDC is within an overlay module for the @@ -399,7 +399,7 @@ class ClangImporter final : public ClangModuleLoader { /// Given the path of a Clang module, collect the names of all its submodules. /// Calling this function does not load the module. void collectSubModuleNames( - ArrayRef> path, + ArrayRef> path, std::vector &names) const; /// Given a Clang module, decide whether this module is imported already. diff --git a/include/swift/Parse/CodeCompletionCallbacks.h b/include/swift/Parse/CodeCompletionCallbacks.h index cf18c459c642e..e0b0de34a65c6 100644 --- a/include/swift/Parse/CodeCompletionCallbacks.h +++ b/include/swift/Parse/CodeCompletionCallbacks.h @@ -194,7 +194,7 @@ class CodeCompletionCallbacks { /// Complete the import decl with importable modules. virtual void - completeImportDecl(std::vector> &Path) {}; + completeImportDecl(std::vector> &Path) {}; /// Complete unresolved members after dot. virtual void completeUnresolvedMember(CodeCompletionExpr *E, diff --git a/include/swift/Sema/SourceLoader.h b/include/swift/Sema/SourceLoader.h index 004e7eb724ebd..cb8f4d19e9bf9 100644 --- a/include/swift/Sema/SourceLoader.h +++ b/include/swift/Sema/SourceLoader.h @@ -56,7 +56,7 @@ class SourceLoader : public ModuleLoader { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - virtual bool canImportModule(std::pair named) override; + virtual bool canImportModule(Located named) override; /// Import a module with the given module path. /// @@ -69,7 +69,7 @@ class SourceLoader : public ModuleLoader { /// returns NULL. virtual ModuleDecl * loadModule(SourceLoc importLoc, - ArrayRef> path) override; + ArrayRef> path) override; /// Load extensions to the given nominal type. /// diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index 094941bcb50ac..7247ee8b34bc9 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -50,7 +50,7 @@ class SerializedModuleLoaderBase : public ModuleLoader { void collectVisibleTopLevelModuleNamesImpl(SmallVectorImpl &names, StringRef extension) const; - using AccessPathElem = std::pair; + using AccessPathElem = Located; bool findModule(AccessPathElem moduleID, SmallVectorImpl *moduleInterfacePath, std::unique_ptr *moduleBuffer, @@ -140,7 +140,7 @@ class SerializedModuleLoaderBase : public ModuleLoader { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - virtual bool canImportModule(std::pair named) override; + virtual bool canImportModule(Located named) override; /// Import a module with the given module path. /// @@ -153,7 +153,7 @@ class SerializedModuleLoaderBase : public ModuleLoader { /// emits a diagnostic and returns a FailedImportModule object. virtual ModuleDecl * loadModule(SourceLoc importLoc, - ArrayRef> path) override; + ArrayRef> path) override; virtual void loadExtensions(NominalTypeDecl *nominal, @@ -240,10 +240,10 @@ class MemoryBufferSerializedModuleLoader : public SerializedModuleLoaderBase { public: virtual ~MemoryBufferSerializedModuleLoader(); - bool canImportModule(std::pair named) override; + bool canImportModule(Located named) override; ModuleDecl * loadModule(SourceLoc importLoc, - ArrayRef> path) override; + ArrayRef> path) override; /// Register a memory buffer that contains the serialized module for the given /// access path. This API is intended to be used by LLDB to add swiftmodules diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 6c3d74ffb322d..96c5257100982 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1468,12 +1468,12 @@ ClangModuleLoader *ASTContext::getDWARFModuleLoader() const { } ModuleDecl *ASTContext::getLoadedModule( - ArrayRef> ModulePath) const { + ArrayRef> ModulePath) const { assert(!ModulePath.empty()); // TODO: Swift submodules. if (ModulePath.size() == 1) { - return getLoadedModule(ModulePath[0].first); + return getLoadedModule(ModulePath[0].item); } return nullptr; } @@ -1723,13 +1723,13 @@ bool ASTContext::shouldPerformTypoCorrection() { return NumTypoCorrections <= LangOpts.TypoCorrectionLimit; } -bool ASTContext::canImportModule(std::pair ModulePath) { +bool ASTContext::canImportModule(Located ModulePath) { // If this module has already been successfully imported, it is importable. if (getLoadedModule(ModulePath) != nullptr) return true; // If we've failed loading this module before, don't look for it again. - if (FailedModuleImportNames.count(ModulePath.first)) + if (FailedModuleImportNames.count(ModulePath.item)) return false; // Otherwise, ask the module loaders. @@ -1739,12 +1739,12 @@ bool ASTContext::canImportModule(std::pair ModulePath) { } } - FailedModuleImportNames.insert(ModulePath.first); + FailedModuleImportNames.insert(ModulePath.item); return false; } ModuleDecl * -ASTContext::getModule(ArrayRef> ModulePath) { +ASTContext::getModule(ArrayRef> ModulePath) { assert(!ModulePath.empty()); if (auto *M = getLoadedModule(ModulePath)) @@ -1752,7 +1752,7 @@ ASTContext::getModule(ArrayRef> ModulePath) { auto moduleID = ModulePath[0]; for (auto &importer : getImpl().ModuleLoaders) { - if (ModuleDecl *M = importer->loadModule(moduleID.second, ModulePath)) { + if (ModuleDecl *M = importer->loadModule(moduleID.loc, ModulePath)) { return M; } } @@ -1761,7 +1761,7 @@ ASTContext::getModule(ArrayRef> ModulePath) { } ModuleDecl *ASTContext::getModuleByName(StringRef ModuleName) { - SmallVector, 4> + SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; @@ -1778,7 +1778,7 @@ ModuleDecl *ASTContext::getStdlibModule(bool loadIfAbsent) { if (loadIfAbsent) { auto mutableThis = const_cast(this); TheStdlibModule = - mutableThis->getModule({ std::make_pair(StdlibModuleName, SourceLoc()) }); + mutableThis->getModule({{ StdlibModuleName, SourceLoc() }}); } else { TheStdlibModule = getLoadedModule(StdlibModuleName); } diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 08e03ecfed560..2c8658354c632 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -589,7 +589,7 @@ namespace { OS << " '"; interleave(ID->getFullAccessPath(), [&](const ImportDecl::AccessPathElement &Elem) { - OS << Elem.first; + OS << Elem.item; }, [&] { OS << '.'; }); OS << "')"; diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index e4e038947fc01..e58dc963f79e3 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2097,10 +2097,10 @@ void PrintAST::visitImportDecl(ImportDecl *decl) { interleave(decl->getFullAccessPath(), [&](const ImportDecl::AccessPathElement &Elem) { if (!Mods.empty()) { - Printer.printModuleRef(Mods.front(), Elem.first); + Printer.printModuleRef(Mods.front(), Elem.item); Mods = Mods.slice(1); } else { - Printer << Elem.first.str(); + Printer << Elem.item.str(); } }, [&] { Printer << "."; }); diff --git a/lib/AST/ImportCache.cpp b/lib/AST/ImportCache.cpp index 2d8ff08890c96..e99e8c8f419ea 100644 --- a/lib/AST/ImportCache.cpp +++ b/lib/AST/ImportCache.cpp @@ -57,7 +57,7 @@ void ImportSet::Profile( for (auto import : topLevelImports) { ID.AddInteger(import.first.size()); for (auto accessPathElt : import.first) { - ID.AddPointer(accessPathElt.first.getAsOpaquePointer()); + ID.AddPointer(accessPathElt.item.getAsOpaquePointer()); } ID.AddPointer(import.second); } diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 486a1e26ba6dd..90fe1cc7aa913 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -303,7 +303,7 @@ void SourceLookupCache::lookupVisibleDecls(AccessPathTy AccessPath, assert(AccessPath.size() <= 1 && "can only refer to top-level decls"); if (!AccessPath.empty()) { - auto I = TopLevelValues.find(AccessPath.front().first); + auto I = TopLevelValues.find(AccessPath.front().item); if (I == TopLevelValues.end()) return; for (auto vd : I->second) @@ -335,7 +335,7 @@ void SourceLookupCache::lookupClassMembers(AccessPathTy accessPath, for (ValueDecl *vd : member.second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); - if (nominal && nominal->getName() == accessPath.front().first) + if (nominal && nominal->getName() == accessPath.front().item) consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); } @@ -367,7 +367,7 @@ void SourceLookupCache::lookupClassMember(AccessPathTy accessPath, if (!accessPath.empty()) { for (ValueDecl *vd : iter->second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); - if (nominal && nominal->getName() == accessPath.front().first) + if (nominal && nominal->getName() == accessPath.front().item) results.push_back(vd); } return; @@ -1181,13 +1181,13 @@ void ModuleDecl::getImportedModulesForLookup( } bool ModuleDecl::isSameAccessPath(AccessPathTy lhs, AccessPathTy rhs) { - using AccessPathElem = std::pair; + using AccessPathElem = Located; if (lhs.size() != rhs.size()) return false; return std::equal(lhs.begin(), lhs.end(), rhs.begin(), [](const AccessPathElem &lElem, const AccessPathElem &rElem) { - return lElem.first == rElem.first; + return lElem.item == rElem.item; }); } @@ -1251,12 +1251,12 @@ ModuleDecl::removeDuplicateImports(SmallVectorImpl &imports) { lhs.second->getReverseFullModuleName(), {}, rhs.second->getReverseFullModuleName(), {}); } - using AccessPathElem = std::pair; + using AccessPathElem = Located; return std::lexicographical_compare(lhs.first.begin(), lhs.first.end(), rhs.first.begin(), rhs.first.end(), [](const AccessPathElem &lElem, const AccessPathElem &rElem) { - return lElem.first.str() < rElem.first.str(); + return lElem.item.str() < rElem.item.str(); }); }); auto last = std::unique(imports.begin(), imports.end(), diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index e74b691817ed7..d4a4dbd05e400 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -1619,19 +1619,19 @@ void ClangImporter::collectVisibleTopLevelModuleNames( } void ClangImporter::collectSubModuleNames( - ArrayRef> path, + ArrayRef> path, std::vector &names) const { auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo(); // Look up the top-level module first. clang::Module *clangModule = clangHeaderSearch.lookupModule( - path.front().first.str(), /*AllowSearch=*/true, + path.front().item.str(), /*AllowSearch=*/true, /*AllowExtraModuleMapSearch=*/true); if (!clangModule) return; clang::Module *submodule = clangModule; for (auto component : path.slice(1)) { - submodule = submodule->findSubmodule(component.first.str()); + submodule = submodule->findSubmodule(component.item.str()); if (!submodule) return; } @@ -1643,12 +1643,12 @@ bool ClangImporter::isModuleImported(const clang::Module *M) { return M->NameVisibility == clang::Module::NameVisibilityKind::AllVisible; } -bool ClangImporter::canImportModule(std::pair moduleID) { +bool ClangImporter::canImportModule(Located moduleID) { // Look up the top-level module to see if it exists. // FIXME: This only works with top-level modules. auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo(); clang::Module *clangModule = - clangHeaderSearch.lookupModule(moduleID.first.str(), /*AllowSearch=*/true, + clangHeaderSearch.lookupModule(moduleID.item.str(), /*AllowSearch=*/true, /*AllowExtraModuleMapSearch=*/true); if (!clangModule) { return false; @@ -1662,13 +1662,13 @@ bool ClangImporter::canImportModule(std::pair moduleID) { } ModuleDecl *ClangImporter::Implementation::loadModuleClang( - SourceLoc importLoc, ArrayRef> path) { + SourceLoc importLoc, ArrayRef> path) { auto &clangContext = getClangASTContext(); auto &clangHeaderSearch = getClangPreprocessor().getHeaderSearchInfo(); // Look up the top-level module first, to see if it exists at all. clang::Module *clangModule = clangHeaderSearch.lookupModule( - path.front().first.str(), /*AllowSearch=*/true, + path.front().item.str(), /*AllowSearch=*/true, /*AllowExtraModuleMapSearch=*/true); if (!clangModule) return nullptr; @@ -1677,8 +1677,8 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( SmallVector, 4> clangPath; for (auto component : path) { - clangPath.push_back({&clangContext.Idents.get(component.first.str()), - exportSourceLoc(component.second)}); + clangPath.push_back({&clangContext.Idents.get(component.item.str()), + exportSourceLoc(component.loc)}); } auto &rawDiagClient = Instance->getDiagnosticClient(); @@ -1728,13 +1728,13 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( // Verify that the submodule exists. clang::Module *submodule = clangModule; for (auto &component : path.slice(1)) { - submodule = submodule->findSubmodule(component.first.str()); + submodule = submodule->findSubmodule(component.item.str()); // Special case: a submodule named "Foo.Private" can be moved to a top-level // module named "Foo_Private". Clang has special support for this. // We're limiting this to just submodules named "Private" because this will // put the Clang AST in a fatal error state if it /doesn't/ exist. - if (!submodule && component.first.str() == "Private" && + if (!submodule && component.item.str() == "Private" && (&component) == (&path[1])) { submodule = loadModule(llvm::makeArrayRef(clangPath).slice(0, 2), false); } @@ -1755,12 +1755,12 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( ModuleDecl * ClangImporter::loadModule(SourceLoc importLoc, - ArrayRef> path) { + ArrayRef> path) { return Impl.loadModule(importLoc, path); } ModuleDecl *ClangImporter::Implementation::loadModule( - SourceLoc importLoc, ArrayRef> path) { + SourceLoc importLoc, ArrayRef> path) { ModuleDecl *MD = nullptr; if (!DisableSourceImport) MD = loadModuleClang(importLoc, path); @@ -2786,7 +2786,7 @@ ImportDecl *swift::createImportDecl(ASTContext &Ctx, ArrayRef Exported) { auto *ImportedMod = ClangN.getClangModule(); assert(ImportedMod); - SmallVector, 4> AccessPath; + SmallVector, 4> AccessPath; auto *TmpMod = ImportedMod; while (TmpMod) { AccessPath.push_back({ Ctx.getIdentifier(TmpMod->Name), SourceLoc() }); diff --git a/lib/ClangImporter/DWARFImporter.cpp b/lib/ClangImporter/DWARFImporter.cpp index c1168b8d2569d..fab4bcbcf3c8b 100644 --- a/lib/ClangImporter/DWARFImporter.cpp +++ b/lib/ClangImporter/DWARFImporter.cpp @@ -99,13 +99,13 @@ static_assert(IsTriviallyDestructible::value, "DWARFModuleUnits are BumpPtrAllocated; the d'tor is not called"); ModuleDecl *ClangImporter::Implementation::loadModuleDWARF( - SourceLoc importLoc, ArrayRef> path) { + SourceLoc importLoc, ArrayRef> path) { // There's no importing from debug info if no importer is installed. if (!DWARFImporter) return nullptr; // FIXME: Implement submodule support! - Identifier name = path[0].first; + Identifier name = path[0].item; auto it = DWARFModuleUnits.find(name); if (it != DWARFModuleUnits.end()) return it->second->getParentModule(); diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index f1e24877cd594..f21345fc10b15 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -621,12 +621,12 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// Load a module using the clang::CompilerInstance. ModuleDecl *loadModuleClang(SourceLoc importLoc, - ArrayRef> path); + ArrayRef> path); /// "Load" a module from debug info. Because debug info types are read on /// demand, this doesn't really do any work. ModuleDecl *loadModuleDWARF(SourceLoc importLoc, - ArrayRef> path); + ArrayRef> path); public: /// Load a module using either method. diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index f43739bf2bde8..fe18c24bc92ff 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -778,7 +778,7 @@ ModuleDecl *CompilerInstance::importUnderlyingModule() { ModuleDecl *objCModuleUnderlyingMixedFramework = static_cast(Context->getClangModuleLoader()) ->loadModule(SourceLoc(), - std::make_pair(MainModule->getName(), SourceLoc())); + {{MainModule->getName(), SourceLoc()}}); if (objCModuleUnderlyingMixedFramework) return objCModuleUnderlyingMixedFramework; Diagnostics.diagnose(SourceLoc(), diag::error_underlying_module_not_found, @@ -808,7 +808,7 @@ void CompilerInstance::getImplicitlyImportedModules( if (Lexer::isIdentifier(ImplicitImportModuleName)) { auto moduleID = Context->getIdentifier(ImplicitImportModuleName); ModuleDecl *importModule = - Context->getModule(std::make_pair(moduleID, SourceLoc())); + Context->getModule({{moduleID, SourceLoc()}}); if (importModule) { importModules.push_back(importModule); } else { diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 4b238525dd14e..42ed6c18fae43 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -340,7 +340,7 @@ struct ModuleRebuildInfo { /// normal cache, the prebuilt cache, a module adjacent to the interface, or /// a module that we'll build from a module interface. class ModuleInterfaceLoaderImpl { - using AccessPathElem = std::pair; + using AccessPathElem = Located; friend class swift::ModuleInterfaceLoader; ASTContext &ctx; llvm::vfs::FileSystem &fs; @@ -1020,10 +1020,10 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( } // Create an instance of the Impl to do the heavy lifting. - auto ModuleName = ModuleID.first.str(); + auto ModuleName = ModuleID.item.str(); ModuleInterfaceLoaderImpl Impl( Ctx, ModPath, InPath, ModuleName, - CacheDir, PrebuiltCacheDir, ModuleID.second, + CacheDir, PrebuiltCacheDir, ModuleID.loc, RemarkOnRebuildFromInterface, dependencyTracker, llvm::is_contained(PreferInterfaceForModules, ModuleName) ? diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 7fa40c3a94b5f..2e30bc98dc0b0 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -49,7 +49,7 @@ static void diagnoseScopedImports(DiagnosticEngine &diags, for (const ModuleDecl::ImportedModule &importPair : imports) { if (importPair.first.empty()) continue; - diags.diagnose(importPair.first.front().second, + diags.diagnose(importPair.first.front().loc, diag::module_interface_scoped_import_unsupported); } } @@ -119,7 +119,7 @@ static void printImports(raw_ostream &out, ModuleDecl *M) { if (!import.first.empty()) { out << "/*"; for (const auto &accessPathElem : import.first) - out << "." << accessPathElem.first; + out << "." << accessPathElem.item; out << "*/"; } diff --git a/lib/FrontendTool/ImportedModules.cpp b/lib/FrontendTool/ImportedModules.cpp index 5b9837f424f99..59b6088da4d54 100644 --- a/lib/FrontendTool/ImportedModules.cpp +++ b/lib/FrontendTool/ImportedModules.cpp @@ -68,7 +68,7 @@ bool swift::emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, auto accessPath = ID->getModulePath(); // only the top-level name is needed (i.e. A in A.B.C) - Modules.insert(accessPath[0].first.str()); + Modules.insert(accessPath[0].item.str()); } // And now look in the C code we're possibly using. @@ -98,8 +98,7 @@ bool swift::emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, } if (opts.ImportUnderlyingModule) { - auto underlyingModule = clangImporter->loadModule( - SourceLoc(), std::make_pair(mainModule->getName(), SourceLoc())); + auto underlyingModule = clangImporter->loadModule(SourceLoc(), {{ mainModule->getName(), SourceLoc() }}); if (!underlyingModule) { Context.Diags.diagnose(SourceLoc(), diag::error_underlying_module_not_found, diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 0c0ec66fb7fb8..fe74c698d2c53 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1377,7 +1377,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { void completeAccessorBeginning(CodeCompletionExpr *E) override; void completePoundAvailablePlatform() override; - void completeImportDecl(std::vector> &Path) override; + void completeImportDecl(std::vector> &Path) override; void completeUnresolvedMember(CodeCompletionExpr *E, SourceLoc DotLoc) override; void completeCallArg(CodeCompletionExpr *E, bool isFirst) override; @@ -4080,10 +4080,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Kind = LookupKind::ImportFromModule; NeedLeadingDot = ResultsHaveLeadingDot; - llvm::SmallVector, 1> LookupAccessPath; + llvm::SmallVector, 1> LookupAccessPath; for (auto Piece : AccessPath) { - LookupAccessPath.push_back( - std::make_pair(Ctx.getIdentifier(Piece), SourceLoc())); + LookupAccessPath.push_back({Ctx.getIdentifier(Piece), SourceLoc()}); } AccessFilteringDeclConsumer FilteringConsumer(CurrDeclContext, *this); TheModule->lookupVisibleDecls(LookupAccessPath, FilteringConsumer, @@ -4725,10 +4724,10 @@ void CodeCompletionCallbacksImpl::completeCaseStmtBeginning(CodeCompletionExpr * } void CodeCompletionCallbacksImpl::completeImportDecl( - std::vector> &Path) { + std::vector> &Path) { Kind = CompletionKind::Import; CurDeclContext = P.CurDeclContext; - DotLoc = Path.empty() ? SourceLoc() : Path.back().second; + DotLoc = Path.empty() ? SourceLoc() : Path.back().loc; if (DotLoc.isInvalid()) return; auto Importer = static_cast(CurDeclContext->getASTContext(). @@ -4737,7 +4736,7 @@ void CodeCompletionCallbacksImpl::completeImportDecl( Importer->collectSubModuleNames(Path, SubNames); ASTContext &Ctx = CurDeclContext->getASTContext(); for (StringRef Sub : SubNames) { - Path.push_back(std::make_pair(Ctx.getIdentifier(Sub), SourceLoc())); + Path.push_back({ Ctx.getIdentifier(Sub), SourceLoc() }); SubModuleNameVisibilityPairs.push_back( std::make_pair(Sub.str(), Ctx.getLoadedModule(Path))); Path.pop_back(); @@ -5574,7 +5573,7 @@ void CodeCompletionCallbacksImpl::doneParsing() { std::vector AccessPath; for (auto Piece : Path) { - AccessPath.push_back(Piece.first.str()); + AccessPath.push_back(Piece.item.str()); } StringRef ModuleFilename = TheModule->getModuleFilename(); diff --git a/lib/IDE/ModuleInterfacePrinting.cpp b/lib/IDE/ModuleInterfacePrinting.cpp index e87deadb53b05..e2cdc3f37eabf 100644 --- a/lib/IDE/ModuleInterfacePrinting.cpp +++ b/lib/IDE/ModuleInterfacePrinting.cpp @@ -448,7 +448,7 @@ void swift::ide::printSubmoduleInterface( auto RHSPath = RHS->getFullAccessPath(); for (unsigned i = 0, e = std::min(LHSPath.size(), RHSPath.size()); i != e; i++) { - if (int Ret = LHSPath[i].first.str().compare(RHSPath[i].first.str())) + if (int Ret = LHSPath[i].item.str().compare(RHSPath[i].item.str())) return Ret < 0; } return false; diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index 156a64129b648..f7d679ae4fd72 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -74,7 +74,7 @@ class SemaAnnotator : public ASTWalker { bool passReference(ValueDecl *D, Type Ty, SourceLoc Loc, SourceRange Range, ReferenceMetaData Data); bool passReference(ValueDecl *D, Type Ty, DeclNameLoc Loc, ReferenceMetaData Data); - bool passReference(ModuleEntity Mod, std::pair IdLoc); + bool passReference(ModuleEntity Mod, Located IdLoc); bool passSubscriptReference(ValueDecl *D, SourceLoc Loc, ReferenceMetaData Data, bool IsOpenBracket); @@ -270,7 +270,7 @@ std::pair SemaAnnotator::walkToExprPre(Expr *E) { if (auto *DRE = dyn_cast(E)) { if (auto *module = dyn_cast(DRE->getDecl())) { if (!passReference(ModuleEntity(module), - std::make_pair(module->getName(), E->getLoc()))) + {module->getName(), E->getLoc()})) return { false, nullptr }; } else if (!passReference(DRE->getDecl(), DRE->getType(), DRE->getNameLoc(), @@ -491,7 +491,7 @@ bool SemaAnnotator::walkToTypeReprPre(TypeRepr *T) { if (ValueDecl *VD = IdT->getBoundDecl()) { if (auto *ModD = dyn_cast(VD)) { auto ident = IdT->getNameRef().getBaseIdentifier(); - return passReference(ModD, std::make_pair(ident, IdT->getLoc())); + return passReference(ModD, { ident, IdT->getLoc() }); } return passReference(VD, Type(), IdT->getNameLoc(), @@ -669,11 +669,11 @@ passReference(ValueDecl *D, Type Ty, SourceLoc BaseNameLoc, SourceRange Range, } bool SemaAnnotator::passReference(ModuleEntity Mod, - std::pair IdLoc) { - if (IdLoc.second.isInvalid()) + Located IdLoc) { + if (IdLoc.loc.isInvalid()) return true; - unsigned NameLen = IdLoc.first.getLength(); - CharSourceRange Range{ IdLoc.second, NameLen }; + unsigned NameLen = IdLoc.item.getLength(); + CharSourceRange Range{ IdLoc.loc, NameLen }; bool Continue = SEWalker.visitModuleReference(Mod, Range); if (!Continue) Cancelled = true; diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index bb645722236da..4861fbe40d92e 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -246,7 +246,7 @@ bool NameMatcher::walkToDeclPre(Decl *D) { } } else if (ImportDecl *ID = dyn_cast(D)) { for(const ImportDecl::AccessPathElement &Element: ID->getFullAccessPath()) { - tryResolve(ASTWalker::ParentTy(D), Element.second); + tryResolve(ASTWalker::ParentTy(D), Element.loc); if (isDone()) break; } diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index cc5e490b601ca..08e6d79e04976 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1091,7 +1091,7 @@ void IRGenModule::finishEmitAfterTopLevel() { if (DebugInfo) { if (ModuleDecl *TheStdlib = Context.getStdlibModule()) { if (TheStdlib != getSwiftModule()) { - std::pair AccessPath[] = { + Located AccessPath[] = { { Context.StdlibModuleName, swift::SourceLoc() } }; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 849ccd8a4fa94..74b87ee2cd791 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -4066,7 +4066,7 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, KindLoc = consumeToken(); } - std::vector> ImportPath; + std::vector> ImportPath; bool HasNext; do { SyntaxParsingContext AccessCompCtx(SyntaxContext, @@ -4078,8 +4078,8 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, } return makeParserCodeCompletionStatus(); } - ImportPath.push_back(std::make_pair(Identifier(), Tok.getLoc())); - if (parseAnyIdentifier(ImportPath.back().first, + ImportPath.push_back({Identifier(), Tok.getLoc()}); + if (parseAnyIdentifier(ImportPath.back().item, diag::expected_identifier_in_decl, "import")) return nullptr; HasNext = consumeIf(tok::period); @@ -4092,8 +4092,8 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, // We omit the code completion token if it immediately follows the module // identifiers. auto BufferId = SourceMgr.getCodeCompletionBufferID(); - auto IdEndOffset = SourceMgr.getLocOffsetInBuffer(ImportPath.back().second, - BufferId) + ImportPath.back().first.str().size(); + auto IdEndOffset = SourceMgr.getLocOffsetInBuffer(ImportPath.back().loc, + BufferId) + ImportPath.back().item.str().size(); auto CCTokenOffset = SourceMgr.getLocOffsetInBuffer(SourceMgr. getCodeCompletionLoc(), BufferId); if (IdEndOffset == CCTokenOffset) { @@ -4102,7 +4102,7 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, } if (Kind != ImportKind::Module && ImportPath.size() == 1) { - diagnose(ImportPath.front().second, diag::decl_expected_module_name); + diagnose(ImportPath.front().loc, diag::decl_expected_module_name); return nullptr; } diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index f20d804df0060..1bbd13839c659 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -60,7 +60,7 @@ class SILParserTUState : public SILParserTUStateBase { /// This is all of the forward referenced functions with /// the location for where the reference is. llvm::DenseMap> ForwardRefFns; + Located> ForwardRefFns; /// A list of all functions forward-declared by a sil_scope. llvm::DenseSet PotentialZombieFns; @@ -85,8 +85,8 @@ class SILParserTUState : public SILParserTUStateBase { SILParserTUState::~SILParserTUState() { if (!ForwardRefFns.empty()) { for (auto Entry : ForwardRefFns) { - if (Entry.second.second.isValid()) { - M.getASTContext().Diags.diagnose(Entry.second.second, + if (Entry.second.loc.isValid()) { + M.getASTContext().Diags.diagnose(Entry.second.loc, diag::sil_use_of_undefined_value, Entry.first.str()); } @@ -613,13 +613,13 @@ SILFunction *SILParser::getGlobalNameForDefinition(Identifier name, // complete the forward reference. auto iter = TUState.ForwardRefFns.find(name); if (iter != TUState.ForwardRefFns.end()) { - SILFunction *fn = iter->second.first; + SILFunction *fn = iter->second.item; // Verify that the types match up. if (fn->getLoweredFunctionType() != ty) { P.diagnose(sourceLoc, diag::sil_value_use_type_mismatch, name.str(), fn->getLoweredFunctionType(), ty); - P.diagnose(iter->second.second, diag::sil_prior_reference); + P.diagnose(iter->second.loc, diag::sil_prior_reference); fn = builder.createFunctionForForwardReference("" /*name*/, ty, silLoc); } @@ -2375,13 +2375,13 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; } - SmallVector, 4> resultNames; + SmallVector, 4> resultNames; SourceLoc resultClauseBegin; // If the instruction has a name '%foo =', parse it. if (P.Tok.is(tok::sil_local_name)) { resultClauseBegin = P.Tok.getLoc(); - resultNames.push_back(std::make_pair(P.Tok.getText(), P.Tok.getLoc())); + resultNames.push_back({P.Tok.getText(), P.Tok.getLoc()}); P.consumeToken(tok::sil_local_name); // If the instruction has a '(%foo, %bar) = ', parse it. @@ -2395,7 +2395,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; } - resultNames.push_back(std::make_pair(P.Tok.getText(), P.Tok.getLoc())); + resultNames.push_back({P.Tok.getText(), P.Tok.getLoc()}); P.consumeToken(tok::sil_local_name); if (P.consumeIf(tok::comma)) @@ -5084,7 +5084,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { results.size()); } else { for (size_t i : indices(results)) { - setLocalValue(results[i], resultNames[i].first, resultNames[i].second); + setLocalValue(results[i], resultNames[i].item, resultNames[i].loc); } } } diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 2548a1901de44..af4e167d37460 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -538,7 +538,7 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { // be imported. ASTContext &ctx = getASTContext(); - std::pair UIKitName = + Located UIKitName = {ctx.getIdentifier("UIKit"), SourceLoc()}; ModuleDecl *UIKit = ctx diff --git a/lib/Sema/NameBinding.cpp b/lib/Sema/NameBinding.cpp index 4b86626e941da..28e6ab7b71127 100644 --- a/lib/Sema/NameBinding.cpp +++ b/lib/Sema/NameBinding.cpp @@ -61,19 +61,19 @@ namespace { /// Load a module referenced by an import statement. /// /// Returns null if no module can be loaded. - ModuleDecl *getModule(ArrayRef> ModuleID); + ModuleDecl *getModule(ArrayRef> ModuleID); }; } // end anonymous namespace ModuleDecl * -NameBinder::getModule(ArrayRef> modulePath) { +NameBinder::getModule(ArrayRef> modulePath) { assert(!modulePath.empty()); auto moduleID = modulePath[0]; // The Builtin module cannot be explicitly imported unless we're a .sil file // or in the REPL. if ((SF.Kind == SourceFileKind::SIL || SF.Kind == SourceFileKind::REPL) && - moduleID.first == Context.TheBuiltinModule->getName()) + moduleID.item == Context.TheBuiltinModule->getName()) return Context.TheBuiltinModule; // If the imported module name is the same as the current module, @@ -82,10 +82,10 @@ NameBinder::getModule(ArrayRef> modulePath) { // // FIXME: We'd like to only use this in SIL mode, but unfortunately we use it // for our fake overlays as well. - if (moduleID.first == SF.getParentModule()->getName() && + if (moduleID.item == SF.getParentModule()->getName() && modulePath.size() == 1) { if (auto importer = Context.getClangModuleLoader()) - return importer->loadModule(moduleID.second, modulePath); + return importer->loadModule(moduleID.loc, modulePath); return nullptr; } @@ -170,17 +170,17 @@ static bool shouldImportSelfImportClang(const ImportDecl *ID, void NameBinder::addImport( SmallVectorImpl &imports, ImportDecl *ID) { - if (ID->getModulePath().front().first == SF.getParentModule()->getName() && + if (ID->getModulePath().front().item == SF.getParentModule()->getName() && ID->getModulePath().size() == 1 && !shouldImportSelfImportClang(ID, SF)) { // If the imported module name is the same as the current module, // produce a diagnostic. StringRef filename = llvm::sys::path::filename(SF.getFilename()); if (filename.empty()) Context.Diags.diagnose(ID, diag::sema_import_current_module, - ID->getModulePath().front().first); + ID->getModulePath().front().item); else Context.Diags.diagnose(ID, diag::sema_import_current_module_with_file, - filename, ID->getModulePath().front().first); + filename, ID->getModulePath().front().item); ID->setModule(SF.getParentModule()); return; } @@ -190,7 +190,7 @@ void NameBinder::addImport( SmallString<64> modulePathStr; interleave(ID->getModulePath(), [&](ImportDecl::AccessPathElement elem) { - modulePathStr += elem.first.str(); + modulePathStr += elem.item.str(); }, [&] { modulePathStr += "."; }); @@ -214,7 +214,7 @@ void NameBinder::addImport( topLevelModule = M; } else { // If we imported a submodule, import the top-level module as well. - Identifier topLevelName = ID->getModulePath().front().first; + Identifier topLevelName = ID->getModulePath().front().item; topLevelModule = Context.getLoadedModule(topLevelName); if (!topLevelModule) { // Clang can sometimes import top-level modules as if they were @@ -234,8 +234,8 @@ void NameBinder::addImport( !topLevelModule->isTestingEnabled() && !topLevelModule->isNonSwiftModule() && Context.LangOpts.EnableTestableAttrRequiresTestableModule) { - diagnose(ID->getModulePath().front().second, diag::module_not_testable, - ID->getModulePath().front().first); + diagnose(ID->getModulePath().front().loc, diag::module_not_testable, + ID->getModulePath().front().item); testableAttr->setInvalid(); } @@ -243,9 +243,9 @@ void NameBinder::addImport( StringRef privateImportFileName; if (privateImportAttr) { if (!topLevelModule || !topLevelModule->arePrivateImportsEnabled()) { - diagnose(ID->getModulePath().front().second, + diagnose(ID->getModulePath().front().loc, diag::module_not_compiled_for_private_import, - ID->getModulePath().front().first); + ID->getModulePath().front().item); privateImportAttr->setInvalid(); } else { privateImportFileName = privateImportAttr->getSourceFile(); @@ -256,7 +256,7 @@ void NameBinder::addImport( !topLevelModule->isResilient() && !topLevelModule->isNonSwiftModule() && !ID->getAttrs().hasAttribute()) { - diagnose(ID->getModulePath().front().second, + diagnose(ID->getModulePath().front().loc, diag::module_not_compiled_with_library_evolution, topLevelModule->getName(), SF.getParentModule()->getName()); } @@ -296,17 +296,17 @@ void NameBinder::addImport( // FIXME: Doesn't handle scoped testable imports correctly. assert(declPath.size() == 1 && "can't handle sub-decl imports"); SmallVector decls; - lookupInModule(topLevelModule, declPath.front().first, decls, + lookupInModule(topLevelModule, declPath.front().item, decls, NLKind::QualifiedLookup, ResolutionKind::Overloadable, &SF); if (decls.empty()) { diagnose(ID, diag::decl_does_not_exist_in_module, static_cast(ID->getImportKind()), - declPath.front().first, - ID->getModulePath().front().first) - .highlight(SourceRange(declPath.front().second, - declPath.back().second)); + declPath.front().item, + ID->getModulePath().front().item) + .highlight(SourceRange(declPath.front().loc, + declPath.back().loc)); return; } @@ -316,7 +316,7 @@ void NameBinder::addImport( if (!actualKind.hasValue()) { // FIXME: print entire module name? diagnose(ID, diag::ambiguous_decl_in_module, - declPath.front().first, M->getName()); + declPath.front().item, M->getName()); for (auto next : decls) diagnose(next, diag::found_candidate); @@ -338,7 +338,7 @@ void NameBinder::addImport( getImportKindString(ID->getImportKind()))); } else { emittedDiag.emplace(diagnose(ID, diag::imported_decl_is_wrong_kind, - declPath.front().first, + declPath.front().item, getImportKindString(ID->getImportKind()), static_cast(*actualKind))); } diff --git a/lib/Sema/SourceLoader.cpp b/lib/Sema/SourceLoader.cpp index 690631dc9f5cb..1c7553f062a7e 100644 --- a/lib/Sema/SourceLoader.cpp +++ b/lib/Sema/SourceLoader.cpp @@ -62,15 +62,15 @@ void SourceLoader::collectVisibleTopLevelModuleNames( // TODO: Implement? } -bool SourceLoader::canImportModule(std::pair ID) { +bool SourceLoader::canImportModule(Located ID) { // Search the memory buffers to see if we can find this file on disk. - FileOrError inputFileOrError = findModule(Ctx, ID.first.str(), - ID.second); + FileOrError inputFileOrError = findModule(Ctx, ID.item.str(), + ID.loc); if (!inputFileOrError) { auto err = inputFileOrError.getError(); if (err != std::errc::no_such_file_or_directory) { - Ctx.Diags.diagnose(ID.second, diag::sema_opening_import, - ID.first, err.message()); + Ctx.Diags.diagnose(ID.loc, diag::sema_opening_import, + ID.item, err.message()); } return false; @@ -79,21 +79,20 @@ bool SourceLoader::canImportModule(std::pair ID) { } ModuleDecl *SourceLoader::loadModule(SourceLoc importLoc, - ArrayRef> path) { + ArrayRef> path) { // FIXME: Swift submodules? if (path.size() > 1) return nullptr; auto moduleID = path[0]; - FileOrError inputFileOrError = findModule(Ctx, moduleID.first.str(), - moduleID.second); + FileOrError inputFileOrError = findModule(Ctx, moduleID.item.str(), + moduleID.loc); if (!inputFileOrError) { auto err = inputFileOrError.getError(); if (err != std::errc::no_such_file_or_directory) { - Ctx.Diags.diagnose(moduleID.second, diag::sema_opening_import, - moduleID.first, err.message()); + Ctx.Diags.diagnose(moduleID.loc, diag::sema_opening_import, + moduleID.item, err.message()); } return nullptr; @@ -116,10 +115,10 @@ ModuleDecl *SourceLoader::loadModule(SourceLoc importLoc, else bufferID = Ctx.SourceMgr.addNewSourceBuffer(std::move(inputFile)); - auto *importMod = ModuleDecl::create(moduleID.first, Ctx); + auto *importMod = ModuleDecl::create(moduleID.item, Ctx); if (EnableLibraryEvolution) importMod->setResilienceStrategy(ResilienceStrategy::Resilient); - Ctx.LoadedModules[moduleID.first] = importMod; + Ctx.LoadedModules[moduleID.item] = importMod; auto implicitImportKind = SourceFile::ImplicitModuleImportKind::Stdlib; if (!Ctx.getStdlibModule()) diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index aad0ccc278fbc..cd099e829f17a 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -1983,7 +1983,7 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, auto scopeID = ctx.getIdentifier(scopePath); assert(!scopeID.empty() && "invalid decl name (non-top-level decls not supported)"); - std::pair accessPathElem(scopeID, SourceLoc()); + Located accessPathElem = { scopeID, SourceLoc() }; dependency.Import = {ctx.AllocateCopy(llvm::makeArrayRef(accessPathElem)), module}; } @@ -2202,14 +2202,14 @@ void ModuleFile::getImportDecls(SmallVectorImpl &Results) { if (Dep.isScoped()) std::tie(ModulePathStr, ScopePath) = ModulePathStr.rsplit('\0'); - SmallVector, 1> AccessPath; + SmallVector, 1> AccessPath; while (!ModulePathStr.empty()) { StringRef NextComponent; std::tie(NextComponent, ModulePathStr) = ModulePathStr.split('\0'); AccessPath.push_back({Ctx.getIdentifier(NextComponent), SourceLoc()}); } - if (AccessPath.size() == 1 && AccessPath[0].first == Ctx.StdlibModuleName) + if (AccessPath.size() == 1 && AccessPath[0].item == Ctx.StdlibModuleName) continue; ModuleDecl *M = Ctx.getLoadedModule(AccessPath); @@ -2228,7 +2228,7 @@ void ModuleFile::getImportDecls(SmallVectorImpl &Results) { // Lookup the decl in the top-level module. ModuleDecl *TopLevelModule = M; if (AccessPath.size() > 1) - TopLevelModule = Ctx.getLoadedModule(AccessPath.front().first); + TopLevelModule = Ctx.getLoadedModule(AccessPath.front().item); SmallVector Decls; TopLevelModule->lookupQualified( @@ -2278,7 +2278,7 @@ void ModuleFile::lookupVisibleDecls(ModuleDecl::AccessPathTy accessPath, }; if (!accessPath.empty()) { - auto iter = TopLevelDecls->find(accessPath.front().first); + auto iter = TopLevelDecls->find(accessPath.front().item); if (iter == TopLevelDecls->end()) return; @@ -2454,7 +2454,7 @@ void ModuleFile::lookupClassMember(ModuleDecl::AccessPathTy accessPath, while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) - if (nominal->getName() == accessPath.front().first) + if (nominal->getName() == accessPath.front().item) results.push_back(vd); } } else { @@ -2467,7 +2467,7 @@ void ModuleFile::lookupClassMember(ModuleDecl::AccessPathTy accessPath, while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) - if (nominal->getName() == accessPath.front().first) + if (nominal->getName() == accessPath.front().item) results.push_back(vd); } } @@ -2496,7 +2496,7 @@ void ModuleFile::lookupClassMembers(ModuleDecl::AccessPathTy accessPath, while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) - if (nominal->getName() == accessPath.front().first) + if (nominal->getName() == accessPath.front().item) consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); } diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 3f9471b7eb565..eb1cc5bafb8ff 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -915,7 +915,7 @@ static void flattenImportPath(const ModuleDecl::ImportedModule &import, outStream << '\0'; assert(import.first.size() == 1 && "can only handle top-level decl imports"); auto accessPathElem = import.first.front(); - outStream << accessPathElem.first.str(); + outStream << accessPathElem.item.str(); } uint64_t getRawModTimeOrHash(const SerializationOptions::FileDependency &dep) { diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 373f17f2b83f1..deab420ba56a7 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -428,7 +428,7 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, std::unique_ptr *moduleDocBuffer, std::unique_ptr *moduleSourceInfoBuffer, bool &isFramework, bool &isSystemModule) { - llvm::SmallString<64> moduleName(moduleID.first.str()); + llvm::SmallString<64> moduleName(moduleID.item.str()); ModuleFilenamePair fileNames(moduleName); SmallVector targetFileNamePairs; @@ -465,7 +465,7 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, // We can only get here if all targetFileNamePairs failed with // 'std::errc::no_such_file_or_directory'. - if (maybeDiagnoseTargetMismatch(moduleID.second, moduleName, + if (maybeDiagnoseTargetMismatch(moduleID.loc, moduleName, primaryTargetSpecificName, currPath)) { return false; } else { @@ -517,7 +517,7 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, case SearchPathKind::Framework: { isFramework = true; llvm::sys::path::append(currPath, - moduleID.first.str() + ".framework"); + moduleID.item.str() + ".framework"); // Check if the framework directory exists. if (!fs.exists(currPath)) @@ -825,7 +825,7 @@ void swift::serialization::diagnoseSerializedASTLoadFailure( } bool SerializedModuleLoaderBase::canImportModule( - std::pair mID) { + Located mID) { // Look on disk. SmallVector *unusedModuleInterfacePath = nullptr; std::unique_ptr *unusedModuleBuffer = nullptr; @@ -839,9 +839,9 @@ bool SerializedModuleLoaderBase::canImportModule( } bool MemoryBufferSerializedModuleLoader::canImportModule( - std::pair mID) { + Located mID) { // See if we find it in the registered memory buffers. - return MemoryBuffers.count(mID.first.str()); + return MemoryBuffers.count(mID.item.str()); } ModuleDecl * @@ -876,15 +876,15 @@ SerializedModuleLoaderBase::loadModule(SourceLoc importLoc, assert(moduleInputBuffer); - auto M = ModuleDecl::create(moduleID.first, Ctx); + auto M = ModuleDecl::create(moduleID.item, Ctx); M->setIsSystemModule(isSystemModule); - Ctx.LoadedModules[moduleID.first] = M; + Ctx.LoadedModules[moduleID.item] = M; SWIFT_DEFER { M->setHasResolvedImports(); }; StringRef moduleInterfacePathStr = Ctx.AllocateCopy(moduleInterfacePath.str()); - if (!loadAST(*M, moduleID.second, moduleInterfacePathStr, + if (!loadAST(*M, moduleID.loc, moduleInterfacePathStr, std::move(moduleInputBuffer), std::move(moduleDocInputBuffer), std::move(moduleSourceInfoInputBuffer), isFramework, /*treatAsPartialModule*/false)) { @@ -908,7 +908,7 @@ MemoryBufferSerializedModuleLoader::loadModule(SourceLoc importLoc, // FIXME: Right now this works only with access paths of length 1. // Once submodules are designed, this needs to support suffix // matching and a search path. - auto bufIter = MemoryBuffers.find(moduleID.first.str()); + auto bufIter = MemoryBuffers.find(moduleID.item.str()); if (bufIter == MemoryBuffers.end()) return nullptr; @@ -919,16 +919,16 @@ MemoryBufferSerializedModuleLoader::loadModule(SourceLoc importLoc, MemoryBuffers.erase(bufIter); assert(moduleInputBuffer); - auto *M = ModuleDecl::create(moduleID.first, Ctx); + auto *M = ModuleDecl::create(moduleID.item, Ctx); SWIFT_DEFER { M->setHasResolvedImports(); }; - if (!loadAST(*M, moduleID.second, /*moduleInterfacePath*/ "", + if (!loadAST(*M, moduleID.loc, /*moduleInterfacePath*/ "", std::move(moduleInputBuffer), {}, {}, isFramework, treatAsPartialModule)) { return nullptr; } - Ctx.LoadedModules[moduleID.first] = M; + Ctx.LoadedModules[moduleID.item] = M; return M; } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp index a439a4ae14b92..922639b27fca5 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp @@ -41,7 +41,7 @@ using namespace swift; using namespace ide; static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { - SmallVector, 4> + SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; @@ -52,7 +52,7 @@ static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { } static ModuleDecl *getModuleByFullName(ASTContext &Ctx, Identifier ModuleName) { - return Ctx.getModule(std::make_pair(ModuleName, SourceLoc())); + return Ctx.getModule({{ModuleName, SourceLoc()}}); } namespace { diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp index a331e2051d61c..87e1a87d0ff09 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp @@ -92,7 +92,7 @@ typedef SwiftInterfaceGenContext::Implementation::TextDecl TextDecl; typedef SwiftInterfaceGenContext::Implementation::SourceTextInfo SourceTextInfo; static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { - SmallVector, 4> + SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; @@ -103,7 +103,7 @@ static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { } static ModuleDecl *getModuleByFullName(ASTContext &Ctx, Identifier ModuleName) { - return Ctx.getModule(std::make_pair(ModuleName, SourceLoc())); + return Ctx.getModule({{ModuleName, SourceLoc()}}); } namespace { diff --git a/tools/lldb-moduleimport-test/lldb-moduleimport-test.cpp b/tools/lldb-moduleimport-test/lldb-moduleimport-test.cpp index def4356f2713d..ce0b3261257d0 100644 --- a/tools/lldb-moduleimport-test/lldb-moduleimport-test.cpp +++ b/tools/lldb-moduleimport-test/lldb-moduleimport-test.cpp @@ -325,14 +325,14 @@ int main(int argc, char **argv) { llvm::outs() << "Importing " << path << "... "; #ifdef SWIFT_SUPPORTS_SUBMODULES - std::vector > AccessPath; + std::vector> AccessPath; for (auto i = llvm::sys::path::begin(path); i != llvm::sys::path::end(path); ++i) if (!llvm::sys::path::is_separator((*i)[0])) AccessPath.push_back({ CI.getASTContext().getIdentifier(*i), swift::SourceLoc() }); #else - std::vector > AccessPath; + std::vector> AccessPath; AccessPath.push_back({ CI.getASTContext().getIdentifier(path), swift::SourceLoc() }); #endif diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 7d452e729a55f..f015a44409930 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -1585,7 +1585,7 @@ static int doInputCompletenessTest(StringRef SourceFilename) { //===----------------------------------------------------------------------===// static ModuleDecl *getModuleByFullName(ASTContext &Context, StringRef ModuleName) { - SmallVector, 4> + SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; @@ -1600,8 +1600,7 @@ static ModuleDecl *getModuleByFullName(ASTContext &Context, StringRef ModuleName } static ModuleDecl *getModuleByFullName(ASTContext &Context, Identifier ModuleName) { - ModuleDecl *Result = Context.getModule(std::make_pair(ModuleName, - SourceLoc())); + ModuleDecl *Result = Context.getModule({{ModuleName,SourceLoc()}}); if (!Result || Result->failedToLoad()) return nullptr; return Result; @@ -2624,7 +2623,7 @@ static int doPrintModuleImports(const CompilerInvocation &InitInvok, for (auto &import : scratch) { llvm::outs() << "\t" << import.second->getName(); for (auto accessPathPiece : import.first) { - llvm::outs() << "." << accessPathPiece.first; + llvm::outs() << "." << accessPathPiece.item; } if (import.second->isClangModule()) From c1444dea18964f97f674cf1ddc15b44427cbf8ba Mon Sep 17 00:00:00 2001 From: "Kita, Maksim" Date: Tue, 10 Dec 2019 23:46:25 +0300 Subject: [PATCH 166/478] SR-11889: Fixed code review issues --- include/swift/AST/ASTContext.h | 2 +- include/swift/AST/ModuleLoader.h | 2 +- include/swift/AST/NameLookup.h | 4 ++-- include/swift/AST/TypeRepr.h | 13 +++++++------ include/swift/Basic/Located.h | 19 +++++++++++++++---- include/swift/IDE/Utils.h | 2 +- include/swift/Parse/Parser.h | 2 +- lib/AST/ConformanceLookupTable.cpp | 14 +++++++------- lib/AST/Decl.cpp | 2 +- lib/AST/GenericSignatureBuilder.cpp | 14 +++++++------- lib/AST/NameLookup.cpp | 10 +++++----- lib/AST/TypeRepr.cpp | 2 +- lib/ClangImporter/ImportDecl.cpp | 2 +- lib/IDE/SwiftSourceDocInfo.cpp | 6 +++--- lib/IDE/SyntaxModel.cpp | 18 ++++++++---------- lib/Parse/ParseExpr.cpp | 8 ++++---- lib/ParseSIL/ParseSIL.cpp | 8 ++++---- lib/Sema/TypeCheckDecl.cpp | 6 +++--- unittests/AST/DiagnosticConsumerTests.cpp | 2 +- 19 files changed, 73 insertions(+), 63 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index fd717bb31df10..d81c4e2121179 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -25,8 +25,8 @@ #include "swift/AST/Types.h" #include "swift/AST/TypeAlignments.h" #include "swift/Basic/LangOptions.h" -#include "swift/Basic/Malloc.h" #include "swift/Basic/Located.h" +#include "swift/Basic/Malloc.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" diff --git a/include/swift/AST/ModuleLoader.h b/include/swift/AST/ModuleLoader.h index 92c05dc1f0630..d149611308e74 100644 --- a/include/swift/AST/ModuleLoader.h +++ b/include/swift/AST/ModuleLoader.h @@ -19,11 +19,11 @@ #include "swift/AST/Identifier.h" #include "swift/Basic/LLVM.h" +#include "swift/Basic/Located.h" #include "swift/Basic/SourceLoc.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/TinyPtrVector.h" -#include "swift/Basic/Located.h" namespace llvm { class FileCollector; diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 374d8b4fcd310..fb0c7fc716056 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -495,7 +495,7 @@ void recordLookupOfTopLevelName(DeclContext *topLevelContext, DeclName name, void getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, unsigned i, - llvm::SmallVectorImpl> &result, + llvm::SmallVectorImpl> &result, bool &anyObject); /// Retrieve the set of nominal type declarations that are directly @@ -503,7 +503,7 @@ void getDirectlyInheritedNominalTypeDecls( /// and splitting out the components of compositions. /// /// If we come across the AnyObject type, set \c anyObject true. -SmallVector, 4> +SmallVector, 4> getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, bool &anyObject); diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index e4cce80d12353..f92cf8e68d18e 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -26,6 +26,7 @@ #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "swift/Basic/Debug.h" +#include "swift/Basic/Located.h" #include "swift/Basic/InlineBitfield.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TrailingObjects.h" @@ -672,9 +673,9 @@ struct TupleTypeReprElement { /// \endcode class TupleTypeRepr final : public TypeRepr, private llvm::TrailingObjects> { + Located> { friend TrailingObjects; - typedef std::pair SourceLocAndIdx; + typedef Located SourceLocAndIdx; SourceRange Parens; @@ -745,12 +746,12 @@ class TupleTypeRepr final : public TypeRepr, SourceLoc getEllipsisLoc() const { return hasEllipsis() ? - getTrailingObjects()[0].first : SourceLoc(); + getTrailingObjects()[0].loc : SourceLoc(); } unsigned getEllipsisIndex() const { return hasEllipsis() ? - getTrailingObjects()[0].second : + getTrailingObjects()[0].item : Bits.TupleTypeRepr.NumElements; } @@ -758,8 +759,8 @@ class TupleTypeRepr final : public TypeRepr, if (hasEllipsis()) { Bits.TupleTypeRepr.HasEllipsis = false; getTrailingObjects()[0] = { - SourceLoc(), - getNumElements() + getNumElements(), + SourceLoc() }; } } diff --git a/include/swift/Basic/Located.h b/include/swift/Basic/Located.h index 34427aaf99ed2..2d21117d04578 100644 --- a/include/swift/Basic/Located.h +++ b/include/swift/Basic/Located.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Copyright (c) 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -10,8 +10,7 @@ // //===----------------------------------------------------------------------===// // -// This file forward declares and imports various common LLVM datatypes that -// swift wants to use unqualified. +// Provides a currency data type Located that should be used instead of std::pair. // //===----------------------------------------------------------------------===// @@ -22,15 +21,27 @@ namespace swift { +/// A currency type for keeping track of items which were found in the source code. +/// +/// Several parts of the compiler need to keep track of a `SourceLoc` corresponding to an item, in case +/// they need to report some diagnostics later. For example, the ClangImporter needs to keep track +/// of where imports were originally written. Located makes it easy to do so while making the code +/// more readable, compared to using `std::pair`. template struct Located { + /// The main item whose source location is being tracked. T item; + /// The original source location from which the item was parsed. SourceLoc loc; + Located() {} + + Located(T item, SourceLoc loc): item(item), loc(loc) {} + template - friend bool operator ==(const Located lhs, const Located rhs) { + friend bool operator ==(const Located& lhs, const Located& rhs) { return lhs.item == rhs.item && lhs.loc == rhs.loc; } }; diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index d0a216a872405..928305e0bffa4 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -240,7 +240,7 @@ class NameMatcher: public ASTWalker { /// The \c Expr argument of a parent \c CustomAttr (if one exists) and /// the \c SourceLoc of the type name it applies to. - llvm::Optional> CustomAttrArg; + llvm::Optional> CustomAttrArg; unsigned InactiveConfigRegionNestings = 0; unsigned SelectorNestings = 0; diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index ff70de7a7674e..8dcd373093321 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -120,7 +120,7 @@ class Parser { DeclContext *CurDeclContext; ASTContext &Context; CodeCompletionCallbacks *CodeCompletion = nullptr; - std::vector>> AnonClosureVars; + std::vector>> AnonClosureVars; bool IsParsingInterfaceTokens = false; diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 642322ce76b07..4b7a25b7dab72 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -140,7 +140,7 @@ void ConformanceLookupTable::destroy() { } namespace { - using ConformanceConstructionInfo = std::pair; + using ConformanceConstructionInfo = Located; } template @@ -194,14 +194,14 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage, loader.first->loadAllConformances(next, loader.second, conformances); loadAllConformances(next, conformances); for (auto conf : conformances) { - protocols.push_back({SourceLoc(), conf->getProtocol()}); + protocols.push_back({conf->getProtocol(), SourceLoc()}); } } else if (next->getParentSourceFile()) { bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(next, anyObject)) { - if (auto proto = dyn_cast(found.second)) - protocols.push_back({found.first, proto}); + if (auto proto = dyn_cast(found.item)) + protocols.push_back({proto, found.loc}); } } @@ -281,7 +281,7 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, // its inherited protocols directly. auto source = ConformanceSource::forExplicit(ext); for (auto locAndProto : protos) - addProtocol(locAndProto.second, locAndProto.first, source); + addProtocol(locAndProto.item, locAndProto.loc, source); }); break; @@ -470,8 +470,8 @@ void ConformanceLookupTable::addInheritedProtocols( bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { - if (auto proto = dyn_cast(found.second)) - addProtocol(proto, found.first, source); + if (auto proto = dyn_cast(found.item)) + addProtocol(proto, found.loc, source); } } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a7ed7ccb7fd01..362c7f95246ca 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4538,7 +4538,7 @@ ProtocolDecl::getInheritedProtocolsSlow() { for (const auto found : getDirectlyInheritedNominalTypeDecls( const_cast(this), anyObject)) { - if (auto proto = dyn_cast(found.second)) { + if (auto proto = dyn_cast(found.item)) { if (known.insert(proto).second) result.push_back(proto); } diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index ad8b0fca4480e..038c81c434806 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -3939,13 +3939,13 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( // Local function to find the insertion point for the protocol's "where" // clause, as well as the string to start the insertion ("where" or ","); - auto getProtocolWhereLoc = [&]() -> std::pair { + auto getProtocolWhereLoc = [&]() -> Located { // Already has a trailing where clause. if (auto trailing = proto->getTrailingWhereClause()) - return { trailing->getRequirements().back().getSourceRange().End, ", " }; + return { ", ", trailing->getRequirements().back().getSourceRange().End }; // Inheritance clause. - return { proto->getInherited().back().getSourceRange().End, " where " }; + return { " where ", proto->getInherited().back().getSourceRange().End }; }; // Retrieve the set of requirements that a given associated type declaration @@ -4060,8 +4060,8 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( assocTypeDecl->getFullName(), inheritedFromProto->getDeclaredInterfaceType()) .fixItInsertAfter( - fixItWhere.first, - getAssociatedTypeReqs(assocTypeDecl, fixItWhere.second)) + fixItWhere.loc, + getAssociatedTypeReqs(assocTypeDecl, fixItWhere.item)) .fixItRemove(assocTypeDecl->getSourceRange()); Diags.diagnose(inheritedAssocTypeDecl, diag::decl_declared_here, @@ -4136,8 +4136,8 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( diag::typealias_override_associated_type, name, inheritedFromProto->getDeclaredInterfaceType()) - .fixItInsertAfter(fixItWhere.first, - getConcreteTypeReq(type, fixItWhere.second)) + .fixItInsertAfter(fixItWhere.loc, + getConcreteTypeReq(type, fixItWhere.item)) .fixItRemove(type->getSourceRange()); Diags.diagnose(inheritedAssocTypeDecl, diag::decl_declared_here, inheritedAssocTypeDecl->getFullName()); diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 193f6c5323f5d..95c1004be5cf8 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -2333,7 +2333,7 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, void swift::getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, unsigned i, - llvm::SmallVectorImpl> &result, + llvm::SmallVectorImpl> &result, bool &anyObject) { auto typeDecl = decl.dyn_cast(); auto extDecl = decl.dyn_cast(); @@ -2362,11 +2362,11 @@ void swift::getDirectlyInheritedNominalTypeDecls( // Form the result. for (auto nominal : nominalTypes) { - result.push_back({loc, nominal}); + result.push_back({nominal, loc}); } } -SmallVector, 4> +SmallVector, 4> swift::getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, bool &anyObject) { @@ -2376,7 +2376,7 @@ swift::getDirectlyInheritedNominalTypeDecls( // Gather results from all of the inherited types. unsigned numInherited = typeDecl ? typeDecl->getInherited().size() : extDecl->getInherited().size(); - SmallVector, 4> result; + SmallVector, 4> result; for (unsigned i : range(numInherited)) { getDirectlyInheritedNominalTypeDecls(decl, i, result, anyObject); } @@ -2413,7 +2413,7 @@ swift::getDirectlyInheritedNominalTypeDecls( anyObject |= selfBounds.anyObject; for (auto inheritedNominal : selfBounds.decls) - result.emplace_back(loc, inheritedNominal); + result.emplace_back(inheritedNominal, loc); return result; } diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 7340027e31c8d..a1fd18d5af19b 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -425,7 +425,7 @@ TupleTypeRepr::TupleTypeRepr(ArrayRef Elements, // Set ellipsis location and index. if (Ellipsis.isValid()) { - getTrailingObjects()[0] = {Ellipsis, EllipsisIdx}; + getTrailingObjects()[0] = {EllipsisIdx, Ellipsis}; } } diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 6459d0e9cbf04..1ceff206802d6 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -5457,7 +5457,7 @@ namespace { bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { - if (auto protoDecl = dyn_cast(found.second)) + if (auto protoDecl = dyn_cast(found.item)) if (protoDecl == proto || protoDecl->inheritsFrom(proto)) return true; } diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index 4861fbe40d92e..8fe2ac59783c4 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -177,7 +177,7 @@ bool NameMatcher::handleCustomAttrs(Decl *D) { // CustomAttr's type, e.g. on `Wrapper` in `@Wrapper(wrappedValue: 10)`. SWIFT_DEFER { CustomAttrArg = None; }; if (Arg && !Arg->isImplicit()) - CustomAttrArg = {Repr->getLoc(), Arg}; + CustomAttrArg = Located(Arg, Repr->getLoc()); if (!Repr->walk(*this)) return false; } @@ -416,9 +416,9 @@ bool NameMatcher::walkToTypeReprPre(TypeRepr *T) { if (isa(T)) { // If we're walking a CustomAttr's type we may have an associated call // argument to resolve with from its semantic initializer. - if (CustomAttrArg.hasValue() && CustomAttrArg->first == T->getLoc()) { + if (CustomAttrArg.hasValue() && CustomAttrArg->loc == T->getLoc()) { tryResolve(ASTWalker::ParentTy(T), T->getLoc(), LabelRangeType::CallArg, - getCallArgLabelRanges(getSourceMgr(), CustomAttrArg->second, LabelRangeEndAt::BeforeElemStart)); + getCallArgLabelRanges(getSourceMgr(), CustomAttrArg->item, LabelRangeEndAt::BeforeElemStart)); } else { tryResolve(ASTWalker::ParentTy(T), T->getLoc()); } diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp index af2e4e13d01af..66adaf5e20aa0 100644 --- a/lib/IDE/SyntaxModel.cpp +++ b/lib/IDE/SyntaxModel.cpp @@ -57,7 +57,7 @@ struct SyntaxModelContext::Implementation { /// If the given tokens start with the expected tokens and they all appear on /// the same line, the source location beyond the final matched token and /// number of matched tokens are returned. Otherwise None is returned. -static Optional> +static Optional> matchImageOrFileLiteralArg(ArrayRef Tokens) { const unsigned NUM_TOKENS = 5; if (Tokens.size() < NUM_TOKENS) @@ -76,8 +76,7 @@ matchImageOrFileLiteralArg(ArrayRef Tokens) { if (Tokens[1].getText() != "resourceName") return None; auto EndToken = Tokens[NUM_TOKENS-1]; - return std::make_pair(EndToken.getLoc().getAdvancedLoc(EndToken.getLength()), - NUM_TOKENS); + return Located(NUM_TOKENS, EndToken.getLoc().getAdvancedLoc(EndToken.getLength())); } /// Matches the tokens in the argument of an image literal expression if its @@ -86,7 +85,7 @@ matchImageOrFileLiteralArg(ArrayRef Tokens) { /// If the given tokens start with the expected tokens and they all appear on /// the same line, the source location beyond the final matched token and number /// of matched tokens are returned. Otherwise None is returned. -static Optional> +static Optional> matchColorLiteralArg(ArrayRef Tokens) { const unsigned NUM_TOKENS = 17; if (Tokens.size() < NUM_TOKENS) @@ -112,8 +111,7 @@ matchColorLiteralArg(ArrayRef Tokens) { Tokens[9].getText() != "blue" || Tokens[13].getText() != "alpha") return None; auto EndToken = Tokens[NUM_TOKENS-1]; - return std::make_pair(EndToken.getLoc().getAdvancedLoc(EndToken.getLength()), - NUM_TOKENS); + return Located(NUM_TOKENS, EndToken.getLoc().getAdvancedLoc(EndToken.getLength())); } SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile) @@ -172,9 +170,9 @@ SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile) case tok::pound_imageLiteral: if (auto Match = matchImageOrFileLiteralArg(Tokens.slice(I+1))) { Kind = SyntaxNodeKind::ObjectLiteral; - Length = SM.getByteDistance(Loc, Match->first); + Length = SM.getByteDistance(Loc, Match->loc); // skip over the extra matched tokens - I += Match->second - 1; + I += Match->item - 1; } else { Kind = SyntaxNodeKind::Keyword; } @@ -182,9 +180,9 @@ SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile) case tok::pound_colorLiteral: if (auto Match = matchColorLiteralArg(Tokens.slice(I+1))) { Kind = SyntaxNodeKind::ObjectLiteral; - Length = SM.getByteDistance(Loc, Match->first); + Length = SM.getByteDistance(Loc, Match->loc); // skip over the matches tokens - I += Match->second - 1; + I += Match->item - 1; } else { Kind = SyntaxNodeKind::Keyword; } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 5963e405678a8..3e0cd03ce8b7c 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2811,7 +2811,7 @@ ParserResult Parser::parseExprClosure() { // FIXME: We could do this all the time, and then provide Fix-Its // to map $i -> the appropriately-named argument. This might help // users who are refactoring code by adding names. - AnonClosureVars.push_back({ leftBrace, {}}); + AnonClosureVars.push_back({{}, leftBrace}); } // Add capture list variables to scope. @@ -2835,7 +2835,7 @@ ParserResult Parser::parseExprClosure() { // anonymous closure arguments. if (!params) { // Create a parameter pattern containing the anonymous variables. - auto &anonVars = AnonClosureVars.back().second; + auto &anonVars = AnonClosureVars.back().item; SmallVector elements; for (auto anonVar : anonVars) elements.push_back(anonVar); @@ -2948,8 +2948,8 @@ Expr *Parser::parseExprAnonClosureArg() { } } - auto leftBraceLoc = AnonClosureVars.back().first; - auto &decls = AnonClosureVars.back().second; + auto leftBraceLoc = AnonClosureVars.back().loc; + auto &decls = AnonClosureVars.back().item; while (ArgNo >= decls.size()) { unsigned nextIdx = decls.size(); SmallVector StrBuf; diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 1bbd13839c659..989ce83f0eda2 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -228,7 +228,7 @@ namespace { /// Data structures used to perform name lookup of basic blocks. llvm::DenseMap BlocksByName; llvm::DenseMap> UndefinedBlocks; + Located> UndefinedBlocks; /// Data structures used to perform name lookup for local values. llvm::StringMap LocalValues; @@ -584,8 +584,8 @@ bool SILParser::diagnoseProblems() { if (!UndefinedBlocks.empty()) { // FIXME: These are going to come out in nondeterministic order. for (auto Entry : UndefinedBlocks) - P.diagnose(Entry.second.first, diag::sil_undefined_basicblock_use, - Entry.second.second); + P.diagnose(Entry.second.loc, diag::sil_undefined_basicblock_use, + Entry.second.item); HadError = true; } @@ -717,7 +717,7 @@ SILBasicBlock *SILParser::getBBForReference(Identifier Name, SourceLoc Loc) { // Otherwise, create it and remember that this is a forward reference so // that we can diagnose use without definition problems. BB = F->createBasicBlock(); - UndefinedBlocks[BB] = {Loc, Name}; + UndefinedBlocks[BB] = {Name, Loc}; return BB; } diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index ab00f3b15603c..1c55842c9af01 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -258,7 +258,7 @@ HasCircularInheritedProtocolsRequest::evaluate(Evaluator &evaluator, bool anyObject = false; auto inherited = getDirectlyInheritedNominalTypeDecls(decl, anyObject); for (auto &found : inherited) { - auto *protoDecl = dyn_cast(found.second); + auto *protoDecl = dyn_cast(found.item); if (!protoDecl) continue; @@ -490,11 +490,11 @@ ProtocolRequiresClassRequest::evaluate(Evaluator &evaluator, // class-bound protocol. for (const auto found : allInheritedNominals) { // Superclass bound. - if (isa(found.second)) + if (isa(found.item)) return true; // A protocol that might be class-constrained. - if (auto proto = dyn_cast(found.second)) { + if (auto proto = dyn_cast(found.item)) { if (proto->requiresClass()) return true; } diff --git a/unittests/AST/DiagnosticConsumerTests.cpp b/unittests/AST/DiagnosticConsumerTests.cpp index d347f8f32845f..d3d24a3a8098a 100644 --- a/unittests/AST/DiagnosticConsumerTests.cpp +++ b/unittests/AST/DiagnosticConsumerTests.cpp @@ -17,7 +17,7 @@ using namespace swift; namespace { - using ExpectedDiagnostic = std::pair; + using ExpectedDiagnostic = Located; class ExpectationDiagnosticConsumer: public DiagnosticConsumer { ExpectationDiagnosticConsumer *previous; From c88ac85b3f8acc41a5fde406b34f72a8de28d411 Mon Sep 17 00:00:00 2001 From: "Kita, Maksim" Date: Wed, 11 Dec 2019 00:19:15 +0300 Subject: [PATCH 167/478] SR-11889: Fixed code review issues 1. Removed two braces {{ usage of Located initialization 2. Wrapped documentation into 80 characters --- include/swift/Basic/Located.h | 14 ++++++++------ lib/AST/ASTContext.cpp | 2 +- lib/Frontend/Frontend.cpp | 4 ++-- lib/FrontendTool/ImportedModules.cpp | 2 +- tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp | 2 +- .../lib/SwiftLang/SwiftEditorInterfaceGen.cpp | 2 +- tools/swift-ide-test/swift-ide-test.cpp | 2 +- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/include/swift/Basic/Located.h b/include/swift/Basic/Located.h index 2d21117d04578..c014d08063c86 100644 --- a/include/swift/Basic/Located.h +++ b/include/swift/Basic/Located.h @@ -10,7 +10,8 @@ // //===----------------------------------------------------------------------===// // -// Provides a currency data type Located that should be used instead of std::pair. +// Provides a currency data type Located that should be used instead +// of std::pair. // //===----------------------------------------------------------------------===// @@ -22,11 +23,12 @@ namespace swift { /// A currency type for keeping track of items which were found in the source code. -/// -/// Several parts of the compiler need to keep track of a `SourceLoc` corresponding to an item, in case -/// they need to report some diagnostics later. For example, the ClangImporter needs to keep track -/// of where imports were originally written. Located makes it easy to do so while making the code -/// more readable, compared to using `std::pair`. +/// Several parts of the compiler need to keep track of a `SourceLoc` corresponding +/// to an item, in case they need to report some diagnostics later. For example, +/// the ClangImporter needs to keep track of where imports were originally written. +/// Located makes it easy to do so while making the code more readable, compared to +/// using `std::pair`. + template struct Located { diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 96c5257100982..cd1524ee6aabc 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1778,7 +1778,7 @@ ModuleDecl *ASTContext::getStdlibModule(bool loadIfAbsent) { if (loadIfAbsent) { auto mutableThis = const_cast(this); TheStdlibModule = - mutableThis->getModule({{ StdlibModuleName, SourceLoc() }}); + mutableThis->getModule({ Located(StdlibModuleName, SourceLoc()) }); } else { TheStdlibModule = getLoadedModule(StdlibModuleName); } diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index fe18c24bc92ff..c0b4f9e3b54dd 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -778,7 +778,7 @@ ModuleDecl *CompilerInstance::importUnderlyingModule() { ModuleDecl *objCModuleUnderlyingMixedFramework = static_cast(Context->getClangModuleLoader()) ->loadModule(SourceLoc(), - {{MainModule->getName(), SourceLoc()}}); + { Located(MainModule->getName(), SourceLoc()) }); if (objCModuleUnderlyingMixedFramework) return objCModuleUnderlyingMixedFramework; Diagnostics.diagnose(SourceLoc(), diag::error_underlying_module_not_found, @@ -808,7 +808,7 @@ void CompilerInstance::getImplicitlyImportedModules( if (Lexer::isIdentifier(ImplicitImportModuleName)) { auto moduleID = Context->getIdentifier(ImplicitImportModuleName); ModuleDecl *importModule = - Context->getModule({{moduleID, SourceLoc()}}); + Context->getModule({ Located(moduleID, SourceLoc()) }); if (importModule) { importModules.push_back(importModule); } else { diff --git a/lib/FrontendTool/ImportedModules.cpp b/lib/FrontendTool/ImportedModules.cpp index 59b6088da4d54..069c989534520 100644 --- a/lib/FrontendTool/ImportedModules.cpp +++ b/lib/FrontendTool/ImportedModules.cpp @@ -98,7 +98,7 @@ bool swift::emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, } if (opts.ImportUnderlyingModule) { - auto underlyingModule = clangImporter->loadModule(SourceLoc(), {{ mainModule->getName(), SourceLoc() }}); + auto underlyingModule = clangImporter->loadModule(SourceLoc(), { Located(mainModule->getName(), SourceLoc()) }); if (!underlyingModule) { Context.Diags.diagnose(SourceLoc(), diag::error_underlying_module_not_found, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp index 922639b27fca5..9ed275a1d1a87 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp @@ -52,7 +52,7 @@ static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { } static ModuleDecl *getModuleByFullName(ASTContext &Ctx, Identifier ModuleName) { - return Ctx.getModule({{ModuleName, SourceLoc()}}); + return Ctx.getModule({ Located(ModuleName, SourceLoc()) }); } namespace { diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp index 87e1a87d0ff09..475629e4a22db 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp @@ -103,7 +103,7 @@ static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { } static ModuleDecl *getModuleByFullName(ASTContext &Ctx, Identifier ModuleName) { - return Ctx.getModule({{ModuleName, SourceLoc()}}); + return Ctx.getModule({ Located(ModuleName, SourceLoc()) }); } namespace { diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index f015a44409930..1704f85756259 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -1600,7 +1600,7 @@ static ModuleDecl *getModuleByFullName(ASTContext &Context, StringRef ModuleName } static ModuleDecl *getModuleByFullName(ASTContext &Context, Identifier ModuleName) { - ModuleDecl *Result = Context.getModule({{ModuleName,SourceLoc()}}); + ModuleDecl *Result = Context.getModule({ Located(ModuleName,SourceLoc()) }); if (!Result || Result->failedToLoad()) return nullptr; return Result; From 5fdea64d7ec7763387d00082db250c2ef46334ab Mon Sep 17 00:00:00 2001 From: "Kita, Maksim" Date: Wed, 11 Dec 2019 00:21:34 +0300 Subject: [PATCH 168/478] SR-11889: Fixed code review issues. --- include/swift/Basic/Located.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/swift/Basic/Located.h b/include/swift/Basic/Located.h index c014d08063c86..d87548f0833b2 100644 --- a/include/swift/Basic/Located.h +++ b/include/swift/Basic/Located.h @@ -28,7 +28,6 @@ namespace swift { /// the ClangImporter needs to keep track of where imports were originally written. /// Located makes it easy to do so while making the code more readable, compared to /// using `std::pair`. - template struct Located { From ea6a2dc094c6490b0819c281e19bd6a54b16990a Mon Sep 17 00:00:00 2001 From: "Kita, Maksim" Date: Sat, 14 Dec 2019 13:39:53 +0300 Subject: [PATCH 169/478] SR-11889: Fixed code review issues 1. Updated Located field names with Pascal Case 2. Updated Located constuctor 3. Formatted lines with more than 80 symbols --- include/swift/AST/Decl.h | 4 +- include/swift/AST/Module.h | 2 +- include/swift/AST/TypeRepr.h | 4 +- include/swift/Basic/Located.h | 10 ++--- lib/AST/ASTContext.cpp | 8 ++-- lib/AST/ASTDumper.cpp | 2 +- lib/AST/ASTPrinter.cpp | 4 +- lib/AST/ConformanceLookupTable.cpp | 10 ++--- lib/AST/Decl.cpp | 2 +- lib/AST/GenericSignatureBuilder.cpp | 8 ++-- lib/AST/ImportCache.cpp | 2 +- lib/AST/Module.cpp | 10 ++--- lib/ClangImporter/ClangImporter.cpp | 16 ++++---- lib/ClangImporter/DWARFImporter.cpp | 2 +- lib/ClangImporter/ImportDecl.cpp | 2 +- lib/Frontend/ModuleInterfaceLoader.cpp | 4 +- lib/Frontend/ModuleInterfaceSupport.cpp | 4 +- lib/FrontendTool/ImportedModules.cpp | 5 ++- lib/IDE/CodeCompletion.cpp | 4 +- lib/IDE/ModuleInterfacePrinting.cpp | 2 +- lib/IDE/SourceEntityWalker.cpp | 6 +-- lib/IDE/SwiftSourceDocInfo.cpp | 6 +-- lib/IDE/SyntaxModel.cpp | 8 ++-- lib/Parse/ParseDecl.cpp | 8 ++-- lib/Parse/ParseExpr.cpp | 6 +-- lib/ParseSIL/ParseSIL.cpp | 14 +++---- lib/Sema/NameBinding.cpp | 40 ++++++++++---------- lib/Sema/SourceLoader.cpp | 20 +++++----- lib/Sema/TypeCheckDecl.cpp | 6 +-- lib/Serialization/ModuleFile.cpp | 12 +++--- lib/Serialization/Serialization.cpp | 2 +- lib/Serialization/SerializedModuleLoader.cpp | 22 +++++------ tools/swift-ide-test/swift-ide-test.cpp | 2 +- 33 files changed, 129 insertions(+), 128 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 4d714102a351f..c3b8bf961daf4 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -1578,9 +1578,9 @@ class ImportDecl final : public Decl, } SourceLoc getStartLoc() const { return ImportLoc; } - SourceLoc getLocFromSource() const { return getFullAccessPath().front().loc; } + SourceLoc getLocFromSource() const { return getFullAccessPath().front().Loc; } SourceRange getSourceRange() const { - return SourceRange(ImportLoc, getFullAccessPath().back().loc); + return SourceRange(ImportLoc, getFullAccessPath().back().Loc); } SourceLoc getKindLoc() const { return KindLoc; } diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index d364053d313eb..833c4cc1e72d7 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -139,7 +139,7 @@ class ModuleDecl : public DeclContext, public TypeDecl { assert(AccessPath.size() <= 1 && "can only refer to top-level decls"); return AccessPath.empty() - || DeclName(AccessPath.front().item).matchesRef(Name); + || DeclName(AccessPath.front().Item).matchesRef(Name); } /// Arbitrarily orders ImportedModule records, for inclusion in sets and such. diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index f92cf8e68d18e..56e4995aad08c 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -746,12 +746,12 @@ class TupleTypeRepr final : public TypeRepr, SourceLoc getEllipsisLoc() const { return hasEllipsis() ? - getTrailingObjects()[0].loc : SourceLoc(); + getTrailingObjects()[0].Loc : SourceLoc(); } unsigned getEllipsisIndex() const { return hasEllipsis() ? - getTrailingObjects()[0].item : + getTrailingObjects()[0].Item : Bits.TupleTypeRepr.NumElements; } diff --git a/include/swift/Basic/Located.h b/include/swift/Basic/Located.h index d87548f0833b2..ceda184fd3845 100644 --- a/include/swift/Basic/Located.h +++ b/include/swift/Basic/Located.h @@ -32,18 +32,18 @@ template struct Located { /// The main item whose source location is being tracked. - T item; + T Item; /// The original source location from which the item was parsed. - SourceLoc loc; + SourceLoc Loc; - Located() {} + Located(): Item(), Loc() {} - Located(T item, SourceLoc loc): item(item), loc(loc) {} + Located(T Item, SourceLoc loc): Item(Item), Loc(loc) {} template friend bool operator ==(const Located& lhs, const Located& rhs) { - return lhs.item == rhs.item && lhs.loc == rhs.loc; + return lhs.Item == rhs.Item && lhs.Loc == rhs.Loc; } }; } diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index cd1524ee6aabc..379deec5e599a 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1473,7 +1473,7 @@ ModuleDecl *ASTContext::getLoadedModule( // TODO: Swift submodules. if (ModulePath.size() == 1) { - return getLoadedModule(ModulePath[0].item); + return getLoadedModule(ModulePath[0].Item); } return nullptr; } @@ -1729,7 +1729,7 @@ bool ASTContext::canImportModule(Located ModulePath) { return true; // If we've failed loading this module before, don't look for it again. - if (FailedModuleImportNames.count(ModulePath.item)) + if (FailedModuleImportNames.count(ModulePath.Item)) return false; // Otherwise, ask the module loaders. @@ -1739,7 +1739,7 @@ bool ASTContext::canImportModule(Located ModulePath) { } } - FailedModuleImportNames.insert(ModulePath.item); + FailedModuleImportNames.insert(ModulePath.Item); return false; } @@ -1752,7 +1752,7 @@ ASTContext::getModule(ArrayRef> ModulePath) { auto moduleID = ModulePath[0]; for (auto &importer : getImpl().ModuleLoaders) { - if (ModuleDecl *M = importer->loadModule(moduleID.loc, ModulePath)) { + if (ModuleDecl *M = importer->loadModule(moduleID.Loc, ModulePath)) { return M; } } diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 2c8658354c632..4fd89ceded63a 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -589,7 +589,7 @@ namespace { OS << " '"; interleave(ID->getFullAccessPath(), [&](const ImportDecl::AccessPathElement &Elem) { - OS << Elem.item; + OS << Elem.Item; }, [&] { OS << '.'; }); OS << "')"; diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index e58dc963f79e3..72b18789df6a0 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2097,10 +2097,10 @@ void PrintAST::visitImportDecl(ImportDecl *decl) { interleave(decl->getFullAccessPath(), [&](const ImportDecl::AccessPathElement &Elem) { if (!Mods.empty()) { - Printer.printModuleRef(Mods.front(), Elem.item); + Printer.printModuleRef(Mods.front(), Elem.Item); Mods = Mods.slice(1); } else { - Printer << Elem.item.str(); + Printer << Elem.Item.str(); } }, [&] { Printer << "."; }); diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 4b7a25b7dab72..414d3d8803984 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -200,8 +200,8 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage, bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(next, anyObject)) { - if (auto proto = dyn_cast(found.item)) - protocols.push_back({proto, found.loc}); + if (auto proto = dyn_cast(found.Item)) + protocols.push_back({proto, found.Loc}); } } @@ -281,7 +281,7 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, // its inherited protocols directly. auto source = ConformanceSource::forExplicit(ext); for (auto locAndProto : protos) - addProtocol(locAndProto.item, locAndProto.loc, source); + addProtocol(locAndProto.Item, locAndProto.Loc, source); }); break; @@ -470,8 +470,8 @@ void ConformanceLookupTable::addInheritedProtocols( bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { - if (auto proto = dyn_cast(found.item)) - addProtocol(proto, found.loc, source); + if (auto proto = dyn_cast(found.Item)) + addProtocol(proto, found.Loc, source); } } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 362c7f95246ca..87030221c2779 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4538,7 +4538,7 @@ ProtocolDecl::getInheritedProtocolsSlow() { for (const auto found : getDirectlyInheritedNominalTypeDecls( const_cast(this), anyObject)) { - if (auto proto = dyn_cast(found.item)) { + if (auto proto = dyn_cast(found.Item)) { if (known.insert(proto).second) result.push_back(proto); } diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index 038c81c434806..1f5bdfdd93f2e 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -4060,8 +4060,8 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( assocTypeDecl->getFullName(), inheritedFromProto->getDeclaredInterfaceType()) .fixItInsertAfter( - fixItWhere.loc, - getAssociatedTypeReqs(assocTypeDecl, fixItWhere.item)) + fixItWhere.Loc, + getAssociatedTypeReqs(assocTypeDecl, fixItWhere.Item)) .fixItRemove(assocTypeDecl->getSourceRange()); Diags.diagnose(inheritedAssocTypeDecl, diag::decl_declared_here, @@ -4136,8 +4136,8 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( diag::typealias_override_associated_type, name, inheritedFromProto->getDeclaredInterfaceType()) - .fixItInsertAfter(fixItWhere.loc, - getConcreteTypeReq(type, fixItWhere.item)) + .fixItInsertAfter(fixItWhere.Loc, + getConcreteTypeReq(type, fixItWhere.Item)) .fixItRemove(type->getSourceRange()); Diags.diagnose(inheritedAssocTypeDecl, diag::decl_declared_here, inheritedAssocTypeDecl->getFullName()); diff --git a/lib/AST/ImportCache.cpp b/lib/AST/ImportCache.cpp index e99e8c8f419ea..1b667a75d7373 100644 --- a/lib/AST/ImportCache.cpp +++ b/lib/AST/ImportCache.cpp @@ -57,7 +57,7 @@ void ImportSet::Profile( for (auto import : topLevelImports) { ID.AddInteger(import.first.size()); for (auto accessPathElt : import.first) { - ID.AddPointer(accessPathElt.item.getAsOpaquePointer()); + ID.AddPointer(accessPathElt.Item.getAsOpaquePointer()); } ID.AddPointer(import.second); } diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 90fe1cc7aa913..df3874d544686 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -303,7 +303,7 @@ void SourceLookupCache::lookupVisibleDecls(AccessPathTy AccessPath, assert(AccessPath.size() <= 1 && "can only refer to top-level decls"); if (!AccessPath.empty()) { - auto I = TopLevelValues.find(AccessPath.front().item); + auto I = TopLevelValues.find(AccessPath.front().Item); if (I == TopLevelValues.end()) return; for (auto vd : I->second) @@ -335,7 +335,7 @@ void SourceLookupCache::lookupClassMembers(AccessPathTy accessPath, for (ValueDecl *vd : member.second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); - if (nominal && nominal->getName() == accessPath.front().item) + if (nominal && nominal->getName() == accessPath.front().Item) consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); } @@ -367,7 +367,7 @@ void SourceLookupCache::lookupClassMember(AccessPathTy accessPath, if (!accessPath.empty()) { for (ValueDecl *vd : iter->second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); - if (nominal && nominal->getName() == accessPath.front().item) + if (nominal && nominal->getName() == accessPath.front().Item) results.push_back(vd); } return; @@ -1187,7 +1187,7 @@ bool ModuleDecl::isSameAccessPath(AccessPathTy lhs, AccessPathTy rhs) { return std::equal(lhs.begin(), lhs.end(), rhs.begin(), [](const AccessPathElem &lElem, const AccessPathElem &rElem) { - return lElem.item == rElem.item; + return lElem.Item == rElem.Item; }); } @@ -1256,7 +1256,7 @@ ModuleDecl::removeDuplicateImports(SmallVectorImpl &imports) { rhs.first.begin(), rhs.first.end(), [](const AccessPathElem &lElem, const AccessPathElem &rElem) { - return lElem.item.str() < rElem.item.str(); + return lElem.Item.str() < rElem.Item.str(); }); }); auto last = std::unique(imports.begin(), imports.end(), diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index d4a4dbd05e400..8a8b5d1781619 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -1625,13 +1625,13 @@ void ClangImporter::collectSubModuleNames( // Look up the top-level module first. clang::Module *clangModule = clangHeaderSearch.lookupModule( - path.front().item.str(), /*AllowSearch=*/true, + path.front().Item.str(), /*AllowSearch=*/true, /*AllowExtraModuleMapSearch=*/true); if (!clangModule) return; clang::Module *submodule = clangModule; for (auto component : path.slice(1)) { - submodule = submodule->findSubmodule(component.item.str()); + submodule = submodule->findSubmodule(component.Item.str()); if (!submodule) return; } @@ -1648,7 +1648,7 @@ bool ClangImporter::canImportModule(Located moduleID) { // FIXME: This only works with top-level modules. auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo(); clang::Module *clangModule = - clangHeaderSearch.lookupModule(moduleID.item.str(), /*AllowSearch=*/true, + clangHeaderSearch.lookupModule(moduleID.Item.str(), /*AllowSearch=*/true, /*AllowExtraModuleMapSearch=*/true); if (!clangModule) { return false; @@ -1668,7 +1668,7 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( // Look up the top-level module first, to see if it exists at all. clang::Module *clangModule = clangHeaderSearch.lookupModule( - path.front().item.str(), /*AllowSearch=*/true, + path.front().Item.str(), /*AllowSearch=*/true, /*AllowExtraModuleMapSearch=*/true); if (!clangModule) return nullptr; @@ -1677,8 +1677,8 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( SmallVector, 4> clangPath; for (auto component : path) { - clangPath.push_back({&clangContext.Idents.get(component.item.str()), - exportSourceLoc(component.loc)}); + clangPath.push_back({&clangContext.Idents.get(component.Item.str()), + exportSourceLoc(component.Loc)}); } auto &rawDiagClient = Instance->getDiagnosticClient(); @@ -1728,13 +1728,13 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( // Verify that the submodule exists. clang::Module *submodule = clangModule; for (auto &component : path.slice(1)) { - submodule = submodule->findSubmodule(component.item.str()); + submodule = submodule->findSubmodule(component.Item.str()); // Special case: a submodule named "Foo.Private" can be moved to a top-level // module named "Foo_Private". Clang has special support for this. // We're limiting this to just submodules named "Private" because this will // put the Clang AST in a fatal error state if it /doesn't/ exist. - if (!submodule && component.item.str() == "Private" && + if (!submodule && component.Item.str() == "Private" && (&component) == (&path[1])) { submodule = loadModule(llvm::makeArrayRef(clangPath).slice(0, 2), false); } diff --git a/lib/ClangImporter/DWARFImporter.cpp b/lib/ClangImporter/DWARFImporter.cpp index fab4bcbcf3c8b..d044355cb0d10 100644 --- a/lib/ClangImporter/DWARFImporter.cpp +++ b/lib/ClangImporter/DWARFImporter.cpp @@ -105,7 +105,7 @@ ModuleDecl *ClangImporter::Implementation::loadModuleDWARF( return nullptr; // FIXME: Implement submodule support! - Identifier name = path[0].item; + Identifier name = path[0].Item; auto it = DWARFModuleUnits.find(name); if (it != DWARFModuleUnits.end()) return it->second->getParentModule(); diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 1ceff206802d6..1466521c15f99 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -5457,7 +5457,7 @@ namespace { bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { - if (auto protoDecl = dyn_cast(found.item)) + if (auto protoDecl = dyn_cast(found.Item)) if (protoDecl == proto || protoDecl->inheritsFrom(proto)) return true; } diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 42ed6c18fae43..ceb0cb7ae7ad8 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -1020,10 +1020,10 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( } // Create an instance of the Impl to do the heavy lifting. - auto ModuleName = ModuleID.item.str(); + auto ModuleName = ModuleID.Item.str(); ModuleInterfaceLoaderImpl Impl( Ctx, ModPath, InPath, ModuleName, - CacheDir, PrebuiltCacheDir, ModuleID.loc, + CacheDir, PrebuiltCacheDir, ModuleID.Loc, RemarkOnRebuildFromInterface, dependencyTracker, llvm::is_contained(PreferInterfaceForModules, ModuleName) ? diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 2e30bc98dc0b0..b1b6b73673822 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -49,7 +49,7 @@ static void diagnoseScopedImports(DiagnosticEngine &diags, for (const ModuleDecl::ImportedModule &importPair : imports) { if (importPair.first.empty()) continue; - diags.diagnose(importPair.first.front().loc, + diags.diagnose(importPair.first.front().Loc, diag::module_interface_scoped_import_unsupported); } } @@ -119,7 +119,7 @@ static void printImports(raw_ostream &out, ModuleDecl *M) { if (!import.first.empty()) { out << "/*"; for (const auto &accessPathElem : import.first) - out << "." << accessPathElem.item; + out << "." << accessPathElem.Item; out << "*/"; } diff --git a/lib/FrontendTool/ImportedModules.cpp b/lib/FrontendTool/ImportedModules.cpp index 069c989534520..82a683236668c 100644 --- a/lib/FrontendTool/ImportedModules.cpp +++ b/lib/FrontendTool/ImportedModules.cpp @@ -68,7 +68,7 @@ bool swift::emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, auto accessPath = ID->getModulePath(); // only the top-level name is needed (i.e. A in A.B.C) - Modules.insert(accessPath[0].item.str()); + Modules.insert(accessPath[0].Item.str()); } // And now look in the C code we're possibly using. @@ -98,7 +98,8 @@ bool swift::emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, } if (opts.ImportUnderlyingModule) { - auto underlyingModule = clangImporter->loadModule(SourceLoc(), { Located(mainModule->getName(), SourceLoc()) }); + auto underlyingModule = clangImporter->loadModule(SourceLoc(), + { Located(mainModule->getName(), SourceLoc()) }); if (!underlyingModule) { Context.Diags.diagnose(SourceLoc(), diag::error_underlying_module_not_found, diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index fe74c698d2c53..88ae650a9c495 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -4727,7 +4727,7 @@ void CodeCompletionCallbacksImpl::completeImportDecl( std::vector> &Path) { Kind = CompletionKind::Import; CurDeclContext = P.CurDeclContext; - DotLoc = Path.empty() ? SourceLoc() : Path.back().loc; + DotLoc = Path.empty() ? SourceLoc() : Path.back().Loc; if (DotLoc.isInvalid()) return; auto Importer = static_cast(CurDeclContext->getASTContext(). @@ -5573,7 +5573,7 @@ void CodeCompletionCallbacksImpl::doneParsing() { std::vector AccessPath; for (auto Piece : Path) { - AccessPath.push_back(Piece.item.str()); + AccessPath.push_back(Piece.Item.str()); } StringRef ModuleFilename = TheModule->getModuleFilename(); diff --git a/lib/IDE/ModuleInterfacePrinting.cpp b/lib/IDE/ModuleInterfacePrinting.cpp index e2cdc3f37eabf..2eaa67f1bb1a9 100644 --- a/lib/IDE/ModuleInterfacePrinting.cpp +++ b/lib/IDE/ModuleInterfacePrinting.cpp @@ -448,7 +448,7 @@ void swift::ide::printSubmoduleInterface( auto RHSPath = RHS->getFullAccessPath(); for (unsigned i = 0, e = std::min(LHSPath.size(), RHSPath.size()); i != e; i++) { - if (int Ret = LHSPath[i].item.str().compare(RHSPath[i].item.str())) + if (int Ret = LHSPath[i].Item.str().compare(RHSPath[i].Item.str())) return Ret < 0; } return false; diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index f7d679ae4fd72..e551ced63b7aa 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -670,10 +670,10 @@ passReference(ValueDecl *D, Type Ty, SourceLoc BaseNameLoc, SourceRange Range, bool SemaAnnotator::passReference(ModuleEntity Mod, Located IdLoc) { - if (IdLoc.loc.isInvalid()) + if (IdLoc.Loc.isInvalid()) return true; - unsigned NameLen = IdLoc.item.getLength(); - CharSourceRange Range{ IdLoc.loc, NameLen }; + unsigned NameLen = IdLoc.Item.getLength(); + CharSourceRange Range{ IdLoc.Loc, NameLen }; bool Continue = SEWalker.visitModuleReference(Mod, Range); if (!Continue) Cancelled = true; diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index 8fe2ac59783c4..a60e6ab70e276 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -246,7 +246,7 @@ bool NameMatcher::walkToDeclPre(Decl *D) { } } else if (ImportDecl *ID = dyn_cast(D)) { for(const ImportDecl::AccessPathElement &Element: ID->getFullAccessPath()) { - tryResolve(ASTWalker::ParentTy(D), Element.loc); + tryResolve(ASTWalker::ParentTy(D), Element.Loc); if (isDone()) break; } @@ -416,9 +416,9 @@ bool NameMatcher::walkToTypeReprPre(TypeRepr *T) { if (isa(T)) { // If we're walking a CustomAttr's type we may have an associated call // argument to resolve with from its semantic initializer. - if (CustomAttrArg.hasValue() && CustomAttrArg->loc == T->getLoc()) { + if (CustomAttrArg.hasValue() && CustomAttrArg->Loc == T->getLoc()) { tryResolve(ASTWalker::ParentTy(T), T->getLoc(), LabelRangeType::CallArg, - getCallArgLabelRanges(getSourceMgr(), CustomAttrArg->item, LabelRangeEndAt::BeforeElemStart)); + getCallArgLabelRanges(getSourceMgr(), CustomAttrArg->Item, LabelRangeEndAt::BeforeElemStart)); } else { tryResolve(ASTWalker::ParentTy(T), T->getLoc()); } diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp index 66adaf5e20aa0..faa7da133ab17 100644 --- a/lib/IDE/SyntaxModel.cpp +++ b/lib/IDE/SyntaxModel.cpp @@ -170,9 +170,9 @@ SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile) case tok::pound_imageLiteral: if (auto Match = matchImageOrFileLiteralArg(Tokens.slice(I+1))) { Kind = SyntaxNodeKind::ObjectLiteral; - Length = SM.getByteDistance(Loc, Match->loc); + Length = SM.getByteDistance(Loc, Match->Loc); // skip over the extra matched tokens - I += Match->item - 1; + I += Match->Item - 1; } else { Kind = SyntaxNodeKind::Keyword; } @@ -180,9 +180,9 @@ SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile) case tok::pound_colorLiteral: if (auto Match = matchColorLiteralArg(Tokens.slice(I+1))) { Kind = SyntaxNodeKind::ObjectLiteral; - Length = SM.getByteDistance(Loc, Match->loc); + Length = SM.getByteDistance(Loc, Match->Loc); // skip over the matches tokens - I += Match->item - 1; + I += Match->Item - 1; } else { Kind = SyntaxNodeKind::Keyword; } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 74b87ee2cd791..b28a8b50164c6 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -4079,7 +4079,7 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, return makeParserCodeCompletionStatus(); } ImportPath.push_back({Identifier(), Tok.getLoc()}); - if (parseAnyIdentifier(ImportPath.back().item, + if (parseAnyIdentifier(ImportPath.back().Item, diag::expected_identifier_in_decl, "import")) return nullptr; HasNext = consumeIf(tok::period); @@ -4092,8 +4092,8 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, // We omit the code completion token if it immediately follows the module // identifiers. auto BufferId = SourceMgr.getCodeCompletionBufferID(); - auto IdEndOffset = SourceMgr.getLocOffsetInBuffer(ImportPath.back().loc, - BufferId) + ImportPath.back().item.str().size(); + auto IdEndOffset = SourceMgr.getLocOffsetInBuffer(ImportPath.back().Loc, + BufferId) + ImportPath.back().Item.str().size(); auto CCTokenOffset = SourceMgr.getLocOffsetInBuffer(SourceMgr. getCodeCompletionLoc(), BufferId); if (IdEndOffset == CCTokenOffset) { @@ -4102,7 +4102,7 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, } if (Kind != ImportKind::Module && ImportPath.size() == 1) { - diagnose(ImportPath.front().loc, diag::decl_expected_module_name); + diagnose(ImportPath.front().Loc, diag::decl_expected_module_name); return nullptr; } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 3e0cd03ce8b7c..b8aaa0bb2f9fc 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2835,7 +2835,7 @@ ParserResult Parser::parseExprClosure() { // anonymous closure arguments. if (!params) { // Create a parameter pattern containing the anonymous variables. - auto &anonVars = AnonClosureVars.back().item; + auto &anonVars = AnonClosureVars.back().Item; SmallVector elements; for (auto anonVar : anonVars) elements.push_back(anonVar); @@ -2948,8 +2948,8 @@ Expr *Parser::parseExprAnonClosureArg() { } } - auto leftBraceLoc = AnonClosureVars.back().loc; - auto &decls = AnonClosureVars.back().item; + auto leftBraceLoc = AnonClosureVars.back().Loc; + auto &decls = AnonClosureVars.back().Item; while (ArgNo >= decls.size()) { unsigned nextIdx = decls.size(); SmallVector StrBuf; diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 989ce83f0eda2..f1bb364e62844 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -85,8 +85,8 @@ class SILParserTUState : public SILParserTUStateBase { SILParserTUState::~SILParserTUState() { if (!ForwardRefFns.empty()) { for (auto Entry : ForwardRefFns) { - if (Entry.second.loc.isValid()) { - M.getASTContext().Diags.diagnose(Entry.second.loc, + if (Entry.second.Loc.isValid()) { + M.getASTContext().Diags.diagnose(Entry.second.Loc, diag::sil_use_of_undefined_value, Entry.first.str()); } @@ -584,8 +584,8 @@ bool SILParser::diagnoseProblems() { if (!UndefinedBlocks.empty()) { // FIXME: These are going to come out in nondeterministic order. for (auto Entry : UndefinedBlocks) - P.diagnose(Entry.second.loc, diag::sil_undefined_basicblock_use, - Entry.second.item); + P.diagnose(Entry.second.Loc, diag::sil_undefined_basicblock_use, + Entry.second.Item); HadError = true; } @@ -613,13 +613,13 @@ SILFunction *SILParser::getGlobalNameForDefinition(Identifier name, // complete the forward reference. auto iter = TUState.ForwardRefFns.find(name); if (iter != TUState.ForwardRefFns.end()) { - SILFunction *fn = iter->second.item; + SILFunction *fn = iter->second.Item; // Verify that the types match up. if (fn->getLoweredFunctionType() != ty) { P.diagnose(sourceLoc, diag::sil_value_use_type_mismatch, name.str(), fn->getLoweredFunctionType(), ty); - P.diagnose(iter->second.loc, diag::sil_prior_reference); + P.diagnose(iter->second.Loc, diag::sil_prior_reference); fn = builder.createFunctionForForwardReference("" /*name*/, ty, silLoc); } @@ -5084,7 +5084,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { results.size()); } else { for (size_t i : indices(results)) { - setLocalValue(results[i], resultNames[i].item, resultNames[i].loc); + setLocalValue(results[i], resultNames[i].Item, resultNames[i].Loc); } } } diff --git a/lib/Sema/NameBinding.cpp b/lib/Sema/NameBinding.cpp index 28e6ab7b71127..54c9f4e01e621 100644 --- a/lib/Sema/NameBinding.cpp +++ b/lib/Sema/NameBinding.cpp @@ -73,7 +73,7 @@ NameBinder::getModule(ArrayRef> modulePath) { // The Builtin module cannot be explicitly imported unless we're a .sil file // or in the REPL. if ((SF.Kind == SourceFileKind::SIL || SF.Kind == SourceFileKind::REPL) && - moduleID.item == Context.TheBuiltinModule->getName()) + moduleID.Item == Context.TheBuiltinModule->getName()) return Context.TheBuiltinModule; // If the imported module name is the same as the current module, @@ -82,10 +82,10 @@ NameBinder::getModule(ArrayRef> modulePath) { // // FIXME: We'd like to only use this in SIL mode, but unfortunately we use it // for our fake overlays as well. - if (moduleID.item == SF.getParentModule()->getName() && + if (moduleID.Item == SF.getParentModule()->getName() && modulePath.size() == 1) { if (auto importer = Context.getClangModuleLoader()) - return importer->loadModule(moduleID.loc, modulePath); + return importer->loadModule(moduleID.Loc, modulePath); return nullptr; } @@ -170,17 +170,17 @@ static bool shouldImportSelfImportClang(const ImportDecl *ID, void NameBinder::addImport( SmallVectorImpl &imports, ImportDecl *ID) { - if (ID->getModulePath().front().item == SF.getParentModule()->getName() && + if (ID->getModulePath().front().Item == SF.getParentModule()->getName() && ID->getModulePath().size() == 1 && !shouldImportSelfImportClang(ID, SF)) { // If the imported module name is the same as the current module, // produce a diagnostic. StringRef filename = llvm::sys::path::filename(SF.getFilename()); if (filename.empty()) Context.Diags.diagnose(ID, diag::sema_import_current_module, - ID->getModulePath().front().item); + ID->getModulePath().front().Item); else Context.Diags.diagnose(ID, diag::sema_import_current_module_with_file, - filename, ID->getModulePath().front().item); + filename, ID->getModulePath().front().Item); ID->setModule(SF.getParentModule()); return; } @@ -190,7 +190,7 @@ void NameBinder::addImport( SmallString<64> modulePathStr; interleave(ID->getModulePath(), [&](ImportDecl::AccessPathElement elem) { - modulePathStr += elem.item.str(); + modulePathStr += elem.Item.str(); }, [&] { modulePathStr += "."; }); @@ -214,7 +214,7 @@ void NameBinder::addImport( topLevelModule = M; } else { // If we imported a submodule, import the top-level module as well. - Identifier topLevelName = ID->getModulePath().front().item; + Identifier topLevelName = ID->getModulePath().front().Item; topLevelModule = Context.getLoadedModule(topLevelName); if (!topLevelModule) { // Clang can sometimes import top-level modules as if they were @@ -234,8 +234,8 @@ void NameBinder::addImport( !topLevelModule->isTestingEnabled() && !topLevelModule->isNonSwiftModule() && Context.LangOpts.EnableTestableAttrRequiresTestableModule) { - diagnose(ID->getModulePath().front().loc, diag::module_not_testable, - ID->getModulePath().front().item); + diagnose(ID->getModulePath().front().Loc, diag::module_not_testable, + ID->getModulePath().front().Item); testableAttr->setInvalid(); } @@ -243,9 +243,9 @@ void NameBinder::addImport( StringRef privateImportFileName; if (privateImportAttr) { if (!topLevelModule || !topLevelModule->arePrivateImportsEnabled()) { - diagnose(ID->getModulePath().front().loc, + diagnose(ID->getModulePath().front().Loc, diag::module_not_compiled_for_private_import, - ID->getModulePath().front().item); + ID->getModulePath().front().Item); privateImportAttr->setInvalid(); } else { privateImportFileName = privateImportAttr->getSourceFile(); @@ -256,7 +256,7 @@ void NameBinder::addImport( !topLevelModule->isResilient() && !topLevelModule->isNonSwiftModule() && !ID->getAttrs().hasAttribute()) { - diagnose(ID->getModulePath().front().loc, + diagnose(ID->getModulePath().front().Loc, diag::module_not_compiled_with_library_evolution, topLevelModule->getName(), SF.getParentModule()->getName()); } @@ -296,17 +296,17 @@ void NameBinder::addImport( // FIXME: Doesn't handle scoped testable imports correctly. assert(declPath.size() == 1 && "can't handle sub-decl imports"); SmallVector decls; - lookupInModule(topLevelModule, declPath.front().item, decls, + lookupInModule(topLevelModule, declPath.front().Item, decls, NLKind::QualifiedLookup, ResolutionKind::Overloadable, &SF); if (decls.empty()) { diagnose(ID, diag::decl_does_not_exist_in_module, static_cast(ID->getImportKind()), - declPath.front().item, - ID->getModulePath().front().item) - .highlight(SourceRange(declPath.front().loc, - declPath.back().loc)); + declPath.front().Item, + ID->getModulePath().front().Item) + .highlight(SourceRange(declPath.front().Loc, + declPath.back().Loc)); return; } @@ -316,7 +316,7 @@ void NameBinder::addImport( if (!actualKind.hasValue()) { // FIXME: print entire module name? diagnose(ID, diag::ambiguous_decl_in_module, - declPath.front().item, M->getName()); + declPath.front().Item, M->getName()); for (auto next : decls) diagnose(next, diag::found_candidate); @@ -338,7 +338,7 @@ void NameBinder::addImport( getImportKindString(ID->getImportKind()))); } else { emittedDiag.emplace(diagnose(ID, diag::imported_decl_is_wrong_kind, - declPath.front().item, + declPath.front().Item, getImportKindString(ID->getImportKind()), static_cast(*actualKind))); } diff --git a/lib/Sema/SourceLoader.cpp b/lib/Sema/SourceLoader.cpp index 1c7553f062a7e..c1300e38e9054 100644 --- a/lib/Sema/SourceLoader.cpp +++ b/lib/Sema/SourceLoader.cpp @@ -64,13 +64,13 @@ void SourceLoader::collectVisibleTopLevelModuleNames( bool SourceLoader::canImportModule(Located ID) { // Search the memory buffers to see if we can find this file on disk. - FileOrError inputFileOrError = findModule(Ctx, ID.item.str(), - ID.loc); + FileOrError inputFileOrError = findModule(Ctx, ID.Item.str(), + ID.Loc); if (!inputFileOrError) { auto err = inputFileOrError.getError(); if (err != std::errc::no_such_file_or_directory) { - Ctx.Diags.diagnose(ID.loc, diag::sema_opening_import, - ID.item, err.message()); + Ctx.Diags.diagnose(ID.Loc, diag::sema_opening_import, + ID.Item, err.message()); } return false; @@ -86,13 +86,13 @@ ModuleDecl *SourceLoader::loadModule(SourceLoc importLoc, auto moduleID = path[0]; - FileOrError inputFileOrError = findModule(Ctx, moduleID.item.str(), - moduleID.loc); + FileOrError inputFileOrError = findModule(Ctx, moduleID.Item.str(), + moduleID.Loc); if (!inputFileOrError) { auto err = inputFileOrError.getError(); if (err != std::errc::no_such_file_or_directory) { - Ctx.Diags.diagnose(moduleID.loc, diag::sema_opening_import, - moduleID.item, err.message()); + Ctx.Diags.diagnose(moduleID.Loc, diag::sema_opening_import, + moduleID.Item, err.message()); } return nullptr; @@ -115,10 +115,10 @@ ModuleDecl *SourceLoader::loadModule(SourceLoc importLoc, else bufferID = Ctx.SourceMgr.addNewSourceBuffer(std::move(inputFile)); - auto *importMod = ModuleDecl::create(moduleID.item, Ctx); + auto *importMod = ModuleDecl::create(moduleID.Item, Ctx); if (EnableLibraryEvolution) importMod->setResilienceStrategy(ResilienceStrategy::Resilient); - Ctx.LoadedModules[moduleID.item] = importMod; + Ctx.LoadedModules[moduleID.Item] = importMod; auto implicitImportKind = SourceFile::ImplicitModuleImportKind::Stdlib; if (!Ctx.getStdlibModule()) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 1c55842c9af01..a394d8671136a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -258,7 +258,7 @@ HasCircularInheritedProtocolsRequest::evaluate(Evaluator &evaluator, bool anyObject = false; auto inherited = getDirectlyInheritedNominalTypeDecls(decl, anyObject); for (auto &found : inherited) { - auto *protoDecl = dyn_cast(found.item); + auto *protoDecl = dyn_cast(found.Item); if (!protoDecl) continue; @@ -490,11 +490,11 @@ ProtocolRequiresClassRequest::evaluate(Evaluator &evaluator, // class-bound protocol. for (const auto found : allInheritedNominals) { // Superclass bound. - if (isa(found.item)) + if (isa(found.Item)) return true; // A protocol that might be class-constrained. - if (auto proto = dyn_cast(found.item)) { + if (auto proto = dyn_cast(found.Item)) { if (proto->requiresClass()) return true; } diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index cd099e829f17a..510a62db0c63b 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -2209,7 +2209,7 @@ void ModuleFile::getImportDecls(SmallVectorImpl &Results) { AccessPath.push_back({Ctx.getIdentifier(NextComponent), SourceLoc()}); } - if (AccessPath.size() == 1 && AccessPath[0].item == Ctx.StdlibModuleName) + if (AccessPath.size() == 1 && AccessPath[0].Item == Ctx.StdlibModuleName) continue; ModuleDecl *M = Ctx.getLoadedModule(AccessPath); @@ -2228,7 +2228,7 @@ void ModuleFile::getImportDecls(SmallVectorImpl &Results) { // Lookup the decl in the top-level module. ModuleDecl *TopLevelModule = M; if (AccessPath.size() > 1) - TopLevelModule = Ctx.getLoadedModule(AccessPath.front().item); + TopLevelModule = Ctx.getLoadedModule(AccessPath.front().Item); SmallVector Decls; TopLevelModule->lookupQualified( @@ -2278,7 +2278,7 @@ void ModuleFile::lookupVisibleDecls(ModuleDecl::AccessPathTy accessPath, }; if (!accessPath.empty()) { - auto iter = TopLevelDecls->find(accessPath.front().item); + auto iter = TopLevelDecls->find(accessPath.front().Item); if (iter == TopLevelDecls->end()) return; @@ -2454,7 +2454,7 @@ void ModuleFile::lookupClassMember(ModuleDecl::AccessPathTy accessPath, while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) - if (nominal->getName() == accessPath.front().item) + if (nominal->getName() == accessPath.front().Item) results.push_back(vd); } } else { @@ -2467,7 +2467,7 @@ void ModuleFile::lookupClassMember(ModuleDecl::AccessPathTy accessPath, while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) - if (nominal->getName() == accessPath.front().item) + if (nominal->getName() == accessPath.front().Item) results.push_back(vd); } } @@ -2496,7 +2496,7 @@ void ModuleFile::lookupClassMembers(ModuleDecl::AccessPathTy accessPath, while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) - if (nominal->getName() == accessPath.front().item) + if (nominal->getName() == accessPath.front().Item) consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); } diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index eb1cc5bafb8ff..fcdbdf15452de 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -915,7 +915,7 @@ static void flattenImportPath(const ModuleDecl::ImportedModule &import, outStream << '\0'; assert(import.first.size() == 1 && "can only handle top-level decl imports"); auto accessPathElem = import.first.front(); - outStream << accessPathElem.item.str(); + outStream << accessPathElem.Item.str(); } uint64_t getRawModTimeOrHash(const SerializationOptions::FileDependency &dep) { diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index deab420ba56a7..bee83dc0e7a7a 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -428,7 +428,7 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, std::unique_ptr *moduleDocBuffer, std::unique_ptr *moduleSourceInfoBuffer, bool &isFramework, bool &isSystemModule) { - llvm::SmallString<64> moduleName(moduleID.item.str()); + llvm::SmallString<64> moduleName(moduleID.Item.str()); ModuleFilenamePair fileNames(moduleName); SmallVector targetFileNamePairs; @@ -465,7 +465,7 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, // We can only get here if all targetFileNamePairs failed with // 'std::errc::no_such_file_or_directory'. - if (maybeDiagnoseTargetMismatch(moduleID.loc, moduleName, + if (maybeDiagnoseTargetMismatch(moduleID.Loc, moduleName, primaryTargetSpecificName, currPath)) { return false; } else { @@ -517,7 +517,7 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, case SearchPathKind::Framework: { isFramework = true; llvm::sys::path::append(currPath, - moduleID.item.str() + ".framework"); + moduleID.Item.str() + ".framework"); // Check if the framework directory exists. if (!fs.exists(currPath)) @@ -841,7 +841,7 @@ bool SerializedModuleLoaderBase::canImportModule( bool MemoryBufferSerializedModuleLoader::canImportModule( Located mID) { // See if we find it in the registered memory buffers. - return MemoryBuffers.count(mID.item.str()); + return MemoryBuffers.count(mID.Item.str()); } ModuleDecl * @@ -876,15 +876,15 @@ SerializedModuleLoaderBase::loadModule(SourceLoc importLoc, assert(moduleInputBuffer); - auto M = ModuleDecl::create(moduleID.item, Ctx); + auto M = ModuleDecl::create(moduleID.Item, Ctx); M->setIsSystemModule(isSystemModule); - Ctx.LoadedModules[moduleID.item] = M; + Ctx.LoadedModules[moduleID.Item] = M; SWIFT_DEFER { M->setHasResolvedImports(); }; StringRef moduleInterfacePathStr = Ctx.AllocateCopy(moduleInterfacePath.str()); - if (!loadAST(*M, moduleID.loc, moduleInterfacePathStr, + if (!loadAST(*M, moduleID.Loc, moduleInterfacePathStr, std::move(moduleInputBuffer), std::move(moduleDocInputBuffer), std::move(moduleSourceInfoInputBuffer), isFramework, /*treatAsPartialModule*/false)) { @@ -908,7 +908,7 @@ MemoryBufferSerializedModuleLoader::loadModule(SourceLoc importLoc, // FIXME: Right now this works only with access paths of length 1. // Once submodules are designed, this needs to support suffix // matching and a search path. - auto bufIter = MemoryBuffers.find(moduleID.item.str()); + auto bufIter = MemoryBuffers.find(moduleID.Item.str()); if (bufIter == MemoryBuffers.end()) return nullptr; @@ -919,16 +919,16 @@ MemoryBufferSerializedModuleLoader::loadModule(SourceLoc importLoc, MemoryBuffers.erase(bufIter); assert(moduleInputBuffer); - auto *M = ModuleDecl::create(moduleID.item, Ctx); + auto *M = ModuleDecl::create(moduleID.Item, Ctx); SWIFT_DEFER { M->setHasResolvedImports(); }; - if (!loadAST(*M, moduleID.loc, /*moduleInterfacePath*/ "", + if (!loadAST(*M, moduleID.Loc, /*moduleInterfacePath*/ "", std::move(moduleInputBuffer), {}, {}, isFramework, treatAsPartialModule)) { return nullptr; } - Ctx.LoadedModules[moduleID.item] = M; + Ctx.LoadedModules[moduleID.Item] = M; return M; } diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 1704f85756259..adfd9d0ee73a2 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -2623,7 +2623,7 @@ static int doPrintModuleImports(const CompilerInvocation &InitInvok, for (auto &import : scratch) { llvm::outs() << "\t" << import.second->getName(); for (auto accessPathPiece : import.first) { - llvm::outs() << "." << accessPathPiece.item; + llvm::outs() << "." << accessPathPiece.Item; } if (import.second->isClangModule()) From b4b3f98d869ef6384d79024704167d9c116a5356 Mon Sep 17 00:00:00 2001 From: "Kita, Maksim" Date: Sun, 15 Dec 2019 20:42:55 +0300 Subject: [PATCH 170/478] SR-11889: Fixed code review issues 1. Use Located in Convention structure --- include/swift/AST/Attr.h | 8 +++----- lib/AST/Attr.cpp | 4 ++-- lib/AST/NameLookup.cpp | 4 ++-- lib/Parse/ParseDecl.cpp | 3 +-- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index f3a7083b49aea..91b3d5410dd12 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -25,6 +25,7 @@ #include "swift/Basic/Range.h" #include "swift/Basic/OptimizationMode.h" #include "swift/Basic/Version.h" +#include "swift/Basic/Located.h" #include "swift/AST/Identifier.h" #include "swift/AST/AttrKind.h" #include "swift/AST/AutoDiff.h" @@ -69,16 +70,13 @@ class TypeAttributes { struct Convention { StringRef Name = {}; DeclNameRef WitnessMethodProtocol = {}; - StringRef ClangType = {}; - // Carry the source location for diagnostics. - SourceLoc ClangTypeLoc = {}; - + Located ClangType = {}; /// Convenience factory function to create a Swift convention. /// /// Don't use this function if you are creating a C convention as you /// probably need a ClangType field as well. static Convention makeSwiftConvention(StringRef name) { - return {name, DeclNameRef(), "", {}}; + return {name, DeclNameRef(), Located("", {})}; } }; diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 65333c36d96de..bb6d7c47d8079 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -92,8 +92,8 @@ void TypeAttributes::getConventionArguments(SmallVectorImpl &buf) const { stream << ": " << convention.WitnessMethodProtocol; return; } - if (!convention.ClangType.empty()) - stream << ", cType: " << QuotedString(convention.ClangType); + if (!convention.ClangType.Item.empty()) + stream << ", cType: " << QuotedString(convention.ClangType.Item); } /// Given a name like "autoclosure", return the type attribute ID that diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 95c1004be5cf8..d4b7b28115633 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -2402,8 +2402,8 @@ swift::getDirectlyInheritedNominalTypeDecls( if (!req.getFirstType()->isEqual(protoSelfTy)) continue; - result.emplace_back( - loc, req.getSecondType()->castTo()->getDecl()); + result.emplace_back(req.getSecondType()->castTo()->getDecl(), + loc); } return result; } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index b28a8b50164c6..a5000705d9b56 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2699,8 +2699,7 @@ bool Parser::parseConventionAttributeInternal( return true; } if (auto ty = getStringLiteralIfNotInterpolated(Tok.getLoc(), "(C type)")) { - convention.ClangType = ty.getValue(); - convention.ClangTypeLoc = Tok.getLoc(); + convention.ClangType = { ty.getValue(), Tok.getLoc() }; } consumeToken(tok::string_literal); } From c70bd2b2c98b5b586d26ec42362a2e87af091c82 Mon Sep 17 00:00:00 2001 From: "Kita, Maksim" Date: Mon, 16 Dec 2019 00:40:04 +0300 Subject: [PATCH 171/478] SR-11889: Located added dump methods --- include/swift/Basic/Located.h | 8 +++++++- lib/Basic/CMakeLists.txt | 1 + lib/Basic/Located.cpp | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 lib/Basic/Located.cpp diff --git a/include/swift/Basic/Located.h b/include/swift/Basic/Located.h index ceda184fd3845..9b2eeb8d6710c 100644 --- a/include/swift/Basic/Located.h +++ b/include/swift/Basic/Located.h @@ -18,6 +18,8 @@ #ifndef SWIFT_BASIC_LOCATED_H #define SWIFT_BASIC_LOCATED_H +#include "swift/Basic/Debug.h" +#include "swift/Basic/LLVM.h" #include "swift/Basic/SourceLoc.h" namespace swift { @@ -41,11 +43,15 @@ struct Located { Located(T Item, SourceLoc loc): Item(Item), Loc(loc) {} + SWIFT_DEBUG_DUMP; + void dump(raw_ostream &os) const; + template friend bool operator ==(const Located& lhs, const Located& rhs) { return lhs.Item == rhs.Item && lhs.Loc == rhs.Loc; } }; -} + +} // end namespace swift #endif // SWIFT_BASIC_LOCATED_H diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt index 8504b672afc70..6cd7577ce3f93 100644 --- a/lib/Basic/CMakeLists.txt +++ b/lib/Basic/CMakeLists.txt @@ -77,6 +77,7 @@ add_swift_host_library(swiftBasic STATIC JSONSerialization.cpp LangOptions.cpp LLVMContext.cpp + Located.cpp Mangler.cpp OutputFileMap.cpp Platform.cpp diff --git a/lib/Basic/Located.cpp b/lib/Basic/Located.cpp new file mode 100644 index 0000000000000..2167d5abde334 --- /dev/null +++ b/lib/Basic/Located.cpp @@ -0,0 +1,24 @@ +//===--- Located.cpp - Source Location and Associated Value ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 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 "llvm/Support/raw_ostream.h" +#include "swift/Basic/Located.h" + +using namespace swift; + +template +void Located::dump() const { + dump(llvm::errs()); +} + +template +void Located::dump(raw_ostream &os) const { + os << Loc << " " << Item; +} From a2cce2b2933884520d3331828590b79c9e156fc8 Mon Sep 17 00:00:00 2001 From: "Kita, Maksim" Date: Fri, 20 Dec 2019 17:12:57 +0300 Subject: [PATCH 172/478] SR-11889: Fixed code review issues --- include/swift/AST/Attr.h | 2 +- include/swift/Basic/Located.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 91b3d5410dd12..e4d624c0bd42e 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -70,7 +70,7 @@ class TypeAttributes { struct Convention { StringRef Name = {}; DeclNameRef WitnessMethodProtocol = {}; - Located ClangType = {}; + Located ClangType = Located(StringRef(), {}); /// Convenience factory function to create a Swift convention. /// /// Don't use this function if you are creating a C convention as you diff --git a/include/swift/Basic/Located.h b/include/swift/Basic/Located.h index 9b2eeb8d6710c..3b7b6d537a66f 100644 --- a/include/swift/Basic/Located.h +++ b/include/swift/Basic/Located.h @@ -39,15 +39,15 @@ struct Located { /// The original source location from which the item was parsed. SourceLoc Loc; - Located(): Item(), Loc() {} + Located() : Item(), Loc() {} - Located(T Item, SourceLoc loc): Item(Item), Loc(loc) {} + Located(T Item, SourceLoc loc) : Item(Item), Loc(loc) {} SWIFT_DEBUG_DUMP; void dump(raw_ostream &os) const; template - friend bool operator ==(const Located& lhs, const Located& rhs) { + friend bool operator ==(const Located &lhs, const Located &rhs) { return lhs.Item == rhs.Item && lhs.Loc == rhs.Loc; } }; From ab0475178a2ad64770e3ac325b7bd86f199c6715 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 19 Dec 2019 22:29:07 -0800 Subject: [PATCH 173/478] [CodeCompletion] Eliminate CodeCompletionCallbacks::completeExpr() Well, this does nothing. It just set the parser position twice and calls the consumer's 'handleResults()' without any reason. It seems it was intended to be a "fallback" completion, but it was never implemented properly. So just remove it for now. rdar://problem/58102910 --- include/swift/Parse/CodeCompletionCallbacks.h | 4 ---- lib/IDE/CodeCompletion.cpp | 18 ------------------ lib/Parse/ParseStmt.cpp | 4 ---- 3 files changed, 26 deletions(-) diff --git a/include/swift/Parse/CodeCompletionCallbacks.h b/include/swift/Parse/CodeCompletionCallbacks.h index cf18c459c642e..f6560dbbfed8e 100644 --- a/include/swift/Parse/CodeCompletionCallbacks.h +++ b/include/swift/Parse/CodeCompletionCallbacks.h @@ -118,10 +118,6 @@ class CodeCompletionCallbacks { /// Set target decl for attribute if the CC token is in attribute of the decl. virtual void setAttrTargetDeclKind(Optional DK) {} - /// Complete the whole expression. This is a fallback that should - /// produce results when more specific completion methods failed. - virtual void completeExpr() {}; - /// Complete expr-dot after we have consumed the dot. virtual void completeDotExpr(Expr *E, SourceLoc DotLoc) {}; diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index ce2171a6118c1..6a3626e7cadfe 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1260,10 +1260,6 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { Builder.addTypeAnnotation(ST.getString()); } - /// Set to true when we have delivered code completion results - /// to the \c Consumer. - bool DeliveredResults = false; - Optional> typeCheckParsedExpr() { assert(ParsedExpr && "should have an expression"); @@ -1335,7 +1331,6 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { AttTargetDK = DK; } - void completeExpr() override; void completeDotExpr(Expr *E, SourceLoc DotLoc) override; void completeStmtOrExpr(CodeCompletionExpr *E) override; void completePostfixExprBeginning(CodeCompletionExpr *E) override; @@ -1381,18 +1376,6 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { }; } // end anonymous namespace -void CodeCompletionCallbacksImpl::completeExpr() { - if (DeliveredResults) - return; - - Parser::ParserPositionRAII RestorePosition(P); - P.restoreParserPosition(ExprBeginPosition); - - // FIXME: implement fallback code completion. - - deliverCompletionResults(); -} - namespace { static bool isTopLevelContext(const DeclContext *DC) { for (; DC && DC->isLocalContext(); DC = DC->getParent()) { @@ -5631,7 +5614,6 @@ void CodeCompletionCallbacksImpl::deliverCompletionResults() { Consumer.handleResultsAndModules(CompletionContext, RequestedModules, DCForModules); RequestedModules.clear(); - DeliveredResults = true; } void PrintingCodeCompletionConsumer::handleResults( diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 9184b08022263..5fa8830946b03 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -167,10 +167,6 @@ ParserStatus Parser::parseExprOrStmt(ASTNode &Result) { } } - if (ResultExpr.hasCodeCompletion() && CodeCompletion) { - CodeCompletion->completeExpr(); - } - return ResultExpr; } From c9bd831004a35e542a8b8cf238b868e3081e1991 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Fri, 20 Dec 2019 08:54:45 -0800 Subject: [PATCH 174/478] DeserializeSIL: Fix two places where we want the SILType inside of the current function according to the TypeExpansionContext rdar://58095210 --- lib/Serialization/DeserializeSIL.cpp | 4 +-- .../Inputs/opaque_types_inlineable_2.swift | 18 ++++++++++++ .../opaque_types_inlineable.swift | 29 +++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 test/Serialization/Inputs/opaque_types_inlineable_2.swift create mode 100644 test/Serialization/opaque_types_inlineable.swift diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 008fcf1eab36e..eb97ef6d9c322 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1429,7 +1429,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // two values in the list are the basic block identifiers. auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - SILType FnTy = getSILType(Ty, SILValueCategory::Object, nullptr); + SILType FnTy = getSILType(Ty, SILValueCategory::Object, Fn); SILType SubstFnTy = getSILType(Ty2, SILValueCategory::Object, Fn); SILBasicBlock *errorBB = getBBForReference(Fn, ListOfValues.back()); @@ -1455,7 +1455,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::PartialApplyInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - SILType FnTy = getSILType(Ty, SILValueCategory::Object, nullptr); + SILType FnTy = getSILType(Ty, SILValueCategory::Object, Fn); SILType closureTy = getSILType(Ty2, SILValueCategory::Object, Fn); SubstitutionMap Substitutions = MF->getSubstitutionMap(NumSubs); diff --git a/test/Serialization/Inputs/opaque_types_inlineable_2.swift b/test/Serialization/Inputs/opaque_types_inlineable_2.swift new file mode 100644 index 0000000000000..1a20580f22f57 --- /dev/null +++ b/test/Serialization/Inputs/opaque_types_inlineable_2.swift @@ -0,0 +1,18 @@ +public protocol P {} + +public struct M : P { + public init(t: T) {} +} +extension Int : P {} + +extension P { + @inlinable + public func o(_ t: T) -> some P { + return M(t: t) + } + + @inlinable + public func p() throws -> some P { + return Int() + } +} diff --git a/test/Serialization/opaque_types_inlineable.swift b/test/Serialization/opaque_types_inlineable.swift new file mode 100644 index 0000000000000..504d6b148273a --- /dev/null +++ b/test/Serialization/opaque_types_inlineable.swift @@ -0,0 +1,29 @@ +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -module-name A -emit-module %s %S/Inputs/opaque_types_inlineable_2.swift + +// This test case use to crash in the merge modules phase when the two partial +// modules are merged as one deserializing the module for this file now has +// access to opaque types in the other file (opaque_types_inlineable_2.swift). + +extension P { + @inlinable + public func r() -> some P { + return f { self.o(Q()) } + } + + @inlinable + public func q() throws -> some P { + return try p() + } +} + +public func f(_ fn: () -> T) -> some P { + return K() +} + +public struct K : P { + public init() {} +} + +public struct Q : P { + public init() {} +} From 419240f4c3e0a615fe333eeeabc60990574ca1b3 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Fri, 20 Dec 2019 10:47:12 -0800 Subject: [PATCH 175/478] Make path suitable for Windows --- lib/Driver/Compilation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 0051ff5f33cda..d8083a069a341 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -2049,6 +2049,7 @@ void Compilation::addDependencyPathOrCreateDummy( HaveAlreadyAddedDependencyPath = true; } else if (!depPath.empty()) { // Create dummy empty file - std::ofstream(depPath.str().c_str()); + std::error_code EC; + llvm::raw_fd_ostream(depPath, EC, llvm::sys::fs::F_None); } } From 3cda934329a76a36f78516184aeac02fd058906c Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Fri, 20 Dec 2019 19:26:50 +0000 Subject: [PATCH 176/478] [Typechecker] Fix duplicate diagnostics when using attributes (#28888) * [Typechecker] resolveAttributedType() should invalidate the type repr after emitting a diagnostic, to prevent duplicate diagnostics later * [Test] Update '@noescape' attribute diagnostics * [Typechecker] Add a special diagnose method to TypeResolver that accepts a TypeRepr and invalidates it * [Typechecker] Rename the special 'diagnose' method to 'diagnoseInvalid' for clarity --- lib/Sema/TypeCheckType.cpp | 93 +++++++++++++++++++------------- test/attr/attr_autoclosure.swift | 3 ++ test/attr/attr_escaping.swift | 3 ++ test/attr/attr_noescape.swift | 36 +++++-------- 4 files changed, 75 insertions(+), 60 deletions(-) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index aaa462cc2af5d..7f3563f4f6b0a 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1782,6 +1782,14 @@ namespace { return diags.diagnose(std::forward(Args)...); } + template + InFlightDiagnostic diagnoseInvalid(TypeRepr *repr, + ArgTypes &&... Args) const { + auto &diags = Context.Diags; + repr->setInvalid(); + return diags.diagnose(std::forward(Args)...); + } + Type resolveAttributedType(AttributedTypeRepr *repr, TypeResolutionOptions options); Type resolveAttributedType(TypeAttributes &attrs, TypeRepr *repr, @@ -2080,18 +2088,21 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Check for @thick. if (attrs.has(TAK_thick)) { - if (storedRepr) - diagnose(repr->getStartLoc(), diag::sil_metatype_multiple_reprs); - + if (storedRepr) { + diagnoseInvalid(repr, repr->getStartLoc(), + diag::sil_metatype_multiple_reprs); + } + storedRepr = MetatypeRepresentation::Thick; attrs.clearAttribute(TAK_thick); } // Check for @objc_metatype. if (attrs.has(TAK_objc_metatype)) { - if (storedRepr) - diagnose(repr->getStartLoc(), diag::sil_metatype_multiple_reprs); - + if (storedRepr) { + diagnoseInvalid(repr, repr->getStartLoc(), + diag::sil_metatype_multiple_reprs); + } storedRepr = MetatypeRepresentation::ObjC; attrs.clearAttribute(TAK_objc_metatype); } @@ -2119,8 +2130,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, auto checkUnsupportedAttr = [&](TypeAttrKind attr) { if (attrs.has(attr)) { - diagnose(attrs.getLoc(attr), diag::unknown_attribute, - TypeAttributes::getAttrName(attr)); + diagnoseInvalid(repr, attrs.getLoc(attr), diag::unknown_attribute, + TypeAttributes::getAttrName(attr)); attrs.clearAttribute(attr); } }; @@ -2170,8 +2181,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, auto calleeConvention = ParameterConvention::Direct_Unowned; if (attrs.has(TAK_callee_owned)) { if (attrs.has(TAK_callee_guaranteed)) { - diagnose(attrs.getLoc(TAK_callee_owned), - diag::sil_function_repeat_convention, /*callee*/ 2); + diagnoseInvalid(repr, attrs.getLoc(TAK_callee_owned), + diag::sil_function_repeat_convention, /*callee*/ 2); } calleeConvention = ParameterConvention::Direct_Owned; } else if (attrs.has(TAK_callee_guaranteed)) { @@ -2197,8 +2208,9 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, SILFunctionType::Representation::WitnessMethod) .Default(None); if (!parsedRep) { - diagnose(attrs.getLoc(TAK_convention), - diag::unsupported_sil_convention, attrs.getConventionName()); + diagnoseInvalid(repr, attrs.getLoc(TAK_convention), + diag::unsupported_sil_convention, + attrs.getConventionName()); rep = SILFunctionType::Representation::Thin; } else { rep = *parsedRep; @@ -2214,8 +2226,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, if (attrs.has(TAK_differentiable) && !Context.LangOpts.EnableExperimentalDifferentiableProgramming) { - diagnose(attrs.getLoc(TAK_differentiable), - diag::experimental_differentiable_programming_disabled); + diagnoseInvalid(repr, attrs.getLoc(TAK_differentiable), + diag::experimental_differentiable_programming_disabled); } DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable; @@ -2246,8 +2258,9 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, .Case("c", FunctionType::Representation::CFunctionPointer) .Default(None); if (!parsedRep) { - diagnose(attrs.getLoc(TAK_convention), diag::unsupported_convention, - attrs.getConventionName()); + diagnoseInvalid(repr, attrs.getLoc(TAK_convention), + diag::unsupported_convention, + attrs.getConventionName()); rep = FunctionType::Representation::Swift; } else { rep = *parsedRep; @@ -2256,8 +2269,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, if (attrs.has(TAK_differentiable) && !Context.LangOpts.EnableExperimentalDifferentiableProgramming) { - diagnose(attrs.getLoc(TAK_differentiable), - diag::experimental_differentiable_programming_disabled); + diagnoseInvalid(repr, attrs.getLoc(TAK_differentiable), + diag::experimental_differentiable_programming_disabled); } DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable; @@ -2279,21 +2292,21 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, if (attrs.hasConvention()) { if (attrs.getConventionName() == "c" || attrs.getConventionName() == "block") { - diagnose(attrs.getLoc(TAK_convention), - diag::invalid_autoclosure_and_convention_attributes, - attrs.getConventionName()); + diagnoseInvalid(repr, attrs.getLoc(TAK_convention), + diag::invalid_autoclosure_and_convention_attributes, + attrs.getConventionName()); attrs.clearAttribute(TAK_convention); didDiagnose = true; } } else if (options.is(TypeResolverContext::VariadicFunctionInput) && !options.hasBase(TypeResolverContext::EnumElementDecl)) { - diagnose(attrs.getLoc(TAK_autoclosure), - diag::attr_not_on_variadic_parameters, "@autoclosure"); + diagnoseInvalid(repr, attrs.getLoc(TAK_autoclosure), + diag::attr_not_on_variadic_parameters, "@autoclosure"); attrs.clearAttribute(TAK_autoclosure); didDiagnose = true; } else if (!options.is(TypeResolverContext::FunctionInput)) { - diagnose(attrs.getLoc(TAK_autoclosure), diag::attr_only_on_parameters, - "@autoclosure"); + diagnoseInvalid(repr, attrs.getLoc(TAK_autoclosure), + diag::attr_only_on_parameters, "@autoclosure"); attrs.clearAttribute(TAK_autoclosure); didDiagnose = true; } @@ -2328,12 +2341,13 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, auto loc = attrs.getLoc(TAK_escaping); auto attrRange = getTypeAttrRangeWithAt(Context, loc); - diagnose(loc, diag::escaping_non_function_parameter) - .fixItRemove(attrRange); + diagnoseInvalid(repr, loc, diag::escaping_non_function_parameter) + .fixItRemove(attrRange); // Try to find a helpful note based on how the type is being used if (options.is(TypeResolverContext::ImmediateOptionalTypeArgument)) { - diagnose(repr->getLoc(), diag::escaping_optional_type_argument); + diagnoseInvalid(repr, repr->getLoc(), + diag::escaping_optional_type_argument); } } @@ -2349,6 +2363,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // @autoclosure is going to be diagnosed when type of // the parameter is validated, because that attribute // applies to the declaration now. + repr->setInvalid(); attrs.clearAttribute(TAK_autoclosure); } @@ -2356,9 +2371,9 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, if (!attrs.has(i)) continue; - auto diag = diagnose(attrs.getLoc(i), - diag::attribute_requires_function_type, - TypeAttributes::getAttrName(i)); + auto diag = diagnoseInvalid(repr, attrs.getLoc(i), + diag::attribute_requires_function_type, + TypeAttributes::getAttrName(i)); // If we see @escaping among the attributes on this type, because it isn't // a function type, we'll remove it. @@ -2368,7 +2383,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Specialize the diagnostic for Optionals. if (ty->getOptionalObjectType()) { diag.flush(); - diagnose(repr->getLoc(), diag::escaping_optional_type_argument); + diagnoseInvalid(repr, repr->getLoc(), + diag::escaping_optional_type_argument); } } attrs.clearAttribute(i); @@ -2398,7 +2414,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // In SIL, handle @opened (n), which creates an existential archetype. if (attrs.has(TAK_opened)) { if (!ty->isExistentialType()) { - diagnose(attrs.getLoc(TAK_opened), diag::opened_non_protocol, ty); + diagnoseInvalid(repr, attrs.getLoc(TAK_opened), diag::opened_non_protocol, + ty); } else { ty = OpenedArchetypeType::get(ty, attrs.OpenedID); } @@ -2438,9 +2455,10 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, } for (unsigned i = 0; i != TypeAttrKind::TAK_Count; ++i) - if (attrs.has((TypeAttrKind)i)) - diagnose(attrs.getLoc((TypeAttrKind)i), - diag::attribute_does_not_apply_to_type); + if (attrs.has((TypeAttrKind)i)) { + diagnoseInvalid(repr, attrs.getLoc((TypeAttrKind)i), + diag::attribute_does_not_apply_to_type); + } return ty; } @@ -3080,8 +3098,7 @@ Type TypeResolver::resolveSpecifierTypeRepr(SpecifierTypeRepr *repr, default: llvm_unreachable("unknown SpecifierTypeRepr kind"); } - diagnose(repr->getSpecifierLoc(), diagID, name); - repr->setInvalid(); + diagnoseInvalid(repr, repr->getSpecifierLoc(), diagID, name); return ErrorType::get(Context); } diff --git a/test/attr/attr_autoclosure.swift b/test/attr/attr_autoclosure.swift index d2cc8b53fdaaa..a180473605531 100644 --- a/test/attr/attr_autoclosure.swift +++ b/test/attr/attr_autoclosure.swift @@ -292,3 +292,6 @@ func sr_11938_3(_ x: [@autoclosure String]) {} // expected-error {{'@autoclosure protocol SR_11938_P {} struct SR_11938_S : @autoclosure SR_11938_P {} // expected-error {{'@autoclosure' may only be used on parameters}} + +// SR-9178 +func bar(_ x: @autoclosure T) {} // expected-error 1{{@autoclosure attribute only applies to function types}} diff --git a/test/attr/attr_escaping.swift b/test/attr/attr_escaping.swift index d4caa72601b2a..16ce674ec24ed 100644 --- a/test/attr/attr_escaping.swift +++ b/test/attr/attr_escaping.swift @@ -226,3 +226,6 @@ extension SR_9760 { func fiz(_: T, _: @escaping F) {} // Ok func baz(_: @escaping G) {} // Ok } + +// SR-9178 +func foo(_ x: @escaping T) {} // expected-error 1{{@escaping attribute only applies to function types}} diff --git a/test/attr/attr_noescape.swift b/test/attr/attr_noescape.swift index 98b2992dd885a..1414a1463bb08 100644 --- a/test/attr/attr_noescape.swift +++ b/test/attr/attr_noescape.swift @@ -6,7 +6,7 @@ func conflictingAttrs(_ fn: @noescape @escaping () -> Int) {} // expected-error func doesEscape(_ fn : @escaping () -> Int) {} -func takesGenericClosure(_ a : Int, _ fn : @noescape () -> T) {} // expected-error 2{{unknown attribute 'noescape'}} +func takesGenericClosure(_ a : Int, _ fn : @noescape () -> T) {} // expected-error {{unknown attribute 'noescape'}} var globalAny: Any = 0 @@ -23,8 +23,8 @@ func takesVariadic(_ fns: () -> Int...) { doesEscape(fns[0]) // Okay - variadic-of-function parameters are escaping } -func takesNoEscapeClosure(_ fn : () -> Int) { // expected-note 2 {{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} - // expected-note@-1 5{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} +func takesNoEscapeClosure(_ fn : () -> Int) { // expected-note 1 {{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} + // expected-note@-1 6{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} takesNoEscapeClosure { 4 } // ok _ = fn() // ok @@ -195,10 +195,7 @@ func testAutoclosure(_ a : @autoclosure () -> Int) { // expected-note{{parameter // QoI: @autoclosure implies @noescape, so you shouldn't be allowed to specify both -func redundant(_ fn : @noescape - @autoclosure () -> Int) { - // expected-error@-2{{unknown attribute 'noescape'}} -} +func redundant(_ fn : @noescape @autoclosure () -> Int) {} // expected-error {{unknown attribute 'noescape'}} protocol P1 { @@ -215,9 +212,7 @@ func overloadedEach(_ source: P, _ transform: @escaping (P.Element) -> struct S : P2 { typealias Element = Int func each(_ transform: @noescape (Int) -> ()) { // expected-error{{unknown attribute 'noescape'}} - // expected-note@-1 {{parameter 'transform' is implicitly non-escaping}} overloadedEach(self, transform, 1) - // expected-error@-1 {{passing non-escaping parameter 'transform' to function expecting an @escaping closure}} } } @@ -261,22 +256,22 @@ public func XCTAssert(_ expression: @autoclosure () -> Bool, _ message: String = /// SR-770 - Currying and `noescape`/`rethrows` don't work together anymore -func curriedFlatMap(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { // expected-error 2{{unknown attribute 'noescape'}} +func curriedFlatMap(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { // expected-error 1{{unknown attribute 'noescape'}} return { f in x.flatMap(f) } } -func curriedFlatMap2(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { // expected-error 2{{unknown attribute 'noescape'}} - return { (f : @noescape (A) -> [B]) in // expected-error{{unknown attribute 'noescape'}} +func curriedFlatMap2(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { // expected-error {{unknown attribute 'noescape'}} + return { (f : @noescape (A) -> [B]) in x.flatMap(f) } } func bad(_ a : @escaping (Int)-> Int) -> Int { return 42 } func escapeNoEscapeResult(_ x: [Int]) -> (@noescape (Int) -> Int) -> Int { // expected-error{{unknown attribute 'noescape'}} - return { f in // expected-note {{parameter 'f' is implicitly non-escaping}} - bad(f) // expected-error {{passing non-escaping parameter 'f' to function expecting an @escaping closure}} + return { f in + bad(f) } } @@ -296,16 +291,13 @@ typealias CompletionHandler = (_ success: Bool) -> () var escape : CompletionHandlerNE var escapeOther : CompletionHandler func doThing1(_ completion: (_ success: Bool) -> ()) { - // expected-note@-1 {{parameter 'completion' is implicitly non-escaping}} - escape = completion // expected-error {{assigning non-escaping parameter 'completion' to an @escaping closure}} + escape = completion } func doThing2(_ completion: CompletionHandlerNE) { - // expected-note@-1 {{parameter 'completion' is implicitly non-escaping}} - escape = completion // expected-error {{assigning non-escaping parameter 'completion' to an @escaping closure}} + escape = completion } func doThing3(_ completion: CompletionHandler) { - // expected-note@-1 {{parameter 'completion' is implicitly non-escaping}} - escape = completion // expected-error {{assigning non-escaping parameter 'completion' to an @escaping closure}} + escape = completion } func doThing4(_ completion: @escaping CompletionHandler) { escapeOther = completion @@ -313,7 +305,7 @@ func doThing4(_ completion: @escaping CompletionHandler) { // @noescape doesn't work on parameters of function type func apply(_ f: @noescape (T) -> U, g: @noescape (@noescape (T) -> U) -> U) -> U { - // expected-error@-1 6{{unknown attribute 'noescape'}} + // expected-error@-1 2{{unknown attribute 'noescape'}} return g(f) } @@ -323,7 +315,7 @@ enum r19997577Type { case Function(() -> r19997577Type, () -> r19997577Type) case Sum(() -> r19997577Type, () -> r19997577Type) - func reduce(_ initial: Result, _ combine: @noescape (Result, r19997577Type) -> Result) -> Result { // expected-error 2{{unknown attribute 'noescape'}} + func reduce(_ initial: Result, _ combine: @noescape (Result, r19997577Type) -> Result) -> Result { // expected-error 1{{unknown attribute 'noescape'}} let binary: @noescape (r19997577Type, r19997577Type) -> Result = { combine(combine(combine(initial, self), $0), $1) } // expected-error{{unknown attribute 'noescape'}} switch self { case .Unit: From fb30e9ec74145d033f918eeeb2240b12d3703ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez=20Troiti=C3=B1o?= Date: Fri, 20 Dec 2019 12:10:31 -0800 Subject: [PATCH 177/478] [MSVC] Evaluate parameters in the right order. Parameter evaluation order is unspecified, and MSVC happens to do it in the opposite order than Clang. Move the evaluation of the parameters outside the invocation, so the order is clear, and the token consuming happens in the right order. --- lib/Parse/ParseExpr.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 5963e405678a8..821b26e8cf999 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2483,8 +2483,9 @@ parseClosureSignatureIfPresent(SourceRange &bracketRange, SyntaxParsingContext CaptureCtx(SyntaxContext, SyntaxKind::ClosureCaptureSignature); - bracketRange = SourceRange(consumeToken(tok::l_square), - consumeToken(tok::r_square)); + SourceLoc lBracketLoc = consumeToken(tok::l_square); + SourceLoc rBracketLoc = consumeToken(tok::r_square); + bracketRange = SourceRange(lBracketLoc, rBracketLoc); } else if (Tok.is(tok::l_square) && !peekToken().is(tok::r_square)) { SyntaxParsingContext CaptureCtx(SyntaxContext, SyntaxKind::ClosureCaptureSignature); From 539d8dac8f817d6896c9dc6a5567655bef8aed6b Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 20 Dec 2019 12:17:17 -0800 Subject: [PATCH 178/478] Remove re-entrancy assertion This was just to confirm that the re-entrancy problem still exists on the bots. There's something OS-dependent (probably the SDKs) that causes this to only reproduce on 10.14.5 and not 10.15. rdar://58116531 --- lib/AST/NameLookup.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 193f6c5323f5d..861ed22541bbc 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1135,8 +1135,6 @@ populateLookupTableEntryFromLazyIDCLoader(ASTContext &ctx, MemberLookupTable &LookupTable, DeclBaseName name, IterableDeclContext *IDC) { - assert(!IDC->isLoadingLazyMembers() && - "Re-entrant member loading is not supported!"); IDC->setLoadingLazyMembers(true); auto ci = ctx.getOrCreateLazyIterableContextData(IDC, /*lazyLoader=*/nullptr); From 621cd3ea5c3a0df24ba480f5af52539a36f82680 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 20 Dec 2019 13:44:56 -0800 Subject: [PATCH 179/478] Revert "Driver: mark test as XFAIL on windows" --- test/Driver/batch_mode_with_supplementary_filelist.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/Driver/batch_mode_with_supplementary_filelist.swift b/test/Driver/batch_mode_with_supplementary_filelist.swift index 4bb87a4afef35..898942b105c99 100644 --- a/test/Driver/batch_mode_with_supplementary_filelist.swift +++ b/test/Driver/batch_mode_with_supplementary_filelist.swift @@ -7,5 +7,3 @@ // RUN: %swiftc_driver -enable-batch-mode -driver-filelist-threshold=0 -j2 %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift -o %t/file-01.o -o %t/file-02.o -o %t/file-03.o -### | %FileCheck %s -check-prefix=CHECK-SUPPLEMENTARY-OUTPUT-FILELIST // // CHECK-SUPPLEMENTARY-OUTPUT-FILELIST: -supplementary-output-file-map {{.*(/|\\)}}supplementaryOutputs- -// -// XFAIL: OS=windows-msvc From 3994a1dcedff0977387c602a0fa72b3fd7475ead Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 20 Dec 2019 13:52:35 -0800 Subject: [PATCH 180/478] [TypeChecker] NFC: Extract array/set element cast checking into a closure --- lib/Sema/TypeCheckConstraints.cpp | 61 +++++++++++++------------------ 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 306adc0edb490..e18a771568fe9 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -4075,27 +4075,32 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, } } - // Check for casts between specific concrete types that cannot succeed. - if (auto toElementType = ConstraintSystem::isArrayType(toType)) { - if (auto fromElementType = ConstraintSystem::isArrayType(fromType)) { - switch (typeCheckCheckedCast(*fromElementType, *toElementType, - CheckedCastContextKind::None, dc, - SourceLoc(), nullptr, SourceRange())) { - case CheckedCastKind::Coercion: - return CheckedCastKind::Coercion; + auto checkElementCast = [&](Type fromElt, Type toElt, + CheckedCastKind castKind) -> CheckedCastKind { + switch (typeCheckCheckedCast(fromElt, toElt, CheckedCastContextKind::None, + dc, SourceLoc(), nullptr, SourceRange())) { + case CheckedCastKind::Coercion: + return CheckedCastKind::Coercion; - case CheckedCastKind::BridgingCoercion: - return CheckedCastKind::BridgingCoercion; + case CheckedCastKind::BridgingCoercion: + return CheckedCastKind::BridgingCoercion; - case CheckedCastKind::ArrayDowncast: - case CheckedCastKind::DictionaryDowncast: - case CheckedCastKind::SetDowncast: - case CheckedCastKind::ValueCast: - return CheckedCastKind::ArrayDowncast; + case CheckedCastKind::ArrayDowncast: + case CheckedCastKind::DictionaryDowncast: + case CheckedCastKind::SetDowncast: + case CheckedCastKind::ValueCast: + return castKind; - case CheckedCastKind::Unresolved: - return failed(); - } + case CheckedCastKind::Unresolved: + return failed(); + } + }; + + // Check for casts between specific concrete types that cannot succeed. + if (auto toElementType = ConstraintSystem::isArrayType(toType)) { + if (auto fromElementType = ConstraintSystem::isArrayType(fromType)) { + return checkElementCast(*fromElementType, *toElementType, + CheckedCastKind::ArrayDowncast); } } @@ -4165,24 +4170,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, if (auto toElementType = ConstraintSystem::isSetType(toType)) { if (auto fromElementType = ConstraintSystem::isSetType(fromType)) { - switch (typeCheckCheckedCast(*fromElementType, *toElementType, - CheckedCastContextKind::None, dc, - SourceLoc(), nullptr, SourceRange())) { - case CheckedCastKind::Coercion: - return CheckedCastKind::Coercion; - - case CheckedCastKind::BridgingCoercion: - return CheckedCastKind::BridgingCoercion; - - case CheckedCastKind::ArrayDowncast: - case CheckedCastKind::DictionaryDowncast: - case CheckedCastKind::SetDowncast: - case CheckedCastKind::ValueCast: - return CheckedCastKind::SetDowncast; - - case CheckedCastKind::Unresolved: - return failed(); - } + return checkElementCast(*fromElementType, *toElementType, + CheckedCastKind::SetDowncast); } } From a4cf567ac42e3950f27b386bba1ec789d2953ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Fri, 20 Dec 2019 13:56:54 -0800 Subject: [PATCH 181/478] [Serialization] Recover from deserializing an enum from a missing module rdar://problem/58022345 --- lib/Serialization/Deserialization.cpp | 19 +++++++++++++++++-- lib/Serialization/ModuleFile.h | 5 +++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 31646a5219c2a..86598392e96c7 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -1889,14 +1889,25 @@ DeclContext *ModuleFile::getLocalDeclContext(LocalDeclContextID DCID) { } DeclContext *ModuleFile::getDeclContext(DeclContextID DCID) { + auto deserialized = getDeclContextChecked(DCID); + if (!deserialized) { + fatal(deserialized.takeError()); + } + return deserialized.get(); +} + +Expected ModuleFile::getDeclContextChecked(DeclContextID DCID) { if (!DCID) return FileContext; if (Optional contextID = DCID.getAsLocalDeclContextID()) return getLocalDeclContext(contextID.getValue()); - auto D = getDecl(DCID.getAsDeclID().getValue()); + auto deserialized = getDeclChecked(DCID.getAsDeclID().getValue()); + if (!deserialized) + return deserialized.takeError(); + auto D = deserialized.get(); if (auto GTD = dyn_cast(D)) return GTD; if (auto ED = dyn_cast(D)) @@ -3496,7 +3507,6 @@ class swift::DeclDeserializer { numConformances, numInherited, rawInheritedAndDependencyIDs); - auto DC = MF.getDeclContext(contextID); if (declOrOffset.isComplete()) return declOrOffset; @@ -3510,6 +3520,11 @@ class swift::DeclDeserializer { } } + auto DCOrError = MF.getDeclContextChecked(contextID); + if (!DCOrError) + return DCOrError.takeError(); + auto DC = DCOrError.get(); + auto genericParams = MF.maybeReadGenericParams(DC); if (declOrOffset.isComplete()) return declOrOffset; diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index bc1110599077a..2ae1a88a3da06 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -913,6 +913,11 @@ class ModuleFile /// Returns the decl context with the given ID, deserializing it if needed. DeclContext *getDeclContext(serialization::DeclContextID DID); + /// Returns the decl context with the given ID, deserializing it if needed, + /// or the first error. + llvm::Expected + getDeclContextChecked(serialization::DeclContextID DCID); + /// Returns the local decl context with the given ID, deserializing it if needed. DeclContext *getLocalDeclContext(serialization::LocalDeclContextID DID); From d7b12a6de7ba24a11f0b303e224ad8b7cdbd4e8d Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 20 Dec 2019 14:02:29 -0800 Subject: [PATCH 182/478] [TypeChecker] Treat tuples specially while validating checked casts Based on the checked cast behavior don't warn about unrelated casts between tuples unless their sizes or labels differ. Resolves: rdar://problem/56436235 --- lib/Sema/TypeCheckConstraints.cpp | 23 +++++++++++++++++++++++ test/Constraints/casts.swift | 16 ++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index e18a771568fe9..16e6f06f1de0e 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -4175,6 +4175,29 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, } } + if (auto toTuple = toType->getAs()) { + if (auto fromTuple = fromType->getAs()) { + if (fromTuple->getNumElements() != toTuple->getNumElements()) + return failed(); + + for (unsigned i = 0, n = toTuple->getNumElements(); i != n; ++i) { + const auto &fromElt = fromTuple->getElement(i); + const auto &toElt = toTuple->getElement(i); + + if (fromElt.getName() != toElt.getName()) + return failed(); + + auto result = checkElementCast(fromElt.getType(), toElt.getType(), + CheckedCastKind::ValueCast); + + if (result == CheckedCastKind::Unresolved) + return result; + } + + return CheckedCastKind::ValueCast; + } + } + assert(!toType->isAny() && "casts to 'Any' should've been handled above"); assert(!toType->isAnyObject() && "casts to 'AnyObject' should've been handled above"); diff --git a/test/Constraints/casts.swift b/test/Constraints/casts.swift index b1e02cf5ff935..3747e66dec5c5 100644 --- a/test/Constraints/casts.swift +++ b/test/Constraints/casts.swift @@ -222,3 +222,19 @@ func compare(_: T, _: T) {} // expected-note {{'compare' declared here}} func compare(_: T?, _: T?) {} _ = nil? as? Int?? // expected-error {{nil literal cannot be the source of a conditional cast}} + +func test_tuple_casts_no_warn() { + struct Foo {} + + let arr: [(Any, Any)] = [(Foo(), Foo())] + let tup: (Any, Any) = (Foo(), Foo()) + + _ = arr as! [(Foo, Foo)] // Ok + _ = tup as! (Foo, Foo) // Ok + + _ = arr as! [(Foo, Foo, Foo)] // expected-warning {{cast from '[(Any, Any)]' to unrelated type '[(Foo, Foo, Foo)]' always fails}} + _ = tup as! (Foo, Foo, Foo) // expected-warning {{cast from '(Any, Any)' to unrelated type '(Foo, Foo, Foo)' always fails}} + + _ = arr as! [(a: Foo, Foo)] // expected-warning {{cast from '[(Any, Any)]' to unrelated type '[(a: Foo, Foo)]' always fails}} + _ = tup as! (a: Foo, Foo) // expected-warning {{cast from '(Any, Any)' to unrelated type '(a: Foo, Foo)' always fails}} +} From 5ff9586102e75588b44e64dea3f5b4323a478c76 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 20 Dec 2019 15:27:49 -0800 Subject: [PATCH 183/478] [NFC] Remove MemberLookupTable::clear() The incremental name lookup cache no longer needs to chuck out the old tables. --- lib/AST/NameLookup.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 861ed22541bbc..e6fdfcda79d47 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -926,21 +926,6 @@ class swift::MemberLookupTable { dump(llvm::errs()); } - // Mark all Decls in this table as not-resident in a table, drop - // references to them. Should only be called when this was not fully-populated - // from an IterableDeclContext. - void clear() { - // LastExtensionIncluded would only be non-null if this was populated from - // an IterableDeclContext (though it might still be null in that case). - assert(LastExtensionIncluded == nullptr); - for (auto const &i : Lookup) { - for (auto d : i.getSecond()) { - d->setAlreadyInLookupTable(false); - } - } - Lookup.clear(); - } - // Only allow allocation of member lookup tables using the allocator in // ASTContext or by doing a placement new. void *operator new(size_t Bytes, ASTContext &C, From 47a2bf3fdc3d0378e7ec2d788a1481207d28cae8 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 20 Dec 2019 15:36:06 -0800 Subject: [PATCH 184/478] [NFC] One-Shot Name Lookup Simplify lookupDirect to attempt one-shot name lookup based on some ideas Slava had. This means we'll try to perform a cache fill up front, then access the table rather than assuming the table is always (relatively) up to date and filling when we miss the first cache access. This avoids a walk over the deserialized members of an extension that fails named lazy member loading. Instead, we eagerly page the members of the extension into the table and remove it from consideration for lazy member loading entirely. In the future, we can convince the Clang Importer to avoid falling off the lazy member loading happy path. --- include/swift/AST/Decl.h | 4 ++ lib/AST/Decl.cpp | 4 ++ lib/AST/NameLookup.cpp | 111 ++++++++++++++++----------------------- 3 files changed, 54 insertions(+), 65 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 46faf5bab401e..951ab02e2c1b0 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3300,6 +3300,10 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// so that it can also be added to the lookup table (if needed). void addedMember(Decl *member); + /// Note that we have added an extension into the nominal type, + /// so that its members can eventually be added to the lookup table. + void addedExtension(ExtensionDecl *ext); + /// A lookup table used to find the protocol conformances of /// a given nominal type. mutable ConformanceLookupTable *ConformanceTable = nullptr; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index f99f7964eccba..638a64c721214 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3664,12 +3664,16 @@ void NominalTypeDecl::addExtension(ExtensionDecl *extension) { if (!FirstExtension) { FirstExtension = extension; LastExtension = extension; + + addedExtension(extension); return; } // Add to the end of the list. LastExtension->NextExtension.setPointer(extension); LastExtension = extension; + + addedExtension(extension); } ArrayRef NominalTypeDecl::getStoredProperties() const { diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index e6fdfcda79d47..4d5906f63d1d5 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1028,25 +1028,25 @@ void MemberLookupTable::updateLookupTable(NominalTypeDecl *nominal) { } } -void NominalTypeDecl::addedMember(Decl *member) { - auto *vd = dyn_cast(member); - if (!vd) - return; +void NominalTypeDecl::addedExtension(ExtensionDecl *ext) { + if (!LookupTable) return; + if (ext->hasLazyMembers()) { + LookupTable->addMembers(ext->getCurrentMembersWithoutLoading()); + } else { + LookupTable->addMembers(ext->getMembers()); + } +} + +void NominalTypeDecl::addedMember(Decl *member) { // If we have a lookup table, add the new member to it. If not, we'll pick up // this member when we first create the table. - if (auto *lookup = LookupTable) { - if (hasLazyMembers()) { - // If we have lazy members, only add the new member to the lookup - // table if we already have another member with the same name. - // The presence of a lookup table entry indicates that the - // nominal as well as all extensions have already been searched. - if (lookup->find(vd->getBaseName()) == lookup->end()) - return; - } + auto *vd = dyn_cast(member); + auto *lookup = LookupTable; + if (!vd || !lookup) + return; - lookup->addMember(vd); - } + lookup->addMember(vd); } void ExtensionDecl::addedMember(Decl *member) { @@ -1079,8 +1079,8 @@ void ExtensionDecl::addedMember(Decl *member) { // │ExtensionDecl *LastExtension ─┼───────┐│ │ └───┐ // │ │ ││ └──────────────────────┐│ // │MemberLookupTable *LookupTable├─┐ ││ ││ -// │bool LookupTableComplete │ │ ││ ┌─────────────────┐ ││ -// └──────────────────────────────┘ │ ││ │ExtensionDecl │ ││ +// └──────────────────────────────┘ │ ││ ┌─────────────────┐ ││ +// │ ││ │ExtensionDecl │ ││ // │ ││ │------------- │ ││ // ┌─────────────┘ │└────▶│ExtensionDecl │ ││ // │ │ │ *NextExtension ├──┐ ││ @@ -1141,34 +1141,27 @@ populateLookupTableEntryFromLazyIDCLoader(ASTContext &ctx, } } -static void populateLookupTableEntryFromCurrentMembers( - ASTContext &ctx, MemberLookupTable &LookupTable, DeclBaseName name, - IterableDeclContext *IDC) { - for (auto m : IDC->getMembers()) { - if (auto v = dyn_cast(m)) { - if (v->getBaseName() == name) { - LookupTable.addMember(m); - } - } - } -} - static void populateLookupTableEntryFromExtensions(ASTContext &ctx, MemberLookupTable &table, NominalTypeDecl *nominal, DeclBaseName name) { for (auto e : nominal->getExtensions()) { - // If we can retrieve the members of this extension without deserializing - // anything, do so now. - if (!e->wasDeserialized() && !e->hasClangNode()) { - populateLookupTableEntryFromCurrentMembers(ctx, table, name, e); + // If there's no lazy members to look at, all the members of this extension + // are present in the lookup table. + if (!e->hasLazyMembers()) { continue; } + assert(e->wasDeserialized() || e->hasClangNode() && + "Extension without deserializable content has lazy members!"); assert(!e->hasUnparsedMembers()); + + // Try lazy loading. If that fails, then we fall back by loading the + // entire extension. FIXME: It's rather unfortunate that we fall off the + // happy path because the Clang Importer can't handle lazy import-as-member. if (populateLookupTableEntryFromLazyIDCLoader(ctx, table, name, e)) { - populateLookupTableEntryFromCurrentMembers(ctx, table, name, e); + e->loadAllMembers(); } } } @@ -1189,19 +1182,18 @@ void NominalTypeDecl::prepareLookupTable() { // Lazy members: if the table needs population, populate the table _only // from those members already in the IDC member list_ such as implicits or - // globals-as-members, then update table entries from the extensions that - // have the same names as any such initial-population members. + // globals-as-members. LookupTable->addMembers(getCurrentMembersWithoutLoading()); - - llvm::SmallSet baseNamesPresent; - for (auto entry : *LookupTable) { - auto baseName = entry.getFirst().getBaseName(); - if (!baseNamesPresent.insert(baseName).second) + for (auto e : getExtensions()) { + // If we can lazy-load this extension, only take the members we've loaded + // so far. + if (e->wasDeserialized() || e->hasClangNode()) { + LookupTable->addMembers(e->getCurrentMembersWithoutLoading()); continue; + } - populateLookupTableEntryFromExtensions(getASTContext(), - *LookupTable, - this, baseName); + // Else, load all the members into the table. + LookupTable->addMembers(e->getMembers()); } } else { LookupTable->addMembers(getMembers()); @@ -1261,8 +1253,9 @@ NominalTypeDecl::lookupDirect(DeclName name, DeclName name) -> Optional> { // Look for a declaration with this name. auto known = table->find(name); - if (known == table->end()) + if (known == table->end()) { return None; + } // We found something; return it. return maybeFilterOutAttrImplements(known->second, name, @@ -1282,29 +1275,17 @@ NominalTypeDecl::lookupDirect(DeclName name, if (!useNamedLazyMemberLoading) { updateLookupTable(LookupTable); - } - - // Look for a declaration with this name. - if (auto lookup = tryCacheLookup(LookupTable, name)) - return lookup.getValue(); - - if (!useNamedLazyMemberLoading) { - return { }; - } - - // If we get here, we had a cache-miss and _are_ using - // NamedLazyMemberLoading. Try to populate a _single_ entry in the - // MemberLookupTable from both this nominal and all of its extensions, and - // retry. - auto &Table = *LookupTable; - if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table, - name.getBaseName(), this)) { - updateLookupTable(LookupTable); } else { - populateLookupTableEntryFromExtensions(ctx, Table, this, - name.getBaseName()); + if (populateLookupTableEntryFromLazyIDCLoader(ctx, *LookupTable, + name.getBaseName(), this)) { + updateLookupTable(LookupTable); + } else { + populateLookupTableEntryFromExtensions(ctx, *LookupTable, this, + name.getBaseName()); + } } + // Look for a declaration with this name. return tryCacheLookup(LookupTable, name) .getValueOr(TinyPtrVector()); } From 1a9f7afdecd723db9036c701c87832850c0a51a0 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Fri, 20 Dec 2019 23:34:35 -0800 Subject: [PATCH 185/478] =?UTF-8?q?[Foundation]=20Fix=20Data.count?= =?UTF-8?q?=E2=80=99s=20setter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Data provides a settable `count` property. Its expected behavior is undocumented, but based on the implementation, it is intended to zero-extend (or truncate) the collection to the specified length. This does not work correctly if we start with an empty Data and we try to increase the count by a small integer: ``` import Foundation var d = Data() d.count = 2 print(d.count) // ⟹ 0 ⁉️ d.count = 100 print(d.count) // ⟹ 100 ✓ ``` It looks like this bug was introduced with the Data overhaul that shipped in Swift 5. (This issue was uncovered by https://github.com/apple/swift/pull/28918.) rdar://58134026 --- stdlib/public/Darwin/Foundation/Data.swift | 2 +- test/stdlib/TestData.swift | 50 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/stdlib/public/Darwin/Foundation/Data.swift b/stdlib/public/Darwin/Foundation/Data.swift index 031c714a93f11..931ed4408781f 100644 --- a/stdlib/public/Darwin/Foundation/Data.swift +++ b/stdlib/public/Darwin/Foundation/Data.swift @@ -1402,7 +1402,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl if newValue == 0 { return nil } else if InlineData.canStore(count: newValue) { - return .inline(InlineData()) + return .inline(InlineData(count: newValue)) } else if InlineSlice.canStore(count: newValue) { return .slice(InlineSlice(count: newValue)) } else { diff --git a/test/stdlib/TestData.swift b/test/stdlib/TestData.swift index cb7d36b8fccb1..7608e9b8cace2 100644 --- a/test/stdlib/TestData.swift +++ b/test/stdlib/TestData.swift @@ -3836,6 +3836,53 @@ class TestData : TestDataSuper { } } } + + func test_increaseCount() { + guard #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) else { return } + let initials: [Range] = [ + 0..<0, + 0..<2, + 0..<4, + 0..<8, + 0..<16, + 0..<32, + 0..<64 + ] + let diffs = [0, 1, 2, 4, 8, 16, 32] + for initial in initials { + for diff in diffs { + var data = Data(initial) + data.count += diff + expectEqualSequence( + Array(initial) + Array(repeating: 0, count: diff), + data) + } + } + } + + func test_decreaseCount() { + guard #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) else { return } + let initials: [Range] = [ + 0..<0, + 0..<2, + 0..<4, + 0..<8, + 0..<16, + 0..<32, + 0..<64 + ] + let diffs = [0, 1, 2, 4, 8, 16, 32] + for initial in initials { + for diff in diffs { + guard initial.count >= diff else { continue } + var data = Data(initial) + data.count -= diff + expectEqualSequence( + initial.dropLast(diff), + data) + } + } + } } #if !FOUNDATION_XCTEST @@ -4159,6 +4206,9 @@ if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { DataTests.test("test_nsdataSequence") { TestData().test_nsdataSequence() } DataTests.test("test_dispatchSequence") { TestData().test_dispatchSequence() } } +DataTests.test("test_increaseCount") { TestData().test_increaseCount() } +DataTests.test("test_decreaseCount") { TestData().test_decreaseCount() } + // XCTest does not have a crash detection, whereas lit does DataTests.test("bounding failure subdata") { From 786c29a1397284dd9debcc63cb98b515c0e06510 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Fri, 20 Dec 2019 19:35:59 -0800 Subject: [PATCH 186/478] Fix a crash in StackPromotion; case not handled in EscapeAnalysis. StackPromotion queries EscapeAnalysis. The escape information for the result of an array semantic was missing because it was an ill-formed call. This fix introduces a new check to determine whether a call is well formed so it can be handled consistently everywhere. Fixes Interpreter/SDK/objc_fast_enumeration.swift failed on iphonesimulator-i386 The bug was introduced here: commit 8b926af49a2aad883cb358812aa67137e8199222 Author: Andrew Trick Date: Mon Dec 16 16:04:09 2019 -0800 EscapeAnalysis: Add PointerKind and interior/reference flags The bug can occur when a semantic "array.ininitialized" call (Array.adoptStorage) at the SIL level has direct "release_value" instructions that don't use the tuple_extract values corresponding to the call's returned value. This is part of the tragedy of not supporting multiple call results. When users of the call's result can use either the entire tuple (which is a meaningless value), or the individual tuple extracts, then there's no way to handle calls uniformly. The change that broke this was to remove the special handling of TupleExtract instructions. That special handling was identical to the way that any normal pointer projection is handled. So, as a simplification, the new code just expects that case to be handled naturally as a pointer projection. This case was already guarded by a check to determine whether the TupleExtract was actually the result of an array.uninitialized call, in which case the normal handling does not apply. However, because of the weirdness mentioned above, the handling of "array.ininitialized" may silently bail out. When that happens, the TupleExtract gets neither the special handling, nor the normal handling. Note that the old special handling of TupleExtract was bizarre and relied on corresponding weirdness in the code that handles "array.uninitialized". The new code simply creates a separate graph node for each result of the call, and creates the edges that exactly correspond to load and stores on those results. So, rather than reinstating the previous code, which I can't quite reason about, this fix creates a single check to determine whether a TupleExtract is treated as an "array.uninitialized" result or not, and use that check in both places. --- .../SILOptimizer/Analysis/EscapeAnalysis.h | 30 +++- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 136 ++++++++++-------- test/SILOptimizer/escape_analysis.sil | 44 ++++++ 3 files changed, 144 insertions(+), 66 deletions(-) diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index d6ee88c0a36a8..0899435b279a2 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -1009,12 +1009,12 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// If EscapeAnalysis should consider the given value to be a derived address /// or pointer based on one of its address or pointer operands, then return /// that operand value. Otherwise, return an invalid value. - SILValue getPointerBase(SILValue value) const; + SILValue getPointerBase(SILValue value); /// Recursively find the given value's pointer base. If the value cannot be /// represented in EscapeAnalysis as one of its operands, then return the same /// value. - SILValue getPointerRoot(SILValue value) const; + SILValue getPointerRoot(SILValue value); PointerKind findRecursivePointerKind(SILType Ty, const SILFunction &F) const; @@ -1055,7 +1055,31 @@ class EscapeAnalysis : public BottomUpIPAnalysis { void buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth); - bool createArrayUninitializedSubgraph(FullApplySite apply, + // @_semantics("array.uninitialized") takes a reference to the storage and + // returns an instantiated array struct and unsafe pointer to the elements. + struct ArrayUninitCall { + SILValue arrayStorageRef; + TupleExtractInst *arrayStruct = nullptr; + TupleExtractInst *arrayElementPtr = nullptr; + + bool isValid() const { + return arrayStorageRef && arrayStruct && arrayElementPtr; + } + }; + + /// If \p ai is an optimizable @_semantics("array.uninitialized") call, return + /// valid call information. + ArrayUninitCall canOptimizeArrayUninitializedCall(ApplyInst *ai, + ConnectionGraph *conGraph); + + /// Return true of this tuple_extract is the result of an optimizable + /// @_semantics("array.uninitialized") call. + bool canOptimizeArrayUninitializedResult(TupleExtractInst *tei); + + /// Handle a call to "@_semantics(array.uninitialized") precisely by mapping + /// each call result to a separate graph node and relating them to the + /// argument. + void createArrayUninitializedSubgraph(ArrayUninitCall call, ConnectionGraph *conGraph); /// Updates the graph by analyzing instruction \p I. diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index bfa437bf439a9..c6c122164e997 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -110,18 +110,10 @@ EscapeAnalysis::findCachedPointerKind(SILType Ty, const SILFunction &F) const { return pointerKind; } -static bool isExtractOfArrayUninitializedPointer(TupleExtractInst *TEI) { - if (auto apply = dyn_cast(TEI->getOperand())) - if (ArraySemanticsCall(apply, "array.uninitialized", false)) - return true; - - return false; -} - // If EscapeAnalysis should consider the given value to be a derived address or // pointer based on one of its address or pointer operands, then return that // operand value. Otherwise, return an invalid value. -SILValue EscapeAnalysis::getPointerBase(SILValue value) const { +SILValue EscapeAnalysis::getPointerBase(SILValue value) { switch (value->getKind()) { case ValueKind::IndexAddrInst: case ValueKind::IndexRawPointerInst: @@ -156,10 +148,8 @@ SILValue EscapeAnalysis::getPointerBase(SILValue value) const { case ValueKind::TupleExtractInst: { auto *TEI = cast(value); // Special handling for extracting the pointer-result from an - // array construction. We handle this like a ref_element_addr - // rather than a projection. See the handling of tuple_extract - // in analyzeInstruction(). - if (isExtractOfArrayUninitializedPointer(TEI)) + // array construction. See createArrayUninitializedSubgraph. + if (canOptimizeArrayUninitializedResult(TEI)) return SILValue(); return TEI->getOperand(); } @@ -188,7 +178,7 @@ SILValue EscapeAnalysis::getPointerBase(SILValue value) const { // Recursively find the given value's pointer base. If the value cannot be // represented in EscapeAnalysis as one of its operands, then return the same // value. -SILValue EscapeAnalysis::getPointerRoot(SILValue value) const { +SILValue EscapeAnalysis::getPointerRoot(SILValue value) { while (true) { if (SILValue v2 = getPointerBase(value)) value = v2; @@ -1721,28 +1711,6 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, << FInfo->Graph.F->getName() << '\n'); } -/// Returns the tuple extract for the first two fields if all uses of \p I are -/// tuple_extract instructions. -static std::pair -onlyUsedInTupleExtract(SILValue V) { - TupleExtractInst *field0 = nullptr; - TupleExtractInst *field1 = nullptr; - for (Operand *Use : getNonDebugUses(V)) { - if (auto *TEI = dyn_cast(Use->getUser())) { - if (TEI->getFieldNo() == 0) { - field0 = TEI; - continue; - } - if (TEI->getFieldNo() == 1) { - field1 = TEI; - continue; - } - } - return std::make_pair(nullptr, nullptr); - } - return std::make_pair(field0, field1); -} - bool EscapeAnalysis::buildConnectionGraphForCallees( SILInstruction *Caller, CalleeList Callees, FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth) { @@ -1799,38 +1767,79 @@ bool EscapeAnalysis::buildConnectionGraphForDestructor( RecursionDepth); } -// Handle array.uninitialized -bool EscapeAnalysis::createArrayUninitializedSubgraph( - FullApplySite apply, ConnectionGraph *conGraph) { +EscapeAnalysis::ArrayUninitCall +EscapeAnalysis::canOptimizeArrayUninitializedCall(ApplyInst *ai, + ConnectionGraph *conGraph) { + ArrayUninitCall call; + // This must be an exact match so we don't accidentally optimize + // "array.uninitialized_intrinsic". + if (!ArraySemanticsCall(ai, "array.uninitialized", false)) + return call; // Check if the result is used in the usual way: extracting the // array and the element pointer with tuple_extract. - TupleExtractInst *arrayStruct; - TupleExtractInst *arrayElementPtr; - std::tie(arrayStruct, arrayElementPtr) = - onlyUsedInTupleExtract(cast(apply.getInstruction())); - if (!arrayStruct || !arrayElementPtr) + for (Operand *use : getNonDebugUses(ai)) { + if (auto *tei = dyn_cast(use->getUser())) { + if (tei->getFieldNo() == 0) { + call.arrayStruct = tei; + continue; + } + if (tei->getFieldNo() == 1) { + call.arrayElementPtr = tei; + continue; + } + } + // If there are any other uses, such as a release_value, erase the previous + // call info and bail out. + call.arrayStruct = nullptr; + call.arrayElementPtr = nullptr; + break; + } + // An "array.uninitialized" call may have a first argument which is the + // allocated array buffer. Make sure the call's argument is recognized by + // EscapeAnalysis as a pointer, otherwise createArrayUninitializedSubgraph + // won't be able to map the result nodes onto it. There is a variant of + // @_semantics("array.uninitialized") that does not take the storage as input, + // so it will effectively bail out here. + if (isPointer(ai->getArgument(0))) + call.arrayStorageRef = ai->getArgument(0); + return call; +} + +bool EscapeAnalysis::canOptimizeArrayUninitializedResult( + TupleExtractInst *tei) { + ApplyInst *ai = dyn_cast(tei->getOperand()); + if (!ai) return false; - // array.uninitialized may have a first argument which is the - // allocated array buffer. The call is like a struct(buffer) - // instruction. - CGNode *arrayRefNode = conGraph->getNode(apply.getArgument(0)); - if (!arrayRefNode) - return false; + auto *conGraph = getConnectionGraph(ai->getFunction()); + return canOptimizeArrayUninitializedCall(ai, conGraph).isValid(); +} - CGNode *arrayStructNode = conGraph->getNode(arrayStruct); +// Handle @_semantics("array.uninitialized") +// +// This call is analagous to a 'struct(storageRef)' instruction--we want a defer +// edge from the returned Array struct to the storage Reference that it +// contains. +// +// The returned unsafe pointer is handled simply by mapping the pointer value +// onto the object node that the storage argument points to. +void EscapeAnalysis::createArrayUninitializedSubgraph( + ArrayUninitCall call, ConnectionGraph *conGraph) { + CGNode *arrayStructNode = conGraph->getNode(call.arrayStruct); assert(arrayStructNode && "Array struct must have a node"); - CGNode *arrayObjNode = conGraph->getValueContent(apply.getArgument(0)); + CGNode *arrayRefNode = conGraph->getNode(call.arrayStorageRef); + assert(arrayRefNode && "canOptimizeArrayUninitializedCall checks isPointer"); + // If the arrayRefNode != null then arrayObjNode must be valid. + CGNode *arrayObjNode = conGraph->getValueContent(call.arrayStorageRef); // The reference argument is effectively stored inside the returned - // array struct. + // array struct. This is like struct(arrayRefNode). conGraph->defer(arrayStructNode, arrayRefNode); // Map the returned element pointer to the array object's field pointer. - conGraph->setNode(arrayElementPtr, arrayObjNode); - return true; + conGraph->setNode(call.arrayElementPtr, arrayObjNode); } void EscapeAnalysis::analyzeInstruction(SILInstruction *I, @@ -1852,10 +1861,15 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case ArrayCallKind::kMakeMutable: // These array semantics calls do not capture anything. return; - case ArrayCallKind::kArrayUninitialized: - if (createArrayUninitializedSubgraph(FAS, ConGraph)) + case ArrayCallKind::kArrayUninitialized: { + ArrayUninitCall call = canOptimizeArrayUninitializedCall( + cast(FAS.getInstruction()), ConGraph); + if (call.isValid()) { + createArrayUninitializedSubgraph(call, ConGraph); return; + } break; + } case ArrayCallKind::kGetElement: if (CGNode *ArrayObjNode = ConGraph->getValueContent(ASC.getSelf())) { CGNode *LoadedElement = nullptr; @@ -2204,13 +2218,9 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::TupleExtractInst: { // This is a tuple_extract which extracts the second result of an // array.uninitialized call (otherwise getPointerBase should have already - // looked through it). The first result is the array itself. The second - // result (which is a pointer to the array elements) must be the content - // node of the first result. It's just like a ref_element_addr - // instruction. It is mapped to a node when processing - // array.uninitialized. + // looked through it). auto *TEI = cast(I); - assert(isExtractOfArrayUninitializedPointer(TEI) + assert(canOptimizeArrayUninitializedResult(TEI) && "tuple_extract should be handled as projection"); return; } diff --git a/test/SILOptimizer/escape_analysis.sil b/test/SILOptimizer/escape_analysis.sil index 1b248fbf25642..13e43a122f4db 100644 --- a/test/SILOptimizer/escape_analysis.sil +++ b/test/SILOptimizer/escape_analysis.sil @@ -1817,3 +1817,47 @@ bb0(%0 : $IntWrapper): %tuple = tuple (%bridge : $Builtin.BridgeObject, %ump : $UnsafeMutablePointer) return %tuple : $(Builtin.BridgeObject, UnsafeMutablePointer) } + +// ============================================================================= +// Test call to array.uninitialized that has extra release_value uses + +class DummyArrayStorage { + @_hasStorage var count: Int { get } + @_hasStorage var capacity: Int { get } + init() +} + +// init_any_array_with_buffer +sil [_semantics "array.uninitialized"] @init_any_array_with_buffer : $@convention(thin) (@owned DummyArrayStorage, Int32, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + +// CHECK-LABEL: CG of testBadArrayUninit +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: (%2.2) +// CHECK-NEXT: Con %2.2 Esc: G, Succ: +// CHECK-NEXT: Val %5 Esc: , Succ: (%5.1) +// CHECK-NEXT: Con %5.1 Esc: G, Succ: %10 +// CHECK-NEXT: Val [ref] %10 Esc: G, Succ: (%10.1) +// CHECK-NEXT: Con %10.1 Esc: G, Succ: +// CHECK-LABEL: End +sil hidden @testBadArrayUninit : $@convention(thin) (Builtin.Word, Int32) -> () { +bb0(%0 : $Builtin.Word, %1 : $Int32): + // create an array + %2 = alloc_ref [tail_elems $AnyObject * %0 : $Builtin.Word] $DummyArrayStorage + %3 = metatype $@thin Array.Type + %4 = function_ref @init_any_array_with_buffer : $@convention(thin) (@owned DummyArrayStorage, Int32, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + %5 = apply %4(%2, %1, %3) : $@convention(thin) (@owned DummyArrayStorage, Int32, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + %6 = tuple_extract %5 : $(Array, UnsafeMutablePointer), 0 + %7 = tuple_extract %5 : $(Array, UnsafeMutablePointer), 1 + %8 = struct_extract %7 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue + %9 = pointer_to_address %8 : $Builtin.RawPointer to [strict] $*AnyObject + + // store an elt + %10 = alloc_ref $C + %11 = init_existential_ref %10 : $C : $C, $AnyObject + store %11 to %9 : $*AnyObject + + // extra use of the call + release_value %5 : $(Array, UnsafeMutablePointer) // id: %228 + %13 = tuple () + return %13 : $() +} From 207c3450e2c1ff026adb88581d24c9edc256631d Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 21 Dec 2019 11:57:46 -0800 Subject: [PATCH 187/478] [Experiment] Stick a cache in front of lazy member loading Add a cache of lazily-imported names so we don't run off and deserialize extensions multiple times. The cache indicates that the lookup table has a complete understanding of any given base name. As a consequence, it must be flushed when a new extension with lazy members is added to avoid returning inconsistent results. This should make lazy member cache misses much, much cheaper. In the best case, we'll avoid repeatedly crawling around on disk. In the average case, we'll have fallen off the lazy member loading path at some point for some extension and the lazily-complete cache will kick in to keep that one extension from pessimizing the rest. In the worst case - when an enormous amount of lookups for non-existent members occur - we'll probably balloon memory usage somewhat adding bogus members to the set. --- lib/AST/NameLookup.cpp | 48 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 4d5906f63d1d5..dfaf9ca71f5b2 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -880,6 +880,11 @@ class swift::MemberLookupTable { /// Lookup table mapping names to the set of declarations with that name. LookupTable Lookup; + /// The set of names of lazily-loaded members that the lookup table has a + /// complete accounting of with respect to all known extensions of its + /// parent nominal type. + llvm::DenseSet LazilyCompleteNames; + public: /// Create a new member lookup table. explicit MemberLookupTable(ASTContext &ctx); @@ -893,6 +898,24 @@ class swift::MemberLookupTable { /// Add the given members to the lookup table. void addMembers(DeclRange members); + /// Returns \c true if the lookup table has a complete accounting of the + /// given name. + bool isLazilyComplete(DeclBaseName name) const { + return LazilyCompleteNames.find(name) != LazilyCompleteNames.end(); + } + + /// Mark a given lazily-loaded name as being complete. + void markLazilyComplete(DeclBaseName name) { + LazilyCompleteNames.insert(name); + } + + /// Clears the cache of lazily-complete names. This _must_ be called when + /// new extensions with lazy members are added to the type, or direct lookup + /// will return inconsistent or stale results. + void clearLazilyCompleteCache() { + LazilyCompleteNames.clear(); + } + /// Iterator into the lookup table. typedef LookupTable::iterator iterator; @@ -912,7 +935,11 @@ class swift::MemberLookupTable { os << "Lookup:\n "; for (auto &pair : Lookup) { - pair.getFirst().print(os) << ":\n "; + pair.getFirst().print(os); + if (isLazilyComplete(pair.getFirst().getBaseName())) { + os << " (lazily complete)"; + } + os << ":\n "; for (auto &decl : pair.getSecond()) { os << "- "; decl->dumpRef(os); @@ -1033,6 +1060,7 @@ void NominalTypeDecl::addedExtension(ExtensionDecl *ext) { if (ext->hasLazyMembers()) { LookupTable->addMembers(ext->getCurrentMembersWithoutLoading()); + LookupTable->clearLazilyCompleteCache(); } else { LookupTable->addMembers(ext->getMembers()); } @@ -1146,6 +1174,9 @@ populateLookupTableEntryFromExtensions(ASTContext &ctx, MemberLookupTable &table, NominalTypeDecl *nominal, DeclBaseName name) { + assert(!table.isLazilyComplete(name) && + "Should not be searching extensions for complete name!"); + for (auto e : nominal->getExtensions()) { // If there's no lazy members to look at, all the members of this extension // are present in the lookup table. @@ -1275,14 +1306,19 @@ NominalTypeDecl::lookupDirect(DeclName name, if (!useNamedLazyMemberLoading) { updateLookupTable(LookupTable); - } else { - if (populateLookupTableEntryFromLazyIDCLoader(ctx, *LookupTable, - name.getBaseName(), this)) { + } else if (!LookupTable->isLazilyComplete(name.getBaseName())) { + // The lookup table believes it doesn't have a complete accounting of this + // name - either because we're never seen it before, or another extension + // was registered since the last time we searched. Ask the loaders to give + // us a hand. + auto &Table = *LookupTable; + DeclBaseName baseName(name.getBaseName()); + if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table, baseName, this)) { updateLookupTable(LookupTable); } else { - populateLookupTableEntryFromExtensions(ctx, *LookupTable, this, - name.getBaseName()); + populateLookupTableEntryFromExtensions(ctx, Table, this, baseName); } + Table.markLazilyComplete(baseName); } // Look for a declaration with this name. From b1c39f1db449afc8408d38f8c60dbc64d14afb6b Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 21 Dec 2019 11:58:13 -0800 Subject: [PATCH 188/478] [NFC] Update some bounds on lazy member loading tests Not sure when these moved, but tighten them up so we don't regress. --- test/NameBinding/named_lazy_member_loading_objc_category.swift | 2 +- test/NameBinding/named_lazy_member_loading_objc_protocol.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/NameBinding/named_lazy_member_loading_objc_category.swift b/test/NameBinding/named_lazy_member_loading_objc_category.swift index c3a45c9a41114..db5932d19630c 100644 --- a/test/NameBinding/named_lazy_member_loading_objc_category.swift +++ b/test/NameBinding/named_lazy_member_loading_objc_category.swift @@ -8,7 +8,7 @@ // Check that named-lazy-member-loading reduces the number of Decls deserialized // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre %s // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post %s -// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -10' %t/stats-pre %t/stats-post +// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -30' %t/stats-pre %t/stats-post import NamedLazyMembers diff --git a/test/NameBinding/named_lazy_member_loading_objc_protocol.swift b/test/NameBinding/named_lazy_member_loading_objc_protocol.swift index 65f66bb372e1e..94607c52b675e 100644 --- a/test/NameBinding/named_lazy_member_loading_objc_protocol.swift +++ b/test/NameBinding/named_lazy_member_loading_objc_protocol.swift @@ -8,7 +8,7 @@ // Check that named-lazy-member-loading reduces the number of Decls deserialized // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre %s // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post %s -// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -9' %t/stats-pre %t/stats-post +// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -40' %t/stats-pre %t/stats-post import NamedLazyMembers From 646e9ac77f779cbf4e77e34372a35e9beb357702 Mon Sep 17 00:00:00 2001 From: "Kita, Maksim" Date: Sun, 22 Dec 2019 14:42:47 +0300 Subject: [PATCH 189/478] SR-11889: Fixed code review issues --- include/swift/Basic/Located.h | 29 +++++++++++++++++++++++++++++ lib/ClangImporter/ImporterImpl.h | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/include/swift/Basic/Located.h b/include/swift/Basic/Located.h index 3b7b6d537a66f..79721d257b041 100644 --- a/include/swift/Basic/Located.h +++ b/include/swift/Basic/Located.h @@ -54,4 +54,33 @@ struct Located { } // end namespace swift +namespace llvm { + +template struct DenseMapInfo; + +template +struct DenseMapInfo> { + + static inline swift::Located getEmptyKey() { + return swift::Located(DenseMapInfo::getEmptyKey(), + DenseMapInfo::getEmptyKey()); + } + + static inline swift::Located getTombstoneKey() { + return swift::Located(DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getTombstoneKey()); + } + + static unsigned getHashValue(const swift::Located &LocatedVal) { + return combineHashValue(DenseMapInfo::getHashValue(LocatedVal.Item), + DenseMapInfo::getHashValue(LocatedVal.Loc)); + } + + static bool isEqual(const swift::Located &LHS, const swift::Located &RHS) { + return DenseMapInfo::isEqual(LHS.Item, RHS.Item) && + DenseMapInfo::isEqual(LHS.Loc, RHS.Loc); + } +}; +} // namespace llvm + #endif // SWIFT_BASIC_LOCATED_H diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index f21345fc10b15..bc4e7028c0158 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -631,7 +631,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation public: /// Load a module using either method. ModuleDecl *loadModule(SourceLoc importLoc, - ArrayRef> path); + ArrayRef> path); void recordImplicitUnwrapForDecl(ValueDecl *decl, bool isIUO) { if (!isIUO) From 01d5652999b48636e33d1f424d1dc608a364d461 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 23 Dec 2019 11:55:10 -0800 Subject: [PATCH 190/478] remove VS2015 workaround (NFC) VS2015 had an issue with the deletion of an operator. Since VS2017 is the minimum version that LLVM uses, we can assume that VS2017+ is in use (_MSC_VER >= 1910). Clean up the now defunct workaround. --- include/swift/AST/Decl.h | 2 +- include/swift/AST/GenericEnvironment.h | 2 +- include/swift/AST/Module.h | 2 +- include/swift/AST/ProtocolConformance.h | 2 +- include/swift/Basic/Compiler.h | 9 --------- include/swift/SIL/SILAllocated.h | 2 +- include/swift/SIL/SILArgument.h | 2 +- include/swift/SIL/SILBasicBlock.h | 2 +- include/swift/SIL/SILInstruction.h | 6 +++--- include/swift/SIL/SILUndef.h | 2 +- 10 files changed, 11 insertions(+), 20 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 46faf5bab401e..cdbd9aae33d76 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -927,7 +927,7 @@ class alignas(1 << DeclAlignInBits) Decl { // Make vanilla new/delete illegal for Decls. void *operator new(size_t Bytes) = delete; - void operator delete(void *Data) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *Data) = delete; // Only allow allocation of Decls using the allocator in ASTContext // or by doing a placement new. diff --git a/include/swift/AST/GenericEnvironment.h b/include/swift/AST/GenericEnvironment.h index 5f70aa56e3121..144ed20c6621e 100644 --- a/include/swift/AST/GenericEnvironment.h +++ b/include/swift/AST/GenericEnvironment.h @@ -109,7 +109,7 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final /// Make vanilla new/delete illegal. void *operator new(size_t Bytes) = delete; - void operator delete(void *Data) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *Data) = delete; /// Only allow placement new. void *operator new(size_t Bytes, void *Mem) { diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 8f4a28ab2da87..cc7a2cd4937e7 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -582,7 +582,7 @@ class ModuleDecl : public DeclContext, public TypeDecl { private: // Make placement new and vanilla new/delete illegal for Modules. void *operator new(size_t Bytes) throw() = delete; - void operator delete(void *Data) throw() SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *Data) throw() = delete; void *operator new(size_t Bytes, void *Mem) throw() = delete; public: // Only allow allocation of Modules using the allocator in ASTContext diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 1b8d4d8879f32..f5fa29f0f6be4 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -285,7 +285,7 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { // Make vanilla new/delete illegal for protocol conformances. void *operator new(size_t bytes) = delete; - void operator delete(void *data) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *data) = delete; // Only allow allocation of protocol conformances using the allocator in // ASTContext or by doing a placement new. diff --git a/include/swift/Basic/Compiler.h b/include/swift/Basic/Compiler.h index eea6c937c8ef8..b422175695ade 100644 --- a/include/swift/Basic/Compiler.h +++ b/include/swift/Basic/Compiler.h @@ -27,15 +27,6 @@ #define __has_attribute(x) 0 #endif -#if SWIFT_COMPILER_IS_MSVC && _MSC_VER < 1910 -// Work around MSVC bug: attempting to reference a deleted function -// https://connect.microsoft.com/VisualStudio/feedback/details/3116505 -#define SWIFT_DELETE_OPERATOR_DELETED \ - { llvm_unreachable("Delete operator should not be called."); } -#else -#define SWIFT_DELETE_OPERATOR_DELETED = delete; -#endif - // __builtin_assume() is an optimization hint. #if __has_builtin(__builtin_assume) #define SWIFT_ASSUME(x) __builtin_assume(x) diff --git a/include/swift/SIL/SILAllocated.h b/include/swift/SIL/SILAllocated.h index 4e967896ceec4..767ba21d7ada5 100644 --- a/include/swift/SIL/SILAllocated.h +++ b/include/swift/SIL/SILAllocated.h @@ -31,7 +31,7 @@ class SILAllocated { void *operator new[](size_t) = delete; /// Disable non-placement delete. - void operator delete(void *) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *) = delete; void operator delete[](void *) = delete; /// Custom version of 'new' that uses the SILModule's BumpPtrAllocator with diff --git a/include/swift/SIL/SILArgument.h b/include/swift/SIL/SILArgument.h index 109e50338a70b..4f23c1cd6ff46 100644 --- a/include/swift/SIL/SILArgument.h +++ b/include/swift/SIL/SILArgument.h @@ -89,7 +89,7 @@ class SILArgument : public ValueBase { public: void operator=(const SILArgument &) = delete; - void operator delete(void *, size_t) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *, size_t) = delete; ValueOwnershipKind getOwnershipKind() const { return static_cast(Bits.SILArgument.VOKind); diff --git a/include/swift/SIL/SILBasicBlock.h b/include/swift/SIL/SILBasicBlock.h index 450e8aa19bb9f..dd5748bb24951 100644 --- a/include/swift/SIL/SILBasicBlock.h +++ b/include/swift/SIL/SILBasicBlock.h @@ -54,7 +54,7 @@ public llvm::ilist_node, public SILAllocated { SILBasicBlock() : Parent(nullptr) {} void operator=(const SILBasicBlock &) = delete; - void operator delete(void *Ptr, size_t) SWIFT_DELETE_OPERATOR_DELETED + void operator delete(void *Ptr, size_t) = delete; SILBasicBlock(SILFunction *F, SILBasicBlock *relativeToBB, bool after); diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 5e72d5b1be14d..6221915986b6a 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -318,7 +318,7 @@ class SILInstruction SILInstruction() = delete; void operator=(const SILInstruction &) = delete; - void operator delete(void *Ptr, size_t) SWIFT_DELETE_OPERATOR_DELETED + void operator delete(void *Ptr, size_t) = delete; /// Check any special state of instructions that are not represented in the /// instructions operands/type. @@ -799,7 +799,7 @@ class SingleValueInstruction : public SILInstruction, public ValueBase { SILModule &getModule() const { return SILInstruction::getModule(); } SILInstructionKind getKind() const { return SILInstruction::getKind(); } - void operator delete(void *Ptr, size_t) SWIFT_DELETE_OPERATOR_DELETED + void operator delete(void *Ptr, size_t) = delete; ValueKind getValueKind() const { return ValueBase::getKind(); @@ -950,7 +950,7 @@ class MultipleValueInstruction : public SILInstruction { : SILInstruction(kind, loc) {} public: - void operator delete(void *Ptr, size_t)SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *Ptr, size_t) = delete; MultipleValueInstruction *clone(SILInstruction *insertPt = nullptr) { return cast(SILInstruction::clone(insertPt)); diff --git a/include/swift/SIL/SILUndef.h b/include/swift/SIL/SILUndef.h index bb1c0edf41ad1..94036ff791682 100644 --- a/include/swift/SIL/SILUndef.h +++ b/include/swift/SIL/SILUndef.h @@ -29,7 +29,7 @@ class SILUndef : public ValueBase { public: void operator=(const SILArgument &) = delete; - void operator delete(void *, size_t) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *, size_t) = delete; static SILUndef *get(SILType ty, SILModule &m, ValueOwnershipKind ownershipKind); static SILUndef *get(SILType ty, const SILFunction &f); From ff5ab0b7ce37b90154b6345a00058677253701e5 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 23 Dec 2019 12:06:39 -0800 Subject: [PATCH 191/478] Basic: adjust aliasing annotation (NFCI) GCC `-fpermissive` flagged this annotation as being discarded as it is being applied after the definition. Move the annotation to the re-definition to which it appertains. --- lib/Basic/PrefixMap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Basic/PrefixMap.cpp b/lib/Basic/PrefixMap.cpp index 3fe43ad6cf7cd..9f7390d698cce 100644 --- a/lib/Basic/PrefixMap.cpp +++ b/lib/Basic/PrefixMap.cpp @@ -34,8 +34,8 @@ enum class ChildKind { Left, Right, Further, Root }; // that's technically instantiation-specific. Redefining the struct here // is technically an aliasing violation, but we can just tell the compilers // that actually use TBAA that this is okay. -typedef struct _Node Node LLVM_MAY_ALIAS; -struct _Node { +typedef struct _Node Node; +struct LLVM_MAY_ALIAS _Node { // If you change the layout in the header, you'll need to change it here. // (This comment is repeated there.) Node *Left, *Right, *Further; From 5d3dafa86aa557fb91c0472f019f63a03389274e Mon Sep 17 00:00:00 2001 From: Daniel Duan Date: Mon, 23 Dec 2019 13:44:06 -0800 Subject: [PATCH 192/478] Ignore clangd index Ignore files generated by clangd when it is launched with the source root as current working directory. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 787b28d3d77f3..4aae0bfe05e29 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,9 @@ docs/_build # Visual Studio metadata .vs +# clangd +.clangd + #==============================================================================# # Ignore CMake temporary files #==============================================================================# From b03487059605d92932dd64751dc6561fde7589e2 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 23 Dec 2019 15:32:07 -0800 Subject: [PATCH 193/478] Serialization: scope declaration properly (NFC) Annotating the namespace on a class is a clang extension. Use `namespace` to enclose the type to a namespace. This was flagged by GCC 7. --- lib/Serialization/Deserialization.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index d742bf418a61b..891fd9349148b 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -2221,7 +2221,8 @@ Decl *ModuleFile::getDecl(DeclID DID) { } /// Used to split up methods that would otherwise live in ModuleFile. -class swift::DeclDeserializer { +namespace swift { +class DeclDeserializer { template using Serialized = ModuleFile::Serialized; using TypeID = serialization::TypeID; @@ -3873,6 +3874,7 @@ class swift::DeclDeserializer { return dtor; } }; +} Expected ModuleFile::getDeclChecked( @@ -4513,7 +4515,8 @@ Type ModuleFile::getType(TypeID TID) { return deserialized.get(); } -class swift::TypeDeserializer { +namespace swift { +class TypeDeserializer { using TypeID = serialization::TypeID; ModuleFile &MF; @@ -5290,6 +5293,7 @@ class swift::TypeDeserializer { return UnboundGenericType::get(genericDecl, parentTy, ctx); } }; +} Expected ModuleFile::getTypeChecked(TypeID TID) { if (TID == 0) From 7d8aac60ca4c1af84ffac23540cf7e323bc07f97 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 23 Dec 2019 15:17:50 -0800 Subject: [PATCH 194/478] disambiguate some type shadowing (NFCI) Adjust the type shadowing identified by GCC 7. The declaration shadows a type which changes the meaning of the identifier subsequently. --- include/swift/ABI/Metadata.h | 56 +++++++++---------- include/swift/AST/DiagnosticEngine.h | 2 +- include/swift/AST/PrintOptions.h | 2 +- include/swift/AST/SourceFile.h | 2 +- include/swift/Markup/AST.h | 6 +- include/swift/Parse/PersistentParserState.h | 2 +- lib/Driver/Compilation.cpp | 2 +- lib/SILGen/SILGenPattern.cpp | 2 +- lib/SILOptimizer/Transforms/Outliner.cpp | 2 +- .../ModuleAnalyzerNodes.cpp | 2 +- 10 files changed, 39 insertions(+), 39 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 6bcdaf8d87a3a..58e5ce74784cc 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -2831,8 +2831,8 @@ struct TargetExtensionContextDescriptor final TrailingGenericContextObjects> { private: - using TrailingGenericContextObjects - = TrailingGenericContextObjects>; + using TrailingGenericContextObjects = + swift::TrailingGenericContextObjects>; public: /// A mangling of the `Self` type context that the extension extends. @@ -2872,10 +2872,10 @@ struct TargetAnonymousContextDescriptor final TargetMangledContextName> { private: - using TrailingGenericContextObjects - = TrailingGenericContextObjects, - TargetGenericContextDescriptorHeader, - TargetMangledContextName>; + using TrailingGenericContextObjects = + swift::TrailingGenericContextObjects, + TargetGenericContextDescriptorHeader, + TargetMangledContextName>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; friend TrailingObjects; @@ -3037,10 +3037,10 @@ struct TargetOpaqueTypeDescriptor final RelativeDirectPointer> { private: - using TrailingGenericContextObjects - = TrailingGenericContextObjects, - TargetGenericContextDescriptorHeader, - RelativeDirectPointer>; + using TrailingGenericContextObjects = + swift::TrailingGenericContextObjects, + TargetGenericContextDescriptorHeader, + RelativeDirectPointer>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; friend TrailingObjects; @@ -3803,16 +3803,16 @@ class TargetClassDescriptor final TargetObjCResilientClassStubInfo> { private: using TrailingGenericContextObjects = - TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader, - TargetResilientSuperclass, - TargetForeignMetadataInitialization, - TargetSingletonMetadataInitialization, - TargetVTableDescriptorHeader, - TargetMethodDescriptor, - TargetOverrideTableHeader, - TargetMethodOverrideDescriptor, - TargetObjCResilientClassStubInfo>; + swift::TrailingGenericContextObjects, + TargetTypeGenericContextDescriptorHeader, + TargetResilientSuperclass, + TargetForeignMetadataInitialization, + TargetSingletonMetadataInitialization, + TargetVTableDescriptorHeader, + TargetMethodDescriptor, + TargetOverrideTableHeader, + TargetMethodOverrideDescriptor, + TargetObjCResilientClassStubInfo>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; @@ -4120,10 +4120,10 @@ class TargetStructDescriptor final private: using TrailingGenericContextObjects = - TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader, - ForeignMetadataInitialization, - SingletonMetadataInitialization>; + swift::TrailingGenericContextObjects, + TargetTypeGenericContextDescriptorHeader, + ForeignMetadataInitialization, + SingletonMetadataInitialization>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; @@ -4196,10 +4196,10 @@ class TargetEnumDescriptor final private: using TrailingGenericContextObjects = - TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader, - ForeignMetadataInitialization, - SingletonMetadataInitialization>; + swift::TrailingGenericContextObjects, + TargetTypeGenericContextDescriptorHeader, + ForeignMetadataInitialization, + SingletonMetadataInitialization>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index 6f1dcadf17031..ab691bb708ecf 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -346,7 +346,7 @@ namespace swift { std::vector ChildNotes; SourceLoc Loc; bool IsChildNote = false; - const Decl *Decl = nullptr; + const swift::Decl *Decl = nullptr; friend DiagnosticEngine; diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 6ed26109f5659..329136a29b222 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -429,7 +429,7 @@ struct PrintOptions { /// and constructors) will be printed by this function. std::function FunctionBody; - BracketOptions BracketOptions; + swift::BracketOptions BracketOptions; // This is explicit to guarantee that it can be called from LLDB. PrintOptions() {} diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index 0ac86a6bcbe28..533ff6696dca3 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -133,7 +133,7 @@ class SourceFile final : public FileUnit { /// A cache of syntax nodes that can be reused when creating the syntax tree /// for this file. - SyntaxParsingCache *SyntaxParsingCache = nullptr; + swift::SyntaxParsingCache *SyntaxParsingCache = nullptr; /// The list of local type declarations in the source file. llvm::SetVector LocalTypeDecls; diff --git a/include/swift/Markup/AST.h b/include/swift/Markup/AST.h index d3ea9867b1bc2..fa5efdedc6be4 100644 --- a/include/swift/Markup/AST.h +++ b/include/swift/Markup/AST.h @@ -37,10 +37,10 @@ struct CommentParts { Optional Brief; ArrayRef BodyNodes; ArrayRef ParamFields; - Optional ReturnsField; - Optional ThrowsField; + Optional ReturnsField; + Optional ThrowsField; llvm::SmallSetVector Tags; - Optional LocalizationKeyField; + Optional LocalizationKeyField; bool isEmpty() const { return !Brief.hasValue() && diff --git a/include/swift/Parse/PersistentParserState.h b/include/swift/Parse/PersistentParserState.h index 6eb337b6781ea..5b01eb94f50bc 100644 --- a/include/swift/Parse/PersistentParserState.h +++ b/include/swift/Parse/PersistentParserState.h @@ -71,7 +71,7 @@ class PersistentParserState { // and adjust the client call 'performParseOnly'. bool PerformConditionEvaluation = true; private: - ScopeInfo ScopeInfo; + swift::ScopeInfo ScopeInfo; /// Parser sets this if it stopped parsing before the buffer ended. ParserPosition MarkedPos; diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index d8083a069a341..7cec90ab69e53 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -244,7 +244,7 @@ namespace driver { /// Dependency graphs for deciding which jobs are dirty (need running) /// or clean (can be skipped). using CoarseGrainedDependencyGraph = - CoarseGrainedDependencyGraph; + swift::CoarseGrainedDependencyGraph; CoarseGrainedDependencyGraph CoarseGrainedDepGraph; CoarseGrainedDependencyGraph CoarseGrainedDepGraphForRanges; diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp index 3b95cc66d571e..711b70d101178 100644 --- a/lib/SILGen/SILGenPattern.cpp +++ b/lib/SILGen/SILGenPattern.cpp @@ -311,7 +311,7 @@ namespace { /// A row which we intend to specialize. struct RowToSpecialize { /// The pattern from this row which we are specializing upon. - Pattern *Pattern; + swift::Pattern *Pattern; /// The index of the target row. unsigned RowIndex; diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index 449bf02bd9f67..a0a827199a37c 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -931,7 +931,7 @@ class ObjCMethodCall : public OutlinePattern { std::string OutlinedName; llvm::BitVector IsBridgedArgument; llvm::BitVector IsGuaranteedArgument; - BridgedReturn BridgedReturn; + ::BridgedReturn BridgedReturn; public: bool matchInstSequence(SILBasicBlock::iterator I) override; diff --git a/tools/swift-api-digester/ModuleAnalyzerNodes.cpp b/tools/swift-api-digester/ModuleAnalyzerNodes.cpp index 8bba7268ff2f7..402d59493adbd 100644 --- a/tools/swift-api-digester/ModuleAnalyzerNodes.cpp +++ b/tools/swift-api-digester/ModuleAnalyzerNodes.cpp @@ -37,7 +37,7 @@ struct swift::ide::api::SDKNodeInitInfo { #define KEY_STRING_ARR(X, Y) std::vector X; #include "swift/IDE/DigesterEnums.def" - ReferenceOwnership ReferenceOwnership = ReferenceOwnership::Strong; + swift::ReferenceOwnership ReferenceOwnership = ReferenceOwnership::Strong; std::vector DeclAttrs; std::vector TypeAttrs; From 9bfe9505cbd387112d73bcd521ece561ddaae228 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 23 Dec 2019 15:38:06 -0800 Subject: [PATCH 195/478] swift-remoteast-test: correct annotation of used functions (NFC) The annotation must precede the declaration to which it appertains during the definition. The re-ordering was silently accepted by clang but is not correct as per the GNU style. This was flagged by GCC 7. --- tools/swift-remoteast-test/swift-remoteast-test.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/swift-remoteast-test/swift-remoteast-test.cpp b/tools/swift-remoteast-test/swift-remoteast-test.cpp index 391c7b3edb98e..9b172eb8ab394 100644 --- a/tools/swift-remoteast-test/swift-remoteast-test.cpp +++ b/tools/swift-remoteast-test/swift-remoteast-test.cpp @@ -72,7 +72,7 @@ static RemoteASTContext &getRemoteASTContext() { // FIXME: swiftcall /// func printType(forMetadata: Any.Type) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printMetadataType(const Metadata *typeMetadata) { auto &remoteAST = getRemoteASTContext(); auto &out = llvm::outs(); @@ -90,7 +90,7 @@ printMetadataType(const Metadata *typeMetadata) { // FIXME: swiftcall /// func printDynamicType(_: AnyObject) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printHeapMetadataType(void *object) { auto &remoteAST = getRemoteASTContext(); auto &out = llvm::outs(); @@ -145,7 +145,7 @@ static void printMemberOffset(const Metadata *typeMetadata, // FIXME: swiftcall /// func printTypeMemberOffset(forType: Any.Type, memberName: StaticString) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printTypeMemberOffset(const Metadata *typeMetadata, const char *memberName) { printMemberOffset(typeMetadata, memberName, /*pass metadata*/ false); @@ -154,7 +154,7 @@ printTypeMemberOffset(const Metadata *typeMetadata, // FIXME: swiftcall /// func printTypeMetadataMemberOffset(forType: Any.Type, /// memberName: StaticString) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printTypeMetadataMemberOffset(const Metadata *typeMetadata, const char *memberName) { printMemberOffset(typeMetadata, memberName, /*pass metadata*/ true); @@ -162,7 +162,7 @@ printTypeMetadataMemberOffset(const Metadata *typeMetadata, // FIXME: swiftcall /// func printDynamicTypeAndAddressForExistential(_: T) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printDynamicTypeAndAddressForExistential(void *object, const Metadata *typeMetadata) { auto &remoteAST = getRemoteASTContext(); @@ -192,7 +192,7 @@ printDynamicTypeAndAddressForExistential(void *object, // FIXME: swiftcall /// func stopRemoteAST(_: AnyObject) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED stopRemoteAST() { if (remoteContext) remoteContext.reset(); From ebac915ef4cd49fe4fc7cab2412a1ae9868e6d3c Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 25 Dec 2019 17:03:03 -0500 Subject: [PATCH 196/478] [NFC] Drop bogus closure interface type reset --- lib/Sema/CSDiag.cpp | 45 --------------------------------------------- 1 file changed, 45 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index fb0b45f6cc625..b2076ce126d6e 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -378,7 +378,6 @@ namespace { llvm::DenseMap ExprTypes; llvm::DenseMap TypeLocTypes; llvm::DenseMap PatternTypes; - llvm::DenseMap ParamDeclInterfaceTypes; ExprTypeSaverAndEraser(const ExprTypeSaverAndEraser&) = delete; void operator=(const ExprTypeSaverAndEraser&) = delete; public: @@ -417,17 +416,6 @@ namespace { if (isa(expr) && !isa(expr) && !(expr->getType() && expr->getType()->hasError())) return { false, expr }; - - // If a ClosureExpr's parameter list has types on the decls, then - // remove them so that they'll get regenerated from the - // associated TypeLocs or resynthesized as fresh typevars. - if (auto *CE = dyn_cast(expr)) - for (auto P : *CE->getParameters()) { - if (P->hasInterfaceType()) { - TS->ParamDeclInterfaceTypes[P] = P->getInterfaceType(); - P->setInterfaceType(Type()); - } - } expr->setType(nullptr); @@ -470,12 +458,6 @@ namespace { for (auto patternElt : PatternTypes) patternElt.first->setType(patternElt.second); - - for (auto paramDeclIfaceElt : ParamDeclInterfaceTypes) { - assert(!paramDeclIfaceElt.first->isImmutable() || - !paramDeclIfaceElt.second->is()); - paramDeclIfaceElt.first->setInterfaceType(paramDeclIfaceElt.second->getInOutObjectType()); - } // Done, don't do redundant work on destruction. ExprTypes.clear(); @@ -502,33 +484,6 @@ namespace { for (auto patternElt : PatternTypes) if (!patternElt.first->hasType()) patternElt.first->setType(patternElt.second); - - for (auto paramDeclIfaceElt : ParamDeclInterfaceTypes) - if (!paramDeclIfaceElt.first->hasInterfaceType()) { - paramDeclIfaceElt.first->setInterfaceType( - getParamBaseType(paramDeclIfaceElt)); - } - } - - private: - static Type getParamBaseType(std::pair &storedParam) { - ParamDecl *param; - Type storedType; - - std::tie(param, storedType) = storedParam; - - // FIXME: We are currently in process of removing `InOutType` - // so `VarDecl::get{Interface}Type` is going to wrap base - // type into `InOutType` if its flag indicates that it's - // an `inout` parameter declaration. But such type can't - // be restored directly using `VarDecl::set{Interface}Type` - // caller needs additional logic to extract base type. - if (auto *IOT = storedType->getAs()) { - assert(param->isInOut()); - return IOT->getObjectType(); - } - - return storedType; } }; } // end anonymous namespace From 0f14ad442c148969df049f0a2d7f892c8ead95ac Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 25 Dec 2019 17:39:51 -0500 Subject: [PATCH 197/478] [NFC] Make it illegal to reset the interface type to null --- lib/AST/Decl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index f99f7964eccba..865c5ebd2dd2e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2940,6 +2940,7 @@ Type ValueDecl::getInterfaceType() const { } void ValueDecl::setInterfaceType(Type type) { + assert(!type.isNull() && "Resetting the interface type to null is forbidden"); getASTContext().evaluator.cacheOutput(InterfaceTypeRequest{this}, std::move(type)); } From 091c36bc82ac45767da6b585110c04f7e7e8f596 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 25 Dec 2019 16:02:50 -0800 Subject: [PATCH 198/478] [CodeCompletion] Evaluate 'PatternBindingEntry' before checking the init If a completion happens in an 'PatternBindingInitializer' context for 'TypedPattern' without any 'VarDecl', e.g.: let _: Int = it crashes because 'typeCheckPatternBinding()' requires that the 'TypedPattern' has the type. rdar://problem/52105899 --- lib/Sema/TypeChecker.cpp | 2 ++ .../IDE/crashers_2_fixed/rdar52105899.swift | 10 ++++++++++ 2 files changed, 12 insertions(+) create mode 100644 validation-test/IDE/crashers_2_fixed/rdar52105899.swift diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 4918500c12e5f..5194117fc9854 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -575,6 +575,8 @@ void swift::typeCheckPatternBinding(PatternBindingDecl *PBD, auto &Ctx = PBD->getASTContext(); DiagnosticSuppression suppression(Ctx.Diags); (void)createTypeChecker(Ctx); + (void)evaluateOrDefault( + Ctx.evaluator, PatternBindingEntryRequest{PBD, bindingIndex}, nullptr); TypeChecker::typeCheckPatternBinding(PBD, bindingIndex); } diff --git a/validation-test/IDE/crashers_2_fixed/rdar52105899.swift b/validation-test/IDE/crashers_2_fixed/rdar52105899.swift new file mode 100644 index 0000000000000..cbce5cd0c6d6e --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/rdar52105899.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) +// RUN: echo '//DUMMY' > %t/dummy.swift +// RUN: %target-swift-ide-test -code-completion -code-completion-token=A -source-filename=%s %t/dummy.swift > /dev/null +// RUN: %target-swift-ide-test -code-completion -code-completion-token=B -source-filename=%s %t/dummy.swift > /dev/null + +struct MyStruct { + let _: Int = #^A^# +} + +let _: Int = #^B^# From f40a67d9f8bfdfb91a37acb48bf8ffdf0a852cb2 Mon Sep 17 00:00:00 2001 From: "Kita, Maksim" Date: Thu, 26 Dec 2019 22:57:30 +0300 Subject: [PATCH 199/478] SR-11148: Separate do and while blocks generate error from legacy diagnostic --- include/swift/AST/DiagnosticsParse.def | 6 +++++- lib/Parse/ParseStmt.cpp | 19 +++++++++++-------- test/stmt/statements.swift | 16 ++++++++++++++-- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index bcbfa22da4787..e4888fe373852 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1037,7 +1037,11 @@ ERROR(expected_expr_repeat_while,PointsToFirstBadToken, "expected expression in 'repeat-while' condition", ()) ERROR(do_while_now_repeat_while,none, - "'do-while' statement is not allowed; use 'repeat-while' instead", ()) + "'do-while' statement is not allowed", ()) +NOTE(do_while_expected_repeat_while, none, + "did you mean 'repeat-while' statement?", ()) +NOTE(do_while_expected_separate_stmt, none, + "did you mean separate 'do' and 'while' statements?", ()) // Do/Catch Statement ERROR(expected_lbrace_after_do,PointsToFirstBadToken, diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 5fa8830946b03..d6ed9a2d6ea83 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1917,18 +1917,21 @@ ParserResult Parser::parseStmtDo(LabeledStmtInfo labelInfo) { DoCatchStmt::create(Context, labelInfo, doLoc, body.get(), allClauses)); } - SourceLoc whileLoc; - - // If we don't see a 'while', this is just the bare 'do' scoping - // statement. - if (!consumeIf(tok::kw_while, whileLoc)) { + // If we dont see a 'while' or see a 'while' that starts + // from new line. This is just the bare `do` scoping statement. + if (Tok.getKind() != tok::kw_while || Tok.isAtStartOfLine()) { return makeParserResult(status, - new (Context) DoStmt(labelInfo, doLoc, body.get())); + new (Context) DoStmt(labelInfo, doLoc, body.get())); } - + SourceLoc whileLoc = Tok.getLoc(); // But if we do, advise the programmer that it's 'repeat' now. - diagnose(doLoc, diag::do_while_now_repeat_while) + diagnose(doLoc, diag::do_while_now_repeat_while); + diagnose(doLoc, diag::do_while_expected_repeat_while) .fixItReplace(doLoc, "repeat"); + diagnose(doLoc, diag::do_while_expected_separate_stmt) + .fixItInsert(whileLoc, "\n"); + + consumeToken(tok::kw_while); status.setIsParseError(); ParserResult condition; if (Tok.is(tok::l_brace)) { diff --git a/test/stmt/statements.swift b/test/stmt/statements.swift index 2536568f3aae5..4714a0003b2e0 100644 --- a/test/stmt/statements.swift +++ b/test/stmt/statements.swift @@ -235,11 +235,23 @@ func DoStmt() { } } -func DoWhileStmt() { - do { // expected-error {{'do-while' statement is not allowed; use 'repeat-while' instead}} {{3-5=repeat}} + +func DoWhileStmt1() { + do { // expected-error {{'do-while' statement is not allowed}} + // expected-note@-1 {{did you mean 'repeat-while' statement?}} {{3-5=repeat}} + // expected-note@-2 {{did you mean separate 'do' and 'while' statements?}} {{5-5=\n}} } while true } +func DoWhileStmt2() { + do { + + } + while true { + + } +} + //===--- Repeat-while statement. func RepeatWhileStmt1() { From 5e4017019b71af1ffd0f9288bed65f368b0c0463 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 26 Dec 2019 16:49:06 -0800 Subject: [PATCH 200/478] AST: remove invalid `static_assert` This removes a set of static assertions that did not do what they were supposed to do. The static assertions were attempting to ensure that the functions are different. However, the address of a function is *not* a constant expression and cannot be used in a static assertion. Furthermore, the static assert is either tautologically true or will be caught as a compile error on the dispatch. The methods being checked here are non-virtual. The PFN that was being computed is tautologically different as there is no virtuality and the two methods must be different (barring any ICF coalescing that may be performed by the linker or by LTO). The method must exist, otherwise, the dispatch itself will trigger a resolution failure. Effectively, the static assertionss did not compute anything as a result: - the method's existence is checked by the dispatch - the address of the function is always different This was identified by building with GCC 7. --- lib/AST/ProtocolConformance.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index d0c3456fe30c9..0baad09194358 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -200,24 +200,12 @@ void *ProtocolConformance::operator new(size_t bytes, ASTContext &context, #define CONFORMANCE_SUBCLASS_DISPATCH(Method, Args) \ switch (getKind()) { \ case ProtocolConformanceKind::Normal: \ - static_assert(&ProtocolConformance::Method != \ - &NormalProtocolConformance::Method, \ - "Must override NormalProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Self: \ - static_assert(&ProtocolConformance::Method != \ - &SelfProtocolConformance::Method, \ - "Must override SelfProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Specialized: \ - static_assert(&ProtocolConformance::Method != \ - &SpecializedProtocolConformance::Method, \ - "Must override SpecializedProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Inherited: \ - static_assert(&ProtocolConformance::Method != \ - &InheritedProtocolConformance::Method, \ - "Must override InheritedProtocolConformance::" #Method); \ return cast(this)->Method Args; \ } \ llvm_unreachable("bad ProtocolConformanceKind"); @@ -225,14 +213,8 @@ llvm_unreachable("bad ProtocolConformanceKind"); #define ROOT_CONFORMANCE_SUBCLASS_DISPATCH(Method, Args) \ switch (getKind()) { \ case ProtocolConformanceKind::Normal: \ - static_assert(&RootProtocolConformance::Method != \ - &NormalProtocolConformance::Method, \ - "Must override NormalProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Self: \ - static_assert(&RootProtocolConformance::Method != \ - &SelfProtocolConformance::Method, \ - "Must override SelfProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Specialized: \ case ProtocolConformanceKind::Inherited: \ From e133b2071837fbdd8d7eeee5391513bc1e0590a2 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 23 Dec 2019 17:10:11 -0800 Subject: [PATCH 201/478] GSB: inline some trivial methods This inlines the comparators since GCC 7 objects to the definition (which is probably due to scoping). However, these methods are templates, which must be visible to the consumers, and should therefore be in the header. Given the size, it seems reasonable to just inline them. --- include/swift/AST/GenericSignatureBuilder.h | 15 +++++++++++++++ lib/AST/GenericSignatureBuilder.cpp | 16 ---------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index 7562da8a0ed8d..e9a36ac02b19b 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -1687,6 +1687,21 @@ class GenericSignatureBuilder::PotentialArchetype { friend class GenericSignatureBuilder; }; +template +bool GenericSignatureBuilder::Constraint::isSubjectEqualTo(Type T) const { + return getSubjectDependentType({ })->isEqual(T); +} + +template +bool GenericSignatureBuilder::Constraint::isSubjectEqualTo(const GenericSignatureBuilder::PotentialArchetype *PA) const { + return getSubjectDependentType({ })->isEqual(PA->getDependentType({ })); +} + +template +bool GenericSignatureBuilder::Constraint::hasSameSubjectAs(const GenericSignatureBuilder::Constraint &C) const { + return getSubjectDependentType({ })->isEqual(C.getSubjectDependentType({ })); +} + /// Describes a requirement whose processing has been delayed for some reason. class GenericSignatureBuilder::DelayedRequirement { public: diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index ad8b0fca4480e..9314718d9a241 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -1712,22 +1712,6 @@ bool EquivalenceClass::recordConformanceConstraint( return inserted; } -template -bool Constraint::isSubjectEqualTo(Type type) const { - return getSubjectDependentType({ })->isEqual(type); -} - -template -bool Constraint::isSubjectEqualTo(const PotentialArchetype *pa) const { - return getSubjectDependentType({ })->isEqual(pa->getDependentType({ })); -} - -template -bool Constraint::hasSameSubjectAs(const Constraint &other) const { - return getSubjectDependentType({ }) - ->isEqual(other.getSubjectDependentType({ })); -} - Optional EquivalenceClass::findAnyConcreteConstraintAsWritten(Type preferredType) const { // If we don't have a concrete type, there's no source. From beafad9651d491eaa18325e7b4ee52d54d2e265d Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 23 Dec 2019 16:27:09 -0800 Subject: [PATCH 202/478] SwiftShims: unify assertion paths (NFC) Use the C++ spelling for the static assertions. This is a C11 extension, but GCC and MSVC both object to the reserved spelling (`_Static_assert`). Use the compatibility spelling of `static_assert` on all targets instead. --- stdlib/public/SwiftShims/RefCount.h | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/stdlib/public/SwiftShims/RefCount.h b/stdlib/public/SwiftShims/RefCount.h index d9ba9e5c77c62..6e0886a432659 100644 --- a/stdlib/public/SwiftShims/RefCount.h +++ b/stdlib/public/SwiftShims/RefCount.h @@ -1475,25 +1475,13 @@ HeapObject* RefCounts::getHeapObject() { // for use by SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS typedef swift::InlineRefCounts InlineRefCounts; -// __cplusplus -#endif - // These assertions apply to both the C and the C++ declarations. -#if defined(_MSC_VER) && !defined(__clang__) static_assert(sizeof(InlineRefCounts) == sizeof(InlineRefCountsPlaceholder), "InlineRefCounts and InlineRefCountsPlaceholder must match"); static_assert(sizeof(InlineRefCounts) == sizeof(__swift_uintptr_t), "InlineRefCounts must be pointer-sized"); static_assert(__alignof(InlineRefCounts) == __alignof(__swift_uintptr_t), "InlineRefCounts must be pointer-aligned"); -#else -_Static_assert(sizeof(InlineRefCounts) == sizeof(InlineRefCountsPlaceholder), - "InlineRefCounts and InlineRefCountsPlaceholder must match"); -_Static_assert(sizeof(InlineRefCounts) == sizeof(__swift_uintptr_t), - "InlineRefCounts must be pointer-sized"); -_Static_assert(_Alignof(InlineRefCounts) == _Alignof(__swift_uintptr_t), - "InlineRefCounts must be pointer-aligned"); -#endif #if defined(_WIN32) && defined(_M_ARM64) #if defined(__cplusplus) @@ -1508,4 +1496,6 @@ inline void _Atomic_storage::_Unlock() const n #endif #endif +#endif // !defined(__swift__) + #endif From 2b17d366b468f68c1fcc5517effb921bdf0cac9f Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 27 Dec 2019 10:22:07 -0800 Subject: [PATCH 203/478] SIL: use `std::array` for `SILSuccessor` array The `SILSuccessor` type does not permit copy or move initialization. Direct initialization is required, however, GCC7 seems unable to cope with the direct initialization of the array of non-copyable UDTs. Use a `std::array` which can be direct-initialized. This enables building with GCC7. --- include/swift/SIL/SILInstruction.h | 28 +++++++++++++++------------- lib/SIL/SILInstructions.cpp | 7 +++---- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 5e72d5b1be14d..cbfad26bed082 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -42,6 +42,7 @@ #include "llvm/ADT/ilist.h" #include "llvm/ADT/ilist_node.h" #include "llvm/Support/TrailingObjects.h" +#include namespace swift { @@ -7138,12 +7139,12 @@ class YieldInst final YieldInst, TermInst> { friend SILBuilder; - SILSuccessor DestBBs[2]; + std::array DestBBs; YieldInst(SILDebugLocation loc, ArrayRef yieldedValues, SILBasicBlock *normalBB, SILBasicBlock *unwindBB) : InstructionBaseWithTrailingOperands(yieldedValues, loc), - DestBBs{{this, normalBB}, {this, unwindBB}} {} + DestBBs{{{this, normalBB}, {this, unwindBB}}} {} static YieldInst *create(SILDebugLocation loc, ArrayRef yieldedValues, @@ -7253,7 +7254,8 @@ class CondBranchInst final FalseIdx }; private: - SILSuccessor DestBBs[2]; + std::array DestBBs; + /// The number of arguments for the True branch. unsigned getNumTrueArgs() const { return SILInstruction::Bits.CondBranchInst.NumTrueArgs; @@ -7638,7 +7640,7 @@ class DynamicMethodBranchInst SILDeclRef Member; - SILSuccessor DestBBs[2]; + std::array DestBBs; /// The operand. FixedOperandList<1> Operands; @@ -7686,7 +7688,7 @@ class CheckedCastBranchInst final: CanType DestFormalTy; bool IsExact; - SILSuccessor DestBBs[2]; + std::array DestBBs; CheckedCastBranchInst(SILDebugLocation DebugLoc, bool IsExact, SILValue Operand, @@ -7698,8 +7700,8 @@ class CheckedCastBranchInst final: TypeDependentOperands), DestLoweredTy(DestLoweredTy), DestFormalTy(DestFormalTy), - IsExact(IsExact), DestBBs{{this, SuccessBB, Target1Count}, - {this, FailureBB, Target2Count}} {} + IsExact(IsExact), DestBBs{{{this, SuccessBB, Target1Count}, + {this, FailureBB, Target2Count}}} {} static CheckedCastBranchInst * create(SILDebugLocation DebugLoc, bool IsExact, SILValue Operand, @@ -7746,7 +7748,7 @@ class CheckedCastValueBranchInst final SILType DestLoweredTy; CanType DestFormalTy; - SILSuccessor DestBBs[2]; + std::array DestBBs; CheckedCastValueBranchInst(SILDebugLocation DebugLoc, SILValue Operand, CanType SourceFormalTy, @@ -7757,7 +7759,7 @@ class CheckedCastValueBranchInst final TypeDependentOperands), SourceFormalTy(SourceFormalTy), DestLoweredTy(DestLoweredTy), DestFormalTy(DestFormalTy), - DestBBs{{this, SuccessBB}, {this, FailureBB}} {} + DestBBs{{{this, SuccessBB}, {this, FailureBB}}} {} static CheckedCastValueBranchInst * create(SILDebugLocation DebugLoc, @@ -7791,7 +7793,7 @@ class CheckedCastAddrBranchInst CastConsumptionKind ConsumptionKind; FixedOperandList<2> Operands; - SILSuccessor DestBBs[2]; + std::array DestBBs; CanType SourceType; CanType TargetType; @@ -7803,8 +7805,8 @@ class CheckedCastAddrBranchInst ProfileCounter Target1Count, ProfileCounter Target2Count) : InstructionBase(DebugLoc), ConsumptionKind(consumptionKind), - Operands{this, src, dest}, DestBBs{{this, successBB, Target1Count}, - {this, failureBB, Target2Count}}, + Operands{this, src, dest}, DestBBs{{{this, successBB, Target1Count}, + {this, failureBB, Target2Count}}}, SourceType(srcType), TargetType(targetType) { assert(ConsumptionKind != CastConsumptionKind::BorrowAlways && "BorrowAlways is not supported on addresses"); @@ -7856,7 +7858,7 @@ class TryApplyInstBase : public TermInst { ErrorIdx }; private: - SILSuccessor DestBBs[2]; + std::array DestBBs; protected: TryApplyInstBase(SILInstructionKind valueKind, SILDebugLocation Loc, diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index bc92771412b21..65679f9b36541 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -573,7 +573,7 @@ TryApplyInstBase::TryApplyInstBase(SILInstructionKind kind, SILDebugLocation loc, SILBasicBlock *normalBB, SILBasicBlock *errorBB) - : TermInst(kind, loc), DestBBs{{this, normalBB}, {this, errorBB}} {} + : TermInst(kind, loc), DestBBs{{{this, normalBB}, {this, errorBB}}} {} TryApplyInst::TryApplyInst( SILDebugLocation Loc, SILValue callee, SILType substCalleeTy, @@ -1267,8 +1267,7 @@ CondBranchInst::CondBranchInst(SILDebugLocation Loc, SILValue Condition, unsigned NumFalse, ProfileCounter TrueBBCount, ProfileCounter FalseBBCount) : InstructionBaseWithTrailingOperands(Condition, Args, Loc), - DestBBs{{this, TrueBB, TrueBBCount}, - {this, FalseBB, FalseBBCount}} { + DestBBs{{{this, TrueBB, TrueBBCount}, {this, FalseBB, FalseBBCount}}} { assert(Args.size() == (NumTrue + NumFalse) && "Invalid number of args"); SILInstruction::Bits.CondBranchInst.NumTrueArgs = NumTrue; assert(SILInstruction::Bits.CondBranchInst.NumTrueArgs == NumTrue && @@ -1706,7 +1705,7 @@ DynamicMethodBranchInst::DynamicMethodBranchInst(SILDebugLocation Loc, SILBasicBlock *NoMethodBB) : InstructionBase(Loc), Member(Member), - DestBBs{{this, HasMethodBB}, {this, NoMethodBB}}, + DestBBs{{{this, HasMethodBB}, {this, NoMethodBB}}}, Operands(this, Operand) { } From 34c7c2f209c15f70f479769c3a4f1f65e59a99c3 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 27 Dec 2019 15:31:46 -0800 Subject: [PATCH 204/478] build: remove some dead code (NFC) This removes an unused function in `build-script-impl`. --- utils/build-script-impl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 7191dbc198d54..4994b90911b2d 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -326,14 +326,6 @@ function to_varname() { toupper "$(echo $1 | tr '-' '_')" } -function set_lldb_build_mode() { - if [[ "${LLDB_BUILD_TYPE}" == "RelWithDebInfo" ]]; then - LLDB_BUILD_MODE="CustomSwift-Release" - else - LLDB_BUILD_MODE="CustomSwift-${LLDB_BUILD_TYPE}" - fi -} - function is_llvm_lto_enabled() { if [[ "${LLVM_ENABLE_LTO}" == "thin" ]] || [[ "${LLVM_ENABLE_LTO}" == "full" ]]; then From a1357c1bed5873173f101f90d64069ffa544039c Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 27 Dec 2019 15:35:42 -0800 Subject: [PATCH 205/478] build: remove unnecessary variable (NFC) The `platform` variable already contains the SDK name, no need to create a new variable for that. --- utils/build-script-impl | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 7191dbc198d54..5ce5b16d6221f 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -470,70 +470,60 @@ function set_build_options_for_host() { watchos-* | watchsimulator-*) case ${host} in macosx-x86_64) - xcrun_sdk_name="macosx" llvm_target_arch="" SWIFT_HOST_TRIPLE="x86_64-apple-macosx${DARWIN_DEPLOYMENT_VERSION_OSX}" SWIFT_HOST_VARIANT_SDK="OSX" cmake_osx_deployment_target="${DARWIN_DEPLOYMENT_VERSION_OSX}" ;; iphonesimulator-i386) - xcrun_sdk_name="iphonesimulator" llvm_target_arch="X86" SWIFT_HOST_TRIPLE="i386-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" SWIFT_HOST_VARIANT_SDK="IOS_SIMULATOR" cmake_osx_deployment_target="" ;; iphonesimulator-x86_64) - xcrun_sdk_name="iphonesimulator" llvm_target_arch="X86" SWIFT_HOST_TRIPLE="x86_64-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" SWIFT_HOST_VARIANT_SDK="IOS_SIMULATOR" cmake_osx_deployment_target="" ;; iphoneos-armv7) - xcrun_sdk_name="iphoneos" llvm_target_arch="ARM" SWIFT_HOST_TRIPLE="armv7-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" SWIFT_HOST_VARIANT_SDK="IOS" cmake_osx_deployment_target="" ;; iphoneos-armv7s) - xcrun_sdk_name="iphoneos" llvm_target_arch="ARM" SWIFT_HOST_TRIPLE="armv7s-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" SWIFT_HOST_VARIANT_SDK="IOS" cmake_osx_deployment_target="" ;; iphoneos-arm64) - xcrun_sdk_name="iphoneos" llvm_target_arch="AArch64" SWIFT_HOST_TRIPLE="arm64-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" SWIFT_HOST_VARIANT_SDK="IOS" cmake_osx_deployment_target="" ;; appletvsimulator-x86_64) - xcrun_sdk_name="appletvsimulator" llvm_target_arch="X86" SWIFT_HOST_TRIPLE="x86_64-apple-tvos${DARWIN_DEPLOYMENT_VERSION_TVOS}" SWIFT_HOST_VARIANT_SDK="TVOS_SIMULATOR" cmake_osx_deployment_target="" ;; appletvos-arm64) - xcrun_sdk_name="appletvos" llvm_target_arch="AArch64" SWIFT_HOST_TRIPLE="arm64-apple-tvos${DARWIN_DEPLOYMENT_VERSION_TVOS}" SWIFT_HOST_VARIANT_SDK="TVOS" cmake_osx_deployment_target="" ;; watchsimulator-i386) - xcrun_sdk_name="watchsimulator" llvm_target_arch="X86" SWIFT_HOST_TRIPLE="i386-apple-watchos${DARWIN_DEPLOYMENT_VERSION_WATCHOS}" SWIFT_HOST_VARIANT_SDK="WATCHOS_SIMULATOR" cmake_osx_deployment_target="" ;; watchos-armv7k) - xcrun_sdk_name="watchos" llvm_target_arch="ARM" SWIFT_HOST_TRIPLE="armv7k-apple-watchos${DARWIN_DEPLOYMENT_VERSION_WATCHOS}" SWIFT_HOST_VARIANT_SDK="WATCHOS" @@ -560,12 +550,12 @@ function set_build_options_for_host() { cmark_cmake_options=( -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" + -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${platform} --show-sdk-path)" -DCMAKE_OSX_DEPLOYMENT_TARGET="${cmake_osx_deployment_target}" ) llvm_cmake_options=( -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING="${cmake_osx_deployment_target}" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" + -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${platform} --show-sdk-path)" -DCOMPILER_RT_ENABLE_IOS:BOOL=FALSE -DCOMPILER_RT_ENABLE_WATCHOS:BOOL=FALSE -DCOMPILER_RT_ENABLE_TVOS:BOOL=FALSE From 14ecd9fa93175fbd300e85b117dd9e15ba795cae Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 27 Dec 2019 15:40:56 -0800 Subject: [PATCH 206/478] build: remove workaround for CMake 3.4.0 With the dependencies now requiring CMake 3.15, everything is built with a newer CMake. This removes the workaround for the 3.4 CMake release. --- utils/build-script-impl | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 7191dbc198d54..2078c94ce94ba 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -573,26 +573,12 @@ function set_build_options_for_host() { -DLLVM_ENABLE_MODULES:BOOL="$(true_false ${LLVM_ENABLE_MODULES})" ) if [[ $(is_llvm_lto_enabled) == "TRUE" ]]; then - if [[ $(cmake_needs_to_specify_standard_computed_defaults) == "TRUE" ]]; then - llvm_cmake_options+=( - "-DCMAKE_C_STANDARD_COMPUTED_DEFAULT=AppleClang" - "-DCMAKE_CXX_STANDARD_COMPUTED_DEFAULT=AppleClang" - ) - fi - llvm_cmake_options+=( "-DLLVM_PARALLEL_LINK_JOBS=${LLVM_NUM_PARALLEL_LTO_LINK_JOBS}" ) fi if [[ $(is_swift_lto_enabled) == "TRUE" ]]; then - if [[ $(cmake_needs_to_specify_standard_computed_defaults) = "TRUE" ]]; then - swift_cmake_options+=( - "-DCMAKE_C_STANDARD_COMPUTED_DEFAULT=AppleClang" - "-DCMAKE_CXX_STANDARD_COMPUTED_DEFAULT=AppleClang" - ) - fi - llvm_cmake_options+=( -DLLVM_ENABLE_MODULE_DEBUGGING:BOOL=NO ) @@ -914,14 +900,6 @@ function cmake_version() { "${CMAKE}" --version | grep "cmake version" | cut -f 3 -d " " } -function cmake_needs_to_specify_standard_computed_defaults() { - if [[ $(cmake_version) = "3.4.0" ]]; then - echo "TRUE" - else - echo "FALSE" - fi -} - # Sanitize the list of cross-compilation targets. # # In the Build/Host/Target paradigm: From 3b578ab25a546dca24b2d57fb940590b2d65a1b7 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 27 Dec 2019 15:45:00 -0800 Subject: [PATCH 207/478] build: remove `USER_CONFIG_ARGS` (NFC) This was deprecated 4 years ago: obsolete the option. --- utils/build-script-impl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 7191dbc198d54..f4c03ccaf2e47 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -247,8 +247,6 @@ KNOWN_SETTINGS=( swift-cmake-options "" "CMake options used for all swift targets" xctest-cmake-options "" "CMake options used for all xctest targets" playgroundsupport-cmake-options "" "CMake options used for all playgroundsupport targets" - # TODO: Remove this some time later. - user-config-args "" "**Renamed to --extra-cmake-options**: User-supplied arguments to cmake when used to do configuration." only-execute "all" "Only execute the named action (see implementation)" llvm-lit-args "" "If set, override the lit args passed to LLVM" clang-profile-instr-use "" "If set, profile file to use for clang PGO while building llvm/clang" @@ -815,12 +813,6 @@ done # TODO: Rename this argument LOCAL_HOST=$HOST_TARGET -# TODO: Remove this some time later. -if [[ "${USER_CONFIG_ARGS}" ]]; then - echo "Error: --user-config-args is renamed to --extra-cmake-options." 1>&2 - exit 1 -fi - if [[ "${CHECK_ARGS_ONLY}" ]]; then exit 0 fi From ad94bc8f2e4f1f2d0309adeea68675b5dabfea59 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 27 Dec 2019 16:02:10 -0800 Subject: [PATCH 208/478] build: remove remnants of libdispatch staging (NFC) libdispatch is required by Foundation. The `FOUNDATION_ENABLE_LIBDISPATCH` flag was removed a while ago from Foundation itself. Remove the handling for this option and simplify the flag handling. --- utils/build-script-impl | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 7191dbc198d54..3a3470588910c 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -2135,18 +2135,6 @@ for host in "${ALL_HOSTS[@]}"; do LIBICU_BUILD_ARGS=() fi - # Staging: require opt-in for building with dispatch - if [[ ! "${SKIP_BUILD_LIBDISPATCH}" ]] ; then - LIBDISPATCH_BUILD_DIR="$(build_directory ${host} libdispatch)" - LIBDISPATCH_BUILD_ARGS=( - -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} - -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=${LIBDISPATCH_BUILD_DIR} - -Ddispatch_DIR=${LIBDISPATCH_BUILD_DIR}/cmake/modules - ) - else - LIBDISPATCH_BUILD_ARGS=( -DFOUNDATION_ENABLE_LIBDISPATCH=NO ) - fi - # FIXME: Always re-build XCTest on non-darwin platforms. # The Swift project might have been changed, but CMake might # not be aware and will not rebuild. @@ -2169,7 +2157,10 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_INSTALL_PREFIX:PATH=$(get_host_install_prefix ${host}) ${LIBICU_BUILD_ARGS[@]} - ${LIBDISPATCH_BUILD_ARGS[@]} + + -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} + -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=$(build_directory ${host} libdispatch) + -Ddispatch_DIR=$(build_directory ${host} libdispatch)/cmake/modules # NOTE(compnerd) we disable tests because XCTest is not ready # yet, but we will reconfigure when the time comes. @@ -2605,16 +2596,6 @@ for host in "${ALL_HOSTS[@]}"; do LIBICU_BUILD_ARGS=() fi - if [[ ! "${SKIP_BUILD_LIBDISPATCH}" ]] ; then - LIBDISPATCH_BUILD_DIR="$(build_directory ${host} libdispatch)" - LIBDISPATCH_BUILD_ARGS=( - -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} - -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=${LIBDISPATCH_BUILD_DIR} - -Ddispatch_DIR=${LIBDISPATCH_BUILD_DIR}/cmake/modules - ) - else - LIBDISPATCH_BUILD_ARGS=( -DFOUNDATION_ENABLE_LIBDISPATCH=NO ) - fi SWIFTC_BIN="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" LLVM_BIN="$(build_directory_bin ${LOCAL_HOST} llvm)" @@ -2629,7 +2610,10 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_INSTALL_PREFIX:PATH=$(get_host_install_prefix ${host}) ${LIBICU_BUILD_ARGS[@]} - ${LIBDISPATCH_BUILD_ARGS[@]} + + -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} + -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=$(build_directory ${host} libdispatch) + -Ddispatch_DIR=$(build_directory ${host} libdispatch)/cmake/modules -DENABLE_TESTING:BOOL=YES -DXCTest_DIR=$(build_directory ${host} xctest)/cmake/modules From 74120f3e9f90213503a4972f796b47026b562e2f Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 20 Dec 2019 15:11:21 -0800 Subject: [PATCH 209/478] [di] Remove dead code. Since collectUses in DI land always asserts that we are processing a pointer, this code for sure has never been called (since it works with destructures), meaning that the code must be dead. --- .../Mandatory/DIMemoryUseCollector.cpp | 43 +++---------------- 1 file changed, 6 insertions(+), 37 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index bfb32ac76f27a..ecb26a839c207 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -512,6 +512,8 @@ static SILValue scalarizeLoad(LoadInst *LI, namespace { +/// Gathers information about a specific address and its uses to determine +/// definite initialization. class ElementUseCollector { SILModule &Module; const DIMemoryObjectInfo &TheMemory; @@ -587,8 +589,6 @@ class ElementUseCollector { void addElementUses(unsigned BaseEltNo, SILType UseTy, SILInstruction *User, DIUseKind Kind); void collectTupleElementUses(TupleElementAddrInst *TEAI, unsigned BaseEltNo); - void collectDestructureTupleResultUses(DestructureTupleResult *DTR, - unsigned BaseEltNo); void collectStructElementUses(StructElementAddrInst *SEAI, unsigned BaseEltNo); }; @@ -640,34 +640,6 @@ void ElementUseCollector::collectTupleElementUses(TupleElementAddrInst *TEAI, collectUses(TEAI, BaseEltNo); } -/// Given a destructure_tuple, compute the new BaseEltNo implicit in the -/// selected member, and recursively add uses of the instruction. -void ElementUseCollector::collectDestructureTupleResultUses( - DestructureTupleResult *DTR, unsigned BaseEltNo) { - - // If we're walking into a tuple within a struct or enum, don't adjust the - // BaseElt. The uses hanging off the tuple_element_addr are going to be - // counted as uses of the struct or enum itself. - if (InStructSubElement || InEnumSubElement) - return collectUses(DTR, BaseEltNo); - - assert(!IsSelfOfNonDelegatingInitializer && "self doesn't have tuple type"); - - // tuple_element_addr P, 42 indexes into the current tuple element. - // Recursively process its uses with the adjusted element number. - unsigned FieldNo = DTR->getIndex(); - auto T = DTR->getParent()->getOperand()->getType(); - if (T.is()) { - for (unsigned i = 0; i != FieldNo; ++i) { - SILType EltTy = T.getTupleElementType(i); - BaseEltNo += getElementCountRec(TypeExpansionContext(*DTR->getFunction()), - Module, EltTy, false); - } - } - - collectUses(DTR, BaseEltNo); -} - void ElementUseCollector::collectStructElementUses(StructElementAddrInst *SEAI, unsigned BaseEltNo) { // Generally, we set the "InStructSubElement" flag and recursively process @@ -733,15 +705,15 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { continue; } - // Look through begin_access and begin_borrow - if (isa(User) || isa(User)) { + // Look through begin_access. + if (isa(User)) { auto begin = cast(User); collectUses(begin, BaseEltNo); continue; } - // Ignore end_access and end_borrow. - if (isa(User) || isa(User)) { + // Ignore end_access. + if (isa(User)) { continue; } @@ -1114,9 +1086,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { collectTupleElementUses(TEAI, BaseEltNo); continue; } - - auto *DTRI = cast(EltPtr); - collectDestructureTupleResultUses(DTRI, BaseEltNo); } } } From 2cf6d02a932c1ab21fbd108dcfba4d836bd5f3cc Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 20 Dec 2019 17:42:18 -0800 Subject: [PATCH 210/478] [di] Now that DI and PMO are split, debride some dead code. Specifically isDefiniteInitFinished is always set to false and TreatAddressToPointerAsInout is set to true when ever DI is run, so this patch just constant propagates those values and then DCEs as appropriate. --- .../Mandatory/DIMemoryUseCollector.cpp | 38 ++++--------------- .../Mandatory/DIMemoryUseCollector.h | 4 +- .../Mandatory/DefiniteInitialization.cpp | 3 +- 3 files changed, 10 insertions(+), 35 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index ecb26a839c207..0ddcc1e088c7c 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -519,23 +519,10 @@ class ElementUseCollector { const DIMemoryObjectInfo &TheMemory; DIElementUseInfo &UseInfo; - /// This is true if definite initialization has finished processing assign - /// and other ambiguous instructions into init vs assign classes. - bool isDefiniteInitFinished; - /// IsSelfOfNonDelegatingInitializer - This is true if we're looking at the /// top level of a 'self' variable in a non-delegating init method. bool IsSelfOfNonDelegatingInitializer; - /// How should address_to_pointer be handled? - /// - /// In DefiniteInitialization it is considered as an inout parameter to get - /// diagnostics about passing a let variable to an inout mutable-pointer - /// argument. - /// In PredictableMemOpt it is considered as an escape point to be - /// conservative. - bool TreatAddressToPointerAsInout; - /// When walking the use list, if we index into a struct element, keep track /// of this, so that any indexes into tuple subelements don't affect the /// element we attribute an access to. @@ -547,11 +534,9 @@ class ElementUseCollector { public: ElementUseCollector(const DIMemoryObjectInfo &TheMemory, - DIElementUseInfo &UseInfo, bool isDefiniteInitFinished, - bool TreatAddressToPointerAsInout) + DIElementUseInfo &UseInfo) : Module(TheMemory.MemoryInst->getModule()), TheMemory(TheMemory), - UseInfo(UseInfo), isDefiniteInitFinished(isDefiniteInitFinished), - TreatAddressToPointerAsInout(TreatAddressToPointerAsInout) {} + UseInfo(UseInfo) {} /// This is the main entry point for the use walker. It collects uses from /// the address and the refcount result of the allocation. @@ -775,8 +760,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { Kind = DIUseKind::PartialStore; \ else if (SWI->isInitializationOfDest()) \ Kind = DIUseKind::Initialization; \ - else if (isDefiniteInitFinished) \ - Kind = DIUseKind::Assign; \ else \ Kind = DIUseKind::InitOrAssign; \ trackUse(DIMemoryUse(User, Kind, BaseEltNo, 1)); \ @@ -803,8 +786,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { Kind = DIUseKind::PartialStore; else if (CAI->isInitializationOfDest()) Kind = DIUseKind::Initialization; - else if (isDefiniteInitFinished) - Kind = DIUseKind::Assign; else Kind = DIUseKind::InitOrAssign; @@ -900,7 +881,7 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { llvm_unreachable("bad parameter convention"); } - if (isa(User) && TreatAddressToPointerAsInout) { + if (isa(User)) { // address_to_pointer is a mutable escape, which we model as an inout use. addElementUses(BaseEltNo, PointeeType, User, DIUseKind::InOutArgument); @@ -1830,12 +1811,11 @@ static bool shouldPerformClassInitSelf(const DIMemoryObjectInfo &MemoryInfo) { MemoryInfo.isDerivedClassSelfOnly(); } -/// collectDIElementUsesFrom - Analyze all uses of the specified allocation -/// instruction (alloc_box, alloc_stack or mark_uninitialized), classifying them -/// and storing the information found into the Uses and Releases lists. +/// Analyze all uses of the specified allocation instruction (alloc_box, +/// alloc_stack or mark_uninitialized), classifying them and storing the +/// information found into the Uses and Releases lists. void swift::ownership::collectDIElementUsesFrom( - const DIMemoryObjectInfo &MemoryInfo, DIElementUseInfo &UseInfo, - bool isDIFinished, bool TreatAddressToPointerAsInout) { + const DIMemoryObjectInfo &MemoryInfo, DIElementUseInfo &UseInfo) { if (shouldPerformClassInitSelf(MemoryInfo)) { ClassInitElementUseCollector UseCollector(MemoryInfo, UseInfo); @@ -1852,7 +1832,5 @@ void swift::ownership::collectDIElementUsesFrom( return; } - ElementUseCollector(MemoryInfo, UseInfo, isDIFinished, - TreatAddressToPointerAsInout) - .collectFrom(); + ElementUseCollector(MemoryInfo, UseInfo).collectFrom(); } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index 01081dbaa82f5..d53750369e01a 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -306,9 +306,7 @@ struct DIElementUseInfo { /// instruction (alloc_box, alloc_stack or mark_uninitialized), classifying them /// and storing the information found into the Uses and Releases lists. void collectDIElementUsesFrom(const DIMemoryObjectInfo &MemoryInfo, - DIElementUseInfo &UseInfo, - bool isDefiniteInitFinished, - bool TreatAddressToPointerAsInout); + DIElementUseInfo &UseInfo); } // end namespace ownership } // end namespace swift diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index f38626d68a82e..a6c68b6e85785 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -2888,8 +2888,7 @@ static void processMemoryObject(MarkUninitializedInst *I) { DIElementUseInfo UseInfo; // Walk the use list of the pointer, collecting them into the Uses array. - collectDIElementUsesFrom(MemInfo, UseInfo, false, - /*TreatAddressToPointerAsInout*/ true); + collectDIElementUsesFrom(MemInfo, UseInfo); LifetimeChecker(MemInfo, UseInfo).doIt(); } From 722d0bb02845075562e149d182d8831ea90f2949 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 27 Dec 2019 19:39:49 -0800 Subject: [PATCH 211/478] [di] Add an accessor for NumElements and use that instead of accessing the field directly. --- .../Mandatory/DIMemoryUseCollector.cpp | 21 ++++---- .../Mandatory/DIMemoryUseCollector.h | 8 ++- .../Mandatory/DefiniteInitialization.cpp | 49 ++++++++++--------- 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 0ddcc1e088c7c..40dd954ad878e 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -561,7 +561,8 @@ class ElementUseCollector { void trackDestroy(SILInstruction *Destroy) { UseInfo.trackDestroy(Destroy); } - unsigned getNumMemoryElements() const { return TheMemory.NumElements; } + /// Return the raw number of elements including the 'super.init' value. + unsigned getNumMemoryElements() const { return TheMemory.getNumElements(); } SILInstruction *getMemoryInst() const { return TheMemory.MemoryInst; } @@ -588,7 +589,8 @@ void ElementUseCollector::addElementUses(unsigned BaseEltNo, SILType UseTy, // If we're in a subelement of a struct or enum, just mark the struct, not // things that come after it in a parent tuple. unsigned NumElements = 1; - if (TheMemory.NumElements != 1 && !InStructSubElement && !InEnumSubElement) + if (TheMemory.getNumElements() != 1 && !InStructSubElement && + !InEnumSubElement) NumElements = getElementCountRec(TypeExpansionContext(*User->getFunction()), Module, UseTy, IsSelfOfNonDelegatingInitializer); @@ -843,14 +845,14 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // by the callee, and this is enforced by sema, so we can consider it // a nonmutating use. bool isLet = true; - - for (unsigned i = 0; i < TheMemory.NumElements; ++i) { + + for (unsigned i = 0; i < TheMemory.getNumElements(); ++i) { if (!TheMemory.isElementLetProperty(i)) { isLet = false; break; } } - + if (isLet) { addElementUses(BaseEltNo, PointeeType, User, DIUseKind::IndirectIn); continue; @@ -1186,7 +1188,7 @@ void ElementUseCollector::collectClassSelfUses() { // We can safely handle anything else as an escape. They should all happen // after super.init is invoked. As such, all elements must be initialized // and super.init must be called. - trackUse(DIMemoryUse(User, DIUseKind::Load, 0, TheMemory.NumElements)); + trackUse(DIMemoryUse(User, DIUseKind::Load, 0, TheMemory.getNumElements())); } assert(StoresOfArgumentToSelf == 1 && @@ -1461,7 +1463,7 @@ void ElementUseCollector::collectClassSelfUses( Kind = DIUseKind::Escape; } - trackUse(DIMemoryUse(User, Kind, 0, TheMemory.NumElements)); + trackUse(DIMemoryUse(User, Kind, 0, TheMemory.getNumElements())); } } @@ -1593,7 +1595,7 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { // When we're analyzing a delegating constructor, we aren't field sensitive at // all. Just treat all members of self as uses of the single // non-field-sensitive value. - assert(TheMemory.NumElements == 1 && "delegating inits only have 1 bit"); + assert(TheMemory.getNumElements() == 1 && "delegating inits only have 1 bit"); auto *MUI = TheMemory.MemoryInst; // The number of stores of the initial 'self' argument into the self box @@ -1827,7 +1829,8 @@ void swift::ownership::collectDIElementUsesFrom( // When we're analyzing a delegating constructor, we aren't field sensitive // at all. Just treat all members of self as uses of the single // non-field-sensitive value. - assert(MemoryInfo.NumElements == 1 && "delegating inits only have 1 bit"); + assert(MemoryInfo.getNumElements() == 1 && + "delegating inits only have 1 bit"); collectDelegatingInitUses(MemoryInfo, UseInfo, MemoryInfo.MemoryInst); return; } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index d53750369e01a..cf162d98b345b 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -60,7 +60,6 @@ class DIMemoryObjectInfo { /// This is the base type of the memory allocation. SILType MemorySILType; -public: /// This is the count of elements being analyzed. For memory objects that are /// tuples, this is the flattened element count. For 'self' members in init /// methods, this is the local field count (+1 for super/self classes were @@ -103,6 +102,13 @@ class DIMemoryObjectInfo { return NumElements - (unsigned)isDerivedClassSelf(); } + /// Return the number of elements, including the extra "super.init" tracker in + /// initializers of derived classes. + /// + /// \see getNumMemoryElements() for the number of elements, excluding the + /// extra "super.init" tracker in the initializers of derived classes. + unsigned getNumElements() const { return NumElements; } + /// Return true if this is 'self' in any kind of initializer. bool isAnyInitSelf() const { return !MemoryInst->isVar(); } diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index a6c68b6e85785..0c527c8d9168f 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -435,8 +435,9 @@ namespace { void emitSelfConsumedDiagnostic(SILInstruction *Inst); LiveOutBlockState &getBlockInfo(SILBasicBlock *BB) { - return PerBlockInfo.insert({BB, - LiveOutBlockState(TheMemory.NumElements)}).first->second; + return PerBlockInfo + .insert({BB, LiveOutBlockState(TheMemory.getNumElements())}) + .first->second; } AvailabilitySet getLivenessAtInst(SILInstruction *Inst, unsigned FirstElt, @@ -640,7 +641,7 @@ void LifetimeChecker::noteUninitializedMembers(const DIMemoryUse &Use) { if (Liveness.get(i) == DIKind::Yes) continue; // Ignore a failed super.init requirement. - if (i == TheMemory.NumElements-1 && TheMemory.isDerivedClassSelf()) + if (i == TheMemory.getNumElements() - 1 && TheMemory.isDerivedClassSelf()) continue; std::string Name; @@ -672,7 +673,7 @@ std::string LifetimeChecker::getUninitElementName(const DIMemoryUse &Use) { // Verify that it isn't the super.init marker that failed. The client should // handle this, not pass it down to diagnoseInitError. assert((!TheMemory.isDerivedClassSelf() || - firstUndefElement != TheMemory.NumElements-1) && + firstUndefElement != TheMemory.getNumElements() - 1) && "super.init failure not handled in the right place"); // If the definition is a declaration, try to reconstruct a name and @@ -938,8 +939,8 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { if (TheMemory.isNonRootClassSelf()) { if (getSelfInitializedAtInst(Use.Inst) != DIKind::Yes) { - auto SelfLiveness = getLivenessAtInst(Use.Inst, - 0, TheMemory.NumElements); + auto SelfLiveness = + getLivenessAtInst(Use.Inst, 0, TheMemory.getNumElements()); if (SelfLiveness.isAllYes()) { emitSelfConsumedDiagnostic(Use.Inst); return; @@ -1816,11 +1817,12 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { // Determine the liveness states of the memory object, including the // self/super.init state. - AvailabilitySet Liveness = getLivenessAtInst(Inst, 0, TheMemory.NumElements); + AvailabilitySet Liveness = + getLivenessAtInst(Inst, 0, TheMemory.getNumElements()); // self/super.init() calls require that self/super.init has not already // been called. If it has, reject the program. - switch (Liveness.get(TheMemory.NumElements-1)) { + switch (Liveness.get(TheMemory.getNumElements() - 1)) { case DIKind::No: // This is good! Keep going. break; case DIKind::Yes: @@ -1838,7 +1840,8 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { } if (TheMemory.isDelegatingInit()) { - assert(TheMemory.NumElements == 1 && "delegating inits have a single elt"); + assert(TheMemory.getNumElements() == 1 && + "delegating inits have a single elt"); // Lower Assign instructions if needed. if (isa(Use.Inst) || isa(Use.Inst)) @@ -1846,7 +1849,7 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { } else { // super.init also requires that all ivars are initialized before the // superclass initializer runs. - for (unsigned i = 0, e = TheMemory.NumElements-1; i != e; ++i) { + for (unsigned i = 0, e = TheMemory.getNumElements() - 1; i != e; ++i) { if (Liveness.get(i) == DIKind::Yes) continue; // If the super.init call is implicit generated, produce a specific @@ -2045,7 +2048,7 @@ void LifetimeChecker::processNonTrivialRelease(unsigned ReleaseID) { assert(isa(Release) || isa(Release) || isa(Release)); - auto Availability = getLivenessAtInst(Release, 0, TheMemory.NumElements); + auto Availability = getLivenessAtInst(Release, 0, TheMemory.getNumElements()); DIKind SelfInitialized = DIKind::Yes; if (TheMemory.isNonRootClassSelf()) { @@ -2188,7 +2191,7 @@ SILValue LifetimeChecker::handleConditionalInitAssign() { SILLocation Loc = TheMemory.getLoc(); Loc.markAutoGenerated(); - unsigned NumMemoryElements = TheMemory.NumElements; + unsigned NumMemoryElements = TheMemory.getNumElements(); // We might need an extra bit to check if self was consumed. if (HasConditionalSelfInitialized) @@ -2338,10 +2341,10 @@ handleConditionalDestroys(SILValue ControlVariableAddr) { SILBuilderWithScope B(TheMemory.MemoryInst); Identifier ShiftRightFn, TruncateFn; - unsigned NumMemoryElements = TheMemory.NumElements; - - unsigned SelfInitializedElt = TheMemory.NumElements; - unsigned SuperInitElt = TheMemory.NumElements - 1; + unsigned NumMemoryElements = TheMemory.getNumElements(); + + unsigned SelfInitializedElt = TheMemory.getNumElements(); + unsigned SuperInitElt = TheMemory.getNumElements() - 1; // We might need an extra bit to check if self was consumed. if (HasConditionalSelfInitialized) @@ -2698,7 +2701,7 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, LLVM_DEBUG(llvm::dbgs() << "Get liveness " << FirstElt << ", #" << NumElts << " at " << *Inst); - AvailabilitySet Result(TheMemory.NumElements); + AvailabilitySet Result(TheMemory.getNumElements()); // Empty tuple queries return a completely "unknown" vector, since they don't // care about any of the elements. @@ -2709,13 +2712,13 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, // The vastly most common case is memory allocations that are not tuples, // so special case this with a more efficient algorithm. - if (TheMemory.NumElements == 1) { + if (TheMemory.getNumElements() == 1) { return getLivenessAtNonTupleInst(Inst, InstBB, Result); } // Check locally to see if any elements are satisfied within the block, and // keep track of which ones are still needed in the NeededElements set. - SmallBitVector NeededElements(TheMemory.NumElements); + SmallBitVector NeededElements(TheMemory.getNumElements()); NeededElements.set(FirstElt, FirstElt+NumElts); // If there is a store in the current block, scan the block to see if the @@ -2842,9 +2845,9 @@ bool LifetimeChecker::isInitializedAtUse(const DIMemoryUse &Use, // If the client wants to know about super.init, check to see if we failed // it or some other element. - if (Use.FirstElement+Use.NumElements == TheMemory.NumElements && + if (Use.FirstElement + Use.NumElements == TheMemory.getNumElements() && TheMemory.isAnyDerivedClassSelf() && - Liveness.get(Liveness.size()-1) != DIKind::Yes) { + Liveness.get(Liveness.size() - 1) != DIKind::Yes) { if (SuperInitDone) *SuperInitDone = false; } @@ -2864,8 +2867,8 @@ bool LifetimeChecker::isInitializedAtUse(const DIMemoryUse &Use, // we caught it, self is no longer available. if (TheMemory.isNonRootClassSelf()) { if (getSelfInitializedAtInst(Use.Inst) != DIKind::Yes) { - auto SelfLiveness = getLivenessAtInst(Use.Inst, - 0, TheMemory.NumElements); + auto SelfLiveness = + getLivenessAtInst(Use.Inst, 0, TheMemory.getNumElements()); if (SelfLiveness.isAllYes()) { if (FailedSelfUse) *FailedSelfUse = true; return false; From ba6763ac10c3e1b55db6a5134e20bdd938ad5384 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 27 Dec 2019 19:56:01 -0800 Subject: [PATCH 212/478] [di] Instead of accessing TheMemory.MemoryInst directly, use the helper getUninitializedValue(). This is going to let me change getUninitializedValue() to special case alloc_box so that we return the project_box (the address we want to analyze) rather than the mark_uninitialized (which is on the box). --- .../Mandatory/DIMemoryUseCollector.cpp | 95 +++++++++---------- .../Mandatory/DIMemoryUseCollector.h | 21 +++- .../Mandatory/DefiniteInitialization.cpp | 39 ++++---- 3 files changed, 82 insertions(+), 73 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 40dd954ad878e..8574a3121e25a 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -28,34 +28,33 @@ using namespace ownership; // Utility //===----------------------------------------------------------------------===// -static void gatherDestroysOfContainer(const MarkUninitializedInst *MUI, - DIElementUseInfo &UseInfo) { - // The MUI must be used on an alloc_box, alloc_stack, or global_addr. If we - // have an alloc_stack or a global_addr, there is nothing further to do. - if (isa(MUI->getOperand()) || - isa(MUI->getOperand()) || - isa(MUI->getOperand()) || +static void gatherDestroysOfContainer(const DIMemoryObjectInfo &memoryInfo, + DIElementUseInfo &useInfo) { + auto *uninitMemory = memoryInfo.getUninitializedValue(); + + // The uninitMemory must be used on an alloc_box, alloc_stack, or global_addr. + // If we have an alloc_stack or a global_addr, there is nothing further to do. + if (isa(uninitMemory->getOperand(0)) || + isa(uninitMemory->getOperand(0)) || + isa(uninitMemory->getOperand(0)) || // FIXME: We only support pointer to address here to not break LLDB. It is // important that long term we get rid of this since this is a situation // where LLDB is breaking SILGen/DI invariants by not creating a new // independent stack location for the pointer to address. - isa(MUI->getOperand())) + isa(uninitMemory->getOperand(0))) { return; + } - // Otherwise, we assume that we have a project_box. This is a hard cast to - // ensure that we catch any new patterns emitted by SILGen and assert. - auto *PBI = cast(MUI->getOperand()); - auto *ABI = cast(PBI->getOperand()); - - // Treat destroys of the container as load+destroys of the original value. + // Otherwise, we assume that we have an alloc_box. Treat destroys of the + // alloc_box as load+destroys of the value stored in the box. // // TODO: This should really be tracked separately from other destroys so that // we distinguish the lifetime of the container from the value itself. - for (auto *Op : ABI->getUses()) { - SILInstruction *User = Op->getUser(); - if (isa(User) || isa(User)) { - UseInfo.trackDestroy(User); - } + assert(isa(uninitMemory)); + auto *pbi = cast(uninitMemory->getOperand(0)); + auto *abi = cast(pbi->getOperand()); + for (auto *user : abi->getUsersOfType()) { + useInfo.trackDestroy(user); } } @@ -215,7 +214,7 @@ SILType DIMemoryObjectInfo::getElementType(unsigned EltNo) const { SILValue DIMemoryObjectInfo::emitElementAddress( unsigned EltNo, SILLocation Loc, SILBuilder &B, llvm::SmallVectorImpl> &EndBorrowList) const { - SILValue Ptr = getAddress(); + SILValue Ptr = getUninitializedValue(); bool IsSelf = isNonDelegatingInit(); auto &Module = MemoryInst->getModule(); @@ -535,8 +534,7 @@ class ElementUseCollector { public: ElementUseCollector(const DIMemoryObjectInfo &TheMemory, DIElementUseInfo &UseInfo) - : Module(TheMemory.MemoryInst->getModule()), TheMemory(TheMemory), - UseInfo(UseInfo) {} + : Module(TheMemory.getModule()), TheMemory(TheMemory), UseInfo(UseInfo) {} /// This is the main entry point for the use walker. It collects uses from /// the address and the refcount result of the allocation. @@ -553,8 +551,8 @@ class ElementUseCollector { return; } - collectUses(TheMemory.MemoryInst, 0); - gatherDestroysOfContainer(TheMemory.MemoryInst, UseInfo); + collectUses(TheMemory.getUninitializedValue(), 0); + gatherDestroysOfContainer(TheMemory, UseInfo); } void trackUse(DIMemoryUse Use) { UseInfo.trackUse(Use); } @@ -564,8 +562,6 @@ class ElementUseCollector { /// Return the raw number of elements including the 'super.init' value. unsigned getNumMemoryElements() const { return TheMemory.getNumElements(); } - SILInstruction *getMemoryInst() const { return TheMemory.MemoryInst; } - private: void collectUses(SILValue Pointer, unsigned BaseEltNo); void collectClassSelfUses(); @@ -867,7 +863,7 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // inout use. DIUseKind Kind; if (TheMemory.isStructInitSelf() && - getAccessedPointer(Pointer) == TheMemory.getAddress()) { + getAccessedPointer(Pointer) == TheMemory.getUninitializedValue()) { Kind = DIUseKind::Escape; } else if (Apply.hasSelfArgument() && Op == &Apply.getSelfArgumentOperand()) { @@ -1089,8 +1085,7 @@ void ElementUseCollector::collectClassSelfUses() { unsigned NumElements = 0; for (auto *VD : NTD->getStoredProperties()) { EltNumbering[VD] = NumElements; - auto expansionContext = - TypeExpansionContext(*TheMemory.MemoryInst->getFunction()); + auto expansionContext = TypeExpansionContext(TheMemory.getFunction()); NumElements += getElementCountRec( expansionContext, Module, T.getFieldType(VD, Module, expansionContext), false); @@ -1099,9 +1094,9 @@ void ElementUseCollector::collectClassSelfUses() { // If we are looking at the init method for a root class, just walk the // MUI use-def chain directly to find our uses. - auto *MemoryInst = TheMemory.MemoryInst; - if (MemoryInst->getKind() == MarkUninitializedInst::RootSelf) { - collectClassSelfUses(MemoryInst, TheMemory.getType(), EltNumbering); + if (TheMemory.isRootSelf()) { + collectClassSelfUses(TheMemory.getUninitializedValue(), TheMemory.getType(), + EltNumbering); return; } @@ -1118,7 +1113,7 @@ void ElementUseCollector::collectClassSelfUses() { // 4) Potential escapes after super.init, if self is closed over. // // Handle each of these in turn. - SmallVector Uses(MemoryInst->getUses()); + SmallVector Uses(TheMemory.getUninitializedValue()->getUses()); while (!Uses.empty()) { Operand *Op = Uses.pop_back_val(); SILInstruction *User = Op->getUser(); @@ -1129,7 +1124,7 @@ void ElementUseCollector::collectClassSelfUses() { // The initial store of 'self' into the box at the start of the // function. Ignore it. if (auto *Arg = dyn_cast(SI->getSrc())) { - if (Arg->getParent() == MemoryInst->getParent()) { + if (Arg->getParent() == TheMemory.getParentBlock()) { StoresOfArgumentToSelf++; continue; } @@ -1144,7 +1139,7 @@ void ElementUseCollector::collectClassSelfUses() { src = conversion->getConverted(); if (auto *LI = dyn_cast(src)) - if (LI->getOperand() == MemoryInst) + if (LI->getOperand() == TheMemory.getUninitializedValue()) continue; // Any other store needs to be recorded. @@ -1583,7 +1578,7 @@ class ClassInitElementUseCollector { // *NOTE* Even though this takes a SILInstruction it actually only accepts // load_borrow and load instructions. This is enforced via an assert. - void collectClassInitSelfLoadUses(MarkUninitializedInst *MUI, + void collectClassInitSelfLoadUses(SingleValueInstruction *MUI, SingleValueInstruction *LI); }; @@ -1596,21 +1591,21 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { // all. Just treat all members of self as uses of the single // non-field-sensitive value. assert(TheMemory.getNumElements() == 1 && "delegating inits only have 1 bit"); - auto *MUI = TheMemory.MemoryInst; + auto *uninitMemory = TheMemory.getUninitializedValue(); // The number of stores of the initial 'self' argument into the self box // that we saw. unsigned StoresOfArgumentToSelf = 0; - // We walk the use chains of the self MUI to find any accesses to it. The - // possible uses are: + // We walk the use chains of the self uninitMemory to find any accesses to it. + // The possible uses are: // 1) The initialization store. // 2) Loads of the box, which have uses of self hanging off of them. // 3) An assign to the box, which happens at super.init. // 4) Potential escapes after super.init, if self is closed over. // Handle each of these in turn. // - SmallVector Uses(MUI->getUses()); + SmallVector Uses(uninitMemory->getUses()); while (!Uses.empty()) { Operand *Op = Uses.pop_back_val(); SILInstruction *User = Op->getUser(); @@ -1634,7 +1629,7 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { // A store of 'self' into the box at the start of the // function. Ignore it. if (auto *Arg = dyn_cast(SI->getSrc())) { - if (Arg->getParent() == MUI->getParent()) { + if (Arg->getParent() == uninitMemory->getParent()) { StoresOfArgumentToSelf++; continue; } @@ -1649,7 +1644,7 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { src = conversion->getConverted(); if (auto *LI = dyn_cast(src)) - if (LI->getOperand() == MUI) + if (LI->getOperand() == uninitMemory) continue; // Any other store needs to be recorded. @@ -1699,7 +1694,8 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { // Loads of the box produce self, so collect uses from them. if (isa(User) || isa(User)) { - collectClassInitSelfLoadUses(MUI, cast(User)); + collectClassInitSelfLoadUses(uninitMemory, + cast(User)); continue; } @@ -1718,12 +1714,12 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { "The 'self' argument should have been stored into the box exactly once"); // Gather the uses of the - gatherDestroysOfContainer(MUI, UseInfo); + gatherDestroysOfContainer(TheMemory, UseInfo); } -void ClassInitElementUseCollector:: -collectClassInitSelfLoadUses(MarkUninitializedInst *MUI, - SingleValueInstruction *LI) { +void ClassInitElementUseCollector::collectClassInitSelfLoadUses( + SingleValueInstruction *MUI, SingleValueInstruction *LI) { + assert(isa(MUI) || isa(MUI)); assert(isa(LI) || isa(LI)); // If we have a load, then this is a use of the box. Look at the uses of @@ -1805,7 +1801,7 @@ collectClassInitSelfLoadUses(MarkUninitializedInst *MUI, //===----------------------------------------------------------------------===// static bool shouldPerformClassInitSelf(const DIMemoryObjectInfo &MemoryInfo) { - if (MemoryInfo.MemoryInst->isDelegatingSelfAllocated()) + if (MemoryInfo.isDelegatingSelfAllocated()) return true; return MemoryInfo.isNonDelegatingInit() && @@ -1831,7 +1827,8 @@ void swift::ownership::collectDIElementUsesFrom( // non-field-sensitive value. assert(MemoryInfo.getNumElements() == 1 && "delegating inits only have 1 bit"); - collectDelegatingInitUses(MemoryInfo, UseInfo, MemoryInfo.MemoryInst); + collectDelegatingInitUses(MemoryInfo, UseInfo, + MemoryInfo.getUninitializedValue()); return; } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index cf162d98b345b..8844469dbe79b 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -52,11 +52,9 @@ struct DIElementUseInfo; /// Derived classes have an additional field at the end that models whether or /// not super.init() has been called or not. class DIMemoryObjectInfo { -public: /// The uninitialized memory that we are analyzing. MarkUninitializedInst *MemoryInst; -private: /// This is the base type of the memory allocation. SILType MemorySILType; @@ -66,7 +64,6 @@ class DIMemoryObjectInfo { /// initialized). unsigned NumElements; -private: /// True if the memory object being analyzed represents a 'let', which is /// initialize-only (reassignments are not allowed). bool IsLet = false; @@ -80,6 +77,8 @@ class DIMemoryObjectInfo { SILLocation getLoc() const { return MemoryInst->getLoc(); } SILFunction &getFunction() const { return *MemoryInst->getFunction(); } + SILModule &getModule() const { return MemoryInst->getModule(); } + SILBasicBlock *getParentBlock() const { return MemoryInst->getParent(); } /// Return the first instruction of the function containing the memory object. SILInstruction *getFunctionEntryPoint() const; @@ -94,7 +93,13 @@ class DIMemoryObjectInfo { /// be non-empty. bool hasDummyElement() const { return HasDummyElement; } - SingleValueInstruction *getAddress() const { return MemoryInst; } + /// Return the actual 'uninitialized' memory. In the case of alloc_ref, + /// alloc_stack, this always just returns the actual mark_uninitialized + /// instruction. For alloc_box though it returns the project_box associated + /// with the memory info. + SingleValueInstruction *getUninitializedValue() const { + return MemoryInst; + } /// Return the number of elements, without the extra "super.init" tracker in /// initializers of derived classes. @@ -194,6 +199,14 @@ class DIMemoryObjectInfo { return false; } + bool isRootSelf() const { + return MemoryInst->getKind() == MarkUninitializedInst::RootSelf; + } + + bool isDelegatingSelfAllocated() const { + return MemoryInst->isDelegatingSelfAllocated(); + } + /// Given an element number (in the flattened sense) return a pointer to a /// leaf element of the specified number. SILValue diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 0c527c8d9168f..c6365749df173 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -509,12 +509,9 @@ namespace { LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, DIElementUseInfo &UseInfo) - : F(*TheMemory.MemoryInst->getFunction()), - Module(TheMemory.MemoryInst->getModule()), - TheMemory(TheMemory), - Uses(UseInfo.Uses), - StoresToSelf(UseInfo.StoresToSelf), - Destroys(UseInfo.Releases) { + : F(TheMemory.getFunction()), Module(TheMemory.getModule()), + TheMemory(TheMemory), Uses(UseInfo.Uses), + StoresToSelf(UseInfo.StoresToSelf), Destroys(UseInfo.Releases) { // The first step of processing an element is to collect information about the // element into data structures we use later. @@ -559,10 +556,10 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, getBlockInfo(bb).markStoreToSelf(); } - // If isn't really a use, but we account for the alloc_box/mark_uninitialized - // as a use so we see it in our dataflow walks. - NonLoadUses[TheMemory.MemoryInst] = ~0U; - auto &MemBBInfo = getBlockInfo(TheMemory.MemoryInst->getParent()); + // If isn't really a use, but we account for the mark_uninitialized or + // project_box as a use so we see it in our dataflow walks. + NonLoadUses[TheMemory.getUninitializedValue()] = ~0U; + auto &MemBBInfo = getBlockInfo(TheMemory.getParentBlock()); MemBBInfo.HasNonLoadUse = true; // There is no scanning required (or desired) for the block that defines the @@ -806,7 +803,7 @@ void LifetimeChecker::doIt() { HasConditionalDestroy || HasConditionalSelfInitialized) { ControlVariable = handleConditionalInitAssign(); - SILValue memAddr = TheMemory.MemoryInst->getOperand(); + SILValue memAddr = TheMemory.getUninitializedValue()->getOperand(0); if (auto *ASI = dyn_cast(memAddr)) { ASI->setDynamicLifetime(); } else if (auto *ABI = dyn_cast(memAddr)) { @@ -1007,7 +1004,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { if (auto *projection = dyn_cast(addr)) addr = projection->getOperand(); - return addr == TheMemory.getAddress(); + return addr == TheMemory.getUninitializedValue(); }; if (!isFullyInitialized && WantsCrossModuleStructInitializerDiagnostic && @@ -1401,7 +1398,7 @@ findMethodForStoreInitializationOfTemporary(const DIMemoryObjectInfo &TheMemory, // argument, so the ownership verifier would trip. So we know that such a // thing can not happen. On the other hand, for store_borrow, we need to // strip the borrow, so lets use idempotence for correctness. - if (stripBorrow(SI->getSrc()) != TheMemory.MemoryInst || + if (stripBorrow(SI->getSrc()) != TheMemory.getUninitializedValue() || !isa(SI->getDest()) || !TheMemory.isClassInitSelf()) { return nullptr; } @@ -1653,8 +1650,8 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use, // Stores back to the 'self' box are OK. if (auto store = dyn_cast(Inst)) { - if (store->getDest() == TheMemory.MemoryInst - && TheMemory.isClassInitSelf()) + if (store->getDest() == TheMemory.getUninitializedValue() && + TheMemory.isClassInitSelf()) return; } @@ -2220,7 +2217,8 @@ SILValue LifetimeChecker::handleConditionalInitAssign() { } // Before the memory allocation, store zero in the control variable. - auto *InsertPoint = &*std::next(TheMemory.MemoryInst->getIterator()); + auto *InsertPoint = + &*std::next(TheMemory.getUninitializedValue()->getIterator()); B.setInsertionPoint(InsertPoint); B.setCurrentDebugScope(InsertPoint->getDebugScope()); SILValue ControlVariableAddr = ControlVariableBox; @@ -2338,7 +2336,7 @@ SILValue LifetimeChecker::handleConditionalInitAssign() { /// to emit branching logic when an element may or may not be initialized. void LifetimeChecker:: handleConditionalDestroys(SILValue ControlVariableAddr) { - SILBuilderWithScope B(TheMemory.MemoryInst); + SILBuilderWithScope B(TheMemory.getUninitializedValue()); Identifier ShiftRightFn, TruncateFn; unsigned NumMemoryElements = TheMemory.getNumElements(); @@ -2675,7 +2673,8 @@ LifetimeChecker::getLivenessAtNonTupleInst(swift::SILInstruction *Inst, // is not defined at all yet. Otherwise, we've found a definition, or // something else that will require that the memory is initialized at // this point. - Result.set(0, TheInst == TheMemory.MemoryInst ? DIKind::No : DIKind::Yes); + Result.set(0, TheInst == TheMemory.getUninitializedValue() ? DIKind::No + : DIKind::Yes); return Result; } } @@ -2736,13 +2735,13 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, // If we found the allocation itself, then we are loading something that // is not defined at all yet. Scan no further. - if (TheInst == TheMemory.MemoryInst) { + if (TheInst == TheMemory.getUninitializedValue()) { // The result is perfectly decided locally. for (unsigned i = FirstElt, e = i+NumElts; i != e; ++i) Result.set(i, NeededElements[i] ? DIKind::No : DIKind::Yes); return Result; } - + // Check to see which tuple elements this instruction defines. Clear them // from the set we're scanning from. auto &TheInstUse = Uses[It->second]; From 321185e82ff0e6e770bc09989c10c200815d54fc Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sat, 28 Dec 2019 15:02:29 -0800 Subject: [PATCH 213/478] [sil] Rename TermInst::{getSuccessorBlockArguments,getSuccessorBlockArgumentLists}() This method returns argument lists, not arguments! We should add in the future an additional API that returns a flap mapped range over all such argument lists to cleanup some of this code. But at least now the name is more accurate. --- include/swift/SIL/SILInstruction.h | 4 ++-- lib/SIL/OperandOwnership.cpp | 4 ++-- lib/SIL/SILInstructions.cpp | 6 +++--- lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp | 2 +- lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp | 2 +- lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 5e72d5b1be14d..55995a62bb0ef 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -6998,13 +6998,13 @@ class TermInst : public NonValueInstruction { }); } - using SuccessorBlockArgumentsListTy = + using SuccessorBlockArgumentListTy = TransformRange>; /// Return the range of Argument arrays for each successor of this /// block. - SuccessorBlockArgumentsListTy getSuccessorBlockArguments() const; + SuccessorBlockArgumentListTy getSuccessorBlockArgumentLists() const; using SuccessorBlockListTy = TransformRangegetSuccessorBlockArguments(), + sei->getSuccessorBlockArgumentLists(), [&](SILPhiArgumentArrayRef array) -> ValueOwnershipKind { // If the array is empty, we have a non-payloaded case. Return any. if (array.empty()) @@ -485,7 +485,7 @@ OperandOwnershipKindClassifier::visitCheckedCastBranchInst( CheckedCastBranchInst *ccbi) { // TODO: Simplify this using ValueOwnershipKind::merge. Optional map; - for (auto argArray : ccbi->getSuccessorBlockArguments()) { + for (auto argArray : ccbi->getSuccessorBlockArgumentLists()) { assert(!argArray.empty()); auto argOwnershipKind = argArray[getOperandIndex()]->getOwnershipKind(); diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index bc92771412b21..87667baa43ada 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -1217,13 +1217,13 @@ bool TermInst::isProgramTerminating() const { llvm_unreachable("Unhandled TermKind in switch."); } -TermInst::SuccessorBlockArgumentsListTy -TermInst::getSuccessorBlockArguments() const { +TermInst::SuccessorBlockArgumentListTy +TermInst::getSuccessorBlockArgumentLists() const { function_ref op; op = [](const SILSuccessor &succ) -> SILPhiArgumentArrayRef { return succ.getBB()->getSILPhiArguments(); }; - return SuccessorBlockArgumentsListTy(getSuccessors(), op); + return SuccessorBlockArgumentListTy(getSuccessors(), op); } YieldInst *YieldInst::create(SILDebugLocation loc, diff --git a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp index d0c9c67e5c49b..441ec5b14f928 100644 --- a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp +++ b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp @@ -187,7 +187,7 @@ cleanupDeadTrivialPhiArgs(SILValue initialValue, continue; auto *termInst = cast(user); - for (auto succBlockArgList : termInst->getSuccessorBlockArguments()) { + for (auto succBlockArgList : termInst->getSuccessorBlockArgumentLists()) { llvm::copy_if(succBlockArgList, std::back_inserter(worklist), [&](SILPhiArgument *succArg) -> bool { auto it = lower_bound(insertedPhis, succArg); diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index d9c84565225f9..06545ba42674f 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -934,7 +934,7 @@ static SILInstruction *getNonPhiBlockIncomingValueDef(SILValue incomingValue, static bool terminatorHasAnyKnownPhis(TermInst *ti, ArrayRef insertedPhiNodesSorted) { - for (auto succArgList : ti->getSuccessorBlockArguments()) { + for (auto succArgList : ti->getSuccessorBlockArgumentLists()) { if (llvm::any_of(succArgList, [&](SILPhiArgument *arg) { return binary_search(insertedPhiNodesSorted, arg); })) { diff --git a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp index 2333a2fb84a3d..d73cb686ea255 100644 --- a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp @@ -292,7 +292,7 @@ struct SemanticARCOptVisitor #define FORWARDING_TERM(NAME) \ bool visit##NAME##Inst(NAME##Inst *cls) { \ - for (auto succValues : cls->getSuccessorBlockArguments()) { \ + for (auto succValues : cls->getSuccessorBlockArgumentLists()) { \ for (SILValue v : succValues) { \ worklist.insert(v); \ } \ From 1b850a2afd758af7cfbd5eb4a0abf2a554749386 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sat, 21 Dec 2019 13:04:07 -0800 Subject: [PATCH 214/478] build: improve libedit handling for builds Use the FindLibEdit.cmake module from LLDB to properly control where the libedit libraries are searched for and linked from as well as where the headers come from. This uses the standard mechanisms which allows users to control where libedit is pulled from (which is important for cross-compilation). This second version is more aggressive about pruning the libedit handling. The Ubuntu 14.04 version of libedit does not have `histedit.h`, and the intent is to rely on that to determine if we have unicode support or not. --- CMakeLists.txt | 11 ++-- cmake/modules/FindLibEdit.cmake | 61 +++++++++++++++++ include/swift/Config.h.in | 2 - lib/Immediate/CMakeLists.txt | 8 ++- lib/Immediate/REPL.cpp | 10 +-- tools/SourceKit/tools/CMakeLists.txt | 2 +- .../tools/sourcekitd-repl/CMakeLists.txt | 66 +++++++++---------- tools/driver/CMakeLists.txt | 3 - tools/swift-remoteast-test/CMakeLists.txt | 3 - 9 files changed, 108 insertions(+), 58 deletions(-) create mode 100644 cmake/modules/FindLibEdit.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index cc4a74ae93336..8a34a7733fd13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -924,14 +924,11 @@ else() find_package(LibXml2) endif() -# You need libedit linked in order to check if you have el_wgets. -cmake_push_check_state() -list(APPEND CMAKE_REQUIRED_LIBRARIES "edit") -check_symbol_exists(el_wgets "histedit.h" HAVE_EL_WGETS) -if(HAVE_EL_WGETS) - set(HAVE_UNICODE_LIBEDIT 1) +if(LLVM_ENABLE_LIBEDIT) + find_package(LibEdit REQUIRED) +else() + find_package(LibEdit) endif() -cmake_pop_check_state() check_symbol_exists(wait4 "sys/wait.h" HAVE_WAIT4) diff --git a/cmake/modules/FindLibEdit.cmake b/cmake/modules/FindLibEdit.cmake new file mode 100644 index 0000000000000..671ca9bcea379 --- /dev/null +++ b/cmake/modules/FindLibEdit.cmake @@ -0,0 +1,61 @@ +#.rst: +# FindLibEdit +# ----------- +# +# Find libedit library and headers +# +# The module defines the following variables: +# +# :: +# +# libedit_FOUND - true if libedit was found +# libedit_INCLUDE_DIRS - include search path +# libedit_LIBRARIES - libraries to link +# libedit_VERSION - version number + +if(libedit_INCLUDE_DIRS AND libedit_LIBRARIES) + set(libedit_FOUND TRUE) +else() + find_package(PkgConfig QUIET) + pkg_check_modules(PC_LIBEDIT QUIET libedit) + + find_path(libedit_INCLUDE_DIRS + NAMES + histedit.h + HINTS + ${PC_LIBEDIT_INCLUDEDIR} + ${PC_LIBEDIT_INCLUDE_DIRS} + ${CMAKE_INSTALL_FULL_INCLUDEDIR}) + find_library(libedit_LIBRARIES + NAMES + edit libedit + HINTS + ${PC_LIBEDIT_LIBDIR} + ${PC_LIBEDIT_LIBRARY_DIRS} + ${CMAKE_INSTALL_FULL_LIBDIR}) + + if(libedit_INCLUDE_DIRS AND EXISTS "${libedit_INCLUDE_DIRS}/histedit.h") + file(STRINGS "${libedit_INCLUDE_DIRS}/histedit.h" + libedit_major_version_str + REGEX "^#define[ \t]+LIBEDIT_MAJOR[ \t]+[0-9]+") + string(REGEX REPLACE "^#define[ \t]+LIBEDIT_MAJOR[ \t]+([0-9]+)" "\\1" + LIBEDIT_MAJOR_VERSION "${libedit_major_version_str}") + + file(STRINGS "${libedit_INCLUDE_DIRS}/histedit.h" + libedit_minor_version_str + REGEX "^#define[ \t]+LIBEDIT_MINOR[ \t]+[0-9]+") + string(REGEX REPLACE "^#define[ \t]+LIBEDIT_MINOR[ \t]+([0-9]+)" "\\1" + LIBEDIT_MINOR_VERSION "${libedit_minor_version_str}") + + set(libedit_VERSION_STRING "${libedit_major_version}.${libedit_minor_version}") + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(libedit + REQUIRED_VARS + libedit_INCLUDE_DIRS + libedit_LIBRARIES + VERSION_VAR + libedit_VERSION_STRING) + mark_as_advanced(libedit_INCLUDE_DIRS libedit_LIBRARIES) +endif() diff --git a/include/swift/Config.h.in b/include/swift/Config.h.in index 0b5534cf7424f..ec8030b3d1b03 100644 --- a/include/swift/Config.h.in +++ b/include/swift/Config.h.in @@ -6,8 +6,6 @@ #cmakedefine SWIFT_HAVE_WORKING_STD_REGEX 1 -#cmakedefine HAVE_UNICODE_LIBEDIT 1 - #cmakedefine HAVE_WAIT4 1 #cmakedefine HAVE_PROC_PID_RUSAGE 1 diff --git a/lib/Immediate/CMakeLists.txt b/lib/Immediate/CMakeLists.txt index c9e27358976d7..0c7d3f5d87351 100644 --- a/lib/Immediate/CMakeLists.txt +++ b/lib/Immediate/CMakeLists.txt @@ -12,7 +12,9 @@ target_link_libraries(swiftImmediate PRIVATE swiftIRGen swiftSILGen swiftSILOptimizer) - -if(HAVE_UNICODE_LIBEDIT) - target_link_libraries(swiftImmediate PRIVATE edit) +if(libedit_FOUND) + target_compile_definitions(swiftImmediate PRIVATE + HAVE_LIBEDIT) + target_link_libraries(swiftImmediate PRIVATE + ${libedit_LIBRARIES}) endif() diff --git a/lib/Immediate/REPL.cpp b/lib/Immediate/REPL.cpp index ab8d6d0f9add4..67c26f1c7d2d3 100644 --- a/lib/Immediate/REPL.cpp +++ b/lib/Immediate/REPL.cpp @@ -37,7 +37,7 @@ #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" -#if HAVE_UNICODE_LIBEDIT +#if HAVE_LIBEDIT #include #include #endif @@ -120,8 +120,8 @@ class ConvertForWcharSize<4> { }; using Convert = ConvertForWcharSize; - -#if HAVE_UNICODE_LIBEDIT + +#if HAVE_LIBEDIT static void convertFromUTF8(llvm::StringRef utf8, llvm::SmallVectorImpl &out) { size_t reserve = out.size() + utf8.size(); @@ -135,7 +135,7 @@ static void convertFromUTF8(llvm::StringRef utf8, (void)res; out.set_size(wide_begin - out.begin()); } - + static void convertToUTF8(llvm::ArrayRef wide, llvm::SmallVectorImpl &out) { size_t reserve = out.size() + wide.size()*4; @@ -153,7 +153,7 @@ static void convertToUTF8(llvm::ArrayRef wide, } // end anonymous namespace -#if HAVE_UNICODE_LIBEDIT +#if HAVE_LIBEDIT static ModuleDecl * typeCheckREPLInput(ModuleDecl *MostRecentModule, StringRef Name, diff --git a/tools/SourceKit/tools/CMakeLists.txt b/tools/SourceKit/tools/CMakeLists.txt index 6c89dc8a6c92b..334b110893da3 100644 --- a/tools/SourceKit/tools/CMakeLists.txt +++ b/tools/SourceKit/tools/CMakeLists.txt @@ -6,7 +6,7 @@ include_directories( add_swift_lib_subdirectory(sourcekitd) add_swift_tool_subdirectory(sourcekitd-test) -if(HAVE_UNICODE_LIBEDIT) +if(libedit_FOUND) add_swift_tool_subdirectory(sourcekitd-repl) endif() add_swift_tool_subdirectory(complete-test) diff --git a/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt index 49c40efe5b9ff..57233bc1a9b61 100644 --- a/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt @@ -1,36 +1,34 @@ -set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} edit) -check_symbol_exists(el_wgets "histedit.h" HAVE_UNICODE_LIBEDIT) - -if(HAVE_UNICODE_LIBEDIT) - add_sourcekit_executable(sourcekitd-repl - sourcekitd-repl.cpp - LLVM_LINK_COMPONENTS coverage lto - ) - target_link_libraries(sourcekitd-repl PRIVATE edit) - if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) - target_link_libraries(sourcekitd-repl PRIVATE sourcekitdInProc) - else() - target_link_libraries(sourcekitd-repl PRIVATE sourcekitd) - endif() - if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) - target_link_libraries(sourcekitd-repl PRIVATE - dispatch - BlocksRuntime) - endif() - - if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set_target_properties(sourcekitd-repl - PROPERTIES - LINK_FLAGS "-Wl,-rpath -Wl,@executable_path/../lib") - endif() - if(SWIFT_ANALYZE_CODE_COVERAGE) - set_property(TARGET sourcekitd-repl APPEND_STRING PROPERTY - LINK_FLAGS " -fprofile-instr-generate -fcoverage-mapping") - endif() +add_sourcekit_executable(sourcekitd-repl + sourcekitd-repl.cpp + LLVM_LINK_COMPONENTS coverage lto +) +if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) + target_link_libraries(sourcekitd-repl PRIVATE sourcekitdInProc) +else() + target_link_libraries(sourcekitd-repl PRIVATE sourcekitd) +endif() +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(sourcekitd-repl PRIVATE + dispatch + BlocksRuntime) +endif() +target_include_directories(sourcekitd-repl PRIVATE + ${libedit_INCLUDE_DIRS}) +target_link_libraries(sourcekitd-repl PRIVATE + ${libedit_LIBRARIES}) - add_dependencies(tools sourcekitd-repl) - swift_install_in_component(TARGETS sourcekitd-repl - RUNTIME - DESTINATION bin - COMPONENT tools) +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set_target_properties(sourcekitd-repl + PROPERTIES + LINK_FLAGS "-Wl,-rpath -Wl,@executable_path/../lib") +endif() +if(SWIFT_ANALYZE_CODE_COVERAGE) + set_property(TARGET sourcekitd-repl APPEND_STRING PROPERTY + LINK_FLAGS " -fprofile-instr-generate -fcoverage-mapping") endif() + +add_dependencies(tools sourcekitd-repl) +swift_install_in_component(TARGETS sourcekitd-repl + RUNTIME + DESTINATION bin + COMPONENT tools) diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 10e926b4e67e8..39aeebb29bafd 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -9,9 +9,6 @@ target_link_libraries(swift PRIVATE swiftDriver swiftFrontendTool) -if(HAVE_UNICODE_LIBEDIT) - target_link_libraries(swift PRIVATE edit) -endif() swift_create_post_build_symlink(swift SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" diff --git a/tools/swift-remoteast-test/CMakeLists.txt b/tools/swift-remoteast-test/CMakeLists.txt index 54987f792c12f..ecf4c953a1857 100644 --- a/tools/swift-remoteast-test/CMakeLists.txt +++ b/tools/swift-remoteast-test/CMakeLists.txt @@ -7,9 +7,6 @@ target_link_libraries(swift-remoteast-test swiftFrontendTool swiftRemoteAST) set_target_properties(swift-remoteast-test PROPERTIES ENABLE_EXPORTS 1) -if(HAVE_UNICODE_LIBEDIT) - target_link_libraries(swift-remoteast-test PRIVATE edit) -endif() # If building as part of clang, make sure the headers are installed. if(NOT SWIFT_BUILT_STANDALONE) From 03fbf6598c2899f6e0a4a16efee85a782801b780 Mon Sep 17 00:00:00 2001 From: breandan Date: Mon, 30 Dec 2019 06:38:02 -0500 Subject: [PATCH 215/478] Fix broken link --- docs/DifferentiableProgramming.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DifferentiableProgramming.md b/docs/DifferentiableProgramming.md index d676f556d3c46..ce7adfa65e74f 100644 --- a/docs/DifferentiableProgramming.md +++ b/docs/DifferentiableProgramming.md @@ -2030,7 +2030,7 @@ and data structures that deal with differentiable function values, including: Arbitrary higher-order functions that require arguments to be differentiable functions. Differential operators, e.g. `derivative(of:)`, described in the -[differential operators and differentiation APIs](differential-operators-and-differentiation-apis) +[differential operators and differentiation APIs](#differential-operators-1) section. Differentiable higher-order functions for collections, e.g. [`Array.differentiableReduce(_:_:)`](https://gist.github.com/rxwei/2739515f77a62d26add66947cb179911). Data structures that store `@differentiable` functions as a property. Neural From 17655b7c3e4730deb99d21e2b19689c27676caa9 Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Tue, 31 Dec 2019 07:31:33 -0800 Subject: [PATCH 216/478] Correct batch mode partitiion count calculation --- lib/Driver/Compilation.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index d8083a069a341..248ffd720b20e 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -1365,7 +1365,14 @@ namespace driver { size_t NumTasks = TQ->getNumberOfParallelTasks(); size_t NumFiles = PendingExecution.size(); size_t SizeLimit = Comp.getBatchSizeLimit().getValueOr(DefaultSizeLimit); - return std::max(NumTasks, NumFiles / SizeLimit); + + // An explanation of why the partition calculation isn't simple division. + // Using the default limit as an example, a module of 26 files must be + // compiled in 2 batches. Simple division yields 26/25 = 1 batch, but a + // single batch of 26 would exceed the limit of 25. To round up, the + // calculation is: `(x - 1) / y + 1`. + size_t NumPartitions = (NumFiles - 1) / SizeLimit + 1; + return std::max(NumTasks, NumPartitions); } /// Select jobs that are batch-combinable from \c PendingExecution, combine From f4dfbe94e5a7a2616a18bc9926a619b45ad234ac Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 31 Dec 2019 16:13:22 -0800 Subject: [PATCH 217/478] [ownership] Require all operands of ref_tail_addr to have guaranteed ownership. This ensures that the optimizer has a summary of where the ref_tail_addr will no longer be used. This is important when analyzing the lifetime of the base of the ref_tail_addr. I also cleaned up a little the description around the specification for this in OperandOwnership. Now all instructions that are "INTERIOR_POINTER_PROJECTIONS" have their own section/macro as a form of self documenting. --- lib/SIL/OperandOwnership.cpp | 14 ++++++++++++-- lib/SILGen/SILGenBuiltin.cpp | 4 ++-- test/SILGen/builtins.swift | 17 +++++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/SIL/OperandOwnership.cpp b/lib/SIL/OperandOwnership.cpp index a08e2bd634ab9..cfd48187cb30f 100644 --- a/lib/SIL/OperandOwnership.cpp +++ b/lib/SIL/OperandOwnership.cpp @@ -142,6 +142,18 @@ SHOULD_NEVER_VISIT_INST(StrongRelease) #include "swift/AST/ReferenceStorage.def" #undef SHOULD_NEVER_VISIT_INST +/// Instructions that are interior pointers into a guaranteed value. +#define INTERIOR_POINTER_PROJECTION(INST) \ + OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ + INST##Inst *i) { \ + assert(i->getNumOperands() && "Expected to have non-zero operands"); \ + return Map::compatibilityMap(ValueOwnershipKind::Guaranteed, \ + UseLifetimeConstraint::MustBeLive); \ + } +INTERIOR_POINTER_PROJECTION(RefElementAddr) +INTERIOR_POINTER_PROJECTION(RefTailAddr) +#undef INTERIOR_POINTER_PROJECTION + /// Instructions whose arguments are always compatible with one convention. #define CONSTANT_OWNERSHIP_INST(OWNERSHIP, USE_LIFETIME_CONSTRAINT, INST) \ OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ @@ -151,7 +163,6 @@ SHOULD_NEVER_VISIT_INST(StrongRelease) ValueOwnershipKind::OWNERSHIP, \ UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT); \ } -CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, RefElementAddr) CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialValue) CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialBoxValue) CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, AutoreleaseValue) @@ -271,7 +282,6 @@ ACCEPTS_ANY_OWNERSHIP_INST(BridgeObjectToWord) ACCEPTS_ANY_OWNERSHIP_INST(ClassifyBridgeObject) ACCEPTS_ANY_OWNERSHIP_INST(CopyBlock) ACCEPTS_ANY_OWNERSHIP_INST(OpenExistentialBox) -ACCEPTS_ANY_OWNERSHIP_INST(RefTailAddr) ACCEPTS_ANY_OWNERSHIP_INST(RefToRawPointer) ACCEPTS_ANY_OWNERSHIP_INST(SetDeallocating) ACCEPTS_ANY_OWNERSHIP_INST(ProjectExistentialBox) diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index 31390a0b9838e..5e61fac605ec0 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -976,8 +976,8 @@ static ManagedValue emitBuiltinProjectTailElems(SILGenFunction &SGF, SILType ElemType = SGF.getLoweredType(subs.getReplacementTypes()[1]-> getCanonicalType()).getObjectType(); - SILValue result = SGF.B.createRefTailAddr(loc, args[0].getValue(), - ElemType.getAddressType()); + SILValue result = SGF.B.createRefTailAddr( + loc, args[0].borrow(SGF, loc).getValue(), ElemType.getAddressType()); SILType rawPointerType = SILType::getRawPointerType(SGF.F.getASTContext()); result = SGF.B.createAddressToPointer(loc, result, rawPointerType); return ManagedValue::forUnmanaged(result); diff --git a/test/SILGen/builtins.swift b/test/SILGen/builtins.swift index 88f3dd656917f..fa7792c4c3aab 100644 --- a/test/SILGen/builtins.swift +++ b/test/SILGen/builtins.swift @@ -360,6 +360,23 @@ func projectTailElems(h: Header, ty: T.Type) -> Builtin.RawPointer { } // CHECK: } // end sil function '$s8builtins16projectTailElems1h2tyBpAA6HeaderC_xmtlF' +// Make sure we borrow if this is owned. +// +// CHECK-LABEL: sil hidden [ossa] @$s8builtins21projectTailElemsOwned{{[_0-9a-zA-Z]*}}F +func projectTailElemsOwned(h: __owned Header, ty: T.Type) -> Builtin.RawPointer { + // CHECK: bb0([[ARG1:%.*]] : @owned $Header + // CHECK: [[BORROWED_ARG1:%.*]] = begin_borrow [[ARG1]] + // CHECK: [[TA:%.*]] = ref_tail_addr [[BORROWED_ARG1]] : $Header + // -- Once we have passed the address through a2p, we no longer provide any guarantees. + // -- We still need to make sure that the a2p itself is in the borrow site though. + // CHECK: [[A2P:%.*]] = address_to_pointer [[TA]] + // CHECK: end_borrow [[BORROWED_ARG1]] + // CHECK: destroy_value [[ARG1]] + // CHECK: return [[A2P]] + return Builtin.projectTailElems(h, ty) +} +// CHECK: } // end sil function '$s8builtins21projectTailElemsOwned{{[_0-9a-zA-Z]*}}F' + // CHECK-LABEL: sil hidden [ossa] @$s8builtins11getTailAddr{{[_0-9a-zA-Z]*}}F func getTailAddr(start: Builtin.RawPointer, i: Builtin.Word, ty1: T1.Type, ty2: T2.Type) -> Builtin.RawPointer { // CHECK: [[P2A:%.*]] = pointer_to_address %0 From efb5e1e70be1e895a01dca3b4ec06ddb70d70af1 Mon Sep 17 00:00:00 2001 From: Austin Conlon Date: Wed, 1 Jan 2020 07:45:13 -0800 Subject: [PATCH 218/478] Fix outdated mention of Bug Reporter Bug Reporter is now Feedback Assistant. --- docs/tools/swift.pod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tools/swift.pod b/docs/tools/swift.pod index 1989ed851b255..ead511b2ad0de 100644 --- a/docs/tools/swift.pod +++ b/docs/tools/swift.pod @@ -86,7 +86,7 @@ for Swift, an open-source project, is located at L. If a bug can be reproduced only within an Xcode project or a playground, or if the bug is associated with an Apple NDA, please file a report to Apple's -bug reporter at L instead. +Feedback Assistant at L instead. =head1 SEE ALSO From 86ca0a174754a1d60eae117a2bf4629f05a38a59 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 27 Dec 2019 08:32:45 -0800 Subject: [PATCH 219/478] SIL: explicitly instantiate the `Operand` move ctor GCC 7 does not like the missing move constructor and does not implicitly synthesize one. Explicitly indicate that this should be synthesized. --- include/swift/SIL/SILValue.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/swift/SIL/SILValue.h b/include/swift/SIL/SILValue.h index 8fca7c4ee09a1..20df96dba132d 100644 --- a/include/swift/SIL/SILValue.h +++ b/include/swift/SIL/SILValue.h @@ -586,6 +586,8 @@ class Operand { Operand(const Operand &use) = delete; Operand &operator=(const Operand &use) = delete; + Operand(Operand &&) = default; + /// Return the current value being used by this operand. SILValue get() const { return TheValue; } From 075a93a4758ca1090810ee0283a9b56d61a97114 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 1 Jan 2020 13:16:45 -0800 Subject: [PATCH 220/478] SIL: add an explicit move assignment operation instantiation --- include/swift/SIL/SILValue.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/swift/SIL/SILValue.h b/include/swift/SIL/SILValue.h index 20df96dba132d..7662ae50c29db 100644 --- a/include/swift/SIL/SILValue.h +++ b/include/swift/SIL/SILValue.h @@ -587,6 +587,7 @@ class Operand { Operand &operator=(const Operand &use) = delete; Operand(Operand &&) = default; + Operand &operator=(Operand &&) = default; /// Return the current value being used by this operand. SILValue get() const { return TheValue; } From 8a6f44da6bb140e0963cf77bbf504f8be14dcc44 Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Wed, 1 Jan 2020 23:35:02 -0800 Subject: [PATCH 221/478] Extract DivideUp lambda --- lib/Driver/Compilation.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 248ffd720b20e..03e5c8d0391c1 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -1361,18 +1361,24 @@ namespace driver { // subprocesses than before. And significantly: it's doing so while // not exceeding the RAM of a typical 2-core laptop. + // An explanation of why the partition calculation isn't integer division. + // Using an example, a module of 26 files exceeds the limit of 25 and must + // be compiled in 2 batches. Integer division yields 26/25 = 1 batch, but + // a single batch of 26 exceeds the limit. The calculation must round up, + // which can be calculated using: `(x + y - 1) / y` + // + // Two key properties of this calculation are: + // DivideUp(M*N, N) = M + // DivideUp(M*N + 1, N) = M + 1 + auto DivideUp = [](size_t Num, size_t Div) -> size_t { + return (Num + Div - 1) / Div; + }; + size_t DefaultSizeLimit = 25; size_t NumTasks = TQ->getNumberOfParallelTasks(); size_t NumFiles = PendingExecution.size(); size_t SizeLimit = Comp.getBatchSizeLimit().getValueOr(DefaultSizeLimit); - - // An explanation of why the partition calculation isn't simple division. - // Using the default limit as an example, a module of 26 files must be - // compiled in 2 batches. Simple division yields 26/25 = 1 batch, but a - // single batch of 26 would exceed the limit of 25. To round up, the - // calculation is: `(x - 1) / y + 1`. - size_t NumPartitions = (NumFiles - 1) / SizeLimit + 1; - return std::max(NumTasks, NumPartitions); + return std::max(NumTasks, DivideUp(NumFiles, SizeLimit)); } /// Select jobs that are batch-combinable from \c PendingExecution, combine From d06d0394ed34259764b827a0268e34d71452029b Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Wed, 1 Jan 2020 23:39:17 -0800 Subject: [PATCH 222/478] Remove incomplete bit about properties --- lib/Driver/Compilation.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 03e5c8d0391c1..608cdfe2bd222 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -1366,10 +1366,6 @@ namespace driver { // be compiled in 2 batches. Integer division yields 26/25 = 1 batch, but // a single batch of 26 exceeds the limit. The calculation must round up, // which can be calculated using: `(x + y - 1) / y` - // - // Two key properties of this calculation are: - // DivideUp(M*N, N) = M - // DivideUp(M*N + 1, N) = M + 1 auto DivideUp = [](size_t Num, size_t Div) -> size_t { return (Num + Div - 1) / Div; }; From daadc3763d6f09cb2dbea18a246f31414fafd7ca Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Thu, 2 Jan 2020 08:37:34 -0800 Subject: [PATCH 223/478] Minor syntax updates to OptimizationTips --- docs/OptimizationTips.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/OptimizationTips.rst b/docs/OptimizationTips.rst index 60171e08f3a13..18ed2644e6707 100644 --- a/docs/OptimizationTips.rst +++ b/docs/OptimizationTips.rst @@ -319,7 +319,7 @@ generics. Some more examples of generics: func pop() -> T { ... } } - func myAlgorithm(_ a: [T], length: Int) { ... } + func myAlgorithm(_ a: [T], length: Int) { ... } // The compiler can specialize code of MyStack var stackOfInts: MyStack @@ -446,7 +446,7 @@ construct such a data structure: var value: T { get { return ref.val } set { - if (!isKnownUniquelyReferenced(&ref)) { + if !isKnownUniquelyReferenced(&ref) { ref = Ref(newValue) return } From 28ffcf9a7ad70915fb92277f8b2c7af2be4c4433 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 19 Dec 2019 10:15:40 -0800 Subject: [PATCH 224/478] [ownership] Eliminate the need for mark_uninitialized fixup. This commit eliminates the need for mark uninitialized fixup by updating the compiler so that we now emit: ``` %0 = alloc_box %1 = mark_uninitialized %0 %2 = project_box %1 ... destroy_value %1 ``` Instead of: ``` %0 = alloc_box %1 = project_box %0 %2 = mark_uninitialized %1 ... destroy_value %0 ``` Now that the first type of code is generated, I can change project_box to only take guaranteed arguments. This will ensure that the OSSA ARC optimizer can eliminate copies of boxes without needing to understand the usage of the project_box. --- lib/SIL/SILVerifier.cpp | 4 - lib/SILOptimizer/IPO/CapturePromotion.cpp | 13 +- .../Mandatory/DIMemoryUseCollector.cpp | 18 +-- .../Mandatory/DIMemoryUseCollector.h | 12 +- .../Mandatory/DefiniteInitialization.cpp | 35 ++++-- lib/SILOptimizer/PassManager/PassPipeline.cpp | 1 - test/SIL/ownership-verifier/definite_init.sil | 91 +++++++------- ..._init_markuninitialized_delegatingself.sil | 12 +- ...ite_init_markuninitialized_derivedself.sil | 18 +-- .../definite_init_markuninitialized_var.sil | 101 ++++++++------- .../definite_init_scalarization_test.sil | 36 +++--- .../SILOptimizer/mark_uninitialized_fixup.sil | 117 ------------------ 12 files changed, 176 insertions(+), 282 deletions(-) delete mode 100644 test/SILOptimizer/mark_uninitialized_fixup.sil diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index 12579365809f1..d4c1dc09ec675 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -2206,10 +2206,6 @@ class SILVerifier : public SILVerifierBase { // outside by the allocating initializer and we pass in the to be // initialized value as a SILArgument. || isa(Src) - // FIXME: Once the MarkUninitializedFixup pass is eliminated, - // mark_uninitialized should never be applied to a project_box. So - // at that point, this should be eliminated. - || isa(Src) // FIXME: We only support pointer to address here to not break LLDB. It is // important that long term we get rid of this since this is a situation // where LLDB is breaking SILGen/DI invariants by not creating a new diff --git a/lib/SILOptimizer/IPO/CapturePromotion.cpp b/lib/SILOptimizer/IPO/CapturePromotion.cpp index 40a136d26c687..8290ac6817cd5 100644 --- a/lib/SILOptimizer/IPO/CapturePromotion.cpp +++ b/lib/SILOptimizer/IPO/CapturePromotion.cpp @@ -1197,7 +1197,7 @@ constructClonedFunction(SILOptFunctionBuilder &FuncBuilder, /// 2. We only see a mark_uninitialized when paired with an (alloc_box, /// project_box). e.x.: /// -/// (mark_uninitialized (project_box (alloc_box))) +/// (project_box (mark_uninitialized (alloc_box))) /// /// The asserts are to make sure that if the initial safety condition check /// is changed, this code is changed as well. @@ -1209,15 +1209,16 @@ static SILValue getOrCreateProjectBoxHelper(SILValue PartialOperand) { } // Otherwise, handle the alloc_box case. If we have a mark_uninitialized on - // the box, we create the project value through that. + // the box, we know that we will have a project_box of that value due to SIL + // verifier invariants. SingleValueInstruction *Box = cast(PartialOperand); - if (auto *Op = Box->getSingleUse()) { - if (auto *MUI = dyn_cast(Op->getUser())) { - Box = MUI; + if (auto *MUI = Box->getSingleUserOfType()) { + if (auto *PBI = MUI->getSingleUserOfType()) { + return PBI; } } - // Just return a project_box. + // Otherwise, create a new project_box. SILBuilderWithScope B(std::next(Box->getIterator())); return B.createProjectBox(Box->getLoc(), Box, 0); } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 8574a3121e25a..125820a51d063 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -50,10 +50,9 @@ static void gatherDestroysOfContainer(const DIMemoryObjectInfo &memoryInfo, // // TODO: This should really be tracked separately from other destroys so that // we distinguish the lifetime of the container from the value itself. - assert(isa(uninitMemory)); - auto *pbi = cast(uninitMemory->getOperand(0)); - auto *abi = cast(pbi->getOperand()); - for (auto *user : abi->getUsersOfType()) { + assert(isa(uninitMemory)); + auto *mui = cast(uninitMemory->getOperand(0)); + for (auto *user : mui->getUsersOfType()) { useInfo.trackDestroy(user); } } @@ -97,7 +96,11 @@ static std::pair computeMemorySILType(MarkUninitializedInst *MemoryInst) { // Compute the type of the memory object. auto *MUI = MemoryInst; - SILType MemorySILType = MUI->getType().getObjectType(); + SILValue Address = MUI; + if (auto *PBI = Address->getSingleUserOfType()) { + Address = PBI; + } + SILType MemorySILType = Address->getType().getObjectType(); // If this is a let variable we're initializing, remember this so we don't // allow reassignment. @@ -1712,9 +1715,6 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { assert(StoresOfArgumentToSelf == 1 && "The 'self' argument should have been stored into the box exactly once"); - - // Gather the uses of the - gatherDestroysOfContainer(TheMemory, UseInfo); } void ClassInitElementUseCollector::collectClassInitSelfLoadUses( @@ -1818,6 +1818,7 @@ void swift::ownership::collectDIElementUsesFrom( if (shouldPerformClassInitSelf(MemoryInfo)) { ClassInitElementUseCollector UseCollector(MemoryInfo, UseInfo); UseCollector.collectClassInitSelfUses(); + gatherDestroysOfContainer(MemoryInfo, UseInfo); return; } @@ -1829,6 +1830,7 @@ void swift::ownership::collectDIElementUsesFrom( "delegating inits only have 1 bit"); collectDelegatingInitUses(MemoryInfo, UseInfo, MemoryInfo.getUninitializedValue()); + gatherDestroysOfContainer(MemoryInfo, UseInfo); return; } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index 8844469dbe79b..ae97da306a2a3 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -98,6 +98,11 @@ class DIMemoryObjectInfo { /// instruction. For alloc_box though it returns the project_box associated /// with the memory info. SingleValueInstruction *getUninitializedValue() const { + if (auto *mui = dyn_cast(MemoryInst)) { + if (auto *pbi = mui->getSingleUserOfType()) { + return pbi; + } + } return MemoryInst; } @@ -209,10 +214,9 @@ class DIMemoryObjectInfo { /// Given an element number (in the flattened sense) return a pointer to a /// leaf element of the specified number. - SILValue - emitElementAddress(unsigned TupleEltNo, SILLocation Loc, SILBuilder &B, - llvm::SmallVectorImpl> - &EndBorrowList) const; + SILValue emitElementAddress( + unsigned TupleEltNo, SILLocation Loc, SILBuilder &B, + SmallVectorImpl> &EndBorrowList) const; /// Return the swift type of the specified element. SILType getElementType(unsigned EltNo) const; diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index c6365749df173..3496285babc51 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -477,10 +477,14 @@ namespace { void processUninitializedRelease(SILInstruction *Release, bool consumed, SILBasicBlock::iterator InsertPt); - void processUninitializedReleaseOfBox(AllocBoxInst *ABI, + + /// Process a mark_uninitialized of an alloc_box that is uninitialized and + /// needs a dealloc_box. + void processUninitializedReleaseOfBox(MarkUninitializedInst *MUI, SILInstruction *Release, bool consumed, SILBasicBlock::iterator InsertPt); + void deleteDeadRelease(unsigned ReleaseID); void processNonTrivialRelease(unsigned ReleaseID); @@ -791,6 +795,7 @@ void LifetimeChecker::doIt() { // thereof) is not initialized on some path, the bad things happen. Process // releases to adjust for this. if (!TheMemory.hasTrivialType()) { + // NOTE: This array may increase in size! for (unsigned i = 0, e = Destroys.size(); i != e; ++i) processNonTrivialRelease(i); } @@ -1941,12 +1946,13 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) { } void LifetimeChecker::processUninitializedReleaseOfBox( - AllocBoxInst *ABI, SILInstruction *Release, bool consumed, + MarkUninitializedInst *MUI, SILInstruction *Release, bool consumed, SILBasicBlock::iterator InsertPt) { - assert(ABI == Release->getOperand(0)); + assert(isa(MUI->getOperand())); + assert(MUI == Release->getOperand(0)); SILBuilderWithScope B(Release); B.setInsertionPoint(InsertPt); - Destroys.push_back(B.createDeallocBox(Release->getLoc(), ABI)); + Destroys.push_back(B.createDeallocBox(Release->getLoc(), MUI)); } void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, @@ -1956,8 +1962,10 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, // dealloc_partial_ref to free the memory. If this is a derived class, we // may have to do a load of the 'self' box to get the class reference. if (!TheMemory.isClassInitSelf()) { - if (auto *ABI = dyn_cast(Release->getOperand(0))) { - return processUninitializedReleaseOfBox(ABI, Release, consumed, InsertPt); + if (auto *MUI = dyn_cast(Release->getOperand(0))) { + if (isa(MUI->getOperand())) { + return processUninitializedReleaseOfBox(MUI, Release, consumed, InsertPt); + } } return; } @@ -1972,9 +1980,14 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, // If we see an alloc_box as the pointer, then we're deallocating a 'box' for // self. Make sure that the box gets deallocated (not released) since the // pointer it contains will be manually cleaned up. - auto *ABI = dyn_cast(Release->getOperand(0)); - if (ABI) - Pointer = getOrCreateProjectBox(ABI, 0); + auto *MUI = dyn_cast(Release->getOperand(0)); + + if (MUI && isa(MUI->getOperand())) { + Pointer = MUI->getSingleUserOfType(); + assert(Pointer); + } else { + MUI = nullptr; + } if (!consumed) { if (Pointer->getType().isAddress()) @@ -1999,8 +2012,8 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, } // dealloc_box the self box if necessary. - if (ABI) { - auto DB = B.createDeallocBox(Loc, ABI); + if (MUI) { + auto DB = B.createDeallocBox(Loc, MUI); Destroys.push_back(DB); } } diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 46626b254534a..382c28bfdc48e 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -74,7 +74,6 @@ static void addOwnershipModelEliminatorPipeline(SILPassPipelinePlan &P) { /// Passes for performing definite initialization. Must be run together in this /// order. static void addDefiniteInitialization(SILPassPipelinePlan &P) { - P.addMarkUninitializedFixup(); P.addDefiniteInitialization(); P.addRawSILInstLowering(); } diff --git a/test/SIL/ownership-verifier/definite_init.sil b/test/SIL/ownership-verifier/definite_init.sil index 4a8e869f03b6a..edce0907eed35 100644 --- a/test/SIL/ownership-verifier/definite_init.sil +++ b/test/SIL/ownership-verifier/definite_init.sil @@ -32,17 +32,17 @@ sil @makesInt : $@convention(thin) () -> Builtin.Int32 //} sil [ossa] @used_by_inout : $@convention(thin) (Builtin.Int32) -> (Builtin.Int32, Builtin.Int32) { bb0(%0 : $Builtin.Int32): - %91 = alloc_box $<τ_0_0> { var τ_0_0 } - %91a = project_box %91 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %91a : $*Builtin.Int32 - store %0 to [trivial] %1 : $*Builtin.Int32 - %3 = load [trivial] %1 : $*Builtin.Int32 - %5 = function_ref @takes_Int_inout : $@convention(thin) (@inout Builtin.Int32) -> () - %6 = apply %5(%1) : $@convention(thin) (@inout Builtin.Int32) -> () - %7 = load [trivial] %1 : $*Builtin.Int32 - %8 = tuple (%3 : $Builtin.Int32, %7 : $Builtin.Int32) - destroy_value %91 : $<τ_0_0> { var τ_0_0 } - return %8 : $(Builtin.Int32, Builtin.Int32) + %2 = alloc_box $<τ_0_0> { var τ_0_0 } + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 + store %0 to [trivial] %4 : $*Builtin.Int32 + %5 = load [trivial] %4 : $*Builtin.Int32 + %6 = function_ref @takes_Int_inout : $@convention(thin) (@inout Builtin.Int32) -> () + %7 = apply %6(%4) : $@convention(thin) (@inout Builtin.Int32) -> () + %8 = load [trivial] %4 : $*Builtin.Int32 + %9 = tuple (%5 : $Builtin.Int32, %8 : $Builtin.Int32) + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + return %9 : $(Builtin.Int32, Builtin.Int32) } struct AddressOnlyStruct { @@ -57,24 +57,24 @@ sil @returns_generic_struct : $@convention(thin) () -> @out AddressOnlyStruct sil [ossa] @call_struct_return_function : $@convention(thin) () -> Builtin.Int32 { bb0: %0 = alloc_box $<τ_0_0> { var τ_0_0 } - %0a = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %0a : $*AddressOnlyStruct + %0a = mark_uninitialized [var] %0 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %0a : $<τ_0_0> { var τ_0_0 } , 0 %2 = function_ref @returns_generic_struct : $@convention(thin) () -> @out AddressOnlyStruct %3 = apply %2(%1) : $@convention(thin) () -> @out AddressOnlyStruct %4 = struct_element_addr %1 : $*AddressOnlyStruct, #AddressOnlyStruct.b %5 = load [trivial] %4 : $*Builtin.Int32 - destroy_value %0 : $<τ_0_0> { var τ_0_0 } + destroy_value %0a : $<τ_0_0> { var τ_0_0 } return %5 : $Builtin.Int32 } sil [ossa] @copy_addr1 : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): %3 = alloc_box $<τ_0_0> { var τ_0_0 } - %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [var] %3a : $*T + %3a = mark_uninitialized [var] %3 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %3a : $<τ_0_0> { var τ_0_0 } , 0 copy_addr [take] %1 to [initialization] %4 : $*T copy_addr %4 to [initialization] %0 : $*T - destroy_value %3 : $<τ_0_0> { var τ_0_0 } + destroy_value %3a : $<τ_0_0> { var τ_0_0 } %9 = tuple () return %9 : $() } @@ -87,8 +87,8 @@ sil @getSomeOptionalClass : $@convention(thin) () -> Optional sil [ossa] @assign_test_trivial : $@convention(thin) (Builtin.Int32) -> Builtin.Int32 { bb0(%0 : $Builtin.Int32): %7 = alloc_box $<τ_0_0> { var τ_0_0 } - %7a = project_box %7 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %7a : $*Builtin.Int32 + %7a = mark_uninitialized [var] %7 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %7a : $<τ_0_0> { var τ_0_0 } , 0 // These assigns are a mix of init + store forms, but because Int is trivial, // they all turn into stores. @@ -97,7 +97,7 @@ bb0(%0 : $Builtin.Int32): assign %0 to %1 : $*Builtin.Int32 %2 = load [trivial] %1 : $*Builtin.Int32 - destroy_value %7 : $<τ_0_0> { var τ_0_0 } + destroy_value %7a : $<τ_0_0> { var τ_0_0 } return %2 : $Builtin.Int32 } @@ -108,29 +108,28 @@ bb0: // lone store), the second becomes an assignment (retain/release dance). %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %c = mark_uninitialized [var] %ba : $*SomeClass + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 %f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass %4 = apply %f() : $@convention(thin) () -> @owned SomeClass assign %4 to %c : $*SomeClass %8 = apply %f() : $@convention(thin) () -> @owned SomeClass assign %8 to %c : $*SomeClass destroy_addr %c : $*SomeClass - dealloc_box %b : $<τ_0_0> { var τ_0_0 } + dealloc_box %ba : $<τ_0_0> { var τ_0_0 } %11 = tuple () return %11 : $() } - sil [ossa] @assign_test_addressonly : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %2 = mark_uninitialized [var] %ba : $*T + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %2 = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 copy_addr %1 to %2 : $*T copy_addr [take] %1 to %2 : $*T copy_addr %2 to [initialization] %0 : $*T - destroy_value %b : $<τ_0_0> { var τ_0_0 } + destroy_value %ba : $<τ_0_0> { var τ_0_0 } %9 = tuple () return %9 : $() } @@ -138,8 +137,8 @@ bb0(%0 : $*T, %1 : $*T): sil [ossa] @assign_test_unowned : $@convention(thin) () -> () { bb0: %b = alloc_box $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0 - %c = mark_uninitialized [var] %ba : $*@sil_unowned SomeClass + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0 %f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass @@ -155,14 +154,12 @@ bb0: destroy_value %8 : $SomeClass destroy_addr %c : $*@sil_unowned SomeClass - dealloc_box %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> + dealloc_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> %11 = tuple () return %11 : $() } - - struct ContainsNativeObject { var x : Builtin.Int32 var y : Builtin.NativeObject @@ -171,12 +168,12 @@ struct ContainsNativeObject { sil [ossa] @test_struct : $@convention(thin) (@inout ContainsNativeObject) -> () { bb0(%0 : $*ContainsNativeObject): %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %c = mark_uninitialized [var] %ba : $*ContainsNativeObject + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 %1 = load [copy] %0 : $*ContainsNativeObject assign %1 to %c : $*ContainsNativeObject - destroy_value %b : $<τ_0_0> { var τ_0_0 } + destroy_value %ba : $<τ_0_0> { var τ_0_0 } %x = tuple () return %x : $() } @@ -337,8 +334,8 @@ sil @superinit : $@convention(method) (@owned RootClassWithIVars) -> @owned Root sil [ossa] @derived_test1 : $@convention(method) (@owned DerivedClassWithIVars) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars // Get an int @@ -359,7 +356,7 @@ bb0(%0 : @owned $DerivedClassWithIVars): assign %16 to %3 : $*DerivedClassWithIVars %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } @@ -467,10 +464,10 @@ bb0(%0 : @owned $DerivedClassWithNontrivialStoredProperties): sil [ossa] @test_delegating_box_release : $@convention(method) (@owned RootClassWithNontrivialStoredProperties) -> () { bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): %2 = alloc_box $<τ_0_0> { var τ_0_0 } - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [delegatingself] %2a : $*RootClassWithNontrivialStoredProperties + %2a = mark_uninitialized [delegatingself] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %2a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %4 : $*RootClassWithNontrivialStoredProperties - destroy_value %2 : $<τ_0_0> { var τ_0_0 } + destroy_value %2a : $<τ_0_0> { var τ_0_0 } %13 = tuple () return %13 : $() } @@ -478,12 +475,12 @@ bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): sil [ossa] @test_delegating_rvalue_release : $@convention(method) (@owned RootClassWithNontrivialStoredProperties) -> () { bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): %2 = alloc_box $<τ_0_0> { var τ_0_0 } - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [delegatingself] %2a : $*RootClassWithNontrivialStoredProperties + %2a = mark_uninitialized [delegatingself] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %2a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %4 : $*RootClassWithNontrivialStoredProperties %6 = load [take] %4 : $*RootClassWithNontrivialStoredProperties destroy_value %6 : $RootClassWithNontrivialStoredProperties - destroy_value %2 : $<τ_0_0> { var τ_0_0 } + destroy_value %2a : $<τ_0_0> { var τ_0_0 } %13 = tuple () return %13 : $() } @@ -502,8 +499,8 @@ bb0(%0 : @owned $DerivedClassWithNontrivialStoredProperties): sil [ossa] @super_init_out_of_order : $@convention(method) (@owned DerivedClassWithIVars, Builtin.Int32) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars, %i : $Builtin.Int32): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars %8 = load_borrow %3 : $*DerivedClassWithIVars @@ -523,7 +520,7 @@ bb0(%0 : @owned $DerivedClassWithIVars, %i : $Builtin.Int32): %16 = unconditional_checked_cast %15 : $RootClassWithIVars to DerivedClassWithIVars assign %16 to %3 : $*DerivedClassWithIVars %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } diff --git a/test/SILOptimizer/definite_init_markuninitialized_delegatingself.sil b/test/SILOptimizer/definite_init_markuninitialized_delegatingself.sil index 5726186e7d536..d2e9a949690bf 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_delegatingself.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_delegatingself.sil @@ -89,10 +89,10 @@ bb0(%0 : $*MyStruct2, %1 : $@thin MyStruct2.Type): sil [ossa] @test_delegating_box_release : $@convention(method) (@owned RootClassWithNontrivialStoredProperties) -> () { bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): %2 = alloc_box $<τ_0_0> { var τ_0_0 } - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [delegatingselfallocated] %2a : $*RootClassWithNontrivialStoredProperties + %2a = mark_uninitialized [delegatingselfallocated] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %2a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %4 : $*RootClassWithNontrivialStoredProperties - destroy_value %2 : $<τ_0_0> { var τ_0_0 } + destroy_value %2a : $<τ_0_0> { var τ_0_0 } %13 = tuple () return %13 : $() @@ -114,12 +114,12 @@ bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): sil [ossa] @test_delegating_rvalue_release : $@convention(method) (@owned RootClassWithNontrivialStoredProperties) -> () { bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): %2 = alloc_box $<τ_0_0> { var τ_0_0 } - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [delegatingselfallocated] %2a : $*RootClassWithNontrivialStoredProperties + %2a = mark_uninitialized [delegatingselfallocated] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %2a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %4 : $*RootClassWithNontrivialStoredProperties %6 = load [take] %4 : $*RootClassWithNontrivialStoredProperties destroy_value %6 : $RootClassWithNontrivialStoredProperties - destroy_value %2 : $<τ_0_0> { var τ_0_0 } + destroy_value %2a : $<τ_0_0> { var τ_0_0 } %13 = tuple () return %13 : $() diff --git a/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil b/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil index d0be2dca9fded..7b7db31d6dc24 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil @@ -63,8 +63,8 @@ sil @getSomeOptionalClass : $@convention(thin) () -> Optional sil [ossa] @derived_test1 : $@convention(method) (@owned DerivedClassWithIVars) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars // Get an int @@ -87,7 +87,7 @@ bb0(%0 : @owned $DerivedClassWithIVars): // Finally perform the epilog. %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } @@ -112,8 +112,8 @@ bb0(%0 : @owned $DerivedClassWithIVars): sil [ossa] @derived_test2 : $@convention(method) (@owned DerivedClassWithIVars) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars %11 = load [take] %3 : $*DerivedClassWithIVars @@ -124,7 +124,7 @@ bb0(%0 : @owned $DerivedClassWithIVars): store %16 to [init] %3 : $*DerivedClassWithIVars %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } @@ -246,8 +246,8 @@ bb0(%0 : @owned $DerivedClassWithNontrivialStoredProperties): sil [ossa] @super_init_out_of_order : $@convention(method) (@owned DerivedClassWithIVars, Int) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars, %i : $Int): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars // Initialize properties in derived class. @@ -274,6 +274,6 @@ bb0(%0 : @owned $DerivedClassWithIVars, %i : $Int): %16 = unchecked_ref_cast %15 : $RootClassWithIVars to $DerivedClassWithIVars store %16 to [init] %3 : $*DerivedClassWithIVars %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } diff --git a/test/SILOptimizer/definite_init_markuninitialized_var.sil b/test/SILOptimizer/definite_init_markuninitialized_var.sil index fc41fed4a68e5..270d6c5b65516 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_var.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_var.sil @@ -15,10 +15,10 @@ sil @makesInt : $@convention(thin) () -> Int sil [ossa] @use_before_init : $@convention(thin) () -> Int { bb0: %0 = alloc_box $<τ_0_0> { var τ_0_0 } - %0a = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %0a : $*Int // expected-note {{variable defined here}} + %0a = mark_uninitialized [var] %0 : $<τ_0_0> { var τ_0_0 } // expected-note {{variable defined here}} + %1 = project_box %0a : $<τ_0_0> { var τ_0_0 } , 0 %4 = load [trivial] %1 : $*Int // expected-error {{variable '' used before being initialized}} - destroy_value %0 : $<τ_0_0> { var τ_0_0 } + destroy_value %0a : $<τ_0_0> { var τ_0_0 } return %4 : $Int } @@ -26,13 +26,13 @@ bb0: sil [ossa] @inout_uninit : $@convention(thin) () -> () { bb0: %0 = alloc_box $<τ_0_0> { var τ_0_0 } - %0a = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %0a : $*Int // expected-note {{variable defined here}} + %0a = mark_uninitialized [var] %0 : $<τ_0_0> { var τ_0_0 } // expected-note {{variable defined here}} + %1 = project_box %0a : $<τ_0_0> { var τ_0_0 } , 0 %5 = function_ref @takes_Int_inout : $@convention(thin) (@inout Int) -> () %6 = apply %5(%1) : $@convention(thin) (@inout Int) -> () // expected-error {{variable '' passed by reference before being initialized}} - destroy_value %0 : $<τ_0_0> { var τ_0_0 } + destroy_value %0a : $<τ_0_0> { var τ_0_0 } %t = tuple () return %t : $() } @@ -48,8 +48,8 @@ bb0: sil [ossa] @used_by_inout : $@convention(thin) (Int) -> (Int, Int) { bb0(%0 : $Int): %91 = alloc_box $<τ_0_0> { var τ_0_0 } - %91a = project_box %91 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %91a : $*Int + %91a = mark_uninitialized [var] %91 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %91a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [trivial] %1 : $*Int %3 = load [trivial] %1 : $*Int @@ -57,7 +57,7 @@ bb0(%0 : $Int): %6 = apply %5(%1) : $@convention(thin) (@inout Int) -> () %7 = load [trivial] %1 : $*Int %8 = tuple (%3 : $Int, %7 : $Int) - destroy_value %91 : $<τ_0_0> { var τ_0_0 } + destroy_value %91a : $<τ_0_0> { var τ_0_0 } return %8 : $(Int, Int) } @@ -74,14 +74,14 @@ sil [ossa] @returns_generic_struct : $@convention(thin) () -> @out AddressOnlySt sil [ossa] @call_struct_return_function : $@convention(thin) () -> Int { bb0: %0 = alloc_box $<τ_0_0> { var τ_0_0 } - %0a = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %0a : $*AddressOnlyStruct + %0a = mark_uninitialized [var] %0 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %0a : $<τ_0_0> { var τ_0_0 } , 0 %2 = function_ref @returns_generic_struct : $@convention(thin) () -> @out AddressOnlyStruct %3 = apply %2(%1) : $@convention(thin) () -> @out AddressOnlyStruct %4 = struct_element_addr %1 : $*AddressOnlyStruct, #AddressOnlyStruct.b %5 = load [trivial] %4 : $*Int - destroy_value %0 : $<τ_0_0> { var τ_0_0 } + destroy_value %0a : $<τ_0_0> { var τ_0_0 } return %5 : $Int } @@ -89,15 +89,14 @@ bb0: sil [ossa] @tuple_elements1 : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Int, Int)> - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0 - %3 = mark_uninitialized [var] %2a : $*(Int, Int) // expected-note {{variable defined here}} + %2a = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)> // expected-note {{variable defined here}} + %3 = project_box %2a : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0 %4 = tuple_element_addr %3 : $*(Int, Int), 0 %5 = tuple_element_addr %3 : $*(Int, Int), 1 %14 = function_ref @takes_Int_inout : $@convention(thin) (@inout Int) -> () %15 = tuple_element_addr %3 : $*(Int, Int), 1 %16 = apply %14(%15) : $@convention(thin) (@inout Int) -> () // expected-error {{variable '.1' passed by reference before being initialized}} - - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)> + destroy_value %2a : $<τ_0_0> { var τ_0_0 } <(Int, Int)> %99 = tuple () return %99 : $() } @@ -106,15 +105,15 @@ bb0(%0 : $Int): sil [ossa] @tuple_elements2 : $@convention(thin) (Int) -> (Int, Int) { bb0(%0 : $Int): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Int, Int)> - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0 - %3 = mark_uninitialized [var] %2a : $*(Int, Int) // expected-note {{variable defined here}} + %2a = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)> // expected-note {{variable defined here}} + %3 = project_box %2a : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0 %18 = tuple_element_addr %3 : $*(Int, Int), 0 store %0 to [trivial] %18 : $*Int %20 = load [trivial] %3 : $*(Int, Int) // expected-error {{variable '.1' used before being initialized}} %21 = tuple_extract %20 : $(Int, Int), 0 %22 = tuple_extract %20 : $(Int, Int), 1 %23 = tuple (%21 : $Int, %22 : $Int) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)> + destroy_value %2a : $<τ_0_0> { var τ_0_0 } <(Int, Int)> return %23 : $(Int, Int) } @@ -122,11 +121,11 @@ bb0(%0 : $Int): sil [ossa] @copy_addr1 : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): %3 = alloc_box $<τ_0_0> { var τ_0_0 } - %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [var] %3a : $*T + %3a = mark_uninitialized [var] %3 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %3a : $<τ_0_0> { var τ_0_0 } , 0 copy_addr [take] %1 to [initialization] %4 : $*T copy_addr %4 to [initialization] %0 : $*T - destroy_value %3 : $<τ_0_0> { var τ_0_0 } + destroy_value %3a : $<τ_0_0> { var τ_0_0 } %9 = tuple () return %9 : $() } @@ -135,10 +134,10 @@ bb0(%0 : $*T, %1 : $*T): sil [ossa] @copy_addr2 : $@convention(thin) (@in_guaranteed T) -> @out T { bb0(%0 : $*T, %1 : $*T): %3 = alloc_box $<τ_0_0> { var τ_0_0 } - %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [var] %3a : $*T // expected-note {{variable defined here}} + %3a = mark_uninitialized [var] %3 : $<τ_0_0> { var τ_0_0 } // expected-note {{variable defined here}} + %4 = project_box %3a : $<τ_0_0> { var τ_0_0 } , 0 copy_addr %4 to [initialization] %0 : $*T // expected-error {{variable '' used before being initialized}} - destroy_value %3 : $<τ_0_0> { var τ_0_0 } + destroy_value %3a : $<τ_0_0> { var τ_0_0 } %9 = tuple () return %9 : $() } @@ -150,16 +149,16 @@ sil [ossa] @closure0 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) sil [ossa] @closure_test : $@convention(thin) () -> () { bb0: %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %0 = mark_uninitialized [var] %1a : $*Int // expected-note {{variable defined here}} + %1a = mark_uninitialized [var] %1 : $<τ_0_0> { var τ_0_0 } // expected-note {{variable defined here}} + %0 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 %5 = function_ref @takes_closure : $@convention(thin) (@owned @callee_owned () -> ()) -> () %6 = function_ref @closure0 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () - %1copy = copy_value %1 : $<τ_0_0> { var τ_0_0 } + %1copy = copy_value %1a : $<τ_0_0> { var τ_0_0 } mark_function_escape %0 : $*Int // expected-error {{variable '' used by function definition before being initialized}} %8 = partial_apply %6(%1copy) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () %9 = apply %5(%8) : $@convention(thin) (@owned @callee_owned () -> ()) -> () - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } %11 = tuple () return %11 : $() @@ -176,8 +175,8 @@ sil [ossa] @getSomeOptionalClass : $@convention(thin) () -> @owned Optional Int { bb0(%0 : $Int): %7 = alloc_box $<τ_0_0> { var τ_0_0 } - %7a = project_box %7 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %7a : $*Int + %7a = mark_uninitialized [var] %7 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %7a : $<τ_0_0> { var τ_0_0 } , 0 // These assigns are a mix of init + store forms, but because Int is trivial, // they all turn into stores. @@ -186,7 +185,7 @@ bb0(%0 : $Int): assign %0 to %1 : $*Int %2 = load [trivial] %1 : $*Int - destroy_value %7 : $<τ_0_0> { var τ_0_0 } + destroy_value %7a : $<τ_0_0> { var τ_0_0 } return %2 : $Int } @@ -198,8 +197,8 @@ bb0: // lone store), the second becomes a reassignment (retain/release dance). %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %c = mark_uninitialized [var] %ba : $*SomeClass + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 %f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass @@ -217,7 +216,7 @@ bb0: destroy_addr %c : $*SomeClass // CHECK-NEXT: destroy_addr - dealloc_box %b : $<τ_0_0> { var τ_0_0 } + dealloc_box %ba : $<τ_0_0> { var τ_0_0 } %11 = tuple () return %11 : $() @@ -227,8 +226,8 @@ bb0: sil [ossa] @assign_test_addressonly : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %2 = mark_uninitialized [var] %ba : $*T + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %2 = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 // CHECK: [[PB:%[0-9]+]] = project_box @@ -244,7 +243,7 @@ bb0(%0 : $*T, %1 : $*T): copy_addr %2 to [initialization] %0 : $*T // CHECK-NEXT: copy_addr [[PB]] to [initialization] %0 : $*T - destroy_value %b : $<τ_0_0> { var τ_0_0 } + destroy_value %ba : $<τ_0_0> { var τ_0_0 } // CHECK-NEXT: destroy_value %2 %9 = tuple () return %9 : $() @@ -257,8 +256,8 @@ bb0: // second becomes an assignment. %b = alloc_box $<τ_0_0> { var τ_0_0 } <@sil_weak Optional> - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional>, 0 - %c = mark_uninitialized [var] %ba : $*@sil_weak Optional + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional> + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional>, 0 %f = function_ref @getSomeOptionalClass : $@convention(thin) () -> @owned Optional @@ -281,7 +280,7 @@ bb0: destroy_value %8 : $Optional // CHECK-NEXT: destroy_value [[C2]] - destroy_value %b : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional> + destroy_value %ba : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional> %11 = tuple () return %11 : $() @@ -294,8 +293,8 @@ bb0: // second becomes a reassignment. %b = alloc_box $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0 - %c = mark_uninitialized [var] %ba : $*@sil_unowned SomeClass + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0 %f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass @@ -328,7 +327,7 @@ bb0: // CHECK-NEXT: destroy_value [[C2]] destroy_addr %c : $*@sil_unowned SomeClass - dealloc_box %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> + dealloc_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> %11 = tuple () return %11 : $() @@ -342,12 +341,12 @@ struct ContainsNativeObject { sil [ossa] @test_struct : $@convention(thin) (@inout ContainsNativeObject) -> () { bb0(%0 : $*ContainsNativeObject): %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %c = mark_uninitialized [var] %ba : $*ContainsNativeObject + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 %1 = load [copy] %0 : $*ContainsNativeObject assign %1 to %c : $*ContainsNativeObject - destroy_value %b : $<τ_0_0> { var τ_0_0 } + destroy_value %ba : $<τ_0_0> { var τ_0_0 } %x = tuple () return %x : $() } @@ -557,8 +556,8 @@ bb2: sil [ossa] @conditionalInitOrAssignAllocBox : $@convention(thin) () -> () { bb0: %box = alloc_box $<τ_0_0> { var τ_0_0 } - %5 = project_box %box : $<τ_0_0> { var τ_0_0 } , 0 - %7 = mark_uninitialized [var] %5 : $*SomeClass + %5 = mark_uninitialized [var] %box : $<τ_0_0> { var τ_0_0 } + %7 = project_box %5 : $<τ_0_0> { var τ_0_0 } , 0 %2 = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass cond_br undef, bb1, bb2 @@ -566,11 +565,11 @@ bb0: bb1: %12 = apply %2() : $@convention(thin) () -> @owned SomeClass assign %12 to %7 : $*SomeClass - destroy_value %box : $<τ_0_0> { var τ_0_0 } + destroy_value %5 : $<τ_0_0> { var τ_0_0 } br bb3 bb2: - destroy_value %box : $<τ_0_0> { var τ_0_0 } + dealloc_box %5 : $<τ_0_0> { var τ_0_0 } br bb3 bb3: diff --git a/test/SILOptimizer/definite_init_scalarization_test.sil b/test/SILOptimizer/definite_init_scalarization_test.sil index 6f45b22dc4c58..a2f4a88d2367d 100644 --- a/test/SILOptimizer/definite_init_scalarization_test.sil +++ b/test/SILOptimizer/definite_init_scalarization_test.sil @@ -43,12 +43,12 @@ struct ObjectValue { sil [ossa] @test_store_trivial : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 - %4 = mark_uninitialized [var] %3 : $*(TripleInt, (TripleInt, TripleInt)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 %5 = tuple(%0 : $TripleInt, %1 : $TripleInt) %6 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) store %6 to [trivial] %4 : $*(TripleInt, (TripleInt, TripleInt)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> %9999 = tuple() return %9999 : $() } @@ -73,12 +73,12 @@ bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): sil [ossa] @test_store_owned : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : @owned $Builtin.NativeObject): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 - %4 = mark_uninitialized [var] %3 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 %5 = tuple(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject) %6 = tuple(%1a : $Builtin.NativeObject, %5 : $(Builtin.NativeObject, Builtin.NativeObject)) store %6 to [init] %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> %9999 = tuple() return %9999 : $() } @@ -103,12 +103,12 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : sil [ossa] @test_assign_trivial : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 - %4 = mark_uninitialized [var] %3 : $*(TripleInt, (TripleInt, TripleInt)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 %5 = tuple(%0 : $TripleInt, %1 : $TripleInt) %6 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) assign %6 to %4 : $*(TripleInt, (TripleInt, TripleInt)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> %9999 = tuple() return %9999 : $() } @@ -139,14 +139,14 @@ bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): sil [ossa] @test_assign_trivial_2 : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 - %4 = mark_uninitialized [var] %3 : $*(TripleInt, (TripleInt, TripleInt)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 %5 = tuple(%0 : $TripleInt, %1 : $TripleInt) %6 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) %7 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) assign %6 to %4 : $*(TripleInt, (TripleInt, TripleInt)) assign %7 to %4 : $*(TripleInt, (TripleInt, TripleInt)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> %9999 = tuple() return %9999 : $() } @@ -171,12 +171,12 @@ bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): sil [ossa] @test_assign_owned : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : @owned $Builtin.NativeObject): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 - %4 = mark_uninitialized [var] %3 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 %5 = tuple(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject) %6 = tuple(%1a : $Builtin.NativeObject, %5 : $(Builtin.NativeObject, Builtin.NativeObject)) assign %6 to %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> %9999 = tuple() return %9999 : $() } @@ -209,14 +209,14 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : sil [ossa] @test_assigned_owned_2 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : @owned $Builtin.NativeObject): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 - %4 = mark_uninitialized [var] %3 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 %5 = tuple(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject) %6 = tuple(%1a : $Builtin.NativeObject, %5 : $(Builtin.NativeObject, Builtin.NativeObject)) %7 = copy_value %6 : $(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) assign %6 to %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) assign %7 to %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> %9999 = tuple() return %9999 : $() } diff --git a/test/SILOptimizer/mark_uninitialized_fixup.sil b/test/SILOptimizer/mark_uninitialized_fixup.sil deleted file mode 100644 index ae09df4f46d6b..0000000000000 --- a/test/SILOptimizer/mark_uninitialized_fixup.sil +++ /dev/null @@ -1,117 +0,0 @@ -// RUN: %target-sil-opt -mark-uninitialized-fixup %s | %FileCheck %s - -sil_stage raw - -import Builtin - -// CHECK-LABEL: sil [ossa] @single_bb_single_proj_case : $@convention(thin) () -> () { -// CHECK: [[BOX:%.*]] = alloc_box ${ var Builtin.Int32 } -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: mark_uninitialized [rootself] [[PB]] -sil [ossa] @single_bb_single_proj_case : $@convention(thin) () -> () { -bb0: - %0 = alloc_box ${ var Builtin.Int32 } - %1 = mark_uninitialized [rootself] %0 : ${ var Builtin.Int32 } - %2 = project_box %1 : ${ var Builtin.Int32 }, 0 - destroy_value %1 : ${ var Builtin.Int32 } - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @single_bb_multiple_proj_case : $@convention(thin) () -> () { -// CHECK: [[BOX:%.*]] = alloc_box ${ var Builtin.Int32 } -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: mark_uninitialized [rootself] [[PB]] -// CHECK: project_box [[BOX]] -sil [ossa] @single_bb_multiple_proj_case : $@convention(thin) () -> () { -bb0: - %0 = alloc_box ${ var Builtin.Int32 } - %1 = mark_uninitialized [rootself] %0 : ${ var Builtin.Int32 } - %2 = project_box %1 : ${ var Builtin.Int32 }, 0 - %3 = project_box %1 : ${ var Builtin.Int32 }, 0 - destroy_value %1 : ${ var Builtin.Int32 } - %9999 = tuple() - return %9999 : $() -} - - -// CHECK-LABEL: sil [ossa] @multiple_bb_case : $@convention(thin) () -> () { -// CHECK: [[BOX:%.*]] = alloc_box ${ var Builtin.Int32 } -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: mark_uninitialized [rootself] [[PB]] -// CHECK: br [[NEXT_BB:bb[0-9]+]] -// -// CHECK: [[NEXT_BB]]: -// CHECK: project_box [[BOX]] -sil [ossa] @multiple_bb_case : $@convention(thin) () -> () { -bb0: - %0 = alloc_box ${ var Builtin.Int32 } - %1 = mark_uninitialized [rootself] %0 : ${ var Builtin.Int32 } - %2 = project_box %1 : ${ var Builtin.Int32 }, 0 - br bb1 - -bb1: - %3 = project_box %1 : ${ var Builtin.Int32 }, 0 - destroy_value %1 : ${ var Builtin.Int32 } - %9999 = tuple() - return %9999 : $() -} - -struct Bool { - var _value: Builtin.Int1 -} - -// CHECK-LABEL: sil [ossa] @test_rauw_uses_of_project_box : $@convention(thin) (Builtin.Int1, @thin Bool.Type) -> Bool { -// CHECK: [[BOX:%.*]] = alloc_box ${ var Bool } -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: [[MUI:%.*]] = mark_uninitialized [rootself] [[PB]] -// CHECK: struct_element_addr [[MUI]] -// CHECK: load [trivial] [[MUI]] -// CHECK: destroy_value [[BOX]] -sil [ossa] @test_rauw_uses_of_project_box : $@convention(thin) (Builtin.Int1, @thin Bool.Type) -> Bool { -bb0(%0 : $Builtin.Int1, %1 : $@thin Bool.Type): - %2 = alloc_box ${ var Bool }, var, name "self" - %3 = mark_uninitialized [rootself] %2 : ${ var Bool } - %4 = project_box %3 : ${ var Bool }, 0 - debug_value %0 : $Builtin.Int1, let, name "v", argno 1 - %6 = struct_element_addr %4 : $*Bool, #Bool._value - assign %0 to %6 : $*Builtin.Int1 - %8 = load [trivial] %4 : $*Bool - destroy_value %3 : ${ var Bool } - return %8 : $Bool -} - -// This is a simulation of the type of callee generated by capture promotion. -sil @capture_promotion_generated_callee : $@convention(thin) (@in Bool) -> () - -// CHECK-LABEL: sil [ossa] @test_copyvalue_use_of_projectbox : $@convention(thin) (Builtin.Int1) -> () { -// CHECK: bb0([[ARG:%.*]] : $Builtin.Int1): -// CHECK: [[BOX:%.*]] = alloc_box ${ var Bool }, var, name "self" -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: [[MUI:%.*]] = mark_uninitialized [rootself] [[PB]] -// CHECK: [[BOX_COPY:%.*]] = copy_value [[BOX]] -// CHECK: [[PB_BOX_COPY:%.*]] = project_box [[BOX_COPY]] -// CHECK: destroy_value [[BOX_COPY]] -// CHECK: destroy_value [[BOX]] -sil [ossa] @test_copyvalue_use_of_projectbox : $@convention(thin) (Builtin.Int1) -> () { -bb0(%0 : $Builtin.Int1): - // Initial initialization. - %1 = alloc_box ${ var Bool }, var, name "self" - %2 = mark_uninitialized [rootself] %1 : ${ var Bool } - %3 = project_box %2 : ${ var Bool }, 0 - %4 = struct_element_addr %3 : $*Bool, #Bool._value - store %0 to [trivial] %4 : $*Builtin.Int1 - - // copy + project_box for function_call. This can happen via - // capture_promotion. - %5 = function_ref @capture_promotion_generated_callee : $@convention(thin) (@in Bool) -> () - %6 = copy_value %2 : ${ var Bool } - %7 = project_box %6 : ${ var Bool }, 0 - %8 = apply %5(%7) : $@convention(thin) (@in Bool) -> () - destroy_value %6 : ${ var Bool } - - // Epilog - destroy_value %2 : ${ var Bool } - %9999 = tuple() - return %9999 : $() -} From c5a655e35b89085da29d1acde98d4480420c06ce Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 19 Nov 2019 20:50:00 -0800 Subject: [PATCH 225/478] [Type checker] Fold more for-each type checking into the constraint solver. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The type checking of the for-each loop is split between the constraint solver (which does most of the work) and the statement checker (which updates the for-each loop AST). Move more of the work into the constraint solver proper, so that the AST updates can happen in one place, making use of the solution produced by the solver. This allows a few things, some of which are short-term gains and others that are more future-facing: * `TypeChecker::convertToType` has been removed, because we can now either use the more general `typeCheckExpression` entry point or perform the appropriate operation within the constraint system. * Solving the constraint system ensures that everything related to the for-each loop full checks out * Additional refactoring will make it easier for the for-each loop to be checked as part of a larger constraint system, e.g., for processing entire closures or function bodies (that’s the futurist bit). --- include/swift/AST/Expr.h | 4 + lib/Sema/TypeCheckConstraints.cpp | 206 +++++++++--------- lib/Sema/TypeCheckStmt.cpp | 63 +----- lib/Sema/TypeChecker.h | 25 ++- .../generic_protocol_witness.swift | 3 +- 5 files changed, 139 insertions(+), 162 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 78a4b0563cc40..20ff74d15cd97 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -3924,6 +3924,10 @@ class OpaqueValueExpr : public Expr { /// value to be specified later. bool isPlaceholder() const { return Bits.OpaqueValueExpr.IsPlaceholder; } + void setIsPlaceholder(bool value) { + Bits.OpaqueValueExpr.IsPlaceholder = value; + } + SourceRange getSourceRange() const { return Range; } static bool classof(const Expr *E) { diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 306adc0edb490..fdb9891ce61a5 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2892,7 +2892,8 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, return hadError; } -bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { +auto TypeChecker::typeCheckForEachBinding( + DeclContext *dc, ForEachStmt *stmt) -> Optional { /// Type checking listener for for-each binding. class BindingListener : public ExprTypeCheckListener { /// The for-each statement. @@ -2901,45 +2902,66 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { /// The locator we're using. ConstraintLocator *Locator; + /// The contextual locator we're using. + ConstraintLocator *ContextualLocator; + + /// The Sequence protocol. + ProtocolDecl *SequenceProto; + + /// The IteratorProtocol. + ProtocolDecl *IteratorProto; + /// The type of the initializer. Type InitType; /// The type of the sequence. Type SequenceType; + /// The conformance of the sequence type to the Sequence protocol. + ProtocolConformanceRef SequenceConformance; + + /// The type of the element. + Type ElementType; + + /// The type of the iterator. + Type IteratorType; + + /// The conformance of the iterator type to IteratorProtocol. + ProtocolConformanceRef IteratorConformance; + + /// The type of makeIterator. + Type MakeIteratorType; + public: explicit BindingListener(ForEachStmt *stmt) : Stmt(stmt) { } bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { // Save the locator we're using for the expression. Locator = cs.getConstraintLocator(expr); - auto *contextualLocator = + ContextualLocator = cs.getConstraintLocator(expr, LocatorPathElt::ContextualType()); - // The expression type must conform to the Sequence. - ProtocolDecl *sequenceProto = TypeChecker::getProtocol( + // The expression type must conform to the Sequence protocol. + SequenceProto = TypeChecker::getProtocol( cs.getASTContext(), Stmt->getForLoc(), KnownProtocolKind::Sequence); - if (!sequenceProto) { + if (!SequenceProto) { return true; } - auto elementAssocType = - sequenceProto->getAssociatedType(cs.getASTContext().Id_Element); - SequenceType = cs.createTypeVariable(Locator, TVO_CanBindToNoEscape); cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), SequenceType, Locator); cs.addConstraint(ConstraintKind::ConformsTo, SequenceType, - sequenceProto->getDeclaredType(), contextualLocator); + SequenceProto->getDeclaredType(), ContextualLocator); // Since we are using "contextual type" here, it has to be recorded // in the constraint system for diagnostics to have access to "purpose". cs.setContextualType( - expr, TypeLoc::withoutLoc(sequenceProto->getDeclaredType()), + expr, TypeLoc::withoutLoc(SequenceProto->getDeclaredType()), CTP_ForEachStmt); auto elementLocator = cs.getConstraintLocator( - contextualLocator, ConstraintLocator::SequenceElementType); + ContextualLocator, ConstraintLocator::SequenceElementType); // Collect constraints from the element pattern. auto pattern = Stmt->getPattern(); @@ -2949,10 +2971,36 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { // Add a conversion constraint between the element type of the sequence // and the type of the element pattern. - auto elementType = DependentMemberType::get(SequenceType, elementAssocType); - cs.addConstraint(ConstraintKind::Conversion, elementType, InitType, + auto elementAssocType = + SequenceProto->getAssociatedType(cs.getASTContext().Id_Element); + ElementType = DependentMemberType::get(SequenceType, elementAssocType); + cs.addConstraint(ConstraintKind::Conversion, ElementType, InitType, elementLocator); + // Determine the iterator type. + auto iteratorAssocType = + SequenceProto->getAssociatedType(cs.getASTContext().Id_Iterator); + IteratorType = DependentMemberType::get(SequenceType, iteratorAssocType); + + // The iterator type must conform to IteratorProtocol. + IteratorProto = TypeChecker::getProtocol( + cs.getASTContext(), Stmt->getForLoc(), + KnownProtocolKind::IteratorProtocol); + if (!IteratorProto) { + return true; + } + + // Reference the makeIterator witness. + // FIXME: Not tied to the actual witness. + ASTContext &ctx = cs.getASTContext(); + DeclName makeIteratorName(ctx, ctx.Id_makeIterator, + ArrayRef()); + MakeIteratorType = cs.createTypeVariable(Locator, TVO_CanBindToNoEscape); + cs.addValueMemberConstraint( + LValueType::get(SequenceType), DeclNameRef(makeIteratorName), + MakeIteratorType, cs.DC, FunctionRefKind::Compound, { }, + ContextualLocator); + Stmt->setSequence(expr); return false; } @@ -2962,13 +3010,24 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { auto &cs = solution.getConstraintSystem(); InitType = solution.simplifyType(InitType); SequenceType = solution.simplifyType(SequenceType); + ElementType = solution.simplifyType(ElementType); + IteratorType = solution.simplifyType(IteratorType); // Perform any necessary conversions of the sequence (e.g. [T]! -> [T]). - expr = solution.coerceToType(expr, SequenceType, cs.getConstraintLocator(expr)); + expr = solution.coerceToType(expr, SequenceType, Locator); if (!expr) return nullptr; + // Convert the sequence as appropriate for the makeIterator() call. + auto makeIteratorOverload = solution.getOverloadChoice(ContextualLocator); + auto makeIteratorSelfType = solution.simplifyType( + makeIteratorOverload.openedFullType + )->castTo()->getParams()[0].getPlainType(); + expr = solution.coerceToType(expr, makeIteratorSelfType, + ContextualLocator); + cs.cacheExprTypes(expr); + Stmt->setSequence(expr); // Apply the solution to the iteration pattern as well. Pattern *pattern = Stmt->getPattern(); @@ -2981,11 +3040,42 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { } Stmt->setPattern(pattern); - Stmt->setSequence(expr); + + // Get the conformance of the sequence type to the Sequence protocol. + // FIXME: Get this from the solution and substitute into that. + SequenceConformance = TypeChecker::conformsToProtocol( + SequenceType, SequenceProto, cs.DC, + ConformanceCheckFlags::InExpression, + expr->getLoc()); + assert(!SequenceConformance.isInvalid() && + "Couldn't find sequence conformance"); + Stmt->setSequenceConformance(SequenceConformance); + + // Retrieve the conformance of the iterator type to IteratorProtocol. + // FIXME: Get this from the solution and substitute into that. + IteratorConformance = TypeChecker::conformsToProtocol( + IteratorType, IteratorProto, cs.DC, + ConformanceCheckFlags::InExpression, + expr->getLoc()); + + // Record the makeIterator declaration we used. + auto makeIteratorDecl = makeIteratorOverload.choice.getDecl(); + auto makeIteratorSubs = SequenceType->getMemberSubstitutionMap( + cs.DC->getParentModule(), makeIteratorDecl); + auto makeIteratorDeclRef = + ConcreteDeclRef(makeIteratorDecl, makeIteratorSubs); + Stmt->setMakeIterator(makeIteratorDeclRef); solution.setExprTypes(expr); return expr; } + + ForEachBinding getBinding() const { + return { + SequenceType, SequenceConformance, IteratorType, IteratorConformance, + ElementType + }; + } }; BindingListener listener(stmt); @@ -2994,7 +3084,9 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { // Type-check the for-each loop sequence and element pattern. auto resultTy = TypeChecker::typeCheckExpression(seq, dc, &listener); - return !resultTy; + if (!resultTy) + return None; + return listener.getBinding(); } bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { @@ -3427,88 +3519,6 @@ TypeChecker::coerceToRValue(ASTContext &Context, Expr *expr, return expr; } -bool TypeChecker::convertToType(Expr *&expr, Type type, DeclContext *dc, - Optional typeFromPattern) { - // TODO: need to add kind arg? - // Construct a constraint system from this expression. - ConstraintSystem cs(dc, ConstraintSystemFlags::AllowFixes); - - // Cache the expression type on the system to ensure it is available - // on diagnostics if the convertion fails. - cs.cacheExprTypes(expr); - - // If there is a type that we're expected to convert to, add the conversion - // constraint. - cs.addConstraint(ConstraintKind::Conversion, expr->getType(), type, - cs.getConstraintLocator(expr)); - - auto &Context = dc->getASTContext(); - if (Context.TypeCheckerOpts.DebugConstraintSolver) { - auto &log = Context.TypeCheckerDebug->getStream(); - log << "---Initial constraints for the given expression---\n"; - expr->dump(log); - log << "\n"; - cs.print(log); - } - - // Attempt to solve the constraint system. - SmallVector viable; - if ((cs.solve(viable) || viable.size() != 1)) { - // Try to fix the system or provide a decent diagnostic. - auto salvagedResult = cs.salvage(); - switch (salvagedResult.getKind()) { - case SolutionResult::Kind::Success: - viable.clear(); - viable.push_back(std::move(salvagedResult).takeSolution()); - break; - - case SolutionResult::Kind::Error: - case SolutionResult::Kind::Ambiguous: - return true; - - case SolutionResult::Kind::UndiagnosedError: - cs.diagnoseFailureForExpr(expr); - salvagedResult.markAsDiagnosed(); - return true; - - case SolutionResult::Kind::TooComplex: - Context.Diags.diagnose(expr->getLoc(), diag::expression_too_complex) - .highlight(expr->getSourceRange()); - salvagedResult.markAsDiagnosed(); - return true; - } - } - - auto &solution = viable[0]; - if (Context.TypeCheckerOpts.DebugConstraintSolver) { - auto &log = Context.TypeCheckerDebug->getStream(); - log << "---Solution---\n"; - solution.dump(log); - } - - cs.cacheExprTypes(expr); - - // Perform the conversion. - Expr *result = solution.coerceToType(expr, type, - cs.getConstraintLocator(expr), - typeFromPattern); - if (!result) { - return true; - } - - solution.setExprTypes(expr); - - if (Context.TypeCheckerOpts.DebugConstraintSolver) { - auto &log = Context.TypeCheckerDebug->getStream(); - log << "---Type-checked expression---\n"; - result->dump(log); - log << "\n"; - } - - expr = result; - return false; -} - //===----------------------------------------------------------------------===// // Debugging //===----------------------------------------------------------------------===// diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index e941374e1ee7d..81c74e24fa71f 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -749,7 +749,8 @@ class StmtChecker : public StmtVisitor { return nullptr; } - if (TypeChecker::typeCheckForEachBinding(DC, S)) + auto binding = TypeChecker::typeCheckForEachBinding(DC, S); + if (!binding) return nullptr; if (auto *Where = S->getWhere()) { @@ -773,34 +774,10 @@ class StmtChecker : public StmtVisitor { return nullptr; } - Expr *sequence = S->getSequence(); - // Invoke iterator() to get an iterator from the sequence. - Type iteratorTy; + Type iteratorTy = binding->iteratorType; VarDecl *iterator; { - Type sequenceType = sequence->getType(); - auto conformance = - TypeChecker::conformsToProtocol(sequenceType, sequenceProto, DC, - ConformanceCheckFlags::InExpression, - sequence->getLoc()); - if (conformance.isInvalid()) - return nullptr; - S->setSequenceConformance(conformance); - - iteratorTy = - conformance.getTypeWitnessByName(sequenceType, - getASTContext().Id_Iterator); - if (iteratorTy->hasError()) - return nullptr; - - auto witness = - conformance.getWitnessByName(sequenceType, - getASTContext().Id_makeIterator); - if (!witness) - return nullptr; - S->setMakeIterator(witness); - // Create a local variable to capture the iterator. std::string name; if (auto np = dyn_cast_or_null(S->getPattern())) @@ -820,23 +797,11 @@ class StmtChecker : public StmtVisitor { // TODO: test/DebugInfo/iteration.swift requires this extra info to // be around. - auto nextResultType = OptionalType::get(conformance.getTypeWitnessByName( - sequenceType, getASTContext().Id_Element)); + auto nextResultType = OptionalType::get(binding->elementType); PatternBindingDecl::createImplicit( getASTContext(), StaticSpellingKind::None, genPat, new (getASTContext()) OpaqueValueExpr(S->getInLoc(), nextResultType), DC, /*VarLoc*/ S->getForLoc()); - - Type newSequenceType = cast(witness.getDecl()) - ->getInterfaceType() - ->castTo() - ->getParams()[0].getPlainType().subst(witness.getSubstitutions()); - - // Necessary type coersion for method application. - if (TypeChecker::convertToType(sequence, newSequenceType, DC, None)) { - return nullptr; - } - S->setSequence(sequence); } // Working with iterators requires Optional. @@ -847,18 +812,10 @@ class StmtChecker : public StmtVisitor { // we'll use to drive the loop. // FIXME: Would like to customize the diagnostic emitted in // conformsToProtocol(). - auto genConformance = TypeChecker::conformsToProtocol( - iteratorTy, iteratorProto, DC, ConformanceCheckFlags::InExpression, - sequence->getLoc()); + auto genConformance = binding->iteratorConformance; if (genConformance.isInvalid()) return nullptr; - Type elementTy = - genConformance.getTypeWitnessByName(iteratorTy, - getASTContext().Id_Element); - if (elementTy->hasError()) - return nullptr; - auto *varRef = TypeChecker::buildCheckedRefExpr(iterator, DC, DeclNameLoc(S->getInLoc()), /*implicit*/ true); @@ -881,12 +838,16 @@ class StmtChecker : public StmtVisitor { auto optPatternType = OptionalType::get(S->getPattern()->getType()); if (!optPatternType->isEqual(nextResultType)) { OpaqueValueExpr *elementExpr = - new (getASTContext()) OpaqueValueExpr(S->getInLoc(), nextResultType); + new (getASTContext()) OpaqueValueExpr(S->getInLoc(), nextResultType, + /*isPlaceholder=*/true); Expr *convertElementExpr = elementExpr; - if (TypeChecker::convertToType(convertElementExpr, optPatternType, DC, - S->getPattern())) { + if (TypeChecker::typeCheckExpression( + convertElementExpr, DC, + TypeLoc::withoutLoc(optPatternType), + CTP_CoerceOperand).isNull()) { return nullptr; } + elementExpr->setIsPlaceholder(false); S->setElementExpr(elementExpr); S->setConvertElementExpr(convertElementExpr); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 0fec4740294f4..79dcb7cb19b28 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1032,8 +1032,20 @@ class TypeChecker final { static bool typeCheckBinding(Pattern *&P, Expr *&Init, DeclContext *DC); static bool typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned patternNumber); + /// Information about a type-checked for-each binding. + struct ForEachBinding { + Type sequenceType; + ProtocolConformanceRef sequenceConformance; + Type iteratorType; + ProtocolConformanceRef iteratorConformance; + Type elementType; + }; + /// Type-check a for-each loop's pattern binding and sequence together. - static bool typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt); + /// + /// \returns the binding, if successful. + static Optional typeCheckForEachBinding( + DeclContext *dc, ForEachStmt *stmt); /// Compute the set of captures for the given function or closure. static void computeCaptures(AnyFunctionRef AFR); @@ -1101,17 +1113,6 @@ class TypeChecker final { /// this protocol. static Type getDefaultType(ProtocolDecl *protocol, DeclContext *dc); - /// Convert the given expression to the given type. - /// - /// \param expr The expression, which will be updated in place. - /// \param type The type to convert to. - /// \param typeFromPattern Optionally, the caller can specify the pattern - /// from where the toType is derived, so that we can deliver better fixit. - /// - /// \returns true if an error occurred, false otherwise. - static bool convertToType(Expr *&expr, Type type, DeclContext *dc, - Optional typeFromPattern = None); - /// Coerce the given expression to materializable type, if it /// isn't already. static Expr *coerceToRValue( diff --git a/test/Constraints/generic_protocol_witness.swift b/test/Constraints/generic_protocol_witness.swift index 75f10061cb8c7..52d4f4bba3950 100644 --- a/test/Constraints/generic_protocol_witness.swift +++ b/test/Constraints/generic_protocol_witness.swift @@ -59,5 +59,6 @@ func usesAGenericMethod(_ x: U) { struct L: Sequence {} // expected-error {{type 'L' does not conform to protocol 'Sequence'}} func z(_ x: L) { - for xx in x {} // expected-warning {{immutable value 'xx' was never used; consider replacing with '_' or removing it}} + for xx in x {} + // expected-error@-1{{for-in loop requires 'L' to conform to 'Sequence'}} } From d0343aeb31576aec32d7d6df609f496d3596da92 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 27 Dec 2019 16:24:21 -0800 Subject: [PATCH 226/478] build: remove more unused functions (NFC) These build-script-impl functions were not used. Simply remove them as they are no longer needed. --- utils/build-script-impl | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 40888afe763d5..2cdb51aa8d8d0 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -870,10 +870,6 @@ function false_true() { fi } -function cmake_version() { - "${CMAKE}" --version | grep "cmake version" | cut -f 3 -d " " -} - # Sanitize the list of cross-compilation targets. # # In the Build/Host/Target paradigm: @@ -1009,28 +1005,6 @@ function get_stdlib_targets_for_host() { fi } -function should_build_stdlib_target() { - local stdlib_target=$1 - local host=$2 - if [[ "${BUILD_STDLIB_DEPLOYMENT_TARGETS}" == "all" ]]; then - echo 1 - else - # Only build the stdlib targets in 'build-stdlib-deployment-targets' - local build_list=($BUILD_STDLIB_DEPLOYMENT_TARGETS) - for t in "${build_list[@]}"; do - if [[ "${t}" == "${stdlib_target}" ]]; then - echo 1 - fi - done - # As with 'stdlib-deployment-targets', 'build-stdlib-deployment-targets' - # only applies to the LOCAL_HOST. For cross-tools hosts, always allow - # their one-and-only stdlib-target to build. - if [[ $(is_cross_tools_host ${host}) ]] && [[ "${stdlib_target}" == "${host}" ]]; then - echo 1 - fi - fi -} - # # Calculate source directories for each product. # From 64c170b5e9f7628ad0881f4217abc05ea9fa6760 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 2 Jan 2020 10:33:16 -0800 Subject: [PATCH 227/478] AST: remove unnecessary variable (NFC) This removes the accidental variable that was being created during the switch. The value is available without the variable. --- lib/AST/Decl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 08a225899633a..a6b9f36b26e88 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6543,7 +6543,7 @@ DeclName AbstractFunctionDecl::getEffectiveFullName() const { auto &ctx = getASTContext(); auto storage = accessor->getStorage(); auto subscript = dyn_cast(storage); - switch (auto accessorKind = accessor->getAccessorKind()) { + switch (accessor->getAccessorKind()) { // These don't have any extra implicit parameters. case AccessorKind::Address: case AccessorKind::MutableAddress: From 0c6c5aa1a2c149eeecdb71964e52c81f961eb7d5 Mon Sep 17 00:00:00 2001 From: Daniel Duan Date: Thu, 2 Jan 2020 11:05:30 -0800 Subject: [PATCH 228/478] [Parse] Remove unnecessary conversion to SourceRange (#28926) `SourceLoc`s are implicitly converted to `SourceRange`. Yay. NFC, Gardening --- lib/Parse/Parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 063b742c87623..be223823a366e 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1054,7 +1054,7 @@ Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, while (true) { while (Tok.is(tok::comma)) { diagnose(Tok, diag::unexpected_separator, ",") - .fixItRemove(SourceRange(Tok.getLoc())); + .fixItRemove(Tok.getLoc()); consumeToken(); } SourceLoc StartLoc = Tok.getLoc(); @@ -1084,7 +1084,7 @@ Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, continue; if (!AllowSepAfterLast) { diagnose(Tok, diag::unexpected_separator, ",") - .fixItRemove(SourceRange(PreviousLoc)); + .fixItRemove(PreviousLoc); } break; } From 43f1e9ba60ab454b616198631291894196c1d822 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 2 Jan 2020 11:06:05 -0800 Subject: [PATCH 229/478] [CSDiag] NFC: Remove obsolete `CalleeListener` callback Since closure handling has been ported to new diagnostic framework `CalleeListener` callback is no longer used. --- lib/Sema/CSDiag.cpp | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index fb0b45f6cc625..11189aaf74280 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -1615,47 +1615,6 @@ bool FailureDiagnosis::visitSubscriptExpr(SubscriptExpr *SE) { return diagnoseSubscriptErrors(SE, /* inAssignmentDestination = */ false); } -namespace { - /// Type checking listener for pattern binding initializers. - class CalleeListener : public ExprTypeCheckListener { - Type contextualType; - public: - explicit CalleeListener(Type contextualType) - : contextualType(contextualType) { } - - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - // If we have no contextual type, there is nothing to do. - if (!contextualType) - return false; - - // If the expression is obviously something that produces a metatype, - // then don't put a constraint on it. - auto semExpr = expr->getValueProvidingExpr(); - if (isa(semExpr)) - return false; - - auto resultLocator = - cs.getConstraintLocator(expr, ConstraintLocator::FunctionResult); - auto resultType = cs.createTypeVariable(resultLocator, - TVO_CanBindToLValue | - TVO_CanBindToNoEscape); - - auto locator = cs.getConstraintLocator(expr); - cs.addConstraint(ConstraintKind::FunctionResult, - cs.getType(expr), - resultType, - locator); - - cs.addConstraint(ConstraintKind::Conversion, - resultType, - contextualType, - locator); - - return false; - } - }; -} // end anonymous namespace - // Check if there is a structural problem in the function expression // by performing type checking with the option to allow unresolved // type variables. If that is going to produce a function type with From bbcaf8c66933aa3bf553ca11a7d7e69cc4a0a1d6 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 2 Jan 2020 11:51:05 -0800 Subject: [PATCH 230/478] [Type checker] Introduce value witness constraints. Introduce a new kind of constraint, the "value witness" constraint, which captures a reference to a witness for a specific protocol conformance. It otherwise acts like a more restricted form of a "value member" constraint, where the specific member is known (as a ValueDecl*) in advance. The constraint is effectively dependent on the protocol conformance itself; if that conformance fails, mark the type variables in the resolved member type as "holes", so that the conformance failure does not cascade. Note that the resolved overload for this constraint always refers to the requirement, rather than the witness, so we will end up recording witness-method references in the AST rather than concrete references, and leave it up to the optimizers to perform devirtualization. This is demonstrated by the SIL changes needed in tests, and is part of the wider resilience issue with conformances described by rdar://problem/22708391. --- include/swift/AST/ASTContext.h | 3 + lib/AST/ASTContext.cpp | 28 ++++++ lib/Sema/CSBindings.cpp | 1 + lib/Sema/CSSimplify.cpp | 85 ++++++++++++++++++- lib/Sema/CSSolver.cpp | 1 + lib/Sema/Constraint.cpp | 66 +++++++++++++- lib/Sema/Constraint.h | 51 ++++++++--- lib/Sema/ConstraintSystem.h | 26 ++++++ lib/Sema/TypeCheckConstraints.cpp | 10 +-- .../generic_protocol_witness.swift | 3 +- test/IRGen/big_types_corner_cases.swift | 2 +- test/SILGen/foreach.swift | 12 +-- test/stmt/foreach.swift | 3 + 13 files changed, 263 insertions(+), 28 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 51e85ae49a550..b3baae19347ce 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -489,6 +489,9 @@ class ASTContext final { /// Get the '+' function on two String. FuncDecl *getPlusFunctionOnString() const; + /// Get Sequence.makeIterator(). + FuncDecl *getSequenceMakeIterator() const; + /// Check whether the standard library provides all the correct /// intrinsic support for Optional. /// diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 6c3d74ffb322d..ec5c403dc5775 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -196,6 +196,9 @@ struct ASTContext::Implementation { /// The declaration of '+' function for two String. FuncDecl *PlusFunctionOnString = nullptr; + /// The declaration of 'Sequence.makeIterator()'. + FuncDecl *MakeIterator = nullptr; + /// The declaration of Swift.Optional.Some. EnumElementDecl *OptionalSomeDecl = nullptr; @@ -710,6 +713,31 @@ FuncDecl *ASTContext::getPlusFunctionOnString() const { return getImpl().PlusFunctionOnString; } +FuncDecl *ASTContext::getSequenceMakeIterator() const { + if (getImpl().MakeIterator) { + return getImpl().MakeIterator; + } + + auto proto = getProtocol(KnownProtocolKind::Sequence); + if (!proto) + return nullptr; + + for (auto result : proto->lookupDirect(Id_makeIterator)) { + if (result->getDeclContext() != proto) + continue; + + if (auto func = dyn_cast(result)) { + if (func->getParameters()->size() != 0) + continue; + + getImpl().MakeIterator = func; + return func; + } + } + + return nullptr; +} + #define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ DECL_CLASS *ASTContext::get##NAME##Decl() const { \ if (getImpl().NAME##Decl) \ diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 741fb3cd4acef..ab5bf7ffa7283 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -610,6 +610,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: // If our type variable shows up in the base type, there's // nothing to do. // FIXME: Can we avoid simplification here? diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 3cbe6b8159839..9c05cf2cd9e20 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1252,6 +1252,7 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::BridgingConversion: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: @@ -1316,6 +1317,7 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: @@ -1594,6 +1596,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::BridgingConversion: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: @@ -3863,6 +3866,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: @@ -6079,7 +6083,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( markMemberTypeAsPotentialHole(memberTy); return SolutionKind::Solved; - } else if (kind == ConstraintKind::ValueMember && baseObjTy->isHole()) { + } else if ((kind == ConstraintKind::ValueMember || + kind == ConstraintKind::ValueWitness) && + baseObjTy->isHole()) { // If base type is a "hole" there is no reason to record any // more "member not found" fixes for chained member references. increaseScore(SK_Fix); @@ -6354,6 +6360,72 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( return SolutionKind::Error; } +ConstraintSystem::SolutionKind +ConstraintSystem::simplifyValueWitnessConstraint( + ConstraintKind kind, Type baseType, ValueDecl *requirement, Type memberType, + DeclContext *useDC, FunctionRefKind functionRefKind, + TypeMatchOptions flags, ConstraintLocatorBuilder locator) { + // We'd need to record original base type because it might be a type + // variable representing another missing member. + auto origBaseType = baseType; + + auto formUnsolved = [&] { + // If requested, generate a constraint. + if (flags.contains(TMF_GenerateConstraints)) { + auto *witnessConstraint = Constraint::createValueWitness( + *this, kind, origBaseType, memberType, requirement, useDC, + functionRefKind, getConstraintLocator(locator)); + + addUnsolvedConstraint(witnessConstraint); + return SolutionKind::Solved; + } + + return SolutionKind::Unsolved; + }; + + // Resolve the base type, if we can. If we can't resolve the base type, + // then we can't solve this constraint. + Type baseObjectType = getFixedTypeRecursive( + baseType, flags, /*wantRValue=*/true); + if (baseObjectType->isTypeVariableOrMember()) { + return formUnsolved(); + } + + // Check conformance to the protocol. If it doesn't conform, this constraint + // fails. Don't attempt to fix it. + // FIXME: Look in the constraint system to see if we've resolved the + // conformance already? + auto proto = requirement->getDeclContext()->getSelfProtocolDecl(); + assert(proto && "Value witness constraint for a non-requirement"); + auto conformance = TypeChecker::conformsToProtocol( + baseObjectType, proto, useDC, + (ConformanceCheckFlags::InExpression | + ConformanceCheckFlags::SkipConditionalRequirements)); + if (!conformance) { + // The conformance failed, so mark the member type as a "hole". We cannot + // do anything further here. + if (!shouldAttemptFixes()) + return SolutionKind::Error; + + memberType.visit([&](Type type) { + if (auto *typeVar = type->getAs()) + recordPotentialHole(typeVar); + }); + + return SolutionKind::Solved; + } + + // Reference the requirement. + Type resolvedBaseType = simplifyType(baseType, flags); + if (resolvedBaseType->isTypeVariableOrMember()) + return formUnsolved(); + + auto choice = OverloadChoice(resolvedBaseType, requirement, functionRefKind); + resolveOverload(getConstraintLocator(locator), memberType, choice, + useDC); + return SolutionKind::Solved; +} + ConstraintSystem::SolutionKind ConstraintSystem::simplifyDefaultableConstraint( Type first, Type second, @@ -8671,6 +8743,7 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::BindOverload: case ConstraintKind::Disjunction: case ConstraintKind::KeyPath: @@ -9058,6 +9131,16 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { TMF_GenerateConstraints, constraint.getLocator()); + case ConstraintKind::ValueWitness: + return simplifyValueWitnessConstraint(constraint.getKind(), + constraint.getFirstType(), + constraint.getRequirement(), + constraint.getSecondType(), + constraint.getMemberUseDC(), + constraint.getFunctionRefKind(), + TMF_GenerateConstraints, + constraint.getLocator()); + case ConstraintKind::Defaultable: return simplifyDefaultableConstraint(constraint.getFirstType(), constraint.getSecondType(), diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index a592219e498d3..f991117946808 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1670,6 +1670,7 @@ void ConstraintSystem::ArgumentInfoCollector::walk(Type argType) { case ConstraintKind::BindToPointerType: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::Disjunction: case ConstraintKind::CheckedCast: diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 0a41efe4197de..a70513e263391 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -77,6 +77,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: llvm_unreachable("Wrong constructor for member constraint"); case ConstraintKind::Defaultable: @@ -127,6 +128,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third, case ConstraintKind::ApplicableFunction: case ConstraintKind::DynamicCallableApplicableFunction: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::Defaultable: case ConstraintKind::BindOverload: @@ -156,7 +158,7 @@ Constraint::Constraint(ConstraintKind kind, Type first, Type second, ArrayRef typeVars) : Kind(kind), HasRestriction(false), IsActive(false), IsDisabled(false), RememberChoice(false), IsFavored(false), - NumTypeVariables(typeVars.size()), Member{first, second, member, useDC}, + NumTypeVariables(typeVars.size()), Member{first, second, {member}, useDC}, Locator(locator) { assert(kind == ConstraintKind::ValueMember || kind == ConstraintKind::UnresolvedValueMember); @@ -168,6 +170,28 @@ Constraint::Constraint(ConstraintKind kind, Type first, Type second, std::copy(typeVars.begin(), typeVars.end(), getTypeVariablesBuffer().begin()); } +Constraint::Constraint(ConstraintKind kind, Type first, Type second, + ValueDecl *requirement, DeclContext *useDC, + FunctionRefKind functionRefKind, + ConstraintLocator *locator, + ArrayRef typeVars) + : Kind(kind), HasRestriction(false), IsActive(false), IsDisabled(false), + RememberChoice(false), IsFavored(false), + NumTypeVariables(typeVars.size()), Locator(locator) { + Member.First = first; + Member.Second = second; + Member.Member.Ref = requirement; + Member.UseDC = useDC; + TheFunctionRefKind = static_cast(functionRefKind); + + assert(kind == ConstraintKind::ValueWitness); + assert(getFunctionRefKind() == functionRefKind); + assert(requirement && "Value witness constraint has no requirement"); + assert(useDC && "Member constraint has no use DC"); + + std::copy(typeVars.begin(), typeVars.end(), getTypeVariablesBuffer().begin()); +} + Constraint::Constraint(Type type, OverloadChoice choice, DeclContext *useDC, ConstraintFix *fix, ConstraintLocator *locator, ArrayRef typeVars) @@ -251,6 +275,11 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const { getMember(), getMemberUseDC(), getFunctionRefKind(), getLocator()); + case ConstraintKind::ValueWitness: + return createValueWitness( + cs, getKind(), getFirstType(), getSecondType(), getRequirement(), + getMemberUseDC(), getFunctionRefKind(), getLocator()); + case ConstraintKind::Disjunction: return createDisjunction(cs, getNestedConstraints(), getLocator()); @@ -386,11 +415,20 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { } case ConstraintKind::ValueMember: - Out << "[." << Member.Member << ": value] == "; + Out << "[." << getMember() << ": value] == "; break; case ConstraintKind::UnresolvedValueMember: - Out << "[(implicit) ." << Member.Member << ": value] == "; + Out << "[(implicit) ." << getMember() << ": value] == "; + break; + + case ConstraintKind::ValueWitness: { + auto requirement = getRequirement(); + auto selfNominal = requirement->getDeclContext()->getSelfNominalTypeDecl(); + Out << "[." << selfNominal->getName() << "::" << requirement->getFullName() + << ": witness] == "; break; + } + case ConstraintKind::Defaultable: Out << " can default to "; break; @@ -510,6 +548,7 @@ gatherReferencedTypeVars(Constraint *constraint, case ConstraintKind::Subtype: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::DynamicTypeOf: case ConstraintKind::EscapableFunctionOf: case ConstraintKind::OpenedExistentialOf: @@ -648,6 +687,27 @@ Constraint *Constraint::createMember(ConstraintSystem &cs, ConstraintKind kind, functionRefKind, locator, typeVars); } +Constraint *Constraint::createValueWitness( + ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, + ValueDecl *requirement, DeclContext *useDC, + FunctionRefKind functionRefKind, ConstraintLocator *locator) { + assert(kind == ConstraintKind::ValueWitness); + + // Collect type variables. + SmallVector typeVars; + if (first->hasTypeVariable()) + first->getTypeVariables(typeVars); + if (second->hasTypeVariable()) + second->getTypeVariables(typeVars); + uniqueTypeVariables(typeVars); + + // Create the constraint. + unsigned size = totalSizeToAlloc(typeVars.size()); + void *mem = cs.getAllocator().Allocate(size, alignof(Constraint)); + return new (mem) Constraint(kind, first, second, requirement, useDC, + functionRefKind, locator, typeVars); +} + Constraint *Constraint::createBindOverload(ConstraintSystem &cs, Type type, OverloadChoice choice, DeclContext *useDC, diff --git a/lib/Sema/Constraint.h b/lib/Sema/Constraint.h index 6af00f357f274..83898a76a7b77 100644 --- a/lib/Sema/Constraint.h +++ b/lib/Sema/Constraint.h @@ -116,6 +116,11 @@ enum class ConstraintKind : char { /// name, and the type of that member, when referenced as a value, is the /// second type. UnresolvedValueMember, + /// The first type conforms to the protocol in which the member requirement + /// resides. Once the conformance is resolved, the value witness will be + /// determined, and the type of that witness, when referenced as a value, + /// will be bound to the second type. + ValueWitness, /// The first type can be defaulted to the second (which currently /// cannot be dependent). This is more like a type property than a /// relational constraint. @@ -315,9 +320,18 @@ class Constraint final : public llvm::ilist_node, /// The type of the member. Type Second; - /// If non-null, the name of a member of the first type is that - /// being related to the second type. - DeclNameRef Member; + union { + /// If non-null, the name of a member of the first type is that + /// being related to the second type. + /// + /// Used for ValueMember an UnresolvedValueMember constraints. + DeclNameRef Name; + + /// If non-null, the member being referenced. + /// + /// Used for ValueWitness constraints. + ValueDecl *Ref; + } Member; /// The DC in which the use appears. DeclContext *UseDC; @@ -365,6 +379,12 @@ class Constraint final : public llvm::ilist_node, ConstraintLocator *locator, ArrayRef typeVars); + /// Construct a new value witness constraint. + Constraint(ConstraintKind kind, Type first, Type second, + ValueDecl *requirement, DeclContext *useDC, + FunctionRefKind functionRefKind, ConstraintLocator *locator, + ArrayRef typeVars); + /// Construct a new overload-binding constraint, which might have a fix. Constraint(Type type, OverloadChoice choice, DeclContext *useDC, ConstraintFix *fix, ConstraintLocator *locator, @@ -410,6 +430,12 @@ class Constraint final : public llvm::ilist_node, FunctionRefKind functionRefKind, ConstraintLocator *locator); + /// Create a new value witness constraint. + static Constraint *createValueWitness( + ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, + ValueDecl *requirement, DeclContext *useDC, + FunctionRefKind functionRefKind, ConstraintLocator *locator); + /// Create an overload-binding constraint. static Constraint *createBindOverload(ConstraintSystem &cs, Type type, OverloadChoice choice, @@ -518,6 +544,7 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: return ConstraintClassification::Member; case ConstraintKind::DynamicTypeOf: @@ -548,6 +575,7 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: return Member.First; default: @@ -564,6 +592,7 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: return Member.Second; default: @@ -589,19 +618,20 @@ class Constraint final : public llvm::ilist_node, DeclNameRef getMember() const { assert(Kind == ConstraintKind::ValueMember || Kind == ConstraintKind::UnresolvedValueMember); - return Member.Member; + return Member.Member.Name; } - /// Determine whether this constraint kind has a second type. - static bool hasMember(ConstraintKind kind) { - return kind == ConstraintKind::ValueMember - || kind == ConstraintKind::UnresolvedValueMember; + /// Retrieve the requirement being referenced by a value witness constraint. + ValueDecl *getRequirement() const { + assert(Kind == ConstraintKind::ValueWitness); + return Member.Member.Ref; } /// Determine the kind of function reference we have for a member reference. FunctionRefKind getFunctionRefKind() const { if (Kind == ConstraintKind::ValueMember || - Kind == ConstraintKind::UnresolvedValueMember) + Kind == ConstraintKind::UnresolvedValueMember || + Kind == ConstraintKind::ValueWitness) return static_cast(TheFunctionRefKind); // Conservative answer: drop all of the labels. @@ -647,7 +677,8 @@ class Constraint final : public llvm::ilist_node, /// Retrieve the DC in which the member was used. DeclContext *getMemberUseDC() const { assert(Kind == ConstraintKind::ValueMember || - Kind == ConstraintKind::UnresolvedValueMember); + Kind == ConstraintKind::UnresolvedValueMember || + Kind == ConstraintKind::ValueWitness); return Member.UseDC; } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index ab68f261434e6..d36a874326518 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -2367,6 +2367,26 @@ class ConstraintSystem { } } + /// Add a value witness constraint to the constraint system. + void addValueWitnessConstraint( + Type baseTy, ValueDecl *requirement, Type memberTy, DeclContext *useDC, + FunctionRefKind functionRefKind, ConstraintLocatorBuilder locator) { + assert(baseTy); + assert(memberTy); + assert(requirement); + assert(useDC); + switch (simplifyValueWitnessConstraint( + ConstraintKind::ValueWitness, baseTy, requirement, memberTy, useDC, + functionRefKind, TMF_GenerateConstraints, locator)) { + case SolutionKind::Unsolved: + llvm_unreachable("Unsolved result when generating constraints!"); + + case SolutionKind::Solved: + case SolutionKind::Error: + break; + } + } + /// Add an explicit conversion constraint (e.g., \c 'x as T'). void addExplicitConversionConstraint(Type fromType, Type toType, bool allowFixes, @@ -3268,6 +3288,12 @@ class ConstraintSystem { ArrayRef outerAlternatives, TypeMatchOptions flags, ConstraintLocatorBuilder locator); + /// Attempt to simplify the given value witness constraint. + SolutionKind simplifyValueWitnessConstraint( + ConstraintKind kind, Type baseType, ValueDecl *member, Type memberType, + DeclContext *useDC, FunctionRefKind functionRefKind, + TypeMatchOptions flags, ConstraintLocatorBuilder locator); + /// Attempt to simplify the optional object constraint. SolutionKind simplifyOptionalObjectConstraint( Type first, Type second, diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index fdb9891ce61a5..208ca7b952189 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2991,14 +2991,12 @@ auto TypeChecker::typeCheckForEachBinding( } // Reference the makeIterator witness. - // FIXME: Not tied to the actual witness. ASTContext &ctx = cs.getASTContext(); - DeclName makeIteratorName(ctx, ctx.Id_makeIterator, - ArrayRef()); + FuncDecl *makeIterator = ctx.getSequenceMakeIterator(); MakeIteratorType = cs.createTypeVariable(Locator, TVO_CanBindToNoEscape); - cs.addValueMemberConstraint( - LValueType::get(SequenceType), DeclNameRef(makeIteratorName), - MakeIteratorType, cs.DC, FunctionRefKind::Compound, { }, + cs.addValueWitnessConstraint( + LValueType::get(SequenceType), makeIterator, + MakeIteratorType, cs.DC, FunctionRefKind::Compound, ContextualLocator); Stmt->setSequence(expr); diff --git a/test/Constraints/generic_protocol_witness.swift b/test/Constraints/generic_protocol_witness.swift index 52d4f4bba3950..6153b7dd03827 100644 --- a/test/Constraints/generic_protocol_witness.swift +++ b/test/Constraints/generic_protocol_witness.swift @@ -60,5 +60,6 @@ struct L: Sequence {} // expected-error {{type 'L' does not conform to pro func z(_ x: L) { for xx in x {} - // expected-error@-1{{for-in loop requires 'L' to conform to 'Sequence'}} + // expected-warning@-1{{immutable value 'xx' was never used; consider replacing with '_' or removing it}} + // expected-error@-2{{type 'L.Iterator' does not conform to protocol 'IteratorProtocol'}} } diff --git a/test/IRGen/big_types_corner_cases.swift b/test/IRGen/big_types_corner_cases.swift index 32ded99505976..0520bbd6cc7bf 100644 --- a/test/IRGen/big_types_corner_cases.swift +++ b/test/IRGen/big_types_corner_cases.swift @@ -215,7 +215,7 @@ public func testGetFunc() { // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden swiftcc void @"$s22big_types_corner_cases7TestBigC5test2yyF"(%T22big_types_corner_cases7TestBigC* swiftself) // CHECK: [[CALL1:%.*]] = call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGMD" // CHECK: [[CALL2:%.*]] = call i8** @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGSayxGSlsWl" -// CHECK: call swiftcc void @"$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF"(%Ts16IndexingIteratorV* noalias nocapture sret {{.*}}, %swift.type* [[CALL1]], i8** [[CALL2]], %swift.opaque* noalias nocapture swiftself {{.*}}) +// CHECK: call swiftcc void @"$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF"(%Ts16IndexingIteratorV{{.*}}* noalias nocapture sret {{.*}}, %swift.type* [[CALL1]], i8** [[CALL2]], %swift.opaque* noalias nocapture swiftself {{.*}}) // CHECK: ret void class TestBig { typealias Handler = (BigStruct) -> Void diff --git a/test/SILGen/foreach.swift b/test/SILGen/foreach.swift index cfb7b67d61241..3e80c92828733 100644 --- a/test/SILGen/foreach.swift +++ b/test/SILGen/foreach.swift @@ -108,8 +108,8 @@ func trivialStructBreak(_ xx: [Int]) { // CHECK: [[PROJECT_ITERATOR_BOX:%.*]] = project_box [[ITERATOR_BOX]] // CHECK: [[BORROWED_ARRAY_STACK:%.*]] = alloc_stack $Array // CHECK: store [[ARRAY_COPY:%.*]] to [init] [[BORROWED_ARRAY_STACK]] -// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF -// CHECK: apply [[MAKE_ITERATOR_FUNC]]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) +// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = witness_method $Array, #Sequence.makeIterator!1 : (__owned Self) -> () -> Self.Iterator : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator +// CHECK: apply [[MAKE_ITERATOR_FUNC]]<[Int]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) // CHECK: br [[LOOP_DEST:bb[0-9]+]] // // CHECK: [[LOOP_DEST]]: @@ -208,8 +208,8 @@ func existentialBreak(_ xx: [P]) { // CHECK: [[PROJECT_ITERATOR_BOX:%.*]] = project_box [[ITERATOR_BOX]] // CHECK: [[BORROWED_ARRAY_STACK:%.*]] = alloc_stack $Array

// CHECK: store [[ARRAY_COPY:%.*]] to [init] [[BORROWED_ARRAY_STACK]] -// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF -// CHECK: apply [[MAKE_ITERATOR_FUNC]]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) +// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = witness_method $Array

, #Sequence.makeIterator!1 : (__owned Self) -> () -> Self.Iterator : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator +// CHECK: apply [[MAKE_ITERATOR_FUNC]]<[P]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) // CHECK: [[ELT_STACK:%.*]] = alloc_stack $Optional

// CHECK: br [[LOOP_DEST:bb[0-9]+]] // @@ -368,8 +368,8 @@ func genericStructBreak(_ xx: [GenericStruct]) { // CHECK: [[PROJECT_ITERATOR_BOX:%.*]] = project_box [[ITERATOR_BOX]] // CHECK: [[BORROWED_ARRAY_STACK:%.*]] = alloc_stack $Array> // CHECK: store [[ARRAY_COPY:%.*]] to [init] [[BORROWED_ARRAY_STACK]] -// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF -// CHECK: apply [[MAKE_ITERATOR_FUNC]]>>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) +// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = witness_method $Array>, #Sequence.makeIterator!1 : (__owned Self) -> () -> Self.Iterator : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator +// CHECK: apply [[MAKE_ITERATOR_FUNC]]<[GenericStruct]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) // CHECK: [[ELT_STACK:%.*]] = alloc_stack $Optional> // CHECK: br [[LOOP_DEST:bb[0-9]+]] // diff --git a/test/stmt/foreach.swift b/test/stmt/foreach.swift index 630840455ef29..7887a6a553431 100644 --- a/test/stmt/foreach.swift +++ b/test/stmt/foreach.swift @@ -15,6 +15,7 @@ struct BadContainer2 : Sequence { // expected-error{{type 'BadContainer2' does n func bad_containers_2(bc: BadContainer2) { for e in bc { } // expected-warning@-1 {{immutable value 'e' was never used; consider replacing with '_' or removing it}} + // expected-error@-2{{type 'BadContainer2.Iterator' does not conform to protocol 'IteratorProtocol'}} } struct BadContainer3 : Sequence { // expected-error{{type 'BadContainer3' does not conform to protocol 'Sequence'}} @@ -24,6 +25,7 @@ struct BadContainer3 : Sequence { // expected-error{{type 'BadContainer3' does n func bad_containers_3(bc: BadContainer3) { for e in bc { } // expected-warning@-1 {{immutable value 'e' was never used; consider replacing with '_' or removing it}} + // expected-error@-2{{type 'BadContainer3.Iterator' does not conform to protocol 'IteratorProtocol'}} } struct BadIterator1 {} @@ -36,6 +38,7 @@ struct BadContainer4 : Sequence { // expected-error{{type 'BadContainer4' does n func bad_containers_4(bc: BadContainer4) { for e in bc { } // expected-warning@-1 {{immutable value 'e' was never used; consider replacing with '_' or removing it}} + // expected-error@-2{{type 'BadContainer4.Iterator' does not conform to protocol 'IteratorProtocol'}} } // Pattern type-checking From 5b557afbbe1cd083f2b130e6fd959b79bfdcac79 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 2 Jan 2020 12:10:07 -0800 Subject: [PATCH 231/478] [sil] Now that we aren't using mark-uninitialized-fixup anywhere, remove it. --- .../swift/SILOptimizer/PassManager/Passes.def | 2 - lib/SILOptimizer/Mandatory/CMakeLists.txt | 1 - .../Mandatory/MarkUninitializedFixup.cpp | 143 ------------------ .../constant_evaluable_subset_test.swift | 2 +- ...onstant_evaluable_subset_test_arch64.swift | 2 +- 5 files changed, 2 insertions(+), 148 deletions(-) delete mode 100644 lib/SILOptimizer/Mandatory/MarkUninitializedFixup.cpp diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 9096a70b6f09a..5b8005e807db6 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -305,8 +305,6 @@ PASS(OwnershipDumper, "ownership-dumper", "Print Ownership information for Testing") PASS(SemanticARCOpts, "semantic-arc-opts", "Semantic ARC Optimization") -PASS(MarkUninitializedFixup, "mark-uninitialized-fixup", - "Temporary pass for staging in mark_uninitialized changes.") PASS(SimplifyUnreachableContainingBlocks, "simplify-unreachable-containing-blocks", "Utility pass. Removes all non-term insts from blocks with unreachable terms") PASS(SerializeSILPass, "serialize-sil", diff --git a/lib/SILOptimizer/Mandatory/CMakeLists.txt b/lib/SILOptimizer/Mandatory/CMakeLists.txt index b441b241f5e38..891c1ba40775e 100644 --- a/lib/SILOptimizer/Mandatory/CMakeLists.txt +++ b/lib/SILOptimizer/Mandatory/CMakeLists.txt @@ -13,7 +13,6 @@ silopt_register_sources( DiagnoseUnreachable.cpp GuaranteedARCOpts.cpp IRGenPrepare.cpp - MarkUninitializedFixup.cpp MandatoryInlining.cpp PredictableMemOpt.cpp PMOMemoryUseCollector.cpp diff --git a/lib/SILOptimizer/Mandatory/MarkUninitializedFixup.cpp b/lib/SILOptimizer/Mandatory/MarkUninitializedFixup.cpp deleted file mode 100644 index 4c1f330c7942b..0000000000000 --- a/lib/SILOptimizer/Mandatory/MarkUninitializedFixup.cpp +++ /dev/null @@ -1,143 +0,0 @@ -//===--- MarkUninitializedFixup.cpp ---------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "sil-ownership-model-eliminator" -#include "swift/SIL/SILBuilder.h" -#include "swift/SIL/SILFunction.h" -#include "swift/SIL/SILVisitor.h" -#include "swift/SILOptimizer/PassManager/Transforms.h" - -using namespace swift; - -//===----------------------------------------------------------------------===// -// Top Level Entry Point -//===----------------------------------------------------------------------===// - -static ProjectBoxInst * -getInitialProjectBox(MarkUninitializedInst *MUI, - ArrayRef Projections) { - assert(!Projections.empty()); - if (Projections.size() == 1) { - auto *PBI = Projections[0]; - assert(PBI->getParent() == MUI->getParent()); - return PBI; - } - - // Otherwise, we want to select the earliest project box. There should - // only be one. - ProjectBoxInst *PBI = Projections[0]; - - // Otherwise, we need to find the one that is closest to the - // mark_uninitialized. It should be in the same block. - for (auto *I : makeArrayRef(Projections).slice(1)) { - // If the new instruction is in a different block than the - // mark_uninitialized, it can not be a good solution, so skip it. - if (I->getParent() != MUI->getParent()) { - continue; - } - - // If PBI is not in the same block as the MUI, but I is, we picked a - // bad initial PBI, set PBI to I. - if (PBI->getParent() != MUI->getParent()) { - // Otherwise, I is a better candidate than PBI so set PBI to I. - PBI = I; - continue; - } - - // Otherwise, we have that PBI and I are both in the same block. See - // which one is first. - auto *BB = PBI->getParent(); - if (BB->end() != std::find_if(PBI->getIterator(), BB->end(), - [&I](const SILInstruction &InnerI) -> bool { - return I == &InnerI; - })) { - continue; - } - - PBI = I; - } - - assert(PBI->getParent() == MUI->getParent()); - return PBI; -} - -namespace { - -struct MarkUninitializedFixup : SILFunctionTransform { - void run() override { - // Don't rerun on deserialized functions. Nothing should have changed. - if (getFunction()->wasDeserializedCanonical()) - return; - - bool MadeChange = false; - for (auto &BB : *getFunction()) { - for (auto II = BB.begin(), IE = BB.end(); II != IE;) { - // Grab our given instruction and advance the iterator. This is - // important since we may be destroying the given instruction. - auto *MUI = dyn_cast(&*II); - ++II; - - // If we do not have a mark_uninitialized or we have a - // mark_uninitialized of an alloc_box, continue. These are not - // interesting to us. - if (!MUI) - continue; - - auto *Box = dyn_cast(MUI->getOperand()); - if (!Box) - continue; - - // We expect there to be in most cases exactly one project_box. That - // being said, it is not impossible for there to be multiple. In such - // a case, we assume that the correct project_box is the one that is - // nearest to the mark_uninitialized in the same block. This preserves - // the existing behavior. - llvm::TinyPtrVector Projections; - for (auto *Op : MUI->getUses()) { - if (auto *PBI = dyn_cast(Op->getUser())) { - Projections.push_back(PBI); - } - } - assert(!Projections.empty() && "SILGen should never emit a " - "mark_uninitialized by itself"); - - // First replace all uses of the mark_uninitialized with the box. - MUI->replaceAllUsesWith(Box); - - // That means now our project box now has the alloc_box as its - // operand. Grab that project_box. - auto *PBI = getInitialProjectBox(MUI, Projections); - - // Then create the new mark_uninitialized and force all uses of the - // project_box to go through the new mark_uninitialized. - SILBuilderWithScope B(&*std::next(PBI->getIterator())); - SILValue Undef = SILUndef::get(PBI->getType(), *PBI->getFunction()); - auto *NewMUI = - B.createMarkUninitialized(PBI->getLoc(), Undef, MUI->getKind()); - PBI->replaceAllUsesWith(NewMUI); - NewMUI->setOperand(PBI); - - // Finally, remove the old mark_uninitialized. - MUI->eraseFromParent(); - MadeChange = true; - } - } - if (MadeChange) - invalidateAnalysis( - SILAnalysis::InvalidationKind::BranchesAndInstructions); - } -}; -} // end anonymous namespace - -SILTransform *swift::createMarkUninitializedFixup() { - return new MarkUninitializedFixup(); -} diff --git a/test/SILOptimizer/constant_evaluable_subset_test.swift b/test/SILOptimizer/constant_evaluable_subset_test.swift index 8e1b6fa33b592..12d710f9165ae 100644 --- a/test/SILOptimizer/constant_evaluable_subset_test.swift +++ b/test/SILOptimizer/constant_evaluable_subset_test.swift @@ -14,7 +14,7 @@ // especially performance inlining as it inlines functions such as String.+= // that the evaluator has special knowledge about. // -// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -mark-uninitialized-fixup -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -guaranteed-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_silgen.sil > /dev/null 2> %t/error-output-mandatory +// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -guaranteed-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_silgen.sil > /dev/null 2> %t/error-output-mandatory // // RUN: %FileCheck %s < %t/error-output-mandatory diff --git a/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift b/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift index 917074e565744..07e17465aa831 100644 --- a/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift +++ b/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift @@ -16,7 +16,7 @@ // especially performance inlining as it inlines functions such as String.+= // that the evaluator has special knowledge about. // -// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -mark-uninitialized-fixup -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -guaranteed-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_arch64_silgen.sil > /dev/null 2> %t/error-output-mandatory +// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -guaranteed-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_arch64_silgen.sil > /dev/null 2> %t/error-output-mandatory // // RUN: %FileCheck %s < %t/error-output-mandatory From 046c9148ec920a6b9c513570281ed2cec8f1f486 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 23 Dec 2019 22:50:12 -0800 Subject: [PATCH 232/478] [CodeCompletion] Use getBodySourceRange() instead of getSourceRange() To check if the completion is happening in the AFD body. Otherwise, local variables are sometimes not suggested because the body and its range is from another file. rdar://problem/58175106 --- lib/Sema/LookupVisibleDecls.cpp | 4 +-- .../complete_sequence_localvar.swift | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 test/SourceKit/CodeComplete/complete_sequence_localvar.swift diff --git a/lib/Sema/LookupVisibleDecls.cpp b/lib/Sema/LookupVisibleDecls.cpp index 16770c7656fe3..b17f5dcb3e30c 100644 --- a/lib/Sema/LookupVisibleDecls.cpp +++ b/lib/Sema/LookupVisibleDecls.cpp @@ -1088,8 +1088,8 @@ static void lookupVisibleDeclsImpl(VisibleDeclConsumer &Consumer, // FIXME: when we can parse and typecheck the function body partially for // code completion, AFD->getBody() check can be removed. if (Loc.isValid() && - AFD->getSourceRange().isValid() && - SM.rangeContainsTokenLoc(AFD->getSourceRange(), Loc) && + AFD->getBodySourceRange().isValid() && + SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc) && AFD->getBody()) { namelookup::FindLocalVal(SM, Loc, Consumer).visit(AFD->getBody()); } diff --git a/test/SourceKit/CodeComplete/complete_sequence_localvar.swift b/test/SourceKit/CodeComplete/complete_sequence_localvar.swift new file mode 100644 index 0000000000000..49a27534782f8 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_localvar.swift @@ -0,0 +1,26 @@ +func test() { + var localVar = 1 + + var afterVar = 1 +} + +// RUN: %sourcekitd-test \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s | %FileCheck %s + +// CHECK-NOT: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" From 9be16b218585431645bf6eab2f4107730149cf30 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 2 Jan 2020 13:06:09 -0800 Subject: [PATCH 233/478] [Type checker] Move conformance resolution during application into Solution. While this doesn't completely use the solution's set of known conformances, it moves the logic for handling the lookup into the right place. --- lib/Sema/CSApply.cpp | 32 +++++++++++++++++++++++++++++++ lib/Sema/ConstraintSystem.h | 5 +++++ lib/Sema/TypeCheckConstraints.cpp | 10 ++++------ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 2be455c2a317b..9a8c38ff25d10 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -87,6 +87,7 @@ Solution::computeSubstitutions(GenericSignature sig, return ProtocolConformanceRef(protoType); } + // FIXME: Retrieve the conformance from the solution itself. return TypeChecker::conformsToProtocol(replacement, protoType, getConstraintSystem().DC, ConformanceCheckFlags::InExpression); @@ -7345,6 +7346,37 @@ class SetExprTypes : public ASTWalker { }; } +ProtocolConformanceRef Solution::resolveConformance( + ConstraintLocator *locator, ProtocolDecl *proto) { + for (const auto &conformance : Conformances) { + if (conformance.first != locator) + continue; + if (conformance.second.getRequirement() != proto) + continue; + + // If the conformance doesn't require substitution, return it immediately. + auto conformanceRef = conformance.second; + if (conformanceRef.isAbstract()) + return conformanceRef; + + auto concrete = conformanceRef.getConcrete(); + auto conformingType = concrete->getType(); + if (!conformingType->hasTypeVariable()) + return conformanceRef; + + // Substitute into the conformance type, then look for a conformance + // again. + // FIXME: Should be able to perform the substitution using the Solution + // itself rather than another conforms-to-protocol check. + Type substConformingType = simplifyType(conformingType); + return TypeChecker::conformsToProtocol( + substConformingType, proto, constraintSystem->DC, + ConformanceCheckFlags::InExpression); + } + + return ProtocolConformanceRef::forInvalid(); +} + void Solution::setExprTypes(Expr *expr) const { if (!expr) return; diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index d36a874326518..d41003d3ae303 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -898,6 +898,11 @@ class Solution { return None; } + /// Retrieve a fully-resolved protocol conformance at the given locator + /// and with the given protocol. + ProtocolConformanceRef resolveConformance(ConstraintLocator *locator, + ProtocolDecl *proto); + void setExprTypes(Expr *expr) const; SWIFT_DEBUG_DUMP; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 208ca7b952189..0f112c4d4f9cc 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -3040,17 +3040,15 @@ auto TypeChecker::typeCheckForEachBinding( Stmt->setPattern(pattern); // Get the conformance of the sequence type to the Sequence protocol. - // FIXME: Get this from the solution and substitute into that. - SequenceConformance = TypeChecker::conformsToProtocol( - SequenceType, SequenceProto, cs.DC, - ConformanceCheckFlags::InExpression, - expr->getLoc()); + SequenceConformance = solution.resolveConformance( + ContextualLocator, SequenceProto); assert(!SequenceConformance.isInvalid() && "Couldn't find sequence conformance"); Stmt->setSequenceConformance(SequenceConformance); // Retrieve the conformance of the iterator type to IteratorProtocol. - // FIXME: Get this from the solution and substitute into that. + // FIXME: We probably don't even need this. If we do, get it from + // SequenceConformance instead. IteratorConformance = TypeChecker::conformsToProtocol( IteratorType, IteratorProto, cs.DC, ConformanceCheckFlags::InExpression, From f22a7e28800cabcead3b2f8e48cee6ec323babdd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 2 Jan 2020 13:29:13 -0800 Subject: [PATCH 234/478] [AST] Drop makeIterator reference from ForEachStmt. Rather than having the type checker form the ConcreteDeclRef for makeIterator, have SILGen do it, because it's fairly trivial. Eliminates some redundant state from the AST. --- include/swift/AST/Stmt.h | 4 ---- lib/AST/ASTDumper.cpp | 3 --- lib/SILGen/SILGenStmt.cpp | 14 +++++++++++++- lib/Sema/TypeCheckConstraints.cpp | 17 ----------------- 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 8031a2188d819..a19a19442497f 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -808,7 +808,6 @@ class ForEachStmt : public LabeledStmt { // Set by Sema: ProtocolConformanceRef sequenceConformance = ProtocolConformanceRef(); - ConcreteDeclRef makeIterator; ConcreteDeclRef iteratorNext; VarDecl *iteratorVar = nullptr; Expr *iteratorVarRef = nullptr; @@ -838,9 +837,6 @@ class ForEachStmt : public LabeledStmt { void setConvertElementExpr(Expr *expr) { convertElementExpr = expr; } Expr *getConvertElementExpr() const { return convertElementExpr; } - void setMakeIterator(ConcreteDeclRef declRef) { makeIterator = declRef; } - ConcreteDeclRef getMakeIterator() const { return makeIterator; } - void setIteratorNext(ConcreteDeclRef declRef) { iteratorNext = declRef; } ConcreteDeclRef getIteratorNext() const { return iteratorNext; } diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 08e03ecfed560..913b28b171596 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1593,9 +1593,6 @@ class PrintStmt : public StmtVisitor { } void visitForEachStmt(ForEachStmt *S) { printCommon(S, "for_each_stmt"); - PrintWithColorRAII(OS, LiteralValueColor) << " make_generator="; - S->getMakeIterator().dump( - PrintWithColorRAII(OS, LiteralValueColor).getOS()); PrintWithColorRAII(OS, LiteralValueColor) << " next="; S->getIteratorNext().dump( PrintWithColorRAII(OS, LiteralValueColor).getOS()); diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 7c33d612fdcc4..078c8b9f7e0ec 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -909,8 +909,20 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) { auto initialization = SGF.emitInitializationForVarDecl(S->getIteratorVar(), false); SILLocation loc = SILLocation(S->getSequence()); + + // Compute the reference to the Sequence's makeIterator(). + FuncDecl *makeIteratorReq = SGF.getASTContext().getSequenceMakeIterator(); + auto sequenceProto = + SGF.getASTContext().getProtocol(KnownProtocolKind::Sequence); + auto sequenceConformance = S->getSequenceConformance(); + Type sequenceType = S->getSequence()->getType(); + auto sequenceSubs = SubstitutionMap::getProtocolSubstitutions( + sequenceProto, sequenceType, sequenceConformance); + ConcreteDeclRef makeIteratorRef(makeIteratorReq, sequenceSubs); + + // Call makeIterator(). RValue result = SGF.emitApplyMethod( - loc, S->getMakeIterator(), ArgumentSource(S->getSequence()), + loc, makeIteratorRef, ArgumentSource(S->getSequence()), PreparedArguments(ArrayRef({})), SGFContext(initialization.get())); if (!result.isInContext()) { diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 0f112c4d4f9cc..718ae34f949e4 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -3013,17 +3013,8 @@ auto TypeChecker::typeCheckForEachBinding( // Perform any necessary conversions of the sequence (e.g. [T]! -> [T]). expr = solution.coerceToType(expr, SequenceType, Locator); - if (!expr) return nullptr; - // Convert the sequence as appropriate for the makeIterator() call. - auto makeIteratorOverload = solution.getOverloadChoice(ContextualLocator); - auto makeIteratorSelfType = solution.simplifyType( - makeIteratorOverload.openedFullType - )->castTo()->getParams()[0].getPlainType(); - expr = solution.coerceToType(expr, makeIteratorSelfType, - ContextualLocator); - cs.cacheExprTypes(expr); Stmt->setSequence(expr); @@ -3054,14 +3045,6 @@ auto TypeChecker::typeCheckForEachBinding( ConformanceCheckFlags::InExpression, expr->getLoc()); - // Record the makeIterator declaration we used. - auto makeIteratorDecl = makeIteratorOverload.choice.getDecl(); - auto makeIteratorSubs = SequenceType->getMemberSubstitutionMap( - cs.DC->getParentModule(), makeIteratorDecl); - auto makeIteratorDeclRef = - ConcreteDeclRef(makeIteratorDecl, makeIteratorSubs); - Stmt->setMakeIterator(makeIteratorDeclRef); - solution.setExprTypes(expr); return expr; } From dbc3b433ad35d3c1b3b57dd8e69fabf9b0b1aa1f Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 2 Jan 2020 14:15:49 -0800 Subject: [PATCH 235/478] build: synchronise with LLDB's version of FindLibEdit The current FOUND_VAR for FindLibEdit is libedit_FOUND but wasn't set by find_package_handle_standard_args. However this isn't valid for the package name. The argument for FOUND_VAR is "libedit_FOUND", but only "LibEdit_FOUND" and "LIBEDIT_FOUND" are valid names. This fixes all the variables set by FindLibEdit to match the desired naming scheme. Thanks to Jonas for fixing the variable names! --- cmake/modules/FindLibEdit.cmake | 37 ++++++++++--------- lib/Immediate/CMakeLists.txt | 4 +- tools/SourceKit/tools/CMakeLists.txt | 2 +- .../tools/sourcekitd-repl/CMakeLists.txt | 4 +- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/cmake/modules/FindLibEdit.cmake b/cmake/modules/FindLibEdit.cmake index 671ca9bcea379..b4f0cb3298114 100644 --- a/cmake/modules/FindLibEdit.cmake +++ b/cmake/modules/FindLibEdit.cmake @@ -8,25 +8,25 @@ # # :: # -# libedit_FOUND - true if libedit was found -# libedit_INCLUDE_DIRS - include search path -# libedit_LIBRARIES - libraries to link -# libedit_VERSION - version number +# LibEdit_FOUND - true if libedit was found +# LibEdit_INCLUDE_DIRS - include search path +# LibEdit_LIBRARIES - libraries to link +# LibEdit_VERSION_STRING - version number -if(libedit_INCLUDE_DIRS AND libedit_LIBRARIES) - set(libedit_FOUND TRUE) +if(LibEdit_INCLUDE_DIRS AND LibEdit_LIBRARIES) + set(LibEdit_FOUND TRUE) else() find_package(PkgConfig QUIET) pkg_check_modules(PC_LIBEDIT QUIET libedit) - find_path(libedit_INCLUDE_DIRS + find_path(LibEdit_INCLUDE_DIRS NAMES histedit.h HINTS ${PC_LIBEDIT_INCLUDEDIR} ${PC_LIBEDIT_INCLUDE_DIRS} ${CMAKE_INSTALL_FULL_INCLUDEDIR}) - find_library(libedit_LIBRARIES + find_library(LibEdit_LIBRARIES NAMES edit libedit HINTS @@ -34,28 +34,31 @@ else() ${PC_LIBEDIT_LIBRARY_DIRS} ${CMAKE_INSTALL_FULL_LIBDIR}) - if(libedit_INCLUDE_DIRS AND EXISTS "${libedit_INCLUDE_DIRS}/histedit.h") - file(STRINGS "${libedit_INCLUDE_DIRS}/histedit.h" + if(LibEdit_INCLUDE_DIRS AND EXISTS "${LibEdit_INCLUDE_DIRS}/histedit.h") + file(STRINGS "${LibEdit_INCLUDE_DIRS}/histedit.h" libedit_major_version_str REGEX "^#define[ \t]+LIBEDIT_MAJOR[ \t]+[0-9]+") string(REGEX REPLACE "^#define[ \t]+LIBEDIT_MAJOR[ \t]+([0-9]+)" "\\1" LIBEDIT_MAJOR_VERSION "${libedit_major_version_str}") - file(STRINGS "${libedit_INCLUDE_DIRS}/histedit.h" + file(STRINGS "${LibEdit_INCLUDE_DIRS}/histedit.h" libedit_minor_version_str REGEX "^#define[ \t]+LIBEDIT_MINOR[ \t]+[0-9]+") string(REGEX REPLACE "^#define[ \t]+LIBEDIT_MINOR[ \t]+([0-9]+)" "\\1" LIBEDIT_MINOR_VERSION "${libedit_minor_version_str}") - set(libedit_VERSION_STRING "${libedit_major_version}.${libedit_minor_version}") + set(LibEdit_VERSION_STRING "${libedit_major_version}.${libedit_minor_version}") endif() include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(libedit + find_package_handle_standard_args(LibEdit + FOUND_VAR + LibEdit_FOUND REQUIRED_VARS - libedit_INCLUDE_DIRS - libedit_LIBRARIES + LibEdit_INCLUDE_DIRS + LibEdit_LIBRARIES VERSION_VAR - libedit_VERSION_STRING) - mark_as_advanced(libedit_INCLUDE_DIRS libedit_LIBRARIES) + LibEdit_VERSION_STRING) + mark_as_advanced(LibEdit_INCLUDE_DIRS LibEdit_LIBRARIES) endif() + diff --git a/lib/Immediate/CMakeLists.txt b/lib/Immediate/CMakeLists.txt index 0c7d3f5d87351..f95071a3853d6 100644 --- a/lib/Immediate/CMakeLists.txt +++ b/lib/Immediate/CMakeLists.txt @@ -12,9 +12,9 @@ target_link_libraries(swiftImmediate PRIVATE swiftIRGen swiftSILGen swiftSILOptimizer) -if(libedit_FOUND) +if(LibEdit_FOUND) target_compile_definitions(swiftImmediate PRIVATE HAVE_LIBEDIT) target_link_libraries(swiftImmediate PRIVATE - ${libedit_LIBRARIES}) + ${LibEdit_LIBRARIES}) endif() diff --git a/tools/SourceKit/tools/CMakeLists.txt b/tools/SourceKit/tools/CMakeLists.txt index 334b110893da3..f49f58be095cd 100644 --- a/tools/SourceKit/tools/CMakeLists.txt +++ b/tools/SourceKit/tools/CMakeLists.txt @@ -6,7 +6,7 @@ include_directories( add_swift_lib_subdirectory(sourcekitd) add_swift_tool_subdirectory(sourcekitd-test) -if(libedit_FOUND) +if(LibEdit_FOUND) add_swift_tool_subdirectory(sourcekitd-repl) endif() add_swift_tool_subdirectory(complete-test) diff --git a/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt index 57233bc1a9b61..fe2bc449a0ab5 100644 --- a/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt @@ -13,9 +13,9 @@ if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) BlocksRuntime) endif() target_include_directories(sourcekitd-repl PRIVATE - ${libedit_INCLUDE_DIRS}) + ${LibEdit_INCLUDE_DIRS}) target_link_libraries(sourcekitd-repl PRIVATE - ${libedit_LIBRARIES}) + ${LibEdit_LIBRARIES}) if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set_target_properties(sourcekitd-repl From 97b5a0d5fbb7116334fdb5571e6da9a3a8a93984 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 2 Jan 2020 14:39:00 -0800 Subject: [PATCH 236/478] [Type checker] Stop devirtualizing the reference to IteratorProtocol.next(). Rather than having the type checker look for the specific witness to next() when type checking the for-each loop, which had the effect of devirtualizing next() even when it shouldn't be, leave the formation of the next() reference to SILGen. There, form it as a witness reference, so that the SIL optimizer can choose whether to devirtualization (or not). --- include/swift/AST/Stmt.h | 4 --- lib/AST/ASTDumper.cpp | 3 -- lib/SILGen/SILGenStmt.cpp | 36 ++++++++++++++----- lib/Sema/TypeCheckConstraints.cpp | 24 +++---------- lib/Sema/TypeCheckStmt.cpp | 24 ++----------- lib/Sema/TypeChecker.h | 1 - .../generic_protocol_witness.swift | 1 - test/SILGen/foreach.swift | 12 +++---- test/SILGen/statements.swift | 4 +-- test/stmt/foreach.swift | 3 -- 10 files changed, 43 insertions(+), 69 deletions(-) diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index a19a19442497f..9a762df4f6de2 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -808,7 +808,6 @@ class ForEachStmt : public LabeledStmt { // Set by Sema: ProtocolConformanceRef sequenceConformance = ProtocolConformanceRef(); - ConcreteDeclRef iteratorNext; VarDecl *iteratorVar = nullptr; Expr *iteratorVarRef = nullptr; OpaqueValueExpr *elementExpr = nullptr; @@ -837,9 +836,6 @@ class ForEachStmt : public LabeledStmt { void setConvertElementExpr(Expr *expr) { convertElementExpr = expr; } Expr *getConvertElementExpr() const { return convertElementExpr; } - void setIteratorNext(ConcreteDeclRef declRef) { iteratorNext = declRef; } - ConcreteDeclRef getIteratorNext() const { return iteratorNext; } - void setSequenceConformance(ProtocolConformanceRef conformance) { sequenceConformance = conformance; } diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 913b28b171596..a2e81377121a3 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1593,9 +1593,6 @@ class PrintStmt : public StmtVisitor { } void visitForEachStmt(ForEachStmt *S) { printCommon(S, "for_each_stmt"); - PrintWithColorRAII(OS, LiteralValueColor) << " next="; - S->getIteratorNext().dump( - PrintWithColorRAII(OS, LiteralValueColor).getOS()); OS << '\n'; printRec(S->getPattern()); OS << '\n'; diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 078c8b9f7e0ec..8cb86b426b4cb 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -903,6 +903,14 @@ void StmtEmitter::visitRepeatWhileStmt(RepeatWhileStmt *S) { } void StmtEmitter::visitForEachStmt(ForEachStmt *S) { + // Dig out information about the sequence conformance. + auto sequenceConformance = S->getSequenceConformance(); + Type sequenceType = S->getSequence()->getType(); + auto sequenceProto = + SGF.getASTContext().getProtocol(KnownProtocolKind::Sequence); + auto sequenceSubs = SubstitutionMap::getProtocolSubstitutions( + sequenceProto, sequenceType, sequenceConformance); + // Emit the 'iterator' variable that we'll be using for iteration. LexicalScope OuterForScope(SGF, CleanupLocation(S)); { @@ -912,12 +920,6 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) { // Compute the reference to the Sequence's makeIterator(). FuncDecl *makeIteratorReq = SGF.getASTContext().getSequenceMakeIterator(); - auto sequenceProto = - SGF.getASTContext().getProtocol(KnownProtocolKind::Sequence); - auto sequenceConformance = S->getSequenceConformance(); - Type sequenceType = S->getSequence()->getType(); - auto sequenceSubs = SubstitutionMap::getProtocolSubstitutions( - sequenceProto, sequenceType, sequenceConformance); ConcreteDeclRef makeIteratorRef(makeIteratorReq, sequenceSubs); // Call makeIterator(). @@ -964,8 +966,26 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) { JumpDest endDest = createJumpDest(S->getBody()); SGF.BreakContinueDestStack.push_back({ S, endDest, loopDest }); + // Compute the reference to the the iterator's next(). + auto iteratorProto = + SGF.getASTContext().getProtocol(KnownProtocolKind::IteratorProtocol); + ValueDecl *iteratorNextReq = iteratorProto->getSingleRequirement( + DeclName(SGF.getASTContext(), SGF.getASTContext().Id_next, + ArrayRef())); + auto iteratorAssocType = + sequenceProto->getAssociatedType(SGF.getASTContext().Id_Iterator); + auto iteratorMemberRef = DependentMemberType::get( + sequenceProto->getSelfInterfaceType(), iteratorAssocType); + auto iteratorType = sequenceConformance.getAssociatedType( + sequenceType, iteratorMemberRef); + auto iteratorConformance = sequenceConformance.getAssociatedConformance( + sequenceType, iteratorMemberRef, iteratorProto); + auto iteratorSubs = SubstitutionMap::getProtocolSubstitutions( + iteratorProto, iteratorType, iteratorConformance); + ConcreteDeclRef iteratorNextRef(iteratorNextReq, iteratorSubs); + auto buildArgumentSource = [&]() { - if (cast(S->getIteratorNext().getDecl())->getSelfAccessKind() == + if (cast(iteratorNextRef.getDecl())->getSelfAccessKind() == SelfAccessKind::Mutating) { LValue lv = SGF.emitLValue(S->getIteratorVarRef(), SGFAccessKind::ReadWrite); @@ -981,7 +1001,7 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) { auto buildElementRValue = [&](SILLocation loc, SGFContext ctx) { RValue result; result = SGF.emitApplyMethod( - loc, S->getIteratorNext(), buildArgumentSource(), + loc, iteratorNextRef, buildArgumentSource(), PreparedArguments(ArrayRef({})), S->getElementExpr() ? SGFContext() : ctx); if (S->getElementExpr()) { diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 718ae34f949e4..0af6163713df7 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2926,12 +2926,6 @@ auto TypeChecker::typeCheckForEachBinding( /// The type of the iterator. Type IteratorType; - /// The conformance of the iterator type to IteratorProtocol. - ProtocolConformanceRef IteratorConformance; - - /// The type of makeIterator. - Type MakeIteratorType; - public: explicit BindingListener(ForEachStmt *stmt) : Stmt(stmt) { } @@ -2993,10 +2987,11 @@ auto TypeChecker::typeCheckForEachBinding( // Reference the makeIterator witness. ASTContext &ctx = cs.getASTContext(); FuncDecl *makeIterator = ctx.getSequenceMakeIterator(); - MakeIteratorType = cs.createTypeVariable(Locator, TVO_CanBindToNoEscape); + Type makeIteratorType = + cs.createTypeVariable(Locator, TVO_CanBindToNoEscape); cs.addValueWitnessConstraint( LValueType::get(SequenceType), makeIterator, - MakeIteratorType, cs.DC, FunctionRefKind::Compound, + makeIteratorType, cs.DC, FunctionRefKind::Compound, ContextualLocator); Stmt->setSequence(expr); @@ -3037,23 +3032,12 @@ auto TypeChecker::typeCheckForEachBinding( "Couldn't find sequence conformance"); Stmt->setSequenceConformance(SequenceConformance); - // Retrieve the conformance of the iterator type to IteratorProtocol. - // FIXME: We probably don't even need this. If we do, get it from - // SequenceConformance instead. - IteratorConformance = TypeChecker::conformsToProtocol( - IteratorType, IteratorProto, cs.DC, - ConformanceCheckFlags::InExpression, - expr->getLoc()); - solution.setExprTypes(expr); return expr; } ForEachBinding getBinding() const { - return { - SequenceType, SequenceConformance, IteratorType, IteratorConformance, - ElementType - }; + return { SequenceType, SequenceConformance, IteratorType, ElementType }; } }; diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 81c74e24fa71f..41ee2e2c61d4b 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -777,6 +777,7 @@ class StmtChecker : public StmtVisitor { // Invoke iterator() to get an iterator from the sequence. Type iteratorTy = binding->iteratorType; VarDecl *iterator; + Type nextResultType = OptionalType::get(binding->elementType); { // Create a local variable to capture the iterator. std::string name; @@ -797,7 +798,6 @@ class StmtChecker : public StmtVisitor { // TODO: test/DebugInfo/iteration.swift requires this extra info to // be around. - auto nextResultType = OptionalType::get(binding->elementType); PatternBindingDecl::createImplicit( getASTContext(), StaticSpellingKind::None, genPat, new (getASTContext()) OpaqueValueExpr(S->getInLoc(), nextResultType), @@ -808,33 +808,15 @@ class StmtChecker : public StmtVisitor { if (TypeChecker::requireOptionalIntrinsics(getASTContext(), S->getForLoc())) return nullptr; - // Gather the witnesses from the Iterator protocol conformance, which - // we'll use to drive the loop. - // FIXME: Would like to customize the diagnostic emitted in - // conformsToProtocol(). - auto genConformance = binding->iteratorConformance; - if (genConformance.isInvalid()) - return nullptr; - + // Create the iterator variable. auto *varRef = TypeChecker::buildCheckedRefExpr(iterator, DC, DeclNameLoc(S->getInLoc()), /*implicit*/ true); if (!varRef) return nullptr; - S->setIteratorVarRef(varRef); - auto witness = - genConformance.getWitnessByName(iteratorTy, getASTContext().Id_next); - if (!witness) - return nullptr; - S->setIteratorNext(witness); - - auto nextResultType = cast(S->getIteratorNext().getDecl()) - ->getResultInterfaceType() - .subst(S->getIteratorNext().getSubstitutions()); - - // Convert that Optional value to Optional. + // Convert that Optional value to the type of the pattern. auto optPatternType = OptionalType::get(S->getPattern()->getType()); if (!optPatternType->isEqual(nextResultType)) { OpaqueValueExpr *elementExpr = diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 79dcb7cb19b28..5cfa10043f353 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1037,7 +1037,6 @@ class TypeChecker final { Type sequenceType; ProtocolConformanceRef sequenceConformance; Type iteratorType; - ProtocolConformanceRef iteratorConformance; Type elementType; }; diff --git a/test/Constraints/generic_protocol_witness.swift b/test/Constraints/generic_protocol_witness.swift index 6153b7dd03827..82d9da908aad0 100644 --- a/test/Constraints/generic_protocol_witness.swift +++ b/test/Constraints/generic_protocol_witness.swift @@ -61,5 +61,4 @@ struct L: Sequence {} // expected-error {{type 'L' does not conform to pro func z(_ x: L) { for xx in x {} // expected-warning@-1{{immutable value 'xx' was never used; consider replacing with '_' or removing it}} - // expected-error@-2{{type 'L.Iterator' does not conform to protocol 'IteratorProtocol'}} } diff --git a/test/SILGen/foreach.swift b/test/SILGen/foreach.swift index 3e80c92828733..5c67eff05efd7 100644 --- a/test/SILGen/foreach.swift +++ b/test/SILGen/foreach.swift @@ -115,8 +115,8 @@ func trivialStructBreak(_ xx: [Int]) { // CHECK: [[LOOP_DEST]]: // CHECK: [[GET_ELT_STACK:%.*]] = alloc_stack $Optional // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PROJECT_ITERATOR_BOX]] : $*IndexingIterator> -// CHECK: [[FUNC_REF:%.*]] = function_ref @$ss16IndexingIteratorV4next7ElementQzSgyF : $@convention(method) -// CHECK: apply [[FUNC_REF]]>([[GET_ELT_STACK]], [[WRITE]]) +// CHECK: [[FUNC_REF:%.*]] = witness_method $IndexingIterator>, #IteratorProtocol.next!1 : (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> +// CHECK: apply [[FUNC_REF]]>>([[GET_ELT_STACK]], [[WRITE]]) // CHECK: [[IND_VAR:%.*]] = load [trivial] [[GET_ELT_STACK]] // CHECK: switch_enum [[IND_VAR]] : $Optional, case #Optional.some!enumelt.1: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] // @@ -215,8 +215,8 @@ func existentialBreak(_ xx: [P]) { // // CHECK: [[LOOP_DEST]]: // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PROJECT_ITERATOR_BOX]] : $*IndexingIterator> -// CHECK: [[FUNC_REF:%.*]] = function_ref @$ss16IndexingIteratorV4next7ElementQzSgyF : $@convention(method) -// CHECK: apply [[FUNC_REF]]>([[ELT_STACK]], [[WRITE]]) +// CHECK: [[FUNC_REF:%.*]] = witness_method $IndexingIterator>, #IteratorProtocol.next!1 : (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> +// CHECK: apply [[FUNC_REF]]>>([[ELT_STACK]], [[WRITE]]) // CHECK: switch_enum_addr [[ELT_STACK]] : $*Optional

, case #Optional.some!enumelt.1: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] // // CHECK: [[SOME_BB]]: @@ -375,8 +375,8 @@ func genericStructBreak(_ xx: [GenericStruct]) { // // CHECK: [[LOOP_DEST]]: // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PROJECT_ITERATOR_BOX]] : $*IndexingIterator>> -// CHECK: [[FUNC_REF:%.*]] = function_ref @$ss16IndexingIteratorV4next7ElementQzSgyF : $@convention(method) -// CHECK: apply [[FUNC_REF]]>>([[ELT_STACK]], [[WRITE]]) +// CHECK: [[FUNC_REF:%.*]] = witness_method $IndexingIterator>>, #IteratorProtocol.next!1 : (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> +// CHECK: apply [[FUNC_REF]]>>>([[ELT_STACK]], [[WRITE]]) // CHECK: switch_enum_addr [[ELT_STACK]] : $*Optional>, case #Optional.some!enumelt.1: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] // // CHECK: [[SOME_BB]]: diff --git a/test/SILGen/statements.swift b/test/SILGen/statements.swift index 3015db1a18a24..f33d40ff8fe18 100644 --- a/test/SILGen/statements.swift +++ b/test/SILGen/statements.swift @@ -167,8 +167,8 @@ func for_loops2() { // rdar://problem/19316670 // CHECK: alloc_stack $Optional // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] - // CHECK: [[NEXT:%[0-9]+]] = function_ref @$ss16IndexingIteratorV4next{{[_0-9a-zA-Z]*}}F - // CHECK-NEXT: apply [[NEXT]]> + // CHECK: [[NEXT:%[0-9]+]] = witness_method $IndexingIterator>, #IteratorProtocol.next!1 : (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> + // CHECK-NEXT: apply [[NEXT]]>> // CHECK: class_method [[OBJ:%[0-9]+]] : $MyClass, #MyClass.foo!1 let objects = [MyClass(), MyClass() ] for obj in objects { diff --git a/test/stmt/foreach.swift b/test/stmt/foreach.swift index 7887a6a553431..630840455ef29 100644 --- a/test/stmt/foreach.swift +++ b/test/stmt/foreach.swift @@ -15,7 +15,6 @@ struct BadContainer2 : Sequence { // expected-error{{type 'BadContainer2' does n func bad_containers_2(bc: BadContainer2) { for e in bc { } // expected-warning@-1 {{immutable value 'e' was never used; consider replacing with '_' or removing it}} - // expected-error@-2{{type 'BadContainer2.Iterator' does not conform to protocol 'IteratorProtocol'}} } struct BadContainer3 : Sequence { // expected-error{{type 'BadContainer3' does not conform to protocol 'Sequence'}} @@ -25,7 +24,6 @@ struct BadContainer3 : Sequence { // expected-error{{type 'BadContainer3' does n func bad_containers_3(bc: BadContainer3) { for e in bc { } // expected-warning@-1 {{immutable value 'e' was never used; consider replacing with '_' or removing it}} - // expected-error@-2{{type 'BadContainer3.Iterator' does not conform to protocol 'IteratorProtocol'}} } struct BadIterator1 {} @@ -38,7 +36,6 @@ struct BadContainer4 : Sequence { // expected-error{{type 'BadContainer4' does n func bad_containers_4(bc: BadContainer4) { for e in bc { } // expected-warning@-1 {{immutable value 'e' was never used; consider replacing with '_' or removing it}} - // expected-error@-2{{type 'BadContainer4.Iterator' does not conform to protocol 'IteratorProtocol'}} } // Pattern type-checking From 40e599e962d6f330dcfbeb0a9c7f8c2e7369edac Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 2 Jan 2020 15:09:23 -0800 Subject: [PATCH 237/478] [Type checker] Move for-each application logic into constraint system. Move the various "application" logic for the for-each loop out of the visitor in TypeCheckStmt and into the constraint system's handling of for-each bindings. --- lib/Sema/TypeCheckConstraints.cpp | 74 +++++++++++++++++++++++---- lib/Sema/TypeCheckStmt.cpp | 84 +------------------------------ lib/Sema/TypeChecker.h | 13 +---- 3 files changed, 68 insertions(+), 103 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 0af6163713df7..92165a2bda010 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2892,8 +2892,7 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, return hadError; } -auto TypeChecker::typeCheckForEachBinding( - DeclContext *dc, ForEachStmt *stmt) -> Optional { +bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { /// Type checking listener for for-each binding. class BindingListener : public ExprTypeCheckListener { /// The for-each statement. @@ -3001,6 +3000,7 @@ auto TypeChecker::typeCheckForEachBinding( Expr *appliedSolution(Solution &solution, Expr *expr) override { // Figure out what types the constraints decided on. auto &cs = solution.getConstraintSystem(); + ASTContext &ctx = cs.getASTContext(); InitType = solution.simplifyType(InitType); SequenceType = solution.simplifyType(SequenceType); ElementType = solution.simplifyType(ElementType); @@ -3012,6 +3012,7 @@ auto TypeChecker::typeCheckForEachBinding( cs.cacheExprTypes(expr); Stmt->setSequence(expr); + solution.setExprTypes(expr); // Apply the solution to the iteration pattern as well. Pattern *pattern = Stmt->getPattern(); @@ -3032,12 +3033,67 @@ auto TypeChecker::typeCheckForEachBinding( "Couldn't find sequence conformance"); Stmt->setSequenceConformance(SequenceConformance); - solution.setExprTypes(expr); - return expr; - } + // Check the + // FIXME: This should be pulled into the constraint system itself. + if (auto *Where = Stmt->getWhere()) { + if (!TypeChecker::typeCheckCondition(Where, cs.DC)) + Stmt->setWhere(Where); + } + + // Invoke iterator() to get an iterator from the sequence. + VarDecl *iterator; + Type nextResultType = OptionalType::get(ElementType); + { + // Create a local variable to capture the iterator. + std::string name; + if (auto np = dyn_cast_or_null(Stmt->getPattern())) + name = "$"+np->getBoundName().str().str(); + name += "$generator"; + + iterator = new (ctx) VarDecl( + /*IsStatic*/ false, VarDecl::Introducer::Var, + /*IsCaptureList*/ false, Stmt->getInLoc(), + ctx.getIdentifier(name), cs.DC); + iterator->setInterfaceType(IteratorType->mapTypeOutOfContext()); + iterator->setImplicit(); + Stmt->setIteratorVar(iterator); + + auto genPat = new (ctx) NamedPattern(iterator); + genPat->setImplicit(); + + // TODO: test/DebugInfo/iteration.swift requires this extra info to + // be around. + PatternBindingDecl::createImplicit( + ctx, StaticSpellingKind::None, genPat, + new (ctx) OpaqueValueExpr(Stmt->getInLoc(), nextResultType), + cs.DC, /*VarLoc*/ Stmt->getForLoc()); + } - ForEachBinding getBinding() const { - return { SequenceType, SequenceConformance, IteratorType, ElementType }; + // Create the iterator variable. + auto *varRef = TypeChecker::buildCheckedRefExpr( + iterator, cs.DC, DeclNameLoc(Stmt->getInLoc()), /*implicit*/ true); + if (varRef) + Stmt->setIteratorVarRef(varRef); + + // Convert that Optional value to the type of the pattern. + auto optPatternType = OptionalType::get(Stmt->getPattern()->getType()); + if (!optPatternType->isEqual(nextResultType)) { + OpaqueValueExpr *elementExpr = + new (ctx) OpaqueValueExpr(Stmt->getInLoc(), nextResultType, + /*isPlaceholder=*/true); + Expr *convertElementExpr = elementExpr; + if (TypeChecker::typeCheckExpression( + convertElementExpr, cs.DC, + TypeLoc::withoutLoc(optPatternType), + CTP_CoerceOperand).isNull()) { + return nullptr; + } + elementExpr->setIsPlaceholder(false); + Stmt->setElementExpr(elementExpr); + Stmt->setConvertElementExpr(convertElementExpr); + } + + return expr; } }; @@ -3048,8 +3104,8 @@ auto TypeChecker::typeCheckForEachBinding( // Type-check the for-each loop sequence and element pattern. auto resultTy = TypeChecker::typeCheckExpression(seq, dc, &listener); if (!resultTy) - return None; - return listener.getBinding(); + return true; + return false; } bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 41ee2e2c61d4b..44fe3629fb155 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -749,91 +749,9 @@ class StmtChecker : public StmtVisitor { return nullptr; } - auto binding = TypeChecker::typeCheckForEachBinding(DC, S); - if (!binding) + if (TypeChecker::typeCheckForEachBinding(DC, S)) return nullptr; - if (auto *Where = S->getWhere()) { - if (TypeChecker::typeCheckCondition(Where, DC)) - return nullptr; - S->setWhere(Where); - } - - - // Retrieve the 'Sequence' protocol. - ProtocolDecl *sequenceProto = TypeChecker::getProtocol( - getASTContext(), S->getForLoc(), KnownProtocolKind::Sequence); - if (!sequenceProto) { - return nullptr; - } - - // Retrieve the 'Iterator' protocol. - ProtocolDecl *iteratorProto = TypeChecker::getProtocol( - getASTContext(), S->getForLoc(), KnownProtocolKind::IteratorProtocol); - if (!iteratorProto) { - return nullptr; - } - - // Invoke iterator() to get an iterator from the sequence. - Type iteratorTy = binding->iteratorType; - VarDecl *iterator; - Type nextResultType = OptionalType::get(binding->elementType); - { - // Create a local variable to capture the iterator. - std::string name; - if (auto np = dyn_cast_or_null(S->getPattern())) - name = "$"+np->getBoundName().str().str(); - name += "$generator"; - - iterator = new (getASTContext()) VarDecl( - /*IsStatic*/ false, VarDecl::Introducer::Var, - /*IsCaptureList*/ false, S->getInLoc(), - getASTContext().getIdentifier(name), DC); - iterator->setInterfaceType(iteratorTy->mapTypeOutOfContext()); - iterator->setImplicit(); - S->setIteratorVar(iterator); - - auto genPat = new (getASTContext()) NamedPattern(iterator); - genPat->setImplicit(); - - // TODO: test/DebugInfo/iteration.swift requires this extra info to - // be around. - PatternBindingDecl::createImplicit( - getASTContext(), StaticSpellingKind::None, genPat, - new (getASTContext()) OpaqueValueExpr(S->getInLoc(), nextResultType), - DC, /*VarLoc*/ S->getForLoc()); - } - - // Working with iterators requires Optional. - if (TypeChecker::requireOptionalIntrinsics(getASTContext(), S->getForLoc())) - return nullptr; - - // Create the iterator variable. - auto *varRef = TypeChecker::buildCheckedRefExpr(iterator, DC, - DeclNameLoc(S->getInLoc()), - /*implicit*/ true); - if (!varRef) - return nullptr; - S->setIteratorVarRef(varRef); - - // Convert that Optional value to the type of the pattern. - auto optPatternType = OptionalType::get(S->getPattern()->getType()); - if (!optPatternType->isEqual(nextResultType)) { - OpaqueValueExpr *elementExpr = - new (getASTContext()) OpaqueValueExpr(S->getInLoc(), nextResultType, - /*isPlaceholder=*/true); - Expr *convertElementExpr = elementExpr; - if (TypeChecker::typeCheckExpression( - convertElementExpr, DC, - TypeLoc::withoutLoc(optPatternType), - CTP_CoerceOperand).isNull()) { - return nullptr; - } - elementExpr->setIsPlaceholder(false); - S->setElementExpr(elementExpr); - S->setConvertElementExpr(convertElementExpr); - } - // Type-check the body of the loop. AddLabeledStmt loopNest(*this, S); BraceStmt *Body = S->getBody(); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 5cfa10043f353..cd4c2e5a7ceb6 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1032,19 +1032,10 @@ class TypeChecker final { static bool typeCheckBinding(Pattern *&P, Expr *&Init, DeclContext *DC); static bool typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned patternNumber); - /// Information about a type-checked for-each binding. - struct ForEachBinding { - Type sequenceType; - ProtocolConformanceRef sequenceConformance; - Type iteratorType; - Type elementType; - }; - /// Type-check a for-each loop's pattern binding and sequence together. /// - /// \returns the binding, if successful. - static Optional typeCheckForEachBinding( - DeclContext *dc, ForEachStmt *stmt); + /// \returns true if a failure occurred. + static bool typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt); /// Compute the set of captures for the given function or closure. static void computeCaptures(AnyFunctionRef AFR); From 7fa84b592e5dc7b98804d0b6d6ec97688aa22643 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 2 Jan 2020 15:29:44 -0800 Subject: [PATCH 238/478] [Type checker] Move for-each pattern checking logic into the solver. --- lib/Sema/TypeCheckConstraints.cpp | 44 +++++++++++++++++++++++-------- lib/Sema/TypeCheckStmt.cpp | 18 ------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 92165a2bda010..76f9639b612af 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2898,6 +2898,9 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { /// The for-each statement. ForEachStmt *Stmt; + /// The declaration context in which this for-each statement resides. + DeclContext *DC; + /// The locator we're using. ConstraintLocator *Locator; @@ -2926,7 +2929,8 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { Type IteratorType; public: - explicit BindingListener(ForEachStmt *stmt) : Stmt(stmt) { } + explicit BindingListener(ForEachStmt *stmt, DeclContext *dc) + : Stmt(stmt), DC(dc) { } bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { // Save the locator we're using for the expression. @@ -2956,6 +2960,25 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { auto elementLocator = cs.getConstraintLocator( ContextualLocator, ConstraintLocator::SequenceElementType); + // Check the element pattern. + ASTContext &ctx = cs.getASTContext(); + if (auto *P = TypeChecker::resolvePattern(Stmt->getPattern(), DC, + /*isStmtCondition*/false)) { + Stmt->setPattern(P); + } else { + Stmt->getPattern()->setType(ErrorType::get(ctx)); + return true; + } + + TypeResolutionOptions options(TypeResolverContext::InExpression); + options |= TypeResolutionFlags::AllowUnspecifiedTypes; + options |= TypeResolutionFlags::AllowUnboundGenerics; + if (TypeChecker::typeCheckPattern(Stmt->getPattern(), DC, options)) { + // FIXME: Handle errors better. + Stmt->getPattern()->setType(ErrorType::get(ctx)); + return true; + } + // Collect constraints from the element pattern. auto pattern = Stmt->getPattern(); InitType = cs.generateConstraints(pattern, elementLocator); @@ -2984,13 +3007,12 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { } // Reference the makeIterator witness. - ASTContext &ctx = cs.getASTContext(); FuncDecl *makeIterator = ctx.getSequenceMakeIterator(); Type makeIteratorType = cs.createTypeVariable(Locator, TVO_CanBindToNoEscape); cs.addValueWitnessConstraint( LValueType::get(SequenceType), makeIterator, - makeIteratorType, cs.DC, FunctionRefKind::Compound, + makeIteratorType, DC, FunctionRefKind::Compound, ContextualLocator); Stmt->setSequence(expr); @@ -3019,7 +3041,7 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { TypeResolutionOptions options(TypeResolverContext::ForEachStmt); options |= TypeResolutionFlags::OverrideType; if (TypeChecker::coercePatternToType(pattern, - TypeResolution::forContextual(cs.DC), + TypeResolution::forContextual(DC), InitType, options)) { return nullptr; } @@ -3033,10 +3055,10 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { "Couldn't find sequence conformance"); Stmt->setSequenceConformance(SequenceConformance); - // Check the + // Check the filtering condition. // FIXME: This should be pulled into the constraint system itself. if (auto *Where = Stmt->getWhere()) { - if (!TypeChecker::typeCheckCondition(Where, cs.DC)) + if (!TypeChecker::typeCheckCondition(Where, DC)) Stmt->setWhere(Where); } @@ -3053,7 +3075,7 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { iterator = new (ctx) VarDecl( /*IsStatic*/ false, VarDecl::Introducer::Var, /*IsCaptureList*/ false, Stmt->getInLoc(), - ctx.getIdentifier(name), cs.DC); + ctx.getIdentifier(name), DC); iterator->setInterfaceType(IteratorType->mapTypeOutOfContext()); iterator->setImplicit(); Stmt->setIteratorVar(iterator); @@ -3066,12 +3088,12 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { PatternBindingDecl::createImplicit( ctx, StaticSpellingKind::None, genPat, new (ctx) OpaqueValueExpr(Stmt->getInLoc(), nextResultType), - cs.DC, /*VarLoc*/ Stmt->getForLoc()); + DC, /*VarLoc*/ Stmt->getForLoc()); } // Create the iterator variable. auto *varRef = TypeChecker::buildCheckedRefExpr( - iterator, cs.DC, DeclNameLoc(Stmt->getInLoc()), /*implicit*/ true); + iterator, DC, DeclNameLoc(Stmt->getInLoc()), /*implicit*/ true); if (varRef) Stmt->setIteratorVarRef(varRef); @@ -3083,7 +3105,7 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { /*isPlaceholder=*/true); Expr *convertElementExpr = elementExpr; if (TypeChecker::typeCheckExpression( - convertElementExpr, cs.DC, + convertElementExpr, DC, TypeLoc::withoutLoc(optPatternType), CTP_CoerceOperand).isNull()) { return nullptr; @@ -3097,7 +3119,7 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { } }; - BindingListener listener(stmt); + BindingListener listener(stmt, dc); Expr *seq = stmt->getSequence(); assert(seq && "type-checking an uninitialized for-each statement?"); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 44fe3629fb155..ac189711f12a8 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -731,24 +731,6 @@ class StmtChecker : public StmtVisitor { } Stmt *visitForEachStmt(ForEachStmt *S) { - TypeResolutionOptions options(TypeResolverContext::InExpression); - options |= TypeResolutionFlags::AllowUnspecifiedTypes; - options |= TypeResolutionFlags::AllowUnboundGenerics; - - if (auto *P = TypeChecker::resolvePattern(S->getPattern(), DC, - /*isStmtCondition*/false)) { - S->setPattern(P); - } else { - S->getPattern()->setType(ErrorType::get(getASTContext())); - return nullptr; - } - - if (TypeChecker::typeCheckPattern(S->getPattern(), DC, options)) { - // FIXME: Handle errors better. - S->getPattern()->setType(ErrorType::get(getASTContext())); - return nullptr; - } - if (TypeChecker::typeCheckForEachBinding(DC, S)) return nullptr; From 1337925608df801fa8668e1ac621f9e6b70143f1 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 2 Jan 2020 18:30:37 -0500 Subject: [PATCH 239/478] Sema: Don't mark property wrapper 'modify' accessors transparent There are certain conditions that cause them to reference non-public properties, in particular if the property wrapper is implemented via a subscript taking a keypath. Instead of trying to detect this specific case though (and possibly missing others), I'm just going to decree that 'modify' is never going to be transparent for property wrappers. Fixes . --- lib/Sema/TypeCheckStorage.cpp | 28 ++++++++-------- test/SILGen/property_wrapper_coroutine.swift | 2 +- .../property_wrapper_coroutine_public.swift | 33 +++++++++++++++++++ 3 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 test/SILGen/property_wrapper_coroutine_public.swift diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index a826176dd3651..168216c9b350b 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2009,22 +2009,22 @@ IsAccessorTransparentRequest::evaluate(Evaluator &evaluator, // Getters and setters for lazy properties are not @_transparent. if (storage->getAttrs().hasAttribute()) return false; + } - // Getters/setters for a property with a wrapper are not @_transparent if - // the backing variable has more-restrictive access than the original - // property. The same goes for its storage wrapper. - if (auto var = dyn_cast(storage)) { - if (auto backingVar = var->getPropertyWrapperBackingProperty()) { - if (backingVar->getFormalAccess() < var->getFormalAccess()) - return false; - } + // Accessors for a property with a wrapper are not @_transparent if + // the backing variable has more-restrictive access than the original + // property. The same goes for its storage wrapper. + if (auto var = dyn_cast(storage)) { + if (auto backingVar = var->getPropertyWrapperBackingProperty()) { + if (backingVar->getFormalAccess() < var->getFormalAccess()) + return false; + } - if (auto original = var->getOriginalWrappedProperty( - PropertyWrapperSynthesizedPropertyKind::StorageWrapper)) { - auto backingVar = original->getPropertyWrapperBackingProperty(); - if (backingVar->getFormalAccess() < var->getFormalAccess()) - return false; - } + if (auto original = var->getOriginalWrappedProperty( + PropertyWrapperSynthesizedPropertyKind::StorageWrapper)) { + auto backingVar = original->getPropertyWrapperBackingProperty(); + if (backingVar->getFormalAccess() < var->getFormalAccess()) + return false; } } diff --git a/test/SILGen/property_wrapper_coroutine.swift b/test/SILGen/property_wrapper_coroutine.swift index bdb6260493a37..0dd8f7cd479eb 100644 --- a/test/SILGen/property_wrapper_coroutine.swift +++ b/test/SILGen/property_wrapper_coroutine.swift @@ -34,7 +34,7 @@ _ = state1.someValues // >> Check that the _modify coroutine is synthesized properly -// CHECK-LABEL: sil hidden [transparent] [ossa] @$s26property_wrapper_coroutine5StateV6valuesSaySSGvM : $@yield_once @convention(method) (@inout State) -> @yields @inout Array { +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_coroutine5StateV6valuesSaySSGvM : $@yield_once @convention(method) (@inout State) -> @yields @inout Array { // CHECK: bb0([[STATE:%.*]] : $*State): // CHECK: debug_value_addr [[STATE]] : $*State, var, name "self", argno {{.*}} // CHECK: [[BEGIN_ACCESS:%.*]] = begin_access [modify] [unknown] [[STATE]] : $*State diff --git a/test/SILGen/property_wrapper_coroutine_public.swift b/test/SILGen/property_wrapper_coroutine_public.swift new file mode 100644 index 0000000000000..1981063e15b8d --- /dev/null +++ b/test/SILGen/property_wrapper_coroutine_public.swift @@ -0,0 +1,33 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +// Makes sure the modify coroutine is not @_transparent, since it references +// private properties. + +public class Store { + @Published public var state: Any + init() {} +} + +@propertyWrapper public struct Published { + public init(wrappedValue: Value) {} + public var wrappedValue: Value { + get {} + set {} + } + public static subscript( + _enclosingInstance object: EnclosingSelf, + wrapped wrappedKeyPath: ReferenceWritableKeyPath, + storage storageKeyPath: ReferenceWritableKeyPath>) + -> Value where EnclosingSelf : AnyObject { + get {} + set {} + } + public struct Publisher {} + public var projectedValue: Publisher { + mutating get {} + } +} + +// CHECK-LABEL: sil [ossa] @$s33property_wrapper_coroutine_public5StoreC5stateypvM : $@yield_once @convention(method) (@guaranteed Store) -> @yields @inout Any { +// CHECK: keypath $ReferenceWritableKeyPath, (root $Store; settable_property $Any, id #Store.state!getter.1 : (Store) -> () -> Any, getter @$s33property_wrapper_coroutine_public5StoreC5stateypvpACTK : $@convention(thin) (@in_guaranteed Store) -> @out Any, setter @$s33property_wrapper_coroutine_public5StoreC5stateypvpACTk : $@convention(thin) (@in_guaranteed Any, @in_guaranteed Store) -> ()) +// CHECK: keypath $ReferenceWritableKeyPath>, (root $Store; stored_property #Store._state : $Published) \ No newline at end of file From 2a13b1dff08d1234beef6e271b20cdcc01454f57 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 2 Jan 2020 16:15:42 -0800 Subject: [PATCH 240/478] [Type checker] Stop coercing patterns while type-checking them. Detangle the "type check" and "coerce" phases somewhat for pattern type checking, as a precursor to making the former more functional. --- lib/Sema/TypeCheckConstraints.cpp | 1 - lib/Sema/TypeCheckPattern.cpp | 18 +----------------- lib/Sema/TypeCheckStorage.cpp | 9 +++++++++ test/decl/var/variables.swift | 1 + 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 76f9639b612af..c21fd89b141ee 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -3045,7 +3045,6 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { InitType, options)) { return nullptr; } - Stmt->setPattern(pattern); // Get the conformance of the sequence type to the Sequence protocol. diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 63bff0af884f1..a6e8cab4ea8de 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -741,23 +741,7 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, case PatternKind::Typed: { auto resolution = TypeResolution::forContextual(dc); TypedPattern *TP = cast(P); - bool hadError = validateTypedPattern(resolution, TP, options); - - // If we have unbound generic types, don't apply them below; instead, - // the caller will call typeCheckBinding() later. - if (P->getType()->hasUnboundGenericType()) - return hadError; - - Pattern *subPattern = TP->getSubPattern(); - if (TypeChecker::coercePatternToType(subPattern, resolution, P->getType(), - options|TypeResolutionFlags::FromNonInferredPattern, - TP->getTypeLoc())) - hadError = true; - else { - TP->setSubPattern(subPattern); - TP->setType(subPattern->getType()); - } - return hadError; + return validateTypedPattern(resolution, TP, options); } // A wildcard or name pattern cannot appear by itself in a context diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index a826176dd3651..ba758918f2d12 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -251,6 +251,15 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, binding->diagnose(diag::inferred_opaque_type, binding->getInit(entryNumber)->getType()); } + } else { + // Coerce the pattern to the computed type. + auto resolution = TypeResolution::forContextual(binding->getDeclContext()); + if (TypeChecker::coercePatternToType(pattern, resolution, + pattern->getType(), options)) { + binding->setInvalid(); + pattern->setType(ErrorType::get(Context)); + return &pbe; + } } // If the pattern binding appears in a type or library file context, then diff --git a/test/decl/var/variables.swift b/test/decl/var/variables.swift index f3a81c9836d66..a355deabd1c17 100644 --- a/test/decl/var/variables.swift +++ b/test/decl/var/variables.swift @@ -107,6 +107,7 @@ func test21057425() -> (Int, Int) { // rdar://problem/21081340 func test21081340() { + func foo() { } let (x: a, y: b): () = foo() // expected-error{{tuple pattern has the wrong length for tuple type '()'}} } From 9e73bb31767b71eb21d4de06850d47aefbe67f21 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 17 Dec 2019 20:44:17 -0800 Subject: [PATCH 241/478] [ConstraintSystem] Start to allow diagnosing ambiguity with fixes for solution sets with more than one fix. --- include/swift/AST/DiagnosticsSema.def | 7 +- lib/Sema/CSDiag.cpp | 15 -- lib/Sema/ConstraintSystem.cpp | 132 ++++++++++++------ test/Constraints/diagnostics.swift | 5 +- test/Constraints/super_constructor.swift | 11 +- .../postfix/dot/init_ref_delegation.swift | 9 +- 6 files changed, 104 insertions(+), 75 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 0f44fbf38e76c..0f96ae72ec773 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -73,6 +73,10 @@ ERROR(no_overloads_match_exactly_in_assignment,none, "no exact matches in assignment to %0", (DeclBaseName)) +NOTE(candidate_partial_match,none, + "candidate has partially matching parameter list %0", + (StringRef)) + ERROR(ambiguous_subscript,none, "ambiguous subscript with base type %0 and index type %1", (Type, Type)) @@ -274,9 +278,6 @@ ERROR(cannot_call_with_params, none, ERROR(cannot_call_non_function_value,none, "cannot call value of non-function type %0", (Type)) -ERROR(wrong_argument_labels_overload,none, - "argument labels '%0' do not match any available overloads", (StringRef)) - ERROR(no_candidates_match_result_type,none, "no '%0' candidates produce the expected contextual result type %1", (StringRef, Type)) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 0f20bb0cb9f71..8b7adac58b014 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -2070,21 +2070,6 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { calleeInfo, TCC_ForceRecheck); if (!argExpr) return true; // already diagnosed. - - // Handle argument label mismatches when we have multiple candidates. - if (calleeInfo.closeness == CC_ArgumentLabelMismatch) { - auto args = decomposeArgType(CS.getType(argExpr), argLabels); - - // If we have multiple candidates that we fail to match, just say we have - // the wrong labels and list the candidates out. - diagnose(callExpr->getLoc(), diag::wrong_argument_labels_overload, - getParamListAsString(args)) - .highlight(argExpr->getSourceRange()); - - // Did the user intend on invoking a different overload? - calleeInfo.suggestPotentialOverloads(fnExpr->getLoc()); - return true; - } auto overloadName = calleeInfo.declName; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index a0ef70251cb2d..f3052f0f78fb0 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2734,34 +2734,52 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( // overloads of the same declaration. ConstraintLocator *commonCalleeLocator = nullptr; SmallPtrSet distinctChoices; - SmallVector, 4> - viableSolutions; + SmallVector viableSolutions; + + enum class AmbiguityKind { + /// There is exactly one fix associated with each candidate. + CloseMatch, + /// Solution is ambiguous because all candidates had partially matching + /// parameter lists. + ParameterList, + /// General ambiguity failure. + General, + }; + auto ambiguityKind = AmbiguityKind::CloseMatch; bool diagnosable = llvm::all_of(solutions, [&](const Solution &solution) { ArrayRef fixes = solution.Fixes; - // Currently only support a single fix in a solution, - // but ultimately should be able to deal with multiple. - if (fixes.size() != 1) + if (fixes.empty()) return false; - const auto *fix = fixes.front(); - auto *locator = fix->getLocator(); + if (fixes.size() > 1) { + ambiguityKind = (ambiguityKind == AmbiguityKind::CloseMatch || + ambiguityKind == AmbiguityKind::ParameterList) && + llvm::all_of(fixes, [](const ConstraintFix *fix) -> bool { + auto *locator = fix->getLocator(); + return locator->findLast().hasValue(); + }) ? AmbiguityKind::ParameterList + : AmbiguityKind::General; + } + + for (const auto *fix: fixes) { + auto *locator = fix->getLocator(); + // Assignment failures are all about the source expression, + // because they treat destination as a contextual type. + if (auto *anchor = locator->getAnchor()) { + if (auto *assignExpr = dyn_cast(anchor)) + locator = getConstraintLocator(assignExpr->getSrc()); + } - // Assignment failures are all about the source expression, - // because they treat destination as a contextual type. - if (auto *anchor = locator->getAnchor()) { - if (auto *assignExpr = dyn_cast(anchor)) - locator = getConstraintLocator(assignExpr->getSrc()); + auto *calleeLocator = getCalleeLocator(locator); + if (!commonCalleeLocator) + commonCalleeLocator = calleeLocator; + else if (commonCalleeLocator != calleeLocator) + return false; } - auto *calleeLocator = getCalleeLocator(locator); - if (commonCalleeLocator && commonCalleeLocator != calleeLocator) - return false; - - commonCalleeLocator = calleeLocator; - - auto overload = solution.getOverloadChoiceIfAvailable(calleeLocator); + auto overload = solution.getOverloadChoiceIfAvailable(commonCalleeLocator); if (!overload) return false; @@ -2773,7 +2791,7 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( // as viable, otherwise we'd produce the same diagnostic multiple // times, which means that actual problem is elsewhere. if (distinctChoices.insert(decl).second) - viableSolutions.push_back({&solution, fix}); + viableSolutions.push_back(&solution); return true; }); @@ -2787,32 +2805,11 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( { DiagnosticTransaction transaction(getASTContext().Diags); - const auto *fix = viableSolutions.front().second; auto *commonAnchor = commonCalleeLocator->getAnchor(); auto &DE = getASTContext().Diags; auto name = decl->getFullName(); - if (fix->getKind() == FixKind::UseSubscriptOperator) { - auto *UDE = cast(commonAnchor); - DE.diagnose(commonAnchor->getLoc(), - diag::could_not_find_subscript_member_did_you_mean, - getType(UDE->getBase())); - } else if (fix->getKind() == FixKind::TreatRValueAsLValue) { - DE.diagnose(commonAnchor->getLoc(), - diag::no_overloads_match_exactly_in_assignment, - decl->getBaseName()); - } else if (llvm::all_of( - viableSolutions, - [](const std::pair - &fix) { - auto *locator = fix.second->getLocator(); - return locator - ->isLastElement(); - })) { - auto baseName = name.getBaseName(); - DE.diagnose(commonAnchor->getLoc(), diag::no_candidates_match_result_type, - baseName.userFacingName(), getContextualType()); - } else { + auto emitGeneralAmbiguityFailure = [&]() { // Three choices here: // 1. If this is a special name avoid printing it because // printing kind is sufficient; @@ -2839,14 +2836,61 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( diag::no_overloads_match_exactly_in_call_no_labels, decl->getDescriptiveKind(), name.getBaseName()); } + }; + + switch (ambiguityKind) { + case AmbiguityKind::CloseMatch: + // Handled below + break; + case AmbiguityKind::ParameterList: { + emitGeneralAmbiguityFailure(); + + for (const auto &viable: viableSolutions) { + auto overload = viable->getOverloadChoice(commonCalleeLocator); + auto *fn = overload.openedType->getAs(); + assert(fn); + DE.diagnose(overload.choice.getDecl()->getLoc(), + diag::candidate_partial_match, + fn->getParamListAsString(fn->getParams())); + } + + return true; + } + case AmbiguityKind::General: + // TODO: Handle general ambiguity here. + return false; + } + + auto *fix = viableSolutions.front()->Fixes.front(); + if (fix->getKind() == FixKind::UseSubscriptOperator) { + auto *UDE = cast(commonAnchor); + DE.diagnose(commonAnchor->getLoc(), + diag::could_not_find_subscript_member_did_you_mean, + getType(UDE->getBase())); + } else if (fix->getKind() == FixKind::TreatRValueAsLValue) { + DE.diagnose(commonAnchor->getLoc(), + diag::no_overloads_match_exactly_in_assignment, + decl->getBaseName()); + } else if (llvm::all_of( + viableSolutions, + [](const Solution *viable) { + auto *locator = viable->Fixes.front()->getLocator(); + return locator + ->isLastElement(); + })) { + auto baseName = name.getBaseName(); + DE.diagnose(commonAnchor->getLoc(), diag::no_candidates_match_result_type, + baseName.userFacingName(), getContextualType()); + } else { + emitGeneralAmbiguityFailure(); } for (const auto &viable : viableSolutions) { // Create scope so each applied solution is rolled back. ConstraintSystem::SolverScope scope(*this); - applySolution(*viable.first); + applySolution(*viable); // All of the solutions supposed to produce a "candidate" note. - diagnosed &= viable.second->diagnose(/*asNote*/ true); + diagnosed &= viable->Fixes.front()->diagnose(/*asNote*/ true); } // If not all of the fixes produced a note, we can't diagnose this. diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 6c98ae232b116..7c600979d795c 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -492,7 +492,9 @@ enum Color { static func rainbow() -> Color {} static func overload(a : Int) -> Color {} // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(a:)')}} + // expected-note@-1 {{candidate has partially matching parameter list (a: Int)}} static func overload(b : Int) -> Color {} // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(b:)')}} + // expected-note@-1 {{candidate has partially matching parameter list (b: Int)}} static func frob(_ a : Int, b : inout Int) -> Color {} static var svar: Color { return .Red } @@ -517,8 +519,7 @@ let _ : (Int, Float) = (42.0, 12) // expected-error {{cannot convert value of t let _ : Color = .rainbow // expected-error {{member 'rainbow()' is a function; did you mean to call it?}} {{25-25=()}} let _: Color = .overload(a : 1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} -let _: Color = .overload(1.0) // expected-error {{ambiguous reference to member 'overload'}} -// expected-note @-1 {{overloads for 'overload' exist with these partially matching parameter lists: (a: Int), (b: Int)}} +let _: Color = .overload(1.0) // expected-error {{no exact matches in call to static method 'overload'}} let _: Color = .overload(1) // expected-error {{no exact matches in call to static method 'overload'}} let _: Color = .frob(1.0, &i) // expected-error {{missing argument label 'b:' in call}} // expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}} diff --git a/test/Constraints/super_constructor.swift b/test/Constraints/super_constructor.swift index c87cfead80eba..01e15a2990432 100644 --- a/test/Constraints/super_constructor.swift +++ b/test/Constraints/super_constructor.swift @@ -20,8 +20,7 @@ class D : B { } init(g:Int) { - super.init("aoeu") // expected-error{{argument labels '(_:)' do not match any available overloads}} - // expected-note @-1 {{overloads for 'B.init' exist with these partially matching parameter lists: (a: UnicodeScalar), (b: UnicodeScalar), (x: Int), (z: Float)}} + super.init("aoeu") // expected-error{{no exact matches in call to initializer}} } init(h:Int) { @@ -40,15 +39,15 @@ class B { init() { } - init(x:Int) { + init(x:Int) { // expected-note{{candidate has partially matching parameter list (x: Int)}} } - init(a:UnicodeScalar) { + init(a:UnicodeScalar) { // expected-note {{candidate has partially matching parameter list (a: UnicodeScalar)}} } - init(b:UnicodeScalar) { + init(b:UnicodeScalar) { // expected-note{{candidate has partially matching parameter list (b: UnicodeScalar)}} } - init(z:Float) { + init(z:Float) { // expected-note{{candidate has partially matching parameter list (z: Float)}} super.init() // expected-error{{'super' members cannot be referenced in a root class}} } } diff --git a/test/expr/postfix/dot/init_ref_delegation.swift b/test/expr/postfix/dot/init_ref_delegation.swift index f9729ef3a8105..33b1512b6ad44 100644 --- a/test/expr/postfix/dot/init_ref_delegation.swift +++ b/test/expr/postfix/dot/init_ref_delegation.swift @@ -323,13 +323,12 @@ class TestOverloadSets { self.init(5, 5) // expected-error{{extra argument in call}} } - convenience init(a : Z0) { - self.init(42 as Int8) // expected-error{{argument labels '(_:)' do not match any available overloads}} - // expected-note @-1 {{overloads for 'TestOverloadSets.init' exist with these partially matching parameter lists: (a: Z0), (value: Double), (value: Int)}} + convenience init(a : Z0) { // expected-note{{candidate has partially matching parameter list (a: Z0)}} + self.init(42 as Int8) // expected-error{{no exact matches in call to initializer}} } - init(value: Int) { /* ... */ } - init(value: Double) { /* ... */ } + init(value: Int) { /* ... */ } // expected-note{{candidate has partially matching parameter list (value: Int)}} + init(value: Double) { /* ... */ } // expected-note{{candidate has partially matching parameter list (value: Double)}} } class TestNestedExpr { From 8ea2b26eb172c7139e77855c907819776ca944b5 Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Fri, 3 Jan 2020 08:56:13 -0800 Subject: [PATCH 242/478] [SwiftPM] Pass --reconfigure when building swiftpm This should help in avoiding incremental build issues on the CI PR test failure to build swiftpm --- .../swift_build_support/swift_build_support/products/swiftpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/swift_build_support/swift_build_support/products/swiftpm.py b/utils/swift_build_support/swift_build_support/products/swiftpm.py index af619c147360a..6a6850e53501b 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftpm.py +++ b/utils/swift_build_support/swift_build_support/products/swiftpm.py @@ -58,7 +58,7 @@ def run_bootstrap_script(self, action, host_target, additional_params=[]): shell.call(helper_cmd) def build(self, host_target): - self.run_bootstrap_script('build', host_target) + self.run_bootstrap_script('build', host_target, ["--reconfigure"]) def should_test(self, host_target): return self.args.test_swiftpm From 23f36a059a15f47b2a622bf16cb89557a97aa18c Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 3 Jan 2020 10:10:16 -0800 Subject: [PATCH 243/478] [semantic-arc-opts] When performing load [copy] -> load_borrow on classes, do not ignore forwarding uses. This is the first of two commits. This commit is a very simple, easily cherry-pickable fix but does not use the LiveRange infrastructure so that we handle forwarding uses here. Instead, we just bail if all uses of our load [copy] are not destroy_value. In a subsequent commit, I am going to change this to use the LiveRange infrastructure so we will handle these cases. Sadly doing so doesn't cherry-pick well. = /. rdar://58289320 --- .../Mandatory/SemanticARCOpts.cpp | 36 ++++++++- test/SILOptimizer/semantic-arc-opts.sil | 74 +++++++++++++++---- 2 files changed, 95 insertions(+), 15 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp index d73cb686ea255..0744927d3ece1 100644 --- a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp @@ -36,6 +36,25 @@ STATISTIC(NumEliminatedInsts, "number of removed instructions"); STATISTIC(NumLoadCopyConvertedToLoadBorrow, "number of load_copy converted to load_borrow"); +//===----------------------------------------------------------------------===// +// Utility +//===----------------------------------------------------------------------===// + +static bool isConsumingOwnedUse(Operand *use) { + assert(use->get().getOwnershipKind() == ValueOwnershipKind::Owned); + + if (use->isTypeDependent()) + return false; + + // We know that a copy_value produces an @owned value. Look through all of + // our uses and classify them as either invalidating or not + // invalidating. Make sure that all of the invalidating ones are + // destroy_value since otherwise the live_range is not complete. + auto map = use->getOwnershipKindMap(); + auto constraint = map.getLifetimeConstraint(ValueOwnershipKind::Owned); + return constraint == UseLifetimeConstraint::MustBeInvalidated; +} + //===----------------------------------------------------------------------===// // Live Range Modeling //===----------------------------------------------------------------------===// @@ -775,8 +794,21 @@ class StorageGuaranteesLoadVisitor std::back_inserter(baseEndBorrows)); SmallVector valueDestroys; - llvm::copy(Load->getUsersOfType(), - std::back_inserter(valueDestroys)); + for (auto *use : Load->getUses()) { + // If this load is not consuming, skip it. + if (!isConsumingOwnedUse(use)) { + continue; + } + + // Otherwise, if this isn't a destroy_value, we have a consuming use we + // don't understand. Return conservatively that this memory location may + // not be guaranteed. + auto *user = use->getUser(); + if (!isa(user)) { + return answer(true); + } + valueDestroys.emplace_back(user); + } SmallPtrSet visitedBlocks; LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks()); diff --git a/test/SILOptimizer/semantic-arc-opts.sil b/test/SILOptimizer/semantic-arc-opts.sil index 854c5f85f02d6..278b8f8613efd 100644 --- a/test/SILOptimizer/semantic-arc-opts.sil +++ b/test/SILOptimizer/semantic-arc-opts.sil @@ -9,6 +9,10 @@ import Builtin ////////////////// enum MyNever {} +enum FakeOptional { +case none +case some(T) +} sil @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () sil @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> () @@ -24,6 +28,7 @@ sil @get_nativeobject_pair : $@convention(thin) () -> @owned NativeObjectPair class Klass {} sil @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> () +sil @guaranteed_fakeoptional_klass_user : $@convention(thin) (@guaranteed FakeOptional) -> () struct MyInt { var value: Builtin.Int32 @@ -40,11 +45,6 @@ struct StructMemberTest { var t : (Builtin.Int32, AnotherStruct) } -enum FakeOptional { -case none -case some(T) -} - class ClassLet { @_hasStorage let aLet: Klass @_hasStorage var aVar: Klass @@ -589,6 +589,52 @@ bb0(%x : @owned $ClassLet): return undef : $() } +// We do not support this today, but we will once forwarding is ignored when +// checking if the load [copy] is a dead live range. +// +// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase : +// CHECK: load [copy] +// CHECK: } // end sil function 'dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase' +sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase : $@convention(thin) (@owned ClassLet) -> () { +bb0(%x : @owned $ClassLet): + %f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> () + + %a = begin_borrow %x : $ClassLet + %p = ref_element_addr %a : $ClassLet, #ClassLet.aLetTuple + %v = load [copy] %p : $*(Klass, Klass) + (%v1, %v2) = destructure_tuple %v : $(Klass, Klass) + apply %f(%v1) : $@convention(thin) (@guaranteed Klass) -> () + apply %f(%v2) : $@convention(thin) (@guaranteed Klass) -> () + destroy_value %v1 : $Klass + destroy_value %v2 : $Klass + end_borrow %a : $ClassLet + destroy_value %x : $ClassLet + + return undef : $() +} + +// We do not support this today, but we will once forwarding is ignored when +// checking if the load [copy] is a dead live range. +// +// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2 : +// CHECK: load [copy] +// CHECK: } // end sil function 'dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2' +sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2 : $@convention(thin) (@owned ClassLet) -> () { +bb0(%x : @owned $ClassLet): + %f = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + + %a = begin_borrow %x : $ClassLet + %p = ref_element_addr %a : $ClassLet, #ClassLet.aLet + %v = load [copy] %p : $*Klass + %v_cast = unchecked_ref_cast %v : $Klass to $Builtin.NativeObject + apply %f(%v_cast) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %v_cast : $Builtin.NativeObject + end_borrow %a : $ClassLet + destroy_value %x : $ClassLet + + return undef : $() +} + // CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_multi_borrowed_base_that_dominates // CHECK: [[OUTER:%.*]] = begin_borrow // CHECK-NEXT: ref_element_addr @@ -662,7 +708,7 @@ bb0(%x : @owned $ClassLet): return undef : $() } -// CHECK-LABEL: sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates +// CHECK-LABEL: sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates_2 : // CHECK: [[OUTER:%.*]] = begin_borrow // CHECK-NEXT: ref_element_addr // CHECK-NEXT: [[INNER:%.*]] = load_borrow @@ -672,12 +718,15 @@ bb0(%x : @owned $ClassLet): // CHECK-NEXT: begin_borrow // CHECK-NEXT: ref_element_addr // CHECK-NEXT: load [copy] -// CHECK-NEXT: apply // CHECK-NEXT: end_borrow // CHECK-NEXT: destroy_value +// CHECK-NEXT: // function_ref +// CHECK-NEXT: function_ref +// CHECK-NEXT: enum // CHECK-NEXT: apply // CHECK-NEXT: destroy_value -sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates : $@convention(thin) (@owned ClassLet) -> () { +// CHECK: } // end sil function 'do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates_2' +sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates_2 : $@convention(thin) (@owned ClassLet) -> () { bb0(%x : @owned $ClassLet): %f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> () @@ -693,18 +742,17 @@ bb0(%x : @owned $ClassLet): %b = begin_borrow %x : $ClassLet %q = ref_element_addr %b : $ClassLet, #ClassLet.aLet %w = load [copy] %q : $*Klass - %d = begin_borrow %w : $Klass - apply %f(%d) : $@convention(thin) (@guaranteed Klass) -> () // End the lifetime of the base object first... end_borrow %b : $ClassLet destroy_value %x : $ClassLet // ...then end the lifetime of the copy. - apply %f(%d) : $@convention(thin) (@guaranteed Klass) -> () + %f2 = function_ref @guaranteed_fakeoptional_klass_user : $@convention(thin) (@guaranteed FakeOptional) -> () + %w2 = enum $FakeOptional, #FakeOptional.some!enumelt.1, %w : $Klass + apply %f2(%w2) : $@convention(thin) (@guaranteed FakeOptional) -> () - end_borrow %d : $Klass - destroy_value %w : $Klass + destroy_value %w2 : $FakeOptional return undef : $() } From 990af4bdd3d0c93771d265bbedf8751d416e5424 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 1 Jan 2020 16:57:40 -0800 Subject: [PATCH 244/478] [sil] Change TermInst::getSuccessorBlockArguments() to yield SILArguments instead of SILPhiArgument since I am going to be adding SwitchEnumResult (which the API can vend as well). --- include/swift/SIL/SILInstruction.h | 2 +- lib/SIL/OperandOwnership.cpp | 5 ++--- lib/SIL/SILInstructions.cpp | 6 +++--- lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp | 6 +++--- lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp | 5 +++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 6f8a859b9f237..4afcb22beb988 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -7000,7 +7000,7 @@ class TermInst : public NonValueInstruction { } using SuccessorBlockArgumentListTy = - TransformRange( const SILSuccessor &)>>; /// Return the range of Argument arrays for each successor of this diff --git a/lib/SIL/OperandOwnership.cpp b/lib/SIL/OperandOwnership.cpp index ed96790e82482..120199e4fde5c 100644 --- a/lib/SIL/OperandOwnership.cpp +++ b/lib/SIL/OperandOwnership.cpp @@ -466,7 +466,7 @@ OperandOwnershipKindClassifier::visitSwitchEnumInst(SwitchEnumInst *sei) { // and merge them. auto mergedKind = ValueOwnershipKind::merge(makeTransformRange( sei->getSuccessorBlockArgumentLists(), - [&](SILPhiArgumentArrayRef array) -> ValueOwnershipKind { + [&](ArrayRef array) -> ValueOwnershipKind { // If the array is empty, we have a non-payloaded case. Return any. if (array.empty()) return ValueOwnershipKind::None; @@ -474,8 +474,7 @@ OperandOwnershipKindClassifier::visitSwitchEnumInst(SwitchEnumInst *sei) { // Otherwise, we should have a single element since a payload is // a tuple. assert(std::distance(array.begin(), array.end()) == 1); - SILPhiArgument *arg = array.front(); - return arg->getOwnershipKind(); + return array.front()->getOwnershipKind(); })); // If we failed to merge, return an empty map so we will fail to pattern match diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index b9facf8ad2cb0..27439f486b245 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -1219,9 +1219,9 @@ bool TermInst::isProgramTerminating() const { TermInst::SuccessorBlockArgumentListTy TermInst::getSuccessorBlockArgumentLists() const { - function_ref op; - op = [](const SILSuccessor &succ) -> SILPhiArgumentArrayRef { - return succ.getBB()->getSILPhiArguments(); + function_ref(const SILSuccessor &)> op; + op = [](const SILSuccessor &succ) -> ArrayRef { + return succ.getBB()->getArguments(); }; return SuccessorBlockArgumentListTy(getSuccessors(), op); } diff --git a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp index 441ec5b14f928..f3b863f32015a 100644 --- a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp +++ b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp @@ -143,8 +143,8 @@ cleanupDeadTrivialPhiArgs(SILValue initialValue, if (ReverseInitialWorklist) { std::reverse(insertedPhis.begin(), insertedPhis.end()); } - SmallVector worklist(insertedPhis.begin(), - insertedPhis.end()); + SmallVector worklist(insertedPhis.begin(), + insertedPhis.end()); sortUnique(insertedPhis); SmallVector incomingValues; @@ -189,7 +189,7 @@ cleanupDeadTrivialPhiArgs(SILValue initialValue, auto *termInst = cast(user); for (auto succBlockArgList : termInst->getSuccessorBlockArgumentLists()) { llvm::copy_if(succBlockArgList, std::back_inserter(worklist), - [&](SILPhiArgument *succArg) -> bool { + [&](SILArgument *succArg) -> bool { auto it = lower_bound(insertedPhis, succArg); return it != insertedPhis.end() && *it == succArg; }); diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 06545ba42674f..351068091448a 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -935,8 +935,9 @@ static bool terminatorHasAnyKnownPhis(TermInst *ti, ArrayRef insertedPhiNodesSorted) { for (auto succArgList : ti->getSuccessorBlockArgumentLists()) { - if (llvm::any_of(succArgList, [&](SILPhiArgument *arg) { - return binary_search(insertedPhiNodesSorted, arg); + if (llvm::any_of(succArgList, [&](SILArgument *arg) { + return binary_search(insertedPhiNodesSorted, + cast(arg)); })) { return true; } From 9c8351b20658825fc42d3893ad691f6fec6f1f10 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 3 Jan 2020 11:25:05 -0800 Subject: [PATCH 245/478] [Type checker] Make typeCheckPattern() functional. Make TypeChecker::typeCheckPattern() return the computed type, rather than returning an "error" flag. More importantly, make it functional, so that it doesn't set the type it computes on the pattern. Use UnresolvedType as a placeholder for types that need to be inferred, rather than a null type. This allows us to produce structural types involving types that need to be inferred. Note that with this change as-is, we get a number of duplicated diagnostics, because typeCheckPattern() will be called multiple times for the same pattern and will emit some diagnostics each time. This will be addressed in a subsequent commit. --- lib/Sema/CSGen.cpp | 7 +-- lib/Sema/TypeCheckConstraints.cpp | 56 +++++++++++++++++----- lib/Sema/TypeCheckPattern.cpp | 79 ++++++++++++------------------- lib/Sema/TypeCheckStorage.cpp | 22 +++++---- lib/Sema/TypeChecker.h | 14 ++++-- test/stmt/foreach.swift | 6 +-- 6 files changed, 102 insertions(+), 82 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index e6d899aef29b1..d9527be9205f4 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2158,10 +2158,11 @@ namespace { } case PatternKind::Typed: { - auto typedPattern = cast(pattern); // FIXME: Need a better locator for a pattern as a base. - Type openedType = CS.openUnboundGenericType(typedPattern->getType(), - locator); + TypeResolutionOptions options(TypeResolverContext::InExpression); + options |= TypeResolutionFlags::AllowUnboundGenerics; + Type type = TypeChecker::typeCheckPattern(pattern, CurDC, options); + Type openedType = CS.openUnboundGenericType(type, locator); // For a typed pattern, simply return the opened type of the pattern. // FIXME: Error recovery if the type is an error type? diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index c21fd89b141ee..e1583b184dc1e 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2566,7 +2566,8 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, } bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, - DeclContext *DC) { + DeclContext *DC, + Type patternType) { /// Type checking listener for pattern binding initializers. class BindingListener : public ExprTypeCheckListener { @@ -2763,8 +2764,10 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // if there's an error we can use that information to inform diagnostics. contextualPurpose = CTP_Initialization; - if (pattern->hasType()) { - contextualType = TypeLoc::withoutLoc(pattern->getType()); + if (isa(pattern)) { + flags |= TypeCheckExprFlags::ExpressionTypeMustBeOptional; + } else if (patternType && !patternType->isEqual(Context.TheUnresolvedType)) { + contextualType = TypeLoc::withoutLoc(patternType); // If we already had an error, don't repeat the problem. if (contextualType.getType()->hasError()) @@ -2772,7 +2775,7 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // Allow the initializer expression to establish the underlying type of an // opaque type. - if (auto opaqueType = pattern->getType()->getAs()){ + if (auto opaqueType = patternType->getAs()){ flags |= TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType; flags -= TypeCheckExprFlags::ConvertTypeIsOnlyAHint; } @@ -2780,11 +2783,12 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // Only provide a TypeLoc if it makes sense to allow diagnostics. if (auto *typedPattern = dyn_cast(pattern)) { const Pattern *inner = typedPattern->getSemanticsProvidingPattern(); - if (isa(inner) || isa(inner)) + if (isa(inner) || isa(inner)) { contextualType = typedPattern->getTypeLoc(); + if (!contextualType.getType()) + contextualType.setType(patternType); + } } - } else if (isa(pattern)) { - flags |= TypeCheckExprFlags::ExpressionTypeMustBeOptional; } // Type-check the initializer. @@ -2797,6 +2801,8 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, ? TypeResolverContext::EditorPlaceholderExpr : TypeResolverContext::InExpression; options |= TypeResolutionFlags::OverrideType; + options |= TypeResolutionFlags::AllowUnspecifiedTypes; + options |= TypeResolutionFlags::AllowUnboundGenerics; // FIXME: initTy should be the same as resultTy; now that typeCheckExpression() // returns a Type and not bool, we should be able to simplify the listener @@ -2819,7 +2825,8 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // and its variables, to prevent it from being referenced by the constraint // system. if (!resultTy && - (!pattern->hasType() || pattern->getType()->hasUnboundGenericType())) { + (patternType->hasUnresolvedType() || + patternType->hasUnboundGenericType())) { pattern->setType(ErrorType::get(Context)); pattern->forEachVariable([&](VarDecl *var) { // Don't change the type of a variable that we've been able to @@ -2837,7 +2844,8 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, } bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, - unsigned patternNumber) { + unsigned patternNumber, + Type patternType) { Pattern *pattern = PBD->getPattern(patternNumber); Expr *init = PBD->getInit(patternNumber); @@ -2851,7 +2859,24 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, DC = initContext; } - bool hadError = TypeChecker::typeCheckBinding(pattern, init, DC); + // If we weren't given a pattern type, compute one now. + if (!patternType) { + if (pattern->hasType()) + patternType = pattern->getType(); + else { + TypeResolutionOptions options(TypeResolverContext::InExpression); + options |= TypeResolutionFlags::AllowUnspecifiedTypes; + options |= TypeResolutionFlags::AllowUnboundGenerics; + patternType = typeCheckPattern(pattern, DC, options); + } + + if (patternType->hasError()) { + PBD->setInvalid(); + return true; + } + } + + bool hadError = TypeChecker::typeCheckBinding(pattern, init, DC, patternType); if (!init) { PBD->setInvalid(); return true; @@ -2973,7 +2998,9 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { TypeResolutionOptions options(TypeResolverContext::InExpression); options |= TypeResolutionFlags::AllowUnspecifiedTypes; options |= TypeResolutionFlags::AllowUnboundGenerics; - if (TypeChecker::typeCheckPattern(Stmt->getPattern(), DC, options)) { + Type patternType = TypeChecker::typeCheckPattern( + Stmt->getPattern(), DC, options); + if (patternType->hasError()) { // FIXME: Handle errors better. Stmt->getPattern()->setType(ErrorType::get(ctx)); return true; @@ -3040,6 +3067,8 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { Pattern *pattern = Stmt->getPattern(); TypeResolutionOptions options(TypeResolverContext::ForEachStmt); options |= TypeResolutionFlags::OverrideType; + options |= TypeResolutionFlags::AllowUnboundGenerics; + options |= TypeResolutionFlags::AllowUnspecifiedTypes; if (TypeChecker::coercePatternToType(pattern, TypeResolution::forContextual(DC), InitType, options)) { @@ -3195,7 +3224,8 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, TypeResolutionOptions options(TypeResolverContext::InExpression); options |= TypeResolutionFlags::AllowUnspecifiedTypes; options |= TypeResolutionFlags::AllowUnboundGenerics; - if (TypeChecker::typeCheckPattern(pattern, dc, options)) { + Type patternType = TypeChecker::typeCheckPattern(pattern, dc, options); + if (patternType->hasError()) { typeCheckPatternFailed(); continue; } @@ -3203,7 +3233,7 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, // If the pattern didn't get a type, it's because we ran into some // unknown types along the way. We'll need to check the initializer. auto init = elt.getInitializer(); - hadError |= TypeChecker::typeCheckBinding(pattern, init, dc); + hadError |= TypeChecker::typeCheckBinding(pattern, init, dc, patternType); elt.setPattern(pattern); elt.setInitializer(init); hadAnyFalsable |= pattern->isRefutablePattern(); diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index a6e8cab4ea8de..465cf5aef4f32 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -664,12 +664,9 @@ Pattern *TypeChecker::resolvePattern(Pattern *P, DeclContext *DC, return P; } -static bool validateTypedPattern(TypeResolution resolution, +static Type validateTypedPattern(TypeResolution resolution, TypedPattern *TP, TypeResolutionOptions options) { - if (TP->hasType()) - return TP->getType()->hasError(); - TypeLoc TL = TP->getTypeLoc(); bool hadError; @@ -678,7 +675,7 @@ static bool validateTypedPattern(TypeResolution resolution, // variable binding, then we can bind the opaque return type from the // property definition. auto &Context = resolution.getASTContext(); - if (auto opaqueRepr = dyn_cast(TL.getTypeRepr())) { + if (auto opaqueRepr = dyn_cast_or_null(TL.getTypeRepr())) { auto named = dyn_cast( TP->getSubPattern()->getSemanticsProvidingPattern()); if (named) { @@ -699,18 +696,14 @@ static bool validateTypedPattern(TypeResolution resolution, } if (hadError) { - TP->setType(ErrorType::get(Context)); - return hadError; + return ErrorType::get(Context); } - TP->setType(TL.getType()); - assert(!dyn_cast_or_null(TL.getTypeRepr())); - - return hadError; + return TL.getType(); } -bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, +Type TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, TypeResolutionOptions options) { auto &Context = dc->getASTContext(); switch (P->getKind()) { @@ -723,17 +716,14 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, SP = PP->getSubPattern(); else SP = cast(P)->getSubPattern(); - if (TypeChecker::typeCheckPattern(SP, dc, options)) { - P->setType(ErrorType::get(Context)); - return true; - } - if (SP->hasType()) { - auto type = SP->getType(); - if (P->getKind() == PatternKind::Paren) - type = ParenType::get(Context, type); - P->setType(type); - } - return false; + Type subType = TypeChecker::typeCheckPattern(SP, dc, options); + if (subType->hasError()) + return ErrorType::get(Context); + + auto type = subType; + if (P->getKind() == PatternKind::Paren) + type = ParenType::get(Context, type); + return type; } // If we see an explicit type annotation, coerce the sub-pattern to @@ -751,16 +741,15 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, // If we're type checking this pattern in a context that can provide type // information, then the lack of type information is not an error. if (options & TypeResolutionFlags::AllowUnspecifiedTypes) - return false; + return Context.TheUnresolvedType; Context.Diags.diagnose(P->getLoc(), diag::cannot_infer_type_for_pattern); - P->setType(ErrorType::get(Context)); if (auto named = dyn_cast(P)) { if (auto var = named->getDecl()) { var->setInvalid(); } } - return true; + return ErrorType::get(Context); // A tuple pattern propagates its tuple-ness out. case PatternKind::Tuple: { @@ -769,31 +758,21 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, SmallVector typeElts; const auto elementOptions = options.withoutContext(); - bool missingType = false; for (unsigned i = 0, e = tuplePat->getNumElements(); i != e; ++i) { TuplePatternElt &elt = tuplePat->getElement(i); Pattern *pattern = elt.getPattern(); - if (TypeChecker::typeCheckPattern(pattern, dc, elementOptions)) { + Type subType = TypeChecker::typeCheckPattern(pattern, dc, elementOptions); + if (subType->hasError()) hadError = true; - continue; - } - if (!pattern->hasType()) { - missingType = true; - continue; - } - typeElts.push_back(TupleTypeElt(pattern->getType(), elt.getLabel())); + typeElts.push_back(TupleTypeElt(subType, elt.getLabel())); } if (hadError) { - P->setType(ErrorType::get(Context)); - return true; + return ErrorType::get(Context); } - if (!missingType && !(options & - TypeResolutionFlags::AllowUnspecifiedTypes)) { - P->setType(TupleType::get(typeElts, Context)); - } - return false; + + return TupleType::get(typeElts, Context); } //--- Refutable patterns. @@ -809,11 +788,10 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, if (!(options & TypeResolutionFlags::AllowUnspecifiedTypes)) { Context.Diags.diagnose(P->getLoc(), diag::refutable_pattern_requires_initializer); - P->setType(ErrorType::get(Context)); - return true; + return ErrorType::get(Context); } - return false; + return Context.TheUnresolvedType; } llvm_unreachable("bad pattern kind!"); } @@ -923,9 +901,10 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, // that type. case PatternKind::Typed: { TypedPattern *TP = cast(P); - bool hadError = validateTypedPattern(resolution, TP, options); - if (!hadError) { - if (!type->isEqual(TP->getType()) && !type->hasError()) { + Type patternType = validateTypedPattern(resolution, TP, options); + bool hadError = false; + if (!patternType->hasError()) { + if (!type->isEqual(patternType) && !type->hasError()) { if (options & TypeResolutionFlags::OverrideType) { TP->setType(type); } else { @@ -934,10 +913,12 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, hadError = true; } } + } else { + hadError = true; } Pattern *sub = TP->getSubPattern(); - hadError |= coercePatternToType(sub, resolution, TP->getType(), + hadError |= coercePatternToType(sub, resolution, type, subOptions | TypeResolutionFlags::FromNonInferredPattern); if (!hadError) { TP->setSubPattern(sub); diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index ba758918f2d12..e1af901e4c457 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -214,7 +214,9 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, options |= TypeResolutionFlags::AllowUnboundGenerics; } - if (TypeChecker::typeCheckPattern(pattern, binding->getDeclContext(), options)) { + Type patternType = TypeChecker::typeCheckPattern( + pattern, binding->getDeclContext(), options); + if (patternType->hasError()) { swift::setBoundVarsTypeError(pattern, Context); binding->setInvalid(); pattern->setType(ErrorType::get(Context)); @@ -225,20 +227,20 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, // default-initializable. If so, do it. if (!pbe.isInitialized() && binding->isDefaultInitializable(entryNumber) && - pattern->hasStorage() && - !pattern->getType()->hasError()) { - auto type = pattern->getType(); - if (auto defaultInit = TypeChecker::buildDefaultInitializer(type)) { + pattern->hasStorage()) { + if (auto defaultInit = TypeChecker::buildDefaultInitializer(patternType)) { // If we got a default initializer, install it and re-type-check it // to make sure it is properly coerced to the pattern type. binding->setInit(entryNumber, defaultInit); } } - // If the pattern didn't get a type or if it contains an unbound generic type, - // we'll need to check the initializer. - if (!pattern->hasType() || pattern->getType()->hasUnboundGenericType()) { - if (TypeChecker::typeCheckPatternBinding(binding, entryNumber)) { + // If the pattern contains some form of unresolved type, we'll need to + // check the initializer. + if (patternType->hasUnresolvedType() || + patternType->hasUnboundGenericType()) { + if (TypeChecker::typeCheckPatternBinding(binding, entryNumber, + patternType)) { binding->setInvalid(); return &pbe; } @@ -255,7 +257,7 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, // Coerce the pattern to the computed type. auto resolution = TypeResolution::forContextual(binding->getDeclContext()); if (TypeChecker::coercePatternToType(pattern, resolution, - pattern->getType(), options)) { + patternType, options)) { binding->setInvalid(); pattern->setType(ErrorType::get(Context)); return &pbe; diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index cd4c2e5a7ceb6..e899817b35a72 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1003,8 +1003,11 @@ class TypeChecker final { /// \param dc The context in which type checking occurs. /// \param options Options that control type resolution. /// - /// \returns true if any errors occurred during type checking. - static bool typeCheckPattern(Pattern *P, DeclContext *dc, + /// \returns the type of the pattern, which may be an error type if an + /// unrecoverable error occurred. If the options permit it, the type may + /// involve \c UnresolvedType (for patterns with no type information) and + /// unbound generic types. + static Type typeCheckPattern(Pattern *P, DeclContext *dc, TypeResolutionOptions options); static bool typeCheckCatchPattern(CatchStmt *S, DeclContext *dc); @@ -1029,8 +1032,11 @@ class TypeChecker final { AnyFunctionType *FN); /// Type-check an initialized variable pattern declaration. - static bool typeCheckBinding(Pattern *&P, Expr *&Init, DeclContext *DC); - static bool typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned patternNumber); + static bool typeCheckBinding(Pattern *&P, Expr *&Init, DeclContext *DC, + Type patternType); + static bool typeCheckPatternBinding(PatternBindingDecl *PBD, + unsigned patternNumber, + Type patternType = Type()); /// Type-check a for-each loop's pattern binding and sequence together. /// diff --git a/test/stmt/foreach.swift b/test/stmt/foreach.swift index 630840455ef29..2082be1455aa6 100644 --- a/test/stmt/foreach.swift +++ b/test/stmt/foreach.swift @@ -133,14 +133,14 @@ func testForEachInference() { // Generic sequence resolved contextually for i: Int in getGenericSeq() { } for d: Double in getGenericSeq() { } - + // Inference of generic arguments in the element type from the // sequence. - for x: X in getXIntSeq() { + for x: X in getXIntSeq() { let z = x.value + 1 } - for x: X in getOvlSeq() { + for x: X in getOvlSeq() { let z = x.value + 1 } From 41a00e57808d9d28567b859355e515c241adbcf4 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 2 Jan 2020 23:26:46 -0500 Subject: [PATCH 246/478] Sema: Fix crash-on-invalid with 'enclosing self' property wrappers The diagnostic is still terrible, but at least we shouldn't crash. Fixes one manifestation of . I also filed to track improving the diagnostic. --- lib/Sema/TypeCheckStorage.cpp | 64 ++++++++++++------- test/decl/var/property_wrappers_invalid.swift | 36 +++++++++++ 2 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 test/decl/var/property_wrappers_invalid.swift diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 168216c9b350b..d65efc930c11b 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -587,7 +587,9 @@ getEnclosingSelfPropertyWrapperAccess(VarDecl *property, bool forProjected) { return result; } -/// Build an l-value for the storage of a declaration. +/// Build an l-value for the storage of a declaration. Returns nullptr if there +/// was an error. This should only occur if an invalid declaration was type +/// checked; another diagnostic should have been emitted already. static Expr *buildStorageReference(AccessorDecl *accessor, AbstractStorageDecl *storage, TargetImpl target, @@ -674,12 +676,8 @@ static Expr *buildStorageReference(AccessorDecl *accessor, auto *backing = var->getPropertyWrapperBackingProperty(); // Error recovery. - if (!backing) { - auto type = storage->getValueInterfaceType(); - if (isLValue) - type = LValueType::get(type); - return new (ctx) ErrorExpr(SourceRange(), type); - } + if (!backing) + return nullptr; storage = backing; @@ -721,12 +719,8 @@ static Expr *buildStorageReference(AccessorDecl *accessor, auto *backing = var->getPropertyWrapperBackingProperty(); // Error recovery. - if (!backing) { - auto type = storage->getValueInterfaceType(); - if (isLValue) - type = LValueType::get(type); - return new (ctx) ErrorExpr(SourceRange(), type); - } + if (!backing) + return nullptr; storage = backing; @@ -836,7 +830,12 @@ static Expr *buildStorageReference(AccessorDecl *accessor, ctx, wrapperMetatype, SourceLoc(), args, subscriptDecl->getFullName().getArgumentNames(), { }, SourceLoc(), nullptr, subscriptDecl, /*Implicit=*/true); - TypeChecker::typeCheckExpression(lookupExpr, accessor); + + // FIXME: Since we're not resolving overloads or anything, we should be + // building fully type-checked AST above; we already have all the + // information that we need. + if (!TypeChecker::typeCheckExpression(lookupExpr, accessor)) + return nullptr; // Make sure we produce an lvalue only when desired. if (isMemberLValue != lookupExpr->getType()->is()) { @@ -1034,6 +1033,10 @@ void createPropertyStoreOrCallSuperclassSetter(AccessorDecl *accessor, Expr *dest = buildStorageReference(accessor, storage, target, /*isLValue=*/true, ctx); + // Error recovery. + if (dest == nullptr) + return; + // A lazy property setter will store a value of type T into underlying storage // of type T?. auto destType = dest->getType()->getWithoutSpecifierType(); @@ -1043,6 +1046,7 @@ void createPropertyStoreOrCallSuperclassSetter(AccessorDecl *accessor, return; if (!destType->isEqual(value->getType())) { + assert(destType->getOptionalObjectType()); assert(destType->getOptionalObjectType()->isEqual(value->getType())); value = new (ctx) InjectIntoOptionalExpr(value, destType); } @@ -1078,10 +1082,15 @@ synthesizeTrivialGetterBody(AccessorDecl *getter, TargetImpl target, Expr *result = createPropertyLoadOrCallSuperclassGetter(getter, storage, target, ctx); - ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, - /*IsImplicit=*/true); - return { BraceStmt::create(ctx, loc, returnStmt, loc, true), + SmallVector body; + if (result != nullptr) { + ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, + /*IsImplicit=*/true); + body.push_back(returnStmt); + } + + return { BraceStmt::create(ctx, loc, body, loc, true), /*isTypeChecked=*/true }; } @@ -1473,7 +1482,13 @@ synthesizeObservedSetterBody(AccessorDecl *Set, TargetImpl target, if (VD->getParsedAccessor(AccessorKind::DidSet)) { Expr *OldValueExpr = buildStorageReference(Set, VD, target, /*isLValue=*/true, Ctx); - OldValueExpr = new (Ctx) LoadExpr(OldValueExpr, VD->getType()); + + // Error recovery. + if (OldValueExpr == nullptr) { + OldValueExpr = new (Ctx) ErrorExpr(SourceRange(), VD->getType()); + } else { + OldValueExpr = new (Ctx) LoadExpr(OldValueExpr, VD->getType()); + } OldValue = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let, /*IsCaptureList*/false, SourceLoc(), @@ -1601,13 +1616,14 @@ synthesizeCoroutineAccessorBody(AccessorDecl *accessor, ASTContext &ctx) { // Build a reference to the storage. Expr *ref = buildStorageReference(accessor, storage, target, isLValue, ctx); + if (ref != nullptr) { + // Wrap it with an `&` marker if this is a modify. + ref = maybeWrapInOutExpr(ref, ctx); - // Wrap it with an `&` marker if this is a modify. - ref = maybeWrapInOutExpr(ref, ctx); - - // Yield it. - YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true); - body.push_back(yield); + // Yield it. + YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true); + body.push_back(yield); + } return { BraceStmt::create(ctx, loc, body, loc, true), /*isTypeChecked=*/true }; diff --git a/test/decl/var/property_wrappers_invalid.swift b/test/decl/var/property_wrappers_invalid.swift new file mode 100644 index 0000000000000..61fb9811289c6 --- /dev/null +++ b/test/decl/var/property_wrappers_invalid.swift @@ -0,0 +1,36 @@ +// RUN: %target-swift-frontend -typecheck %s -verify -verify-ignore-unknown + +// FIXME: This should produce a diagnostic with a proper +// source location. Right now, we just get three useless errors: + +// :0: error: type of expression is ambiguous without more context +// :0: error: type of expression is ambiguous without more context +// :0: error: type of expression is ambiguous without more context + +// The actual problem is the type of the subscript declaration is wrong. + +public class Store { + @Published public var state: Any + init() { + self.state = 0 + } +} + +@propertyWrapper public struct Published { + public init(wrappedValue: Value) {} + public var wrappedValue: Value { + get { fatalError() } + set {} + } + public static subscript(_enclosingInstance object: Any, + wrapped wrappedKeyPath: Any, + storage storageKeyPath: Any) + -> Value { + get { fatalError() } + set {} + } + public struct Publisher {} + public var projectedValue: Publisher { + mutating get { fatalError() } + } +} From 3997aa5754dbc27ed6aacd7ab11930fbdd161043 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 3 Jan 2020 15:37:17 -0500 Subject: [PATCH 247/478] SIL: Remove SILBuilder::tryCreateUncheckedRefCast() --- include/swift/SIL/SILBuilder.h | 6 ------ lib/SIL/SILBuilder.cpp | 17 ++------------- lib/SILGen/SILGenBuilder.cpp | 11 ---------- lib/SILGen/SILGenBuilder.h | 5 ----- lib/SILGen/SILGenBuiltin.cpp | 21 +++++++------------ .../SILCombiner/SILCombinerCastVisitors.cpp | 18 +++++++++------- 6 files changed, 20 insertions(+), 58 deletions(-) diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index cbfa005100669..bda95ab9f3aca 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -1783,12 +1783,6 @@ class SILBuilder { // Unchecked cast helpers //===--------------------------------------------------------------------===// - // Create an UncheckedRefCast if the source and dest types are legal, - // otherwise return null. - // Unwrap or wrap optional types as needed. - SingleValueInstruction *tryCreateUncheckedRefCast(SILLocation Loc, SILValue Op, - SILType ResultTy); - // Create the appropriate cast instruction based on result type. SingleValueInstruction *createUncheckedBitCast(SILLocation Loc, SILValue Op, SILType Ty); diff --git a/lib/SIL/SILBuilder.cpp b/lib/SIL/SILBuilder.cpp index 907c3be1f22a7..a6a9bed4a3173 100644 --- a/lib/SIL/SILBuilder.cpp +++ b/lib/SIL/SILBuilder.cpp @@ -110,19 +110,6 @@ ProjectBoxInst *SILBuilder::createProjectBox(SILLocation Loc, getSILDebugLocation(Loc), boxOperand, index, fieldTy)); } -// If legal, create an unchecked_ref_cast from the given operand and result -// type, otherwise return null. -SingleValueInstruction * -SILBuilder::tryCreateUncheckedRefCast(SILLocation Loc, SILValue Op, - SILType ResultTy) { - if (!SILType::canRefCast(Op->getType(), ResultTy, getModule())) - return nullptr; - - return insert(UncheckedRefCastInst::create(getSILDebugLocation(Loc), Op, - ResultTy, getFunction(), - C.OpenedArchetypes)); -} - ClassifyBridgeObjectInst * SILBuilder::createClassifyBridgeObject(SILLocation Loc, SILValue value) { auto &ctx = getASTContext(); @@ -142,8 +129,8 @@ SILBuilder::createUncheckedBitCast(SILLocation Loc, SILValue Op, SILType Ty) { return insert(UncheckedTrivialBitCastInst::create( getSILDebugLocation(Loc), Op, Ty, getFunction(), C.OpenedArchetypes)); - if (auto refCast = tryCreateUncheckedRefCast(Loc, Op, Ty)) - return refCast; + if (SILType::canRefCast(Op->getType(), Ty, getModule())) + return createUncheckedRefCast(Loc, Op, Ty); // The destination type is nontrivial, and may be smaller than the source // type, so RC identity cannot be assumed. diff --git a/lib/SILGen/SILGenBuilder.cpp b/lib/SILGen/SILGenBuilder.cpp index d5381bd9bea70..7815383d495b7 100644 --- a/lib/SILGen/SILGenBuilder.cpp +++ b/lib/SILGen/SILGenBuilder.cpp @@ -804,17 +804,6 @@ ManagedValue SILGenBuilder::createUncheckedAddrCast(SILLocation loc, ManagedValu return cloner.clone(cast); } -ManagedValue SILGenBuilder::tryCreateUncheckedRefCast(SILLocation loc, - ManagedValue original, - SILType type) { - CleanupCloner cloner(*this, original); - SILValue result = tryCreateUncheckedRefCast(loc, original.getValue(), type); - if (!result) - return ManagedValue(); - original.forward(SGF); - return cloner.clone(result); -} - ManagedValue SILGenBuilder::createUncheckedTrivialBitCast(SILLocation loc, ManagedValue original, SILType type) { diff --git a/lib/SILGen/SILGenBuilder.h b/lib/SILGen/SILGenBuilder.h index 2c391369774d0..c4b8cd3f1b42e 100644 --- a/lib/SILGen/SILGenBuilder.h +++ b/lib/SILGen/SILGenBuilder.h @@ -256,11 +256,6 @@ class SILGenBuilder : public SILBuilder { ManagedValue createUpcast(SILLocation loc, ManagedValue original, SILType type); - using SILBuilder::tryCreateUncheckedRefCast; - ManagedValue tryCreateUncheckedRefCast(SILLocation loc, - ManagedValue op, - SILType type); - using SILBuilder::createUncheckedTrivialBitCast; ManagedValue createUncheckedTrivialBitCast(SILLocation loc, ManagedValue original, diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index 5e61fac605ec0..f1772f0e511df 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -649,13 +649,14 @@ emitBuiltinCastReference(SILGenFunction &SGF, auto &toTL = SGF.getTypeLowering(toTy); assert(!fromTL.isTrivial() && !toTL.isTrivial() && "expected ref type"); + auto arg = args[0]; + // TODO: Fix this API. if (!fromTL.isAddress() || !toTL.isAddress()) { - if (auto refCast = SGF.B.tryCreateUncheckedRefCast(loc, args[0], - toTL.getLoweredType())) { + if (SILType::canRefCast(arg.getType(), toTL.getLoweredType(), SGF.SGM.M)) { // Create a reference cast, forwarding the cleanup. // The cast takes the source reference. - return refCast; + return SGF.B.createUncheckedRefCast(loc, arg, toTL.getLoweredType()); } } @@ -670,7 +671,7 @@ emitBuiltinCastReference(SILGenFunction &SGF, // TODO: For now, we leave invalid casts in address form so that the runtime // will trap. We could emit a noreturn call here instead which would provide // more information to the optimizer. - SILValue srcVal = args[0].ensurePlusOne(SGF, loc).forward(SGF); + SILValue srcVal = arg.ensurePlusOne(SGF, loc).forward(SGF); SILValue fromAddr; if (!fromTL.isAddress()) { // Move the loadable value into a "source temp". Since the source and @@ -745,17 +746,9 @@ static ManagedValue emitBuiltinReinterpretCast(SILGenFunction &SGF, } // Create the appropriate bitcast based on the source and dest types. ManagedValue in = args[0]; - SILType resultTy = toTL.getLoweredType(); - if (resultTy.isTrivial(SGF.F)) - return SGF.B.createUncheckedTrivialBitCast(loc, in, resultTy); - // If we can perform a ref cast, just return. - if (auto refCast = SGF.B.tryCreateUncheckedRefCast(loc, in, resultTy)) - return refCast; - - // Otherwise leave the original cleanup and retain the cast value. - SILValue out = SGF.B.createUncheckedBitwiseCast(loc, in.getValue(), resultTy); - return SGF.emitManagedRetain(loc, out, toTL); + SILType resultTy = toTL.getLoweredType(); + return SGF.B.createUncheckedBitCast(loc, in, resultTy); } /// Specialized emitter for Builtin.castToBridgeObject. diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp index 7d9a15df654cf..15810b75d66f5 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp @@ -286,9 +286,12 @@ SILCombiner::visitUncheckedRefCastAddrInst(UncheckedRefCastAddrInst *URCI) { Builder.setCurrentDebugScope(URCI->getDebugScope()); LoadInst *load = Builder.createLoad(Loc, URCI->getSrc(), LoadOwnershipQualifier::Unqualified); - auto *cast = Builder.tryCreateUncheckedRefCast(Loc, load, - DestTy.getObjectType()); - assert(cast && "SILBuilder cannot handle reference-castable types"); + + assert(SILType::canRefCast(load->getType(), DestTy.getObjectType(), + Builder.getModule()) && + "SILBuilder cannot handle reference-castable types"); + auto *cast = Builder.createUncheckedRefCast(Loc, load, + DestTy.getObjectType()); Builder.createStore(Loc, cast, URCI->getDest(), StoreOwnershipQualifier::Unqualified); @@ -393,11 +396,12 @@ visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) { UBCI->getOperand(), UBCI->getType()); - if (auto refCast = Builder.tryCreateUncheckedRefCast( - UBCI->getLoc(), UBCI->getOperand(), UBCI->getType())) - return refCast; + if (!SILType::canRefCast(UBCI->getOperand()->getType(), UBCI->getType(), + Builder.getModule())) + return nullptr; - return nullptr; + return Builder.createUncheckedRefCast(UBCI->getLoc(), UBCI->getOperand(), + UBCI->getType()); } SILInstruction * From b5a0906ebc6f5d9c5e8f6ab746ccda98dd2c6062 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Fri, 3 Jan 2020 13:27:43 -0800 Subject: [PATCH 248/478] ASTMangler: ignore original module name when DWARFMangling is enabled Enabling DWARFMangling indicates the mangled name will be used by either debugger or IDE. We could and should avoid using the original module name so demangling will keep working. --- lib/AST/ASTMangler.cpp | 8 +++++++- test/IDE/reconstruct_type_from_mangled_name.swift | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 9e2a0f9416144..493832ce0fc72 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -499,6 +499,7 @@ std::string ASTMangler::mangleTypeAsUSR(Type Ty) { std::string ASTMangler::mangleDeclAsUSR(const ValueDecl *Decl, StringRef USRPrefix) { + DWARFMangling = true; beginManglingWithoutPrefix(); llvm::SaveAndRestore allowUnnamedRAII(AllowNamelessEntities, true); Buffer << USRPrefix; @@ -1790,7 +1791,12 @@ void ASTMangler::appendModule(const ModuleDecl *module, assert(useModuleName.empty()); return appendOperator("SC"); } - if (!useModuleName.empty()) + + // Enabling DWARFMangling indicate the mangled names are not part of the ABI, + // probably used by the debugger or IDE (USR). These mangled names will not be + // demangled successfully if we use the original module name instead of the + // actual module name. + if (!useModuleName.empty() && !DWARFMangling) appendIdentifier(useModuleName); else appendIdentifier(ModName); diff --git a/test/IDE/reconstruct_type_from_mangled_name.swift b/test/IDE/reconstruct_type_from_mangled_name.swift index 8e1ac96af60cd..f5ad1f0b6ff35 100644 --- a/test/IDE/reconstruct_type_from_mangled_name.swift +++ b/test/IDE/reconstruct_type_from_mangled_name.swift @@ -247,3 +247,9 @@ private func patatino(_ vers1: T, _ vers2: T) -> Bool { return vers1 < vers2; } + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "OtherModule", OSX 10.13) +public struct MovedHere { + public func foo() {} +} From 78de8f959d5440f1814bc3704eaa40a94d3c31d1 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 3 Jan 2020 13:49:58 -0800 Subject: [PATCH 249/478] docs: remove GitWorkflows The Swift project has entirely migrated to GitHub for development. This document covering the migration aspects of subversion are no longer really relevant. Prune the obsoleted documentation. --- docs/GitWorkflows.rst | 281 ------------------------------------------ 1 file changed, 281 deletions(-) delete mode 100644 docs/GitWorkflows.rst diff --git a/docs/GitWorkflows.rst b/docs/GitWorkflows.rst deleted file mode 100644 index 2027743712173..0000000000000 --- a/docs/GitWorkflows.rst +++ /dev/null @@ -1,281 +0,0 @@ -:orphan: - -.. highlight:: bash - -Git Workflows -============= - -Purpose -------- - -Swift development has been based on SVN since its inception. As part of the -transition to Git this document helps to address questions about how common SVN -workflows we use today translate to their Git counterparts as well as to discuss -Git workflow practices we plan on having -- at least initially -- after the Git -transition. Notably we will follow a model where commits to trunk -- which is -the 'master' branch in Git -- has commits land (in the common case) via rebasing -instead of merging. This model is open to evolution later, but this mimics the -workflow we have today with SVN. - -SVN -> GIT Workflows -==================== - -The general SVN workflow consists of the following commands: - -1. Checkout: This means checking out/setting up a new repository. -2. Update: Pulling the latest remote changes into a local repository. -3. Committing: Committing a change to the remote repository. -4. Reverting: Reverting a change from a remote repository. -5. Browsing: Looking at commits. - -This document will show how to translate these commands to Git and additionally -how to configure Git. It assumes that one is attempting to manipulate a Git -repository via bash in a terminal. A lot of information since this is supposed -to be a short, actionable guide. For more information, please see the Git crash -course guide for SVN users at - -*NOTE* Whenever we say the Swift repository, we mean any repository in the -Swift project. - -Quicksetup (TLDR) ------------------ - -For those who do not want to read the full document, use the following commands -to perform a simple repo setup for the Swift repository:: - - $ git config --global user.name "" - $ git config --global user.email "" - $ mkdir swift-source && cd swift-source - $ git clone - $ git clone - $ git clone - $ (cd swift && git config branch.autosetuprebase always) - $ git clone - $ git clone - -Then to commit a new commit to the remote swift repository:: - - $ git commit - $ git push origin master - -and to pull new commits from the remote swift repository:: - - $ git pull origin master - -In order to ease updating all repositories, consider using the script in -'./utils/update-checkout'. This will automate updating the repositories in the -proper way. - -Preliminary ------------ - -Before beginning, we need to perform some global configuration of Git. Git -includes a username/email of the committer in every commit. By default this is -the current logged in user and the hostname of the machine. This is /not/ what -one wants. We configure Git globally (i.e. across all repositories) to have our -proper name and email by running the following commands:: - - $ git config --global user.name "" - $ git config --global user.email "" - -Checkout --------- - -Normally if one wishes to checkout a repository in SVN, one would use a command -like this:: - - $ SVN co - -This would then checkout the latest revision from the repository specified by -'repository url' and place it into the directory 'local directory'. In Git, -instead of checking out the repository, one clones the repository. This is done -as follows:: - - $ git clone - -This will cause Git to clone the repository at 'repository url' and check out -the 'master' branch. The 'master' branch corresponds to 'trunk' in SVN. For more -information about branching in Git please see - - -Before beginning to commit though, we /must/ perform some default configuration -of our repository to match the Swift repository default configuration by -enabling default rebasing. - -Repository Configuration (Enabling Default Rebasing) ----------------------------------------------------- - -Once we have cloned the repository, we need to configure the repository for our -use. Specifically we want to configure the swift repository so that we rebase -whenever we update the repository (see the update section below for more -details):: - - $ git config branch.autosetuprebase always - -By default when updating, Git will attempt to merge the remote changes and your -local changes. Ignoring what that sentence means, this is not an SVN-esque -model. Instead we want any local changes that we have to be applied on top of -any new remote changes. The 'branch.autosetuprebase' flag causes this to be done -automatically whenever one updates the local repository. - -Update ------- - -In SVN, one updates your local repository by running the update command:: - - $ SVN update - -In Git, instead of performing SVN update, one pulls from the remote repository:: - - $ git pull --rebase origin master - -This will pull any new remote commits into your local repository and then replay -your current local commits on top of those new commits. - -By default the '--rebase' flag is not necessary for the Swift repository because -it is configured to always rebase by setting the 'branch.autosetuprebase' flag -(see the section 'Repository Configuration (Enabling Default Rebasing)' above). - -Commit ------- - -In SVN, committing always means submitting changes to a remote repository. In -Git, committing refers to the process of first telling Git to track a change by -staging the change and then committing all staged changes into a change in the -local repository. One can have many such commits. Then when one is ready, one -pushes the new local changes to the remote repository. We go through these steps -in more detail below: - -In terms of replicating the SVN model, there are now two steps. In order to -commit changes one first stages a changed file using 'git add':: - - $ git add - -Then once all changes that you want to be apart of the commit have been staged, -a commit is created in the local repository via the 'commit' command:: - - $ git commit - -As a shortcut to commit /all/ changes to local files that are already being -tracked by Git to the local repository, you can use the '-a' command:: - - $ git commit -a - -In both of these cases, an editor will pop up to accept a commit message. To -specify a short commit message at the commandline, you can use the '-m' flag:: - - $ git commit -m 'My great commit message.' - -In order to see the diff of changes that have not been staged, run the command:: - - $ git diff - -To see all changes that have been staged, run the command:: - - $ git diff --staged - -To get a diff for a specific revision/path, perform the following command:: - - $ git diff - -In order to get a more concise view of the files that have staged and or -unstaged changes, run the command:: - - $ git status - -In order to restore a file from the last revision, one uses the checkout -command:: - - $ git checkout - -To restore a file to a specific revision, one must use a longer form of the -command:: - - $ git checkout -- - -To unstage a file, one uses the 'reset' command:: - - $ git reset - -This tells Git to reset '' in the staging area to the top of tree commit -(which in Git is called 'HEAD'). In order to correct a mistake, you can pass the -'amend' flag to Git:: - - $ git commit --amend - -This will cause all staged changes to be merged into 'HEAD'. Once one has made -all the relevant commits, in order to push the changes to the remote repository -the 'push' command is used:: - - $ git push origin master - -If a different committer has committed changes such that there are remote -commits that are not present locally, this will fail. In order to get around -this issue, perform:: - - $ git pull --rebase origin master - -in order to pull the new remote commits and replay your new commits on top. Then -try to push again. See the 'Checkout' section above how to configure the local -swift repository to always rebase allowing you to drop the '--rebase' flag. - -Revert ------- - -In SVN reverting a commit implies performing a reverse merge. In Git, this is no -longer true. Instead one now just uses the 'revert' command:: - - $ git revert - -This will cause Git to perform the reverse merge of that revision for you -against HEAD and bring up a message window for you to write a commit -message. This will be autofilled in with the title of the commit that is going -to be reverted and the revision number of that commit like so:: - - Revert "" - - This reverts commit . - -One can edit this message as one sees fit. Once this has been done, the revert -will become a normal commit in your repository like any other commit. Thus to -revert the commit in the remote repository, you need to perform a Git push:: - - $ git push origin master - -Browsing --------- - -This section explains how one can view Git changes. In order to view a history -of all changes on a branch to the beginning of time use the 'log' command:: - - $ git log - -This will for each commit show the following information:: - - commit - Author: - Date: - - - -To see history starting at a specific commit use the following form of a Git log -command:: - - $ git log - -To see an oneline summary that includes just the title of the commit and its -hash, pass the '--oneline' command:: - - $ git log --oneline - -It will not show you what was actually changed in each commit. In order to see -what was actually changed in a commit, use the command 'show':: - - $ git show - -This will show the aforementioned information shown by Git log, but additionally -will perform a diff against top of tree showing you the contents of the -change. To see the changes for a specific commit, pass the revision to Git -show:: - - $ git show From 96b3b9f0f412dc3d9d49a6da9adca2ba025cea7f Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 21 Dec 2019 21:01:10 -0800 Subject: [PATCH 250/478] [NFC] Hide SourceFile::Decls In preparation for installing some stable paths infrastructure here, hide access to the array of top-level decls. --- include/swift/AST/SourceFile.h | 21 ++++++++++++++-- lib/AST/ASTDumper.cpp | 2 +- lib/AST/ASTScopeCreation.cpp | 6 ++--- lib/AST/ASTScopeSourceRange.cpp | 6 ++--- ...endenciesSourceFileDepGraphConstructor.cpp | 2 +- lib/AST/Module.cpp | 8 +++--- lib/AST/NameLookup.cpp | 2 +- lib/Frontend/Frontend.cpp | 4 +-- lib/FrontendTool/FrontendTool.cpp | 2 +- lib/FrontendTool/ReferenceDependencies.cpp | 2 +- lib/IDE/CompletionInstance.cpp | 4 +-- lib/IDE/ExprContextAnalysis.cpp | 2 +- lib/IDE/ModuleInterfacePrinting.cpp | 2 +- lib/IDE/REPLCodeCompletion.cpp | 4 +-- lib/IRGen/GenDecl.cpp | 2 +- lib/Parse/ParseDecl.cpp | 4 +-- lib/Parse/Parser.cpp | 2 +- lib/SILGen/SILGen.cpp | 2 +- lib/Sema/DebuggerTestingTransform.cpp | 4 +-- lib/Sema/NameBinding.cpp | 4 +-- lib/Sema/PCMacro.cpp | 2 +- lib/Sema/TypeCheckAvailability.cpp | 7 +++--- lib/Sema/TypeCheckREPL.cpp | 25 ++++++++++--------- lib/Sema/TypeChecker.cpp | 6 ++--- unittests/AST/TestContext.cpp | 2 +- 25 files changed, 73 insertions(+), 54 deletions(-) diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index 533ff6696dca3..a9078e67defbb 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -125,11 +125,28 @@ class SourceFile final : public FileUnit { /// been validated. llvm::SetVector UnvalidatedDeclsWithOpaqueReturnTypes; + /// The list of top-level declarations in the source file. + std::vector Decls; + friend ASTContext; friend Impl; + public: - /// The list of top-level declarations in the source file. - std::vector Decls; + /// Appends the given declaration to the end of the top-level decls list. + void addTopLevelDecl(Decl *d) { + Decls.push_back(d); + } + + /// Retrieves an immutable view of the list of top-level decls in this file. + ArrayRef getTopLevelDecls() const { + return Decls; + } + + /// Truncates the list of top-level decls so it contains \c count elements. + void truncateTopLevelDecls(unsigned count) { + assert(count <= Decls.size() && "Can only truncate top-level decls!"); + Decls.resize(count); + } /// A cache of syntax nodes that can be reused when creating the syntax tree /// for this file. diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index a2e81377121a3..7b7bf462b9cd7 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -794,7 +794,7 @@ namespace { PrintWithColorRAII(OS, ASTNodeColor) << "source_file "; PrintWithColorRAII(OS, LocationColor) << '\"' << SF.getFilename() << '\"'; - for (Decl *D : SF.Decls) { + for (Decl *D : SF.getTopLevelDecls()) { if (D->isImplicit()) continue; diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 4c1966841c53e..bb54182e1f0fd 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -1193,7 +1193,7 @@ AnnotatedInsertionPoint ASTSourceFileScope::expandAScopeThatCreatesANewInsertionPoint( ScopeCreator &scopeCreator) { ASTScopeAssert(SF, "Must already have a SourceFile."); - ArrayRef decls = SF->Decls; + ArrayRef decls = SF->getTopLevelDecls(); // Assume that decls are only added at the end, in source order ArrayRef newDecls = decls.slice(numberOfDeclsAlreadySeen); std::vector newNodes(newDecls.begin(), newDecls.end()); @@ -1865,10 +1865,10 @@ void ASTScopeImpl::beCurrent() {} bool ASTScopeImpl::isCurrentIfWasExpanded() const { return true; } void ASTSourceFileScope::beCurrent() { - numberOfDeclsAlreadySeen = SF->Decls.size(); + numberOfDeclsAlreadySeen = SF->getTopLevelDecls().size(); } bool ASTSourceFileScope::isCurrentIfWasExpanded() const { - return SF->Decls.size() == numberOfDeclsAlreadySeen; + return SF->getTopLevelDecls().size() == numberOfDeclsAlreadySeen; } void IterableTypeScope::beCurrent() { portion->beCurrent(this); } diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index c5689881e176f..99a38518883c5 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -284,12 +284,12 @@ SourceRange ASTSourceFileScope::getSourceRangeOfThisASTNode( return SourceRange(charRange.getStart(), charRange.getEnd()); } - if (SF->Decls.empty()) + if (SF->getTopLevelDecls().empty()) return SourceRange(); // Use the source ranges of the declarations in the file. - return SourceRange(SF->Decls.front()->getStartLoc(), - SF->Decls.back()->getEndLoc()); + return SourceRange(SF->getTopLevelDecls().front()->getStartLoc(), + SF->getTopLevelDecls().back()->getEndLoc()); } SourceRange GenericTypeOrExtensionScope::getSourceRangeOfThisASTNode( diff --git a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp index 24f7dbb8a3b63..853aaaaade902 100644 --- a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp +++ b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp @@ -191,7 +191,7 @@ struct SourceFileDeclFinder { // clang-format off SourceFileDeclFinder(const SourceFile *const SF, const bool includePrivateDecls) : includePrivateDecls(includePrivateDecls) { - for (const Decl *const D : SF->Decls) { + for (const Decl *const D : SF->getTopLevelDecls()) { select(D, extensions, false) || select(D, operators, false) || diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 486a1e26ba6dd..0d719728b0398 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -230,7 +230,7 @@ void SourceLookupCache::populateMemberCache(const SourceFile &SF) { FrontendStatsTracer tracer(SF.getASTContext().Stats, "populate-source-file-class-member-cache"); - addToMemberCache(SF.Decls); + addToMemberCache(SF.getTopLevelDecls()); MemberCachePopulated = true; } @@ -243,7 +243,7 @@ void SourceLookupCache::populateMemberCache(const ModuleDecl &Mod) { for (const FileUnit *file : Mod.getFiles()) { auto &SF = *cast(file); - addToMemberCache(SF.Decls); + addToMemberCache(SF.getTopLevelDecls()); } MemberCachePopulated = true; @@ -275,7 +275,7 @@ void SourceLookupCache::addToMemberCache(Range decls) { SourceLookupCache::SourceLookupCache(const SourceFile &SF) { FrontendStatsTracer tracer(SF.getASTContext().Stats, "source-file-populate-cache"); - addToUnqualifiedLookupCache(SF.Decls, false); + addToUnqualifiedLookupCache(SF.getTopLevelDecls(), false); } SourceLookupCache::SourceLookupCache(const ModuleDecl &M) { @@ -283,7 +283,7 @@ SourceLookupCache::SourceLookupCache(const ModuleDecl &M) { "module-populate-cache"); for (const FileUnit *file : M.getFiles()) { auto &SF = *cast(file); - addToUnqualifiedLookupCache(SF.Decls, false); + addToUnqualifiedLookupCache(SF.getTopLevelDecls(), false); } } diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index dfaf9ca71f5b2..450f762eeecad 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -2470,7 +2470,7 @@ void FindLocalVal::checkGenericParams(GenericParamList *Params) { } void FindLocalVal::checkSourceFile(const SourceFile &SF) { - for (Decl *D : SF.Decls) + for (Decl *D : SF.getTopLevelDecls()) if (auto *TLCD = dyn_cast(D)) visitBraceStmt(TLCD->getBody(), /*isTopLevel=*/true); } diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index f43739bf2bde8..2bb8ff300c30f 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -984,13 +984,13 @@ void CompilerInstance::parseAndTypeCheckMainFileUpTo( // For SIL we actually have to interleave parsing and type checking // because the SIL parser expects to see fully type checked declarations. if (TheSILModule) { - if (Done || CurTUElem < MainFile.Decls.size()) { + if (Done || CurTUElem < MainFile.getTopLevelDecls().size()) { assert(mainIsPrimary); performTypeChecking(MainFile, CurTUElem); } } - CurTUElem = MainFile.Decls.size(); + CurTUElem = MainFile.getTopLevelDecls().size(); } while (!Done); if (!TheSILModule) { diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index fd01c1131faf5..676a601f35fdb 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -667,7 +667,7 @@ static void countStatsOfSourceFile(UnifiedStatsReporter &Stats, SourceFile *SF) { auto &C = Stats.getFrontendCounters(); auto &SM = Instance.getSourceMgr(); - C.NumDecls += SF->Decls.size(); + C.NumDecls += SF->getTopLevelDecls().size(); C.NumLocalTypeDecls += SF->LocalTypeDecls.size(); C.NumObjCMethods += SF->ObjCMethods.size(); C.NumInfixOperators += SF->InfixOperators.size(); diff --git a/lib/FrontendTool/ReferenceDependencies.cpp b/lib/FrontendTool/ReferenceDependencies.cpp index ef198c095afd1..c9cb5117a0a57 100644 --- a/lib/FrontendTool/ReferenceDependencies.cpp +++ b/lib/FrontendTool/ReferenceDependencies.cpp @@ -252,7 +252,7 @@ ProvidesEmitter::emitTopLevelNames() const { out << providesTopLevel << ":\n"; CollectedDeclarations cpd; - for (const Decl *D : SF->Decls) + for (const Decl *D : SF->getTopLevelDecls()) emitTopLevelDecl(D, cpd); for (auto *operatorFunction : cpd.memberOperatorDecls) out << "- \"" << escape(operatorFunction->getName()) << "\"\n"; diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index ab3c289019116..155b72c5b859f 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -88,7 +88,7 @@ static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC, auto *parentDC = newDC->getParent(); unsigned N; if (auto parentSF = dyn_cast(parentDC)) - N = findIndexInRange(D, parentSF->Decls); + N = findIndexInRange(D, parentSF->getTopLevelDecls()); else if (auto parentIDC = dyn_cast(parentDC->getAsDecl())) N = findIndexInRange(D, parentIDC->getMembers()); @@ -114,7 +114,7 @@ static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC, auto N = IndexStack.pop_back_val(); Decl *D; if (auto parentSF = dyn_cast(newDC)) - D = getElementAt(parentSF->Decls, N); + D = getElementAt(parentSF->getTopLevelDecls(), N); else if (auto parentIDC = dyn_cast(newDC->getAsDecl())) D = getElementAt(parentIDC->getMembers(), N); else diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index d7dbf5fd1bcab..c955d2f81d184 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -111,7 +111,7 @@ void swift::ide::typeCheckContextUntil(DeclContext *DC, SourceLoc Loc) { // Here, 'value' is '' unless we explicitly typecheck the // 'guard' statement. SourceFile *SF = DC->getParentSourceFile(); - for (auto *D : SF->Decls) { + for (auto *D : SF->getTopLevelDecls()) { if (auto Code = dyn_cast(D)) { typeCheckTopLevelCodeDecl(Code); if (Code == TLCD) diff --git a/lib/IDE/ModuleInterfacePrinting.cpp b/lib/IDE/ModuleInterfacePrinting.cpp index e87deadb53b05..81a19b86f80dd 100644 --- a/lib/IDE/ModuleInterfacePrinting.cpp +++ b/lib/IDE/ModuleInterfacePrinting.cpp @@ -667,7 +667,7 @@ static SourceLoc getDeclStartPosition(SourceFile &File) { return false; }; - for (auto D : File.Decls) { + for (auto D : File.getTopLevelDecls()) { if (tryUpdateStart(D->getStartLoc())) { tryUpdateStart(D->getAttrs().getStartLoc()); auto RawComment = D->getRawComment(); diff --git a/lib/IDE/REPLCodeCompletion.cpp b/lib/IDE/REPLCodeCompletion.cpp index 4d3356c440687..247ddb4d0e473 100644 --- a/lib/IDE/REPLCodeCompletion.cpp +++ b/lib/IDE/REPLCodeCompletion.cpp @@ -205,7 +205,7 @@ doCodeCompletion(SourceFile &SF, StringRef EnteredCode, unsigned *BufferID, Ctx.SourceMgr.setCodeCompletionPoint(*BufferID, CodeCompletionOffset); // Parse, typecheck and temporarily insert the incomplete code into the AST. - const unsigned OriginalDeclCount = SF.Decls.size(); + const unsigned OriginalDeclCount = SF.getTopLevelDecls().size(); PersistentParserState PersistentState; bool Done; @@ -218,7 +218,7 @@ doCodeCompletion(SourceFile &SF, StringRef EnteredCode, unsigned *BufferID, // Now we are done with code completion. Remove the declarations we // temporarily inserted. - SF.Decls.resize(OriginalDeclCount); + SF.truncateTopLevelDecls(OriginalDeclCount); // Reset the error state because it's only relevant to the code that we just // processed, which now gets thrown away. diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index cc5e490b601ca..3aae01d228ae2 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -431,7 +431,7 @@ void IRGenModule::emitSourceFile(SourceFile &SF) { llvm::SaveAndRestore SetCurSourceFile(CurSourceFile, &SF); // Emit types and other global decls. - for (auto *decl : SF.Decls) + for (auto *decl : SF.getTopLevelDecls()) emitGlobalDecl(decl); for (auto *localDecl : SF.LocalTypeDecls) emitGlobalDecl(localDecl); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 719f64d318e70..83574c17c2f16 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -175,7 +175,7 @@ namespace { DebuggerClient *debug_client = getDebuggerClient(); assert (debug_client); debug_client->didGlobalize(D); - SF->Decls.push_back(D); + SF->addTopLevelDecl(D); P.markWasHandled(D); } }; @@ -250,7 +250,7 @@ void Parser::parseTopLevel() { for (auto Item : Items) { if (auto *D = Item.dyn_cast()) { assert(!isa(D) && "accessors should not be added here"); - SF.Decls.push_back(D); + SF.addTopLevelDecl(D); } } diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index be223823a366e..1a76ec7359319 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -182,7 +182,7 @@ void Parser::performCodeCompletionSecondPassImpl( } else if (auto *ED = dyn_cast(DC)) { ED->addMember(D); } else if (auto *SF = dyn_cast(DC)) { - SF->Decls.push_back(D); + SF->addTopLevelDecl(D); } else { llvm_unreachable("invalid decl context kind"); } diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 242f3d32106c9..b87b9521dbffb 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1670,7 +1670,7 @@ class SourceFileScope { void SILGenModule::emitSourceFile(SourceFile *sf) { SourceFileScope scope(*this, sf); FrontendStatsTracer StatsTracer(getASTContext().Stats, "SILgen-file", sf); - for (Decl *D : sf->Decls) { + for (Decl *D : sf->getTopLevelDecls()) { FrontendStatsTracer StatsTracer(getASTContext().Stats, "SILgen-decl", D); visit(D); } diff --git a/lib/Sema/DebuggerTestingTransform.cpp b/lib/Sema/DebuggerTestingTransform.cpp index 0a4ad23767bfc..4853943d687d6 100644 --- a/lib/Sema/DebuggerTestingTransform.cpp +++ b/lib/Sema/DebuggerTestingTransform.cpp @@ -261,11 +261,11 @@ void swift::performDebuggerTestingTransform(SourceFile &SF) { // Walk over all decls in the file to find the next available closure // discriminator. DiscriminatorFinder DF; - for (Decl *D : SF.Decls) + for (Decl *D : SF.getTopLevelDecls()) D->walk(DF); // Instrument the decls with checkExpect() sanity-checks. - for (Decl *D : SF.Decls) { + for (Decl *D : SF.getTopLevelDecls()) { DebuggerTestingTransform Transform{D->getASTContext(), DF}; D->walk(Transform); swift::verify(D); diff --git a/lib/Sema/NameBinding.cpp b/lib/Sema/NameBinding.cpp index 4b86626e941da..328562e9c23e8 100644 --- a/lib/Sema/NameBinding.cpp +++ b/lib/Sema/NameBinding.cpp @@ -402,7 +402,7 @@ void swift::performNameBinding(SourceFile &SF, unsigned StartElem) { // Make sure we skip adding the standard library imports if the // source file is empty. - if (SF.ASTStage == SourceFile::NameBound || SF.Decls.empty()) { + if (SF.ASTStage == SourceFile::NameBound || SF.getTopLevelDecls().empty()) { SF.ASTStage = SourceFile::NameBound; return; } @@ -417,7 +417,7 @@ void swift::performNameBinding(SourceFile &SF, unsigned StartElem) { // Do a prepass over the declarations to find and load the imported modules // and map operator decls. - for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) { + for (auto D : SF.getTopLevelDecls().slice(StartElem)) { if (auto *ID = dyn_cast(D)) { Binder.addImport(ImportedModules, ID); } else if (auto *OD = dyn_cast(D)) { diff --git a/lib/Sema/PCMacro.cpp b/lib/Sema/PCMacro.cpp index 9109dd250f607..839176b7aa88d 100644 --- a/lib/Sema/PCMacro.cpp +++ b/lib/Sema/PCMacro.cpp @@ -705,7 +705,7 @@ void swift::performPCMacro(SourceFile &SF) { }; ExpressionFinder EF; - for (Decl *D : SF.Decls) { + for (Decl *D : SF.getTopLevelDecls()) { D->walk(EF); } } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 72f3abe58b2ba..0f6812903c459 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -604,7 +604,7 @@ void TypeChecker::buildTypeRefinementContextHierarchy(SourceFile &SF, // Build refinement contexts, if necessary, for all declarations starting // with StartElem. TypeRefinementContextBuilder Builder(RootTRC, Context); - for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) { + for (auto D : SF.getTopLevelDecls().slice(StartElem)) { Builder.build(D); } } @@ -904,8 +904,9 @@ static const Decl *findContainingDeclaration(SourceRange ReferenceRange, if (!SF) return nullptr; - auto BestTopLevelDecl = llvm::find_if(SF->Decls, ContainsReferenceRange); - if (BestTopLevelDecl != SF->Decls.end()) + auto BestTopLevelDecl = llvm::find_if(SF->getTopLevelDecls(), + ContainsReferenceRange); + if (BestTopLevelDecl != SF->getTopLevelDecls().end()) return *BestTopLevelDecl; return nullptr; diff --git a/lib/Sema/TypeCheckREPL.cpp b/lib/Sema/TypeCheckREPL.cpp index 59c3cf05d14f1..ef4a5aab3dc9e 100644 --- a/lib/Sema/TypeCheckREPL.cpp +++ b/lib/Sema/TypeCheckREPL.cpp @@ -300,7 +300,7 @@ void REPLChecker::generatePrintOfExpression(StringRef NameStr, Expr *E) { newTopLevel->setBody(BS); TypeChecker::checkTopLevelErrorHandling(newTopLevel); - SF.Decls.push_back(newTopLevel); + SF.addTopLevelDecl(newTopLevel); } /// When we see an expression in a TopLevelCodeDecl in the REPL, process it, @@ -323,7 +323,7 @@ void REPLChecker::processREPLTopLevelExpr(Expr *E) { // Remove the expression from being in the list of decls to execute, we're // going to reparent it. - auto TLCD = cast(SF.Decls.back()); + auto TLCD = cast(SF.getTopLevelDecls().back()); E = TypeChecker::coerceToRValue(Context, E); @@ -333,7 +333,7 @@ void REPLChecker::processREPLTopLevelExpr(Expr *E) { /*IsCaptureList*/false, E->getStartLoc(), name, &SF); vd->setInterfaceType(E->getType()); - SF.Decls.push_back(vd); + SF.addTopLevelDecl(vd); // Create a PatternBindingDecl to bind the expression into the decl. Pattern *metavarPat = new (Context) NamedPattern(vd); @@ -397,8 +397,8 @@ void REPLChecker::processREPLTopLevelPatternBinding(PatternBindingDecl *PBD) { // replPrint(r123) // Remove PBD from the list of Decls so we can insert before it. - auto PBTLCD = cast(SF.Decls.back()); - SF.Decls.pop_back(); + auto PBTLCD = cast(SF.getTopLevelDecls().back()); + SF.truncateTopLevelDecls(SF.getTopLevelDecls().size() - 1); // Create the meta-variable, let the typechecker name it. Identifier name = getNextResponseVariableName(SF.getParentModule()); @@ -407,7 +407,7 @@ void REPLChecker::processREPLTopLevelPatternBinding(PatternBindingDecl *PBD) { /*IsCaptureList*/false, PBD->getStartLoc(), name, &SF); vd->setInterfaceType(pattern->getType()); - SF.Decls.push_back(vd); + SF.addTopLevelDecl(vd); // Create a PatternBindingDecl to bind the expression into the decl. Pattern *metavarPat = new (Context) NamedPattern(vd); @@ -422,7 +422,7 @@ void REPLChecker::processREPLTopLevelPatternBinding(PatternBindingDecl *PBD) { metavarBinding->getEndLoc()); auto *MVTLCD = new (Context) TopLevelCodeDecl(&SF, MVBrace); - SF.Decls.push_back(MVTLCD); + SF.addTopLevelDecl(MVTLCD); // Replace the initializer of PBD with a reference to our repl temporary. @@ -431,7 +431,7 @@ void REPLChecker::processREPLTopLevelPatternBinding(PatternBindingDecl *PBD) { /*Implicit=*/true); E = TypeChecker::coerceToRValue(Context, E); PBD->setInit(entryIdx, E); - SF.Decls.push_back(PBTLCD); + SF.addTopLevelDecl(PBTLCD); // Finally, print out the result, by referring to the repl temp. E = TypeChecker::buildCheckedRefExpr(vd, &SF, @@ -466,19 +466,20 @@ void TypeChecker::processREPLTopLevel(SourceFile &SF, unsigned FirstDecl) { // Walk over all decls in the file to find the next available closure // discriminator. DiscriminatorFinder DF; - for (Decl *D : SF.Decls) + for (Decl *D : SF.getTopLevelDecls()) D->walk(DF); // Move new declarations out. - std::vector NewDecls(SF.Decls.begin()+FirstDecl, SF.Decls.end()); - SF.Decls.resize(FirstDecl); + std::vector NewDecls(SF.getTopLevelDecls().begin()+FirstDecl, + SF.getTopLevelDecls().end()); + SF.truncateTopLevelDecls(FirstDecl); REPLChecker RC(SF, DF); // Loop over each of the new decls, processing them, adding them back to // the Decls list. for (Decl *D : NewDecls) { - SF.Decls.push_back(D); + SF.addTopLevelDecl(D); auto *TLCD = dyn_cast(D); if (!TLCD || TLCD->getBody()->getElements().empty()) diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 5194117fc9854..b5a702309c7f3 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -263,7 +263,7 @@ static void bindExtensions(SourceFile &SF) { if (!SF) continue; - for (auto D : SF->Decls) { + for (auto D : SF->getTopLevelDecls()) { if (auto ED = dyn_cast(D)) if (!tryBindExtension(ED)) worklist.push_back(ED); @@ -378,7 +378,7 @@ TypeCheckSourceFileRequest::evaluate(Evaluator &eval, ::bindExtensions(*SF); // Type check the top-level elements of the source file. - for (auto D : llvm::makeArrayRef(SF->Decls).slice(StartElem)) { + for (auto D : SF->getTopLevelDecls().slice(StartElem)) { if (auto *TLCD = dyn_cast(D)) { // Immediately perform global name-binding etc. TypeChecker::typeCheckTopLevelCodeDecl(TLCD); @@ -465,7 +465,7 @@ void swift::checkInconsistentImplementationOnlyImports(ModuleDecl *MainModule) { if (!SF) continue; - for (auto *topLevelDecl : SF->Decls) { + for (auto *topLevelDecl : SF->getTopLevelDecls()) { auto *nextImport = dyn_cast(topLevelDecl); if (!nextImport) continue; diff --git a/unittests/AST/TestContext.cpp b/unittests/AST/TestContext.cpp index d246d8a0fce33..70d5e2edebb6b 100644 --- a/unittests/AST/TestContext.cpp +++ b/unittests/AST/TestContext.cpp @@ -30,7 +30,7 @@ static void declareOptionalType(ASTContext &ctx, SourceFile *fileForLookups, auto decl = new (ctx) EnumDecl(SourceLoc(), name, SourceLoc(), /*inherited*/{}, params, fileForLookups); wrapped->setDeclContext(decl); - fileForLookups->Decls.push_back(decl); + fileForLookups->addTopLevelDecl(decl); } TestContext::TestContext(ShouldDeclareOptionalTypes optionals) From 5f428f594a1efe2bb74ef499621c7b3856b7106e Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 3 Jan 2020 14:40:12 -0800 Subject: [PATCH 251/478] build: use the new CMark export targets CMark upstream now provides an exports target entry that we can use. --- cmake/modules/SwiftConfig.cmake.in | 2 +- cmake/modules/SwiftSharedCMakeConfig.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/modules/SwiftConfig.cmake.in b/cmake/modules/SwiftConfig.cmake.in index 7b0d002a75946..5dcdf5e1b9c1d 100644 --- a/cmake/modules/SwiftConfig.cmake.in +++ b/cmake/modules/SwiftConfig.cmake.in @@ -17,7 +17,7 @@ set(SWIFT_LIBRARY_DIR "@SWIFT_LIBRARY_DIRS@") set(SWIFT_CMAKE_DIR "@SWIFT_CMAKE_DIR@") set(SWIFT_BINARY_DIR "@SWIFT_BINARY_DIR@") -set(CMARK_TARGETS_FILE @SWIFT_PATH_TO_CMARK_BUILD@/src/CMarkExports.cmake) +set(CMARK_TARGETS_FILE @SWIFT_PATH_TO_CMARK_BUILD@/src/cmarkTargets.cmake) if(NOT TARGET libcmark_static AND EXISTS ${CMARK_TARGETS_FILE}) include(${CMARK_TARGETS_FILE}) endif() diff --git a/cmake/modules/SwiftSharedCMakeConfig.cmake b/cmake/modules/SwiftSharedCMakeConfig.cmake index 1a76d67adb786..5bddf37d86816 100644 --- a/cmake/modules/SwiftSharedCMakeConfig.cmake +++ b/cmake/modules/SwiftSharedCMakeConfig.cmake @@ -193,7 +193,7 @@ macro(swift_common_standalone_build_config_cmark product) include_directories("${CMARK_MAIN_INCLUDE_DIR}" "${CMARK_BUILD_INCLUDE_DIR}") - include(${PATH_TO_CMARK_BUILD}/src/CMarkExports.cmake) + include(${PATH_TO_CMARK_BUILD}/src/cmarkTargets.cmake) add_definitions(-DCMARK_STATIC_DEFINE) endmacro() From 033f9c79271452c3a99cdecce601d112885b358c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 3 Jan 2020 14:42:35 -0800 Subject: [PATCH 252/478] [Type checker] Introduce "contextual patterns". Contextual pattern describes a particular pattern with enough contextual information to determine its type. Use this to simplify TypeChecker::typeCheckPattern()'s interface in a manner that will admit request'ification. --- include/swift/AST/Pattern.h | 88 ++++++++++++++++++++++++++++++- lib/AST/Pattern.cpp | 30 +++++++++++ lib/Sema/CSGen.cpp | 6 +-- lib/Sema/TypeCheckConstraints.cpp | 20 +++---- lib/Sema/TypeCheckPattern.cpp | 24 ++++++--- lib/Sema/TypeCheckStorage.cpp | 5 +- lib/Sema/TypeChecker.h | 7 +-- 7 files changed, 149 insertions(+), 31 deletions(-) diff --git a/include/swift/AST/Pattern.h b/include/swift/AST/Pattern.h index a0f6345910d50..4bf10c283ed1d 100644 --- a/include/swift/AST/Pattern.h +++ b/include/swift/AST/Pattern.h @@ -756,7 +756,93 @@ inline Pattern *Pattern::getSemanticsProvidingPattern() { return vp->getSubPattern()->getSemanticsProvidingPattern(); return this; } - + +/// Describes a pattern and the context in which it occurs. +class ContextualPattern { + /// The pattern and whether this is the top-level pattern. + llvm::PointerIntPair patternAndTopLevel; + + /// Either the declaration context or the enclosing pattern binding + /// declaration. + llvm::PointerUnion declOrContext; + + /// Index into the pattern binding declaration, when there is one. + unsigned index = 0; + + ContextualPattern( + Pattern *pattern, bool topLevel, + llvm::PointerUnion declOrContext, + unsigned index + ) : patternAndTopLevel(pattern, topLevel), + declOrContext(declOrContext), + index(index) { } + +public: + /// Produce a contextual pattern for a pattern binding declaration entry. + static ContextualPattern forPatternBindingDecl( + PatternBindingDecl *pbd, unsigned index); + + /// Produce a contextual pattern for a raw pattern that always allows + /// inference. + static ContextualPattern forRawPattern(Pattern *pattern, DeclContext *dc) { + return ContextualPattern(pattern, /*topLevel=*/true, dc, /*index=*/0); + } + + /// Retrieve a contextual pattern for the given subpattern. + ContextualPattern forSubPattern( + Pattern *subpattern, bool retainTopLevel) const { + return ContextualPattern( + subpattern, isTopLevel() && retainTopLevel, declOrContext, index); + } + + /// Retrieve the pattern. + Pattern *getPattern() const { + return patternAndTopLevel.getPointer(); + } + + /// Whether this is the top-level pattern in this context. + bool isTopLevel() const { + return patternAndTopLevel.getInt(); + } + + /// Retrieve the declaration context of the pattern. + DeclContext *getDeclContext() const; + + /// Retrieve the pattern binding declaration that owns this pattern, if + /// there is one. + PatternBindingDecl *getPatternBindingDecl() const; + + /// Retrieve the index into the pattern binding declaration for the top-level + /// pattern. + unsigned getPatternBindingIndex() const { + assert(getPatternBindingDecl() != nullptr); + return index; + } + + /// Whether this pattern allows type inference, e.g., from an initializer + /// expression. + bool allowsInference() const; + + friend llvm::hash_code hash_value(const ContextualPattern &pattern) { + return llvm::hash_combine(pattern.getPattern(), + pattern.isTopLevel(), + pattern.declOrContext); + } + + friend bool operator==(const ContextualPattern &lhs, + const ContextualPattern &rhs) { + return lhs.patternAndTopLevel == rhs.patternAndTopLevel && + lhs.declOrContext == rhs.declOrContext; + } + + friend bool operator!=(const ContextualPattern &lhs, + const ContextualPattern &rhs) { + return !(lhs == rhs); + } +}; + +void simple_display(llvm::raw_ostream &out, const ContextualPattern &pattern); + } // end namespace swift #endif diff --git a/lib/AST/Pattern.cpp b/lib/AST/Pattern.cpp index 447ba7fe0abbd..9f2924ac2945c 100644 --- a/lib/AST/Pattern.cpp +++ b/lib/AST/Pattern.cpp @@ -496,3 +496,33 @@ const UnifiedStatsReporter::TraceFormatter* FrontendStatsTracer::getTraceFormatter() { return &TF; } + + +ContextualPattern ContextualPattern::forPatternBindingDecl( + PatternBindingDecl *pbd, unsigned index) { + return ContextualPattern( + pbd->getPattern(index), /*isTopLevel=*/true, pbd, index); +} + +DeclContext *ContextualPattern::getDeclContext() const { + if (auto pbd = getPatternBindingDecl()) + return pbd->getDeclContext(); + + return declOrContext.get(); +} + +PatternBindingDecl *ContextualPattern::getPatternBindingDecl() const { + return declOrContext.dyn_cast(); +} + +bool ContextualPattern::allowsInference() const { + if (auto pbd = getPatternBindingDecl()) + return pbd->isInitialized(index); + + return true; +} + +void swift::simple_display(llvm::raw_ostream &out, + const ContextualPattern &pattern) { + out << "(pattern @ " << pattern.getPattern() << ")"; +} diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index d9527be9205f4..90b131fe1bc3d 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2159,9 +2159,9 @@ namespace { case PatternKind::Typed: { // FIXME: Need a better locator for a pattern as a base. - TypeResolutionOptions options(TypeResolverContext::InExpression); - options |= TypeResolutionFlags::AllowUnboundGenerics; - Type type = TypeChecker::typeCheckPattern(pattern, CurDC, options); + auto contextualPattern = + ContextualPattern::forRawPattern(pattern, CurDC); + Type type = TypeChecker::typeCheckPattern(contextualPattern); Type openedType = CS.openUnboundGenericType(type, locator); // For a typed pattern, simply return the opened type of the pattern. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index e1583b184dc1e..f3bbb92c23ed7 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2864,10 +2864,8 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, if (pattern->hasType()) patternType = pattern->getType(); else { - TypeResolutionOptions options(TypeResolverContext::InExpression); - options |= TypeResolutionFlags::AllowUnspecifiedTypes; - options |= TypeResolutionFlags::AllowUnboundGenerics; - patternType = typeCheckPattern(pattern, DC, options); + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + patternType = typeCheckPattern(contextualPattern); } if (patternType->hasError()) { @@ -2995,11 +2993,9 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { return true; } - TypeResolutionOptions options(TypeResolverContext::InExpression); - options |= TypeResolutionFlags::AllowUnspecifiedTypes; - options |= TypeResolutionFlags::AllowUnboundGenerics; - Type patternType = TypeChecker::typeCheckPattern( - Stmt->getPattern(), DC, options); + auto contextualPattern = + ContextualPattern::forRawPattern(Stmt->getPattern(), DC); + Type patternType = TypeChecker::typeCheckPattern(contextualPattern); if (patternType->hasError()) { // FIXME: Handle errors better. Stmt->getPattern()->setType(ErrorType::get(ctx)); @@ -3221,10 +3217,8 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, // Check the pattern, it allows unspecified types because the pattern can // provide type information. - TypeResolutionOptions options(TypeResolverContext::InExpression); - options |= TypeResolutionFlags::AllowUnspecifiedTypes; - options |= TypeResolutionFlags::AllowUnboundGenerics; - Type patternType = TypeChecker::typeCheckPattern(pattern, dc, options); + auto contextualPattern = ContextualPattern::forRawPattern(pattern, dc); + Type patternType = TypeChecker::typeCheckPattern(contextualPattern); if (patternType->hasError()) { typeCheckPatternFailed(); continue; diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 465cf5aef4f32..ce079497dc447 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -703,8 +703,20 @@ static Type validateTypedPattern(TypeResolution resolution, return TL.getType(); } -Type TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, - TypeResolutionOptions options) { +Type TypeChecker::typeCheckPattern(ContextualPattern pattern) { + Pattern *P = pattern.getPattern(); + DeclContext *dc = pattern.getDeclContext(); + + TypeResolutionOptions options(pattern.getPatternBindingDecl() + ? TypeResolverContext::PatternBindingDecl + : TypeResolverContext::InExpression); + if (pattern.allowsInference()) { + options |= TypeResolutionFlags::AllowUnspecifiedTypes; + options |= TypeResolutionFlags::AllowUnboundGenerics; + } + if (!pattern.isTopLevel()) + options = options.withoutContext(); + auto &Context = dc->getASTContext(); switch (P->getKind()) { // Type-check paren patterns by checking the sub-pattern and @@ -716,7 +728,8 @@ Type TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, SP = PP->getSubPattern(); else SP = cast(P)->getSubPattern(); - Type subType = TypeChecker::typeCheckPattern(SP, dc, options); + Type subType = TypeChecker::typeCheckPattern( + pattern.forSubPattern(SP, /*retainTopLevel=*/true)); if (subType->hasError()) return ErrorType::get(Context); @@ -757,11 +770,10 @@ Type TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, bool hadError = false; SmallVector typeElts; - const auto elementOptions = options.withoutContext(); for (unsigned i = 0, e = tuplePat->getNumElements(); i != e; ++i) { TuplePatternElt &elt = tuplePat->getElement(i); - Pattern *pattern = elt.getPattern(); - Type subType = TypeChecker::typeCheckPattern(pattern, dc, elementOptions); + Type subType = TypeChecker::typeCheckPattern( + pattern.forSubPattern(elt.getPattern(), /*retainTopLevel=*/false)); if (subType->hasError()) hadError = true; diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index e1af901e4c457..b66c7ca2b7661 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -206,6 +206,8 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, // In particular, it's /not/ correct to check the PBD's DeclContext because // top-level variables in a script file are accessible from other files, // even though the PBD is inside a TopLevelCodeDecl. + auto contextualPattern = + ContextualPattern::forPatternBindingDecl(binding, entryNumber); TypeResolutionOptions options(TypeResolverContext::PatternBindingDecl); if (binding->isInitialized(entryNumber)) { @@ -214,8 +216,7 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, options |= TypeResolutionFlags::AllowUnboundGenerics; } - Type patternType = TypeChecker::typeCheckPattern( - pattern, binding->getDeclContext(), options); + Type patternType = TypeChecker::typeCheckPattern(contextualPattern); if (patternType->hasError()) { swift::setBoundVarsTypeError(pattern, Context); binding->setInvalid(); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index e899817b35a72..d4bab77dfc6d5 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -999,16 +999,11 @@ class TypeChecker final { /// Type check the given pattern. /// - /// \param P The pattern to type check. - /// \param dc The context in which type checking occurs. - /// \param options Options that control type resolution. - /// /// \returns the type of the pattern, which may be an error type if an /// unrecoverable error occurred. If the options permit it, the type may /// involve \c UnresolvedType (for patterns with no type information) and /// unbound generic types. - static Type typeCheckPattern(Pattern *P, DeclContext *dc, - TypeResolutionOptions options); + static Type typeCheckPattern(ContextualPattern pattern); static bool typeCheckCatchPattern(CatchStmt *S, DeclContext *dc); From cc11fc68385f7e8149fff815c1f89f0ba6b63435 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 3 Jan 2020 16:14:48 -0800 Subject: [PATCH 253/478] [Type checker] Introduce a request for computing the type of a pattern. Compute the "raw" type of a pattern via a request, caching the result of TypeChecker::typeCheckPattern(). Retain typeCheckPattern() as the general entrypoint for performing this computation so that other code doesn't need to change yet. --- include/swift/AST/TypeCheckRequests.h | 28 +++++++++++++++++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 3 +++ lib/Sema/TypeCheckPattern.cpp | 9 +++++++ 3 files changed, 40 insertions(+) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 1704b817d85c6..05391c166363e 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -20,6 +20,7 @@ #include "swift/AST/GenericSignature.h" #include "swift/AST/Type.h" #include "swift/AST/Evaluator.h" +#include "swift/AST/Pattern.h" #include "swift/AST/SimpleRequest.h" #include "swift/AST/TypeResolutionStage.h" #include "swift/Basic/AnyValue.h" @@ -33,6 +34,7 @@ namespace swift { class AbstractStorageDecl; class AccessorDecl; enum class AccessorKind; +class ContextualPattern; class DefaultArgumentExpr; class GenericParamList; class PrecedenceGroupDecl; @@ -1991,6 +1993,32 @@ class HasDynamicMemberLookupAttributeRequest } }; +/// Determines the type of a given pattern. +/// +/// Note that this returns the "raw" pattern type, which can involve +/// unresolved types and unbound generic types where type inference is +/// allowed. +class PatternTypeRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate( + Evaluator &evaluator, ContextualPattern pattern) const; + +public: + bool isCached() const { return true; } + + SourceLoc getNearestLoc() const { + return std::get<0>(getStorage()).getPattern()->getLoc(); + } +}; + // Allow AnyValue to compare two Type values, even though Type doesn't // support ==. template<> diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 2e9f6036e4d11..4df7737c807ee 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -216,3 +216,6 @@ SWIFT_REQUEST(TypeChecker, TypeWitnessRequest, SWIFT_REQUEST(TypeChecker, ValueWitnessRequest, Witness(NormalProtocolConformance *, ValueDecl *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, PatternTypeRequest, + Type(ContextualPattern), + Cached, HasNearestLocation) diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index ce079497dc447..dcefd3d015feb 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -24,6 +24,7 @@ #include "swift/AST/SourceFile.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/TypeCheckRequests.h" #include "llvm/Support/SaveAndRestore.h" #include using namespace swift; @@ -704,6 +705,14 @@ static Type validateTypedPattern(TypeResolution resolution, } Type TypeChecker::typeCheckPattern(ContextualPattern pattern) { + DeclContext *dc = pattern.getDeclContext(); + ASTContext &ctx = dc->getASTContext(); + return evaluateOrDefault( + ctx.evaluator, PatternTypeRequest{pattern}, ErrorType::get(ctx)); +} + +llvm::Expected PatternTypeRequest::evaluate( + Evaluator &evaluator, ContextualPattern pattern) const { Pattern *P = pattern.getPattern(); DeclContext *dc = pattern.getDeclContext(); From 2b09547c0713f91c1e3a999f0d809f80104d63ed Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 2 Jan 2020 22:53:33 -0800 Subject: [PATCH 254/478] [semantic-arc-opts] Use the LiveRange abstraction to find destroys instead of iterating over direct uses so we handle forwarding uses. Previously, we were incorrectly handling these test cases since we weren't properly finding destroys through forwarding instructions. I fixed that in a previous commit by changing the code here to only support load [copy] without forwarding instructions. In this commit, I change the code to instead use the LiveRange abstraction. The LiveRange abstraction already knows how to find destroys through forwarding instructions and has this destroy array already computed for us! So we get better runtime performance of code (due to the better opt) and better compile time (since we aren't computing the destroy list twice)! rdar://58289320 --- .../Mandatory/SemanticARCOpts.cpp | 73 +++++-------------- test/SILOptimizer/semantic-arc-opts.sil | 10 +-- 2 files changed, 19 insertions(+), 64 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp index 0744927d3ece1..9915714f5c575 100644 --- a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp @@ -36,25 +36,6 @@ STATISTIC(NumEliminatedInsts, "number of removed instructions"); STATISTIC(NumLoadCopyConvertedToLoadBorrow, "number of load_copy converted to load_borrow"); -//===----------------------------------------------------------------------===// -// Utility -//===----------------------------------------------------------------------===// - -static bool isConsumingOwnedUse(Operand *use) { - assert(use->get().getOwnershipKind() == ValueOwnershipKind::Owned); - - if (use->isTypeDependent()) - return false; - - // We know that a copy_value produces an @owned value. Look through all of - // our uses and classify them as either invalidating or not - // invalidating. Make sure that all of the invalidating ones are - // destroy_value since otherwise the live_range is not complete. - auto map = use->getOwnershipKindMap(); - auto constraint = map.getLifetimeConstraint(ValueOwnershipKind::Owned); - return constraint == UseLifetimeConstraint::MustBeInvalidated; -} - //===----------------------------------------------------------------------===// // Live Range Modeling //===----------------------------------------------------------------------===// @@ -325,7 +306,7 @@ struct SemanticARCOptVisitor FORWARDING_TERM(CondBranch) #undef FORWARDING_TERM - bool isWrittenTo(LoadInst *li, ArrayRef destroys); + bool isWrittenTo(LoadInst *li, const LiveRange &lr); bool processWorklist(); @@ -695,22 +676,20 @@ class StorageGuaranteesLoadVisitor { // The outer SemanticARCOptVisitor. SemanticARCOptVisitor &ARCOpt; - - // The original load instruction. - LoadInst *Load; - + + // The live range of the original load. + const LiveRange &liveRange; + // The current address being visited. SILValue currentAddress; Optional isWritten; - ArrayRef destroyValues; - public: StorageGuaranteesLoadVisitor(SemanticARCOptVisitor &arcOpt, LoadInst *load, - ArrayRef destroyValues) - : ARCOpt(arcOpt), Load(load), currentAddress(load->getOperand()), - destroyValues(destroyValues) {} + const LiveRange &liveRange) + : ARCOpt(arcOpt), liveRange(liveRange), + currentAddress(load->getOperand()) {} void answer(bool written) { currentAddress = nullptr; @@ -793,28 +772,11 @@ class StorageGuaranteesLoadVisitor llvm::copy(borrowInst->getUsersOfType(), std::back_inserter(baseEndBorrows)); - SmallVector valueDestroys; - for (auto *use : Load->getUses()) { - // If this load is not consuming, skip it. - if (!isConsumingOwnedUse(use)) { - continue; - } - - // Otherwise, if this isn't a destroy_value, we have a consuming use we - // don't understand. Return conservatively that this memory location may - // not be guaranteed. - auto *user = use->getUser(); - if (!isa(user)) { - return answer(true); - } - valueDestroys.emplace_back(user); - } - SmallPtrSet visitedBlocks; LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks()); // Returns true on success. So we invert. - bool foundError = - !checker.validateLifetime(baseObject, baseEndBorrows, valueDestroys); + bool foundError = !checker.validateLifetime(baseObject, baseEndBorrows, + liveRange.getDestroys()); return answer(foundError); } @@ -850,9 +812,9 @@ class StorageGuaranteesLoadVisitor SmallPtrSet visitedBlocks; LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks()); // Returns true on success. So we invert. - bool foundError = - !checker.validateLifetime(stack, destroyAddrs /*consuming users*/, - destroyValues /*non consuming users*/); + bool foundError = !checker.validateLifetime( + stack, destroyAddrs /*consuming users*/, + liveRange.getDestroys() /*non consuming users*/); return answer(foundError); } @@ -866,9 +828,8 @@ class StorageGuaranteesLoadVisitor } // namespace -bool SemanticARCOptVisitor::isWrittenTo(LoadInst *load, - ArrayRef destroys) { - StorageGuaranteesLoadVisitor visitor(*this, load, destroys); +bool SemanticARCOptVisitor::isWrittenTo(LoadInst *load, const LiveRange &lr) { + StorageGuaranteesLoadVisitor visitor(*this, load, lr); return visitor.doIt(); } @@ -892,8 +853,7 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) { // Then check if our address is ever written to. If it is, then we cannot use // the load_borrow because the stored value may be released during the loaded // value's live range. - auto destroyValues = lr.getDestroys(); - if (isWrittenTo(li, destroyValues)) + if (isWrittenTo(li, lr)) return false; // Ok, we can perform our optimization. Convert the load [copy] into a @@ -906,6 +866,7 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) { // to find the post-dominating block set of these destroy value to ensure that // we do not insert multiple end_borrow. assert(lifetimeFrontier.empty()); + auto destroyValues = lr.getDestroys(); ValueLifetimeAnalysis analysis(li, destroyValues); bool foundCriticalEdges = !analysis.computeFrontier( lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, diff --git a/test/SILOptimizer/semantic-arc-opts.sil b/test/SILOptimizer/semantic-arc-opts.sil index 278b8f8613efd..2c4e0e9bafeed 100644 --- a/test/SILOptimizer/semantic-arc-opts.sil +++ b/test/SILOptimizer/semantic-arc-opts.sil @@ -589,11 +589,8 @@ bb0(%x : @owned $ClassLet): return undef : $() } -// We do not support this today, but we will once forwarding is ignored when -// checking if the load [copy] is a dead live range. -// // CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase : -// CHECK: load [copy] +// CHECK: load_borrow // CHECK: } // end sil function 'dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase' sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase : $@convention(thin) (@owned ClassLet) -> () { bb0(%x : @owned $ClassLet): @@ -613,11 +610,8 @@ bb0(%x : @owned $ClassLet): return undef : $() } -// We do not support this today, but we will once forwarding is ignored when -// checking if the load [copy] is a dead live range. -// // CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2 : -// CHECK: load [copy] +// CHECK: load_borrow // CHECK: } // end sil function 'dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2' sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2 : $@convention(thin) (@owned ClassLet) -> () { bb0(%x : @owned $ClassLet): From 64971e7e55ec132d129a5cd1c1e5047711082836 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 3 Jan 2020 17:32:34 -0500 Subject: [PATCH 255/478] SILOptimizer: Fix speculative devirtualization to correctly use casted value We used to emit this: checked_cast_br [exact] %0 : $Foo to $Bar, bb1, bb2 bb1(%1 : $Bar): %2 = unchecked_ref_cast %0 : $Foo to %1 : $Bar apply ...(..., %2) br ... bb2: ... This is not ownership SIL-safe, because we're re-using the original operand, which will have already been consumed at that point. The more immediate problem here is that this is actually not safe when combined with other optimizations. Suppose that after the speculative devirtualization, our function is inlined into another function where the operand was an upcast. Now we have this code: %0 = upcast ... checked_cast_br [exact] %0 : $Foo to $Bar, bb1, bb2 bb1(%1 : $Bar): %2 = unchecked_ref_cast %0 : $Foo to %1 : $Bar apply ...(..., %2) br ... bb2: ... The SILCombiner will simplify the unchecked_ref_cast of the upcast into an unchecked_ref_cast of the upcast's original operand. At this point, we have an unchecked_ref_cast between two unrelated class types. While this means the block bb1 is unreachable, we might perform further optimizations on it before we run the cast optimizer and delete it altogether. In particular, the devirtualizer follows chains of reference cast instructions, and it will get very confused if it finds this invalid cast between unrelated types. Fixes , . --- .../Transforms/SpeculativeDevirtualizer.cpp | 16 ++-- test/SILOptimizer/devirt_speculate.swift | 86 +++++++++++++++---- test/SILOptimizer/devirt_speculative.sil | 1 - 3 files changed, 80 insertions(+), 23 deletions(-) diff --git a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp index effb3dfffa500..5c2fb108633a1 100644 --- a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp @@ -81,14 +81,20 @@ static SILBasicBlock *cloneEdge(TermInst *TI, unsigned SuccIndex) { } // A utility function for cloning the apply instruction. -static FullApplySite CloneApply(FullApplySite AI, SILBuilder &Builder) { +static FullApplySite CloneApply(FullApplySite AI, SILValue SelfArg, + SILBuilder &Builder) { // Clone the Apply. Builder.setCurrentDebugScope(AI.getDebugScope()); Builder.addOpenedArchetypeOperands(AI.getInstruction()); auto Args = AI.getArguments(); SmallVector Ret(Args.size()); - for (unsigned i = 0, e = Args.size(); i != e; ++i) - Ret[i] = Args[i]; + for (unsigned i = 0, e = Args.size(); i != e; ++i) { + if (i == e - 1 && SelfArg) { + Ret[i] = SelfArg; + } else { + Ret[i] = Args[i]; + } + } FullApplySite NAI; @@ -169,8 +175,8 @@ static FullApplySite speculateMonomorphicTarget(FullApplySite AI, SILValue DownCastedClassInstance = Iden->getArgument(0); // Copy the two apply instructions into the two blocks. - FullApplySite IdenAI = CloneApply(AI, IdenBuilder); - FullApplySite VirtAI = CloneApply(AI, VirtBuilder); + FullApplySite IdenAI = CloneApply(AI, DownCastedClassInstance, IdenBuilder); + FullApplySite VirtAI = CloneApply(AI, SILValue(), VirtBuilder); // See if Continue has a release on self as the instruction right after the // apply. If it exists, move it into position in the diamond. diff --git a/test/SILOptimizer/devirt_speculate.swift b/test/SILOptimizer/devirt_speculate.swift index f9a75f4f12de2..e45f573d1147a 100644 --- a/test/SILOptimizer/devirt_speculate.swift +++ b/test/SILOptimizer/devirt_speculate.swift @@ -10,44 +10,96 @@ public class Base { public init() {} public func foo() {} } + +@_optimize(none) +func blackHole(_: T) {} + class Sub1 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub2 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub3 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub4 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub5 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub6 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub7 : Base { - override func foo() {} + override func foo() { blackHole(self) } } // CHECK: @$s16devirt_speculate28testMaxNumSpeculativeTargetsyyAA4BaseCF -// CHECK: checked_cast_br [exact] %0 : $Base to Base -// CHECK: checked_cast_br [exact] %0 : $Base to Sub1 -// CHECK: checked_cast_br [exact] %0 : $Base to Sub2 -// CHECK: checked_cast_br [exact] %0 : $Base to Sub3 -// CHECK: checked_cast_br [exact] %0 : $Base to Sub4 -// CHECK: checked_cast_br [exact] %0 : $Base to Sub5 -// CHECK: checked_cast_br [exact] %0 : $Base to Sub6 +// CHECK: bb0(%0 : $Base): +// CHECK: checked_cast_br [exact] %0 : $Base to Base, bb2, bb3 + +// CHECK: bb2([[CASTED:%.*]]): +// CHECK: br bb1 + +// CHECK: bb3: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub1, bb4, bb5 + +// CHECK: bb4([[CASTED:%.*]] : $Sub1): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb5: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub2, bb6, bb7 + +// CHECK: bb6([[CASTED:%.*]] : $Sub2): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb7: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub3, bb8, bb9 + +// CHECK: bb8([[CASTED:%.*]] : $Sub3): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb9: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub4, bb10, bb11 + +// CHECK: bb10([[CASTED:%.*]] : $Sub4): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb11: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub5, bb12, bb13 + +// CHECK: bb12([[CASTED:%.*]] : $Sub5): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb13: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub6, bb14, bb15 + +// CHECK: bb14([[CASTED:%.*]] : $Sub6): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb15: // CHECK-NOT: checked_cast_br -// CHECK: %[[CM:[0-9]+]] = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () -// CHECK: apply %[[CM]](%0) : $@convention(method) (@guaranteed Base) -> () +// CHECK: %[[CM:[0-9]+]] = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () +// CHECK: apply %[[CM]](%0) : $@convention(method) (@guaranteed Base) -> () // YAML: Pass: sil-speculative-devirtualizer // YAML-NEXT: Name: sil.PartialSpecDevirt // YAML-NEXT: DebugLoc: // YAML-NEXT: File: {{.*}}/devirt_speculate.swift -// YAML-NEXT: Line: 66 +// YAML-NEXT: Line: 118 // YAML-NEXT: Column: 5 // YAML-NEXT: Function: 'testMaxNumSpeculativeTargets(_:)' // YAML-NEXT: Args: diff --git a/test/SILOptimizer/devirt_speculative.sil b/test/SILOptimizer/devirt_speculative.sil index 77ae33ce71d96..cac316601dc3c 100644 --- a/test/SILOptimizer/devirt_speculative.sil +++ b/test/SILOptimizer/devirt_speculative.sil @@ -145,7 +145,6 @@ bb0(%0: $Base2): // CHECK-NEXT: unreachable // CHECK: checked_cast_br // CHECK: function_ref @Sub_exit -// CHECK-NEXT: unchecked_ref_cast // CHECK-NEXT: apply // CHECK-NEXT: unreachable sil hidden [noinline] @test_devirt_of_noreturn_function : $@convention(thin) (@owned Base) -> () { From 61ac2534e79e9681c2da44081a2008229255341e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 3 Jan 2020 17:27:55 -0800 Subject: [PATCH 256/478] [Type checker] Make coercePatternToType return the new Pattern. Rather than mutating the parameter pattern in place and separately return whether an error occurred, return the new pattern or NULL if an error occurred. While here, switch over to ContextualPattern for the input. And get rid of that infernal "goto". --- lib/Sema/CSGen.cpp | 16 ++- lib/Sema/TypeCheckConstraints.cpp | 21 +-- lib/Sema/TypeCheckPattern.cpp | 205 +++++++++++++++++++----------- lib/Sema/TypeCheckStmt.cpp | 36 ++++-- lib/Sema/TypeCheckStorage.cpp | 8 +- lib/Sema/TypeChecker.h | 13 +- test/decl/var/variables.swift | 2 +- 7 files changed, 189 insertions(+), 112 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 90b131fe1bc3d..f51ec576cc4f0 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2259,6 +2259,7 @@ namespace { // or exhaustive catches. class FindInnerThrows : public ASTWalker { ConstraintSystem &CS; + DeclContext *DC; bool FoundThrow = false; std::pair walkToExprPre(Expr *expr) override { @@ -2344,12 +2345,12 @@ namespace { Type exnType = CS.getASTContext().getErrorDecl()->getDeclaredType(); if (!exnType) return false; - if (TypeChecker::coercePatternToType(pattern, - TypeResolution::forContextual(CS.DC), - exnType, - TypeResolverContext::InExpression)) { + auto contextualPattern = + ContextualPattern::forRawPattern(pattern, DC); + pattern = TypeChecker::coercePatternToType( + contextualPattern, exnType, TypeResolverContext::InExpression); + if (!pattern) return false; - } clause->setErrorPattern(pattern); return clause->isSyntacticallyExhaustive(); @@ -2385,7 +2386,8 @@ namespace { } public: - FindInnerThrows(ConstraintSystem &cs) : CS(cs) {} + FindInnerThrows(ConstraintSystem &cs, DeclContext *dc) + : CS(cs), DC(dc) {} bool foundThrow() { return FoundThrow; } }; @@ -2398,7 +2400,7 @@ namespace { if (!body) return false; - auto tryFinder = FindInnerThrows(CS); + auto tryFinder = FindInnerThrows(CS, expr); body->walk(tryFinder); return tryFinder.foundThrow(); } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index f3bbb92c23ed7..7b86e6248eca6 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2801,8 +2801,6 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, ? TypeResolverContext::EditorPlaceholderExpr : TypeResolverContext::InExpression; options |= TypeResolutionFlags::OverrideType; - options |= TypeResolutionFlags::AllowUnspecifiedTypes; - options |= TypeResolutionFlags::AllowUnboundGenerics; // FIXME: initTy should be the same as resultTy; now that typeCheckExpression() // returns a Type and not bool, we should be able to simplify the listener @@ -2812,8 +2810,12 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, return true; // Apply the solution to the pattern as well. - if (coercePatternToType(pattern, TypeResolution::forContextual(DC), initTy, - options, TypeLoc())) { + auto contextualPattern = + ContextualPattern::forRawPattern(pattern, DC); + if (auto coercedPattern = TypeChecker::coercePatternToType( + contextualPattern, initTy, options)) { + pattern = coercedPattern; + } else { return true; } } @@ -3063,13 +3065,11 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { Pattern *pattern = Stmt->getPattern(); TypeResolutionOptions options(TypeResolverContext::ForEachStmt); options |= TypeResolutionFlags::OverrideType; - options |= TypeResolutionFlags::AllowUnboundGenerics; - options |= TypeResolutionFlags::AllowUnspecifiedTypes; - if (TypeChecker::coercePatternToType(pattern, - TypeResolution::forContextual(DC), - InitType, options)) { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + pattern = TypeChecker::coercePatternToType(contextualPattern, + InitType, options); + if (!pattern) return nullptr; - } Stmt->setPattern(pattern); // Get the conformance of the sequence type to the Sequence protocol. @@ -3189,6 +3189,7 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, hadAnyFalsable = true; continue; } + assert(elt.getKind() != StmtConditionElement::CK_Boolean); // This is cleanup goop run on the various paths where type checking of the // pattern binding fails. diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index dcefd3d015feb..3d359a84fee94 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -711,6 +711,17 @@ Type TypeChecker::typeCheckPattern(ContextualPattern pattern) { ctx.evaluator, PatternTypeRequest{pattern}, ErrorType::get(ctx)); } +/// Apply the contextual pattern's context to the type resolution options. +static TypeResolutionOptions applyContextualPatternOptions( + TypeResolutionOptions options, ContextualPattern pattern) { + if (pattern.allowsInference()) { + options |= TypeResolutionFlags::AllowUnspecifiedTypes; + options |= TypeResolutionFlags::AllowUnboundGenerics; + } + + return options; +} + llvm::Expected PatternTypeRequest::evaluate( Evaluator &evaluator, ContextualPattern pattern) const { Pattern *P = pattern.getPattern(); @@ -719,12 +730,10 @@ llvm::Expected PatternTypeRequest::evaluate( TypeResolutionOptions options(pattern.getPatternBindingDecl() ? TypeResolverContext::PatternBindingDecl : TypeResolverContext::InExpression); - if (pattern.allowsInference()) { - options |= TypeResolutionFlags::AllowUnspecifiedTypes; - options |= TypeResolutionFlags::AllowUnboundGenerics; - } - if (!pattern.isTopLevel()) + options = applyContextualPatternOptions(options, pattern); + if (!pattern.isTopLevel()) { options = options.withoutContext(); + } auto &Context = dc->getASTContext(); switch (P->getKind()) { @@ -858,18 +867,20 @@ void implicitlyUntuplePatternIfApplicable(DiagnosticEngine &DE, } /// Perform top-down type coercion on the given pattern. -bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, - Type type, - TypeResolutionOptions options, - TypeLoc tyLoc) { -recur: +Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, + Type type, + TypeResolutionOptions options, + TypeLoc tyLoc) { if (tyLoc.isNull()) { tyLoc = TypeLoc::withoutLoc(type); } - auto dc = resolution.getDeclContext(); + auto P = pattern.getPattern(); + auto dc = pattern.getDeclContext(); auto &Context = dc->getASTContext(); auto &diags = Context.Diags; + + options = applyContextualPatternOptions(options, pattern); auto subOptions = options; subOptions.setContext(None); switch (P->getKind()) { @@ -887,42 +898,50 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, if (tupleType->getNumElements() == 1 && !tupleType->getElement(0).isVararg()) { auto elementTy = tupleType->getElementType(0); - if (coercePatternToType(sub, resolution, elementTy, subOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/true), elementTy, + subOptions); + if (!sub) + return nullptr; TuplePatternElt elt(sub); P = TuplePattern::create(Context, PP->getLParenLoc(), elt, PP->getRParenLoc()); if (PP->isImplicit()) P->setImplicit(); P->setType(type); - return false; + return P; } } } - - if (coercePatternToType(sub, resolution, type, subOptions)) - return true; + + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions); + if (!sub) + return nullptr; + PP->setSubPattern(sub); PP->setType(sub->getType()); - return false; + return P; } case PatternKind::Var: { auto VP = cast(P); Pattern *sub = VP->getSubPattern(); - if (coercePatternToType(sub, resolution, type, subOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions); + if (!sub) + return nullptr; VP->setSubPattern(sub); if (sub->hasType()) VP->setType(sub->getType()); - return false; + return P; } // If we see an explicit type annotation, coerce the sub-pattern to // that type. case PatternKind::Typed: { TypedPattern *TP = cast(P); - Type patternType = validateTypedPattern(resolution, TP, options); + Type patternType = TypeChecker::typeCheckPattern(pattern); bool hadError = false; if (!patternType->hasError()) { if (!type->isEqual(patternType) && !type->hasError()) { @@ -939,13 +958,15 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } Pattern *sub = TP->getSubPattern(); - hadError |= coercePatternToType(sub, resolution, type, - subOptions | TypeResolutionFlags::FromNonInferredPattern); - if (!hadError) { - TP->setSubPattern(sub); - TP->setType(sub->getType()); - } - return hadError; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, + subOptions | TypeResolutionFlags::FromNonInferredPattern); + if (!sub) + return nullptr; + + TP->setSubPattern(sub); + TP->setType(sub->getType()); + return P; } // For wildcard and name patterns, set the type. @@ -1012,11 +1033,11 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, .fixItInsertAfter(var->getNameLoc(), ": " + type->getWithoutParens()->getString()); } - return false; + return P; } case PatternKind::Any: P->setType(type); - return false; + return P; // We can match a tuple pattern with a tuple type. // TODO: permit implicit conversions? @@ -1027,12 +1048,15 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, // Sometimes a paren is just a paren. If the tuple pattern has a single // element, we can reduce it to a paren pattern. bool canDecayToParen = TP->getNumElements() == 1; - auto decayToParen = [&]() -> bool { + auto decayToParen = [&]() -> Pattern * { assert(canDecayToParen); Pattern *sub = TP->getElement(0).getPattern(); - if (TypeChecker::coercePatternToType(sub, resolution, type, subOptions)) - return true; - + sub = TypeChecker::coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, + subOptions); + if (!sub) + return nullptr; + if (TP->getLParenLoc().isValid()) { P = new (Context) ParenPattern(TP->getLParenLoc(), sub, TP->getRParenLoc(), @@ -1041,7 +1065,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } else { P = sub; } - return false; + return P; }; // The context type must be a tuple. @@ -1069,7 +1093,6 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, for (unsigned i = 0, e = TP->getNumElements(); i != e; ++i) { TuplePatternElt &elt = TP->getElement(i); - Pattern *pattern = elt.getPattern(); Type CoercionType; if (hadError) @@ -1085,14 +1108,21 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, elt.getLabel(), tupleTy->getElement(i).getName()); hadError = true; } - - hadError |= coercePatternToType(pattern, resolution, CoercionType, - options); + + auto sub = coercePatternToType( + pattern.forSubPattern(elt.getPattern(), /*retainTopLevel=*/false), + CoercionType, subOptions); + if (!sub) + return nullptr; + if (!hadError) - elt.setPattern(pattern); + elt.setPattern(sub); } - return hadError; + if (hadError) + return nullptr; + + return P; } // Coerce expressions by finding a '~=' operator that can compare the @@ -1108,7 +1138,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, EP->getSubExpr()->getSemanticsProvidingExpr())) { P = new (Context) BoolPattern(BLE->getLoc(), BLE->getValue()); P->setType(type); - return false; + return P; } } @@ -1122,10 +1152,15 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, DeclNameLoc(NLE->getLoc()), NoneEnumElement->createNameRef(), NoneEnumElement, nullptr, false); - return TypeChecker::coercePatternToType(P, resolution, type, options); + return TypeChecker::coercePatternToType( + pattern.forSubPattern(P, /*retainTopLevel=*/true), type, options); } } - return TypeChecker::typeCheckExprPattern(cast(P), dc, type); + + if (TypeChecker::typeCheckExprPattern(cast(P), dc, type)) + return nullptr; + + return P; } // Coerce an 'is' pattern by determining the cast kind. @@ -1133,9 +1168,10 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, auto IP = cast(P); // Type-check the type parameter. - TypeResolutionOptions paramOptions(TypeResolverContext::InExpression); + TypeResolutionOptions paramOptions(TypeResolverContext::InExpression); + TypeResolution resolution = TypeResolution::forContextual(dc); if (validateType(Context, IP->getCastTypeLoc(), resolution, paramOptions)) - return true; + return nullptr; auto castType = IP->getCastTypeLoc().getType(); @@ -1161,7 +1197,8 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } P = sub; - return coercePatternToType(P, resolution, type, options); + return coercePatternToType( + pattern.forSubPattern(P, /*retainTopLevle=*/true), type, options); } @@ -1176,7 +1213,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, IP->getCastTypeLoc().getSourceRange()); switch (castKind) { case CheckedCastKind::Unresolved: - return true; + return nullptr; case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: // If this is an 'as' pattern coercing between two different types, then @@ -1198,7 +1235,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, diags.diagnose(IP->getLoc(), diag::isa_collection_downcast_pattern_value_unimplemented, IP->getCastTypeLoc().getType()); - return false; + return P; } case CheckedCastKind::ValueCast: @@ -1209,13 +1246,17 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, // Coerce the subpattern to the destination type. if (Pattern *sub = IP->getSubPattern()) { - if (coercePatternToType(sub, resolution, IP->getCastTypeLoc().getType(), - subOptions|TypeResolutionFlags::FromNonInferredPattern)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), + IP->getCastTypeLoc().getType(), + subOptions|TypeResolutionFlags::FromNonInferredPattern); + if (!sub) + return nullptr; + IP->setSubPattern(sub); } - return false; + return P; } case PatternKind::EnumElement: { @@ -1252,7 +1293,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, /*message*/ StringRef()) .fixItReplace(EEP->getLoc(), Rename.str()); - return true; + return nullptr; } // If we have the original expression parse tree, try reinterpreting @@ -1261,7 +1302,9 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } else if (EEP->hasUnresolvedOriginalExpr()) { P = new (Context) ExprPattern(EEP->getUnresolvedOriginalExpr(), nullptr, nullptr); - goto recur; + return coercePatternToType( + pattern.forSubPattern(P, /*retainTopLevel=*/true), type, + options); } // If we have an optional type, let's try to see if the case @@ -1275,19 +1318,21 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, EEP->getName(), EEP->getLoc())) { P = new (Context) OptionalSomePattern(EEP, EEP->getEndLoc(), /*implicit*/true); - return coercePatternToType(P, resolution, type, options); + return coercePatternToType( + pattern.forSubPattern(P, /*retainTopLevel=*/true), type, + options); } else { diags.diagnose(EEP->getLoc(), diag::enum_element_pattern_member_not_found, EEP->getName(), type); - return true; + return nullptr; } } } } if (!elt) - return true; + return nullptr; enumTy = type; } else { @@ -1309,7 +1354,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } else { diags.diagnose(EEP->getLoc(), diag::ambiguous_enum_pattern_type, parentTy, type); - return true; + return nullptr; } } // Otherwise, see if we can introduce a cast pattern to get from an @@ -1323,7 +1368,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, nullptr, SourceRange()); // If the cast failed, we can't resolve the pattern. if (foundCastKind < CheckedCastKind::First_Resolved) - return true; + return nullptr; // Otherwise, we can type-check as the enum type, and insert a cast // from the outer pattern type. @@ -1333,7 +1378,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, diags.diagnose(EEP->getLoc(), diag::enum_element_pattern_not_member_of_enum, EEP->getName(), type); - return true; + return nullptr; } } @@ -1348,7 +1393,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, diags.diagnose(EEP->getLoc(), diag::enum_element_pattern_assoc_values_remove) .fixItRemove(sub->getSourceRange()); - return true; + return nullptr; } Type elementType; @@ -1363,8 +1408,12 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, ::implicitlyUntuplePatternIfApplicable(Context.Diags, sub, elementType); - if (coercePatternToType(sub, resolution, elementType, newSubOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, + newSubOptions); + if (!sub) + return nullptr; + EEP->setSubPattern(sub); } else if (argType) { // Else if the element pattern has no sub-pattern but the element type has @@ -1394,8 +1443,11 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, auto newSubOptions = subOptions; newSubOptions.setContext(TypeResolverContext::EnumPatternPayload); newSubOptions |= TypeResolutionFlags::FromNonInferredPattern; - if (coercePatternToType(sub, resolution, elementType, newSubOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, + newSubOptions); + if (!sub) + return nullptr; EEP->setSubPattern(sub); } @@ -1417,7 +1469,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, P = isPattern; } - return false; + return P; } case PatternKind::OptionalSome: { @@ -1434,12 +1486,12 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } diags.diagnose(loc, diagID, type); - return true; + return nullptr; } EnumElementDecl *elementDecl = Context.getOptionalSomeDecl(); if (!elementDecl) - return true; + return nullptr; OP->setElementDecl(elementDecl); @@ -1447,21 +1499,24 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, auto newSubOptions = subOptions; newSubOptions.setContext(TypeResolverContext::EnumPatternPayload); newSubOptions |= TypeResolutionFlags::FromNonInferredPattern; - if (coercePatternToType(sub, resolution, elementType, newSubOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, + newSubOptions); + if (!sub) + return nullptr; + OP->setSubPattern(sub); OP->setType(type); - return false; + return P; } case PatternKind::Bool: P->setType(type); - return false; + return P; } llvm_unreachable("bad pattern kind!"); } - /// Coerce the specified parameter list of a ClosureExpr to the specified /// contextual type. void TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index ac189711f12a8..d8e0ea2eba617 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -931,12 +931,21 @@ class StmtChecker : public StmtVisitor { } pattern = newPattern; + // Coerce the pattern to the subject's type. - TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); - if (!subjectType || - TypeChecker::coercePatternToType(pattern, - TypeResolution::forContextual(DC), - subjectType, patternOptions)) { + bool coercionError = false; + if (subjectType) { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); + auto coercedPattern = TypeChecker::coercePatternToType( + contextualPattern, subjectType, patternOptions); + if (coercedPattern) + pattern = coercedPattern; + else + coercionError = true; + } + + if (!subjectType || coercionError) { limitExhaustivityChecks = true; // If that failed, mark any variables binding pieces of the pattern @@ -1340,10 +1349,19 @@ bool TypeChecker::typeCheckCatchPattern(CatchStmt *S, DeclContext *DC) { pattern = newPattern; // Coerce the pattern to the exception type. - TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); - if (!exnType || - coercePatternToType(pattern, TypeResolution::forContextual(DC), exnType, - patternOptions)) { + bool coercionError = false; + if (exnType) { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); + auto coercedPattern = coercePatternToType( + contextualPattern, exnType, patternOptions); + if (coercedPattern) + pattern = coercedPattern; + else + coercionError = true; + } + + if (!exnType || coercionError) { // If that failed, be sure to give the variables error types // before we type-check the guard. (This will probably kill // most of the type-checking, but maybe not.) diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index b66c7ca2b7661..e49b327647adf 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -256,9 +256,11 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, } } else { // Coerce the pattern to the computed type. - auto resolution = TypeResolution::forContextual(binding->getDeclContext()); - if (TypeChecker::coercePatternToType(pattern, resolution, - patternType, options)) { + if (auto newPattern = TypeChecker::coercePatternToType( + contextualPattern, patternType, + TypeResolverContext::PatternBindingDecl)) { + pattern = newPattern; + } else { binding->setInvalid(); pattern->setType(ErrorType::get(Context)); return &pbe; diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index d4bab77dfc6d5..13a5aac4f5ba2 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1009,15 +1009,14 @@ class TypeChecker final { /// Coerce a pattern to the given type. /// - /// \param P The pattern, which may be modified by this coercion. - /// \param resolution The type resolution. + /// \param pattern The contextual pattern. /// \param type the type to coerce the pattern to. - /// \param options Options describing how to perform this coercion. + /// \param options Options that control the coercion. /// - /// \returns true if an error occurred, false otherwise. - static bool coercePatternToType(Pattern *&P, TypeResolution resolution, Type type, - TypeResolutionOptions options, - TypeLoc tyLoc = TypeLoc()); + /// \returns the coerced pattern, or nullptr if the coercion failed. + static Pattern *coercePatternToType(ContextualPattern pattern, Type type, + TypeResolutionOptions options, + TypeLoc tyLoc = TypeLoc()); static bool typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, Type type); diff --git a/test/decl/var/variables.swift b/test/decl/var/variables.swift index a355deabd1c17..0acd1a1bec08e 100644 --- a/test/decl/var/variables.swift +++ b/test/decl/var/variables.swift @@ -96,7 +96,7 @@ func tuplePatternDestructuring(_ x : Int, y : Int) { _ = i+j // QoI: type variable reconstruction failing for tuple types - let (x: g1, a: h1) = (b: x, a: y) // expected-error {{tuple type '(b: Int, a: Int)' is not convertible to tuple type '(x: Int, a: Int)'}} + let (x: g1, a: h1) = (b: x, a: y) // expected-error {{cannot convert value of type '(b: Int, a: Int)' to specified type '(x: Int, a: Int)'}} } // Crash while compiling attached test-app. From 6c74b33f6005be61305ca63353ab794b58717281 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 3 Jan 2020 17:37:06 -0800 Subject: [PATCH 257/478] Minor test case fixes --- test/Parse/ConditionalCompilation/switch_case.swift | 1 + test/Parse/matching_patterns.swift | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Parse/ConditionalCompilation/switch_case.swift b/test/Parse/ConditionalCompilation/switch_case.swift index 822fa94f99674..941de52907719 100644 --- a/test/Parse/ConditionalCompilation/switch_case.swift +++ b/test/Parse/ConditionalCompilation/switch_case.swift @@ -209,6 +209,7 @@ func foo(x: E, intVal: Int) { fallthrough // expected-error {{'fallthrough' from a case which doesn't bind variable 'val'}} #if ENABLE_C case let val: + _ = val break #endif case 2: diff --git a/test/Parse/matching_patterns.swift b/test/Parse/matching_patterns.swift index d32ab35273643..39cadef2359ec 100644 --- a/test/Parse/matching_patterns.swift +++ b/test/Parse/matching_patterns.swift @@ -333,6 +333,6 @@ case (_?)?: break // expected-warning {{case is already handled by previous patt let (responseObject: Int?) = op1 // expected-error @-1 {{expected ',' separator}} {{25-25=,}} // expected-error @-2 {{expected pattern}} -// expected-error @-3 {{expression type 'Int?' is ambiguous without more context}} +// expected-error @-3 {{cannot convert value of type 'Int?' to specified type '(responseObject: _)'}} From 3fda9059e1efcc422711a2db1b3e6ead425d87ce Mon Sep 17 00:00:00 2001 From: Cruz Date: Sat, 4 Jan 2020 14:01:31 +0900 Subject: [PATCH 258/478] [Docs] Update `TypeChecker.rst` document with lower cased of optional case --- docs/TypeChecker.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/TypeChecker.rst b/docs/TypeChecker.rst index dee4f32198764..a8498a9007ef4 100644 --- a/docs/TypeChecker.rst +++ b/docs/TypeChecker.rst @@ -370,12 +370,12 @@ guesswork. However, we note that the type of an enum member actually has a regular structure. For example, consider the ``Optional`` type:: enum Optional { - case None - case Some(T) + case none + case some(T) } -The type of ``Optional.None`` is ``Optional``, while the type of -``Optional.Some`` is ``(T) -> Optional``. In fact, the +The type of ``Optional.none`` is ``Optional``, while the type of +``Optional.some`` is ``(T) -> Optional``. In fact, the type of an enum element can have one of two forms: it can be ``T0``, for an enum element that has no extra data, or it can be ``T2 -> T0``, where ``T2`` is the data associated with the enum element. For the From ff64262cc9a4016e956bc70411eef369a95cab3e Mon Sep 17 00:00:00 2001 From: David Zarzycki Date: Sat, 4 Jan 2020 08:19:43 -0500 Subject: [PATCH 259/478] [CMake] Add missing dependency Found via LLVM's BUILD_SHARED_LIBS build option. --- unittests/ClangImporter/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/unittests/ClangImporter/CMakeLists.txt b/unittests/ClangImporter/CMakeLists.txt index 4f86c3da90e39..f6041b16f2760 100644 --- a/unittests/ClangImporter/CMakeLists.txt +++ b/unittests/ClangImporter/CMakeLists.txt @@ -7,4 +7,5 @@ target_link_libraries(SwiftClangImporterTests swiftClangImporter swiftParse swiftAST + LLVMBitstreamReader ) From 477763e7ed483d6cfdfbaf9ff18e2abe222f7d6b Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Sat, 4 Jan 2020 06:53:32 -0800 Subject: [PATCH 260/478] DivideRoundingUp --- lib/Driver/Compilation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 608cdfe2bd222..bb21d18282a66 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -1366,7 +1366,7 @@ namespace driver { // be compiled in 2 batches. Integer division yields 26/25 = 1 batch, but // a single batch of 26 exceeds the limit. The calculation must round up, // which can be calculated using: `(x + y - 1) / y` - auto DivideUp = [](size_t Num, size_t Div) -> size_t { + auto DivideRoundingUp = [](size_t Num, size_t Div) -> size_t { return (Num + Div - 1) / Div; }; @@ -1374,7 +1374,7 @@ namespace driver { size_t NumTasks = TQ->getNumberOfParallelTasks(); size_t NumFiles = PendingExecution.size(); size_t SizeLimit = Comp.getBatchSizeLimit().getValueOr(DefaultSizeLimit); - return std::max(NumTasks, DivideUp(NumFiles, SizeLimit)); + return std::max(NumTasks, DivideRoundingUp(NumFiles, SizeLimit)); } /// Select jobs that are batch-combinable from \c PendingExecution, combine From 76373b6c7469106ac68aca61a7f60e1e036f4ec5 Mon Sep 17 00:00:00 2001 From: "Kita, Maksim" Date: Sat, 4 Jan 2020 20:20:41 +0300 Subject: [PATCH 261/478] SR-11889: Fixed code review issues --- unittests/AST/DiagnosticConsumerTests.cpp | 153 +++++++++++----------- 1 file changed, 77 insertions(+), 76 deletions(-) diff --git a/unittests/AST/DiagnosticConsumerTests.cpp b/unittests/AST/DiagnosticConsumerTests.cpp index d3d24a3a8098a..77032b705a935 100644 --- a/unittests/AST/DiagnosticConsumerTests.cpp +++ b/unittests/AST/DiagnosticConsumerTests.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/AST/DiagnosticConsumer.h" +#include "swift/Basic/Located.h" #include "swift/Basic/SourceManager.h" #include "gtest/gtest.h" @@ -37,7 +38,7 @@ namespace { void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override { ASSERT_FALSE(expected.empty()); - EXPECT_EQ(std::make_pair(Info.Loc, Info.FormatString), expected.front()); + EXPECT_EQ(Located(Info.FormatString, Info.Loc), expected.front()); expected.erase(expected.begin()); } @@ -83,7 +84,7 @@ TEST(FileSpecificDiagnosticConsumer, InvalidLocDiagsGoToEveryConsumer) { (void)sourceMgr.addMemBufferCopy("abcde", "A"); (void)sourceMgr.addMemBufferCopy("vwxyz", "B"); - ExpectedDiagnostic expected[] = { {SourceLoc(), "dummy"} }; + ExpectedDiagnostic expected[] = { Located("dummy", SourceLoc()) }; auto consumerA = llvm::make_unique( nullptr, expected); auto consumerUnaffiliated = llvm::make_unique( @@ -116,14 +117,14 @@ TEST(FileSpecificDiagnosticConsumer, ErrorsWithLocationsGoToExpectedConsumers) { SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "front"}, - {middleOfA, "middle"}, - {backOfA, "back"}, + {"front", frontOfA}, + {"middle", middleOfA}, + {"back", backOfA}, }; ExpectedDiagnostic expectedB[] = { - {frontOfB, "front"}, - {middleOfB, "middle"}, - {backOfB, "back"} + {"front", frontOfB}, + {"middle", middleOfB}, + {"back", backOfB} }; auto consumerA = llvm::make_unique( @@ -170,17 +171,17 @@ TEST(FileSpecificDiagnosticConsumer, SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "front"}, - {frontOfB, "front"}, - {middleOfA, "middle"}, - {middleOfB, "middle"}, - {backOfA, "back"}, - {backOfB, "back"} + {"front", frontOfA}, + {"front", frontOfB}, + {"middle", middleOfA}, + {"middle", middleOfB}, + {"back", backOfA}, + {"back", backOfB} }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "front"}, - {middleOfB, "middle"}, - {backOfB, "back"} + {"front", frontOfB}, + {"middle", middleOfB}, + {"back", backOfB} }; auto consumerA = llvm::make_unique( @@ -221,14 +222,14 @@ TEST(FileSpecificDiagnosticConsumer, WarningsAndRemarksAreTreatedLikeErrors) { SourceLoc frontOfB = sourceMgr.getLocForBufferStart(bufferB); ExpectedDiagnostic expectedA[] = { - {frontOfA, "warning"}, - {frontOfB, "warning"}, - {frontOfA, "remark"}, - {frontOfB, "remark"}, + {"warning", frontOfA}, + {"warning", frontOfB}, + {"remark", frontOfA}, + {"remark", frontOfB}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "warning"}, - {frontOfB, "remark"}, + {"warning", frontOfB}, + {"remark", frontOfB}, }; auto consumerA = llvm::make_unique( @@ -272,20 +273,20 @@ TEST(FileSpecificDiagnosticConsumer, NotesAreAttachedToErrors) { SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "error"}, - {middleOfA, "note"}, - {backOfA, "note"}, - {frontOfB, "error"}, - {middleOfB, "note"}, - {backOfB, "note"}, - {frontOfA, "error"}, - {middleOfA, "note"}, - {backOfA, "note"}, + {"error", frontOfA}, + {"note", middleOfA}, + {"note", backOfA}, + {"error", frontOfB}, + {"note", middleOfB}, + {"note", backOfB}, + {"error", frontOfA}, + {"note", middleOfA}, + {"note", backOfA}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "error"}, - {middleOfB, "note"}, - {backOfB, "note"}, + {"error", frontOfB}, + {"note", middleOfB}, + {"note", backOfB}, }; auto consumerA = llvm::make_unique( @@ -335,20 +336,20 @@ TEST(FileSpecificDiagnosticConsumer, NotesAreAttachedToWarningsAndRemarks) { SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "warning"}, - {middleOfA, "note"}, - {backOfA, "note"}, - {frontOfB, "warning"}, - {middleOfB, "note"}, - {backOfB, "note"}, - {frontOfA, "remark"}, - {middleOfA, "note"}, - {backOfA, "note"}, + {"warning", frontOfA}, + {"note", middleOfA}, + {"note", backOfA}, + {"warning", frontOfB}, + {"note", middleOfB}, + {"note", backOfB}, + {"remark", frontOfA}, + {"note", middleOfA}, + {"note", backOfA}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "warning"}, - {middleOfB, "note"}, - {backOfB, "note"}, + {"warning", frontOfB}, + {"note", middleOfB}, + {"note", backOfB}, }; auto consumerA = llvm::make_unique( @@ -401,17 +402,17 @@ TEST(FileSpecificDiagnosticConsumer, NotesAreAttachedToErrorsEvenAcrossFiles) { SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "error"}, - {middleOfB, "note"}, - {backOfA, "note"}, - {frontOfA, "error"}, - {middleOfB, "note"}, - {backOfA, "note"}, + {"error", frontOfA}, + {"note", middleOfB}, + {"note", backOfA}, + {"error", frontOfA}, + {"note", middleOfB}, + {"note", backOfA}, }; ExpectedDiagnostic expectedB[] = { - {frontOfB, "error"}, - {middleOfA, "note"}, - {backOfB, "note"}, + {"error", frontOfB}, + {"note", middleOfA}, + {"note", backOfB}, }; auto consumerA = llvm::make_unique( @@ -462,20 +463,20 @@ TEST(FileSpecificDiagnosticConsumer, SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "error"}, - {middleOfB, "note"}, - {backOfA, "note"}, - {frontOfB, "error"}, - {middleOfA, "note"}, - {backOfB, "note"}, - {frontOfA, "error"}, - {middleOfB, "note"}, - {backOfA, "note"}, + {"error", frontOfA}, + {"note", middleOfB}, + {"note", backOfA}, + {"error", frontOfB}, + {"note", middleOfA}, + {"note", backOfB}, + {"error", frontOfA}, + {"note", middleOfB}, + {"note", backOfA}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "error"}, - {middleOfA, "note"}, - {backOfB, "note"}, + {"error", frontOfB}, + {"note", middleOfA}, + {"note", backOfB}, }; auto consumerA = llvm::make_unique( @@ -522,16 +523,16 @@ TEST(FileSpecificDiagnosticConsumer, SourceLoc frontOfB = sourceMgr.getLocForBufferStart(bufferB); ExpectedDiagnostic expectedA[] = { - {frontOfA, "error"}, - {SourceLoc(), "note"}, - {frontOfB, "error"}, - {SourceLoc(), "note"}, - {frontOfA, "error"}, - {SourceLoc(), "note"}, + {"error", frontOfA}, + {"note", SourceLoc()}, + {"error", frontOfB}, + {"note", SourceLoc()}, + {"error", frontOfA}, + {"note", SourceLoc()}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "error"}, - {SourceLoc(), "note"}, + {"error", frontOfB}, + {"note", SourceLoc()}, }; auto consumerA = llvm::make_unique( From 533d9acf73f3bcf55251c25b9d39d0eeeba78b6c Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sat, 4 Jan 2020 15:54:46 -0300 Subject: [PATCH 262/478] [CSSimplify] Fallback to contextual mismatch in repair failures for CoerceExpr --- lib/Sema/CSSimplify.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 9c05cf2cd9e20..fcc12d21892ef 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2823,6 +2823,16 @@ bool ConstraintSystem::repairFailures( conversionsOrFixes.push_back(coerceToCheckCastFix); return true; } + + // If it has a deep equality restriction defer the diagnostic to a + // GenericMismatch fix. + if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality)) { + return false; + } + + auto *fix = ContextualMismatch::create(*this, lhs, rhs, + getConstraintLocator(locator)); + conversionsOrFixes.push_back(fix); } // This could be: From 49e8eee3e5f5c652d584d39d860d631bbeb51a96 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sat, 4 Jan 2020 15:58:52 -0300 Subject: [PATCH 263/478] [CSDiagnostics] Handle CoercionExpr empty path in ContextualMismatch --- lib/Sema/CSDiagnostics.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index d01349c410e5c..9c84d967ed6c1 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1853,6 +1853,22 @@ bool ContextualFailure::diagnoseAsError() { getFromType(), getToType()); return true; } + + if (auto *coerceExpr = dyn_cast(anchor)) { + auto fromType = getFromType(); + auto toType = getType(coerceExpr->getCastTypeLoc()); + auto diagnostic = + getDiagnosticFor(CTP_CoerceOperand, + /*forProtocol=*/toType->isAnyExistentialType()); + + auto diag = + emitDiagnostic(anchor->getLoc(), *diagnostic, fromType, toType); + diag.highlight(anchor->getSourceRange()); + + (void)tryFixIts(diag); + + return true; + } return false; } From 6093bafd8e97ba2c7c8f35043359e23e8a80c7c0 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sat, 4 Jan 2020 16:01:06 -0300 Subject: [PATCH 264/478] [tests] Fix diagnostic (TODO) --- test/type/subclass_composition.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/type/subclass_composition.swift b/test/type/subclass_composition.swift index 69ed053d87cb5..b9567ef0b3b36 100644 --- a/test/type/subclass_composition.swift +++ b/test/type/subclass_composition.swift @@ -105,8 +105,7 @@ func basicSubtyping( let _: Derived = baseAndP2 // expected-error {{cannot convert value of type 'Base & P2' to specified type 'Derived'}} let _: Derived & P2 = baseAndP2 // expected-error {{value of type 'Base & P2' does not conform to specified type 'Derived & P2'}} - // TODO(diagnostics): Diagnostic regression, better message is `value of type 'Unrelated' does not conform to 'Derived & P2' in coercion` - let _ = Unrelated() as Derived & P2 // expected-error {{cannot convert value of type 'Unrelated' to type 'Derived' in coercion}} + let _ = Unrelated() as Derived & P2 // expected-error {{value of type 'Unrelated' does not conform to 'Derived & P2' in coercion}} let _ = Unrelated() as? Derived & P2 // expected-warning {{always fails}} let _ = baseAndP2 as Unrelated // expected-error {{cannot convert value of type 'Base & P2' to type 'Unrelated' in coercion}} let _ = baseAndP2 as? Unrelated // expected-warning {{always fails}} From 6fffe724f2ac21ed775110b8bf0c2401ebe7bdb4 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Mon, 23 Dec 2019 21:51:28 -0800 Subject: [PATCH 265/478] Fine-grained and driver fixes. Restructure fine-grained-dependencies to enable unit testing Get frontend to emit correct swiftdeps file (fine-grained when needed) and only emit dot file for -emit-fine-grained-dependency-sourcefile-dot-files Use deterministic order for more information outputs. Set EnableFineGrainedDependencies consistently in frontend. Tolerate errors that result in null getExtendedNominal() Fix memory issue by removing node everywhere. Break up print routine Be more verbose so it will compile on Linux. Sort batchable jobs, too. --- include/swift/AST/FineGrainedDependencies.h | 64 ++- include/swift/Basic/LangOptions.h | 10 +- include/swift/Driver/Action.h | 20 + include/swift/Driver/Compilation.h | 12 + .../Driver/FineGrainedDependencyDriverGraph.h | 146 +++--- include/swift/Driver/Job.h | 9 + include/swift/Option/Options.td | 11 +- lib/AST/FineGrainedDependencies.cpp | 52 ++- ...endenciesSourceFileDepGraphConstructor.cpp | 431 +++++++++++++----- lib/Driver/Compilation.cpp | 97 ++-- lib/Driver/Driver.cpp | 3 +- .../FineGrainedDependencyDriverGraph.cpp | 213 ++++++--- lib/Driver/Job.cpp | 5 + lib/Driver/ToolChain.cpp | 51 +-- lib/Driver/ToolChains.cpp | 8 + lib/Frontend/CompilerInvocation.cpp | 10 +- lib/FrontendTool/FrontendTool.cpp | 4 +- ...h_mode_dependencies_make_wrong_order.swift | 12 +- 18 files changed, 813 insertions(+), 345 deletions(-) diff --git a/include/swift/AST/FineGrainedDependencies.h b/include/swift/AST/FineGrainedDependencies.h index 60ef8b24a7bdf..19bfd16c05ef6 100644 --- a/include/swift/AST/FineGrainedDependencies.h +++ b/include/swift/AST/FineGrainedDependencies.h @@ -325,7 +325,7 @@ class BiIndexedTwoStageMap { /// Write out the .swiftdeps file for a frontend compilation of a primary file. bool emitReferenceDependencies(DiagnosticEngine &diags, SourceFile *SF, const DependencyTracker &depTracker, - StringRef outputPath); + StringRef outputPath, bool alsoEmitDotFile); //============================================================================== // MARK: Enums //============================================================================== @@ -438,8 +438,8 @@ class DependencyKey { name() {} /// For constructing a key in the frontend. - DependencyKey(NodeKind kind, DeclAspect aspect, std::string context, - std::string name) + DependencyKey(NodeKind kind, DeclAspect aspect, const std::string &context, + const std::string &name) : kind(kind), aspect(aspect), context(context), name(name) { assert(verify()); } @@ -449,7 +449,7 @@ class DependencyKey { StringRef getContext() const { return context; } StringRef getName() const { return name; } - StringRef getSwiftDepsFromSourceFileProvide() const { + StringRef getSwiftDepsFromASourceFileProvideNodeKey() const { assert(getKind() == NodeKind::sourceFileProvide && "Receiver must be sourceFileProvide."); return getName(); @@ -494,11 +494,19 @@ class DependencyKey { static std::string computeNameForProvidedEntity(Entity); /// Given some type of depended-upon entity create the key. - template - static DependencyKey createDependedUponKey(const Entity &); + static DependencyKey createDependedUponKey(StringRef mangledHolderName, + StringRef memberBaseName); + + template + static DependencyKey createDependedUponKey(StringRef); + + static DependencyKey + createTransitiveKeyForWholeSourceFile(StringRef swiftDeps); std::string humanReadableName() const; + StringRef aspectName() const { return DeclAspectNames[size_t(aspect)]; } + void dump(llvm::raw_ostream &os) const { os << asString() << "\n"; } SWIFT_DEBUG_DUMP { dump(llvm::errs()); } @@ -535,6 +543,13 @@ struct std::hash { } }; +namespace swift { +namespace fine_grained_dependencies { +using ContextNameFingerprint = + std::tuple>; +} +} // namespace swift + //============================================================================== // MARK: DepGraphNode //============================================================================== @@ -684,6 +699,9 @@ class SourceFileDepGraphNode : public DepGraphNode { } /// Record the sequence number, \p n, of another use. + /// The relationship between an interface and its implementation is NOT + /// included here. See \c + /// SourceFileDepGraph::findExistingNodePairOrCreateAndAddIfNew. void addDefIDependUpon(size_t n) { if (n != getSequenceNumber()) defsIDependUpon.insert(n); @@ -727,6 +745,25 @@ class SourceFileDepGraph { SourceFileDepGraph(const SourceFileDepGraph &g) = delete; SourceFileDepGraph(SourceFileDepGraph &&g) = default; + /// Simulate loading for unit testing: + /// \param swiftDepsFileName The name of the swiftdeps file of the phony job + /// \param includePrivateDeps Whether the graph includes intra-file arcs + /// \param hadCompilationError Simulate a compilation error + /// \param interfaceHash The interface hash of the simulated graph + /// \param simpleNamesByRDK A map of vectors of names keyed by reference + /// dependency key \param compoundNamesByRDK A map of (mangledHolder, + /// baseName) pairs keyed by reference dependency key. For single-name + /// dependencies, an initial underscore indicates that the name does not + /// cascade. For compound names, it is the first name, the holder which + /// indicates non-cascading. For member names, an initial underscore indicates + /// file-privacy. + static SourceFileDepGraph + simulateLoad(std::string swiftDepsFileName, const bool includePrivateDeps, + const bool hadCompilationError, std::string interfaceHash, + llvm::StringMap> simpleNamesByRDK, + llvm::StringMap>> + compoundNamesByRDK); + /// Nodes are owned by the graph. ~SourceFileDepGraph() { forEachNode([&](SourceFileDepGraphNode *n) { delete n; }); @@ -746,7 +783,7 @@ class SourceFileDepGraph { InterfaceAndImplementationPair getSourceFileNodePair() const; - StringRef getSwiftDepsFromSourceFileProvide() const; + StringRef getSwiftDepsOfJobThatProducedThisGraph() const; std::string getGraphID() const { return getSourceFileNodePair().getInterface()->getKey().humanReadableName(); @@ -770,12 +807,13 @@ class SourceFileDepGraph { /// The frontend creates a pair of nodes for every tracked Decl and the source /// file itself. InterfaceAndImplementationPair - findExistingNodePairOrCreateAndAddIfNew(NodeKind k, StringRef context, - StringRef name, - Optional fingerprint); + findExistingNodePairOrCreateAndAddIfNew( + NodeKind k, const ContextNameFingerprint &contextNameFingerprint); - SourceFileDepGraphNode *findExistingNodeOrCreateIfNew( - DependencyKey key, Optional fingerprint, bool isProvides); + SourceFileDepGraphNode * + findExistingNodeOrCreateIfNew(DependencyKey key, + const Optional &fingerprint, + bool isProvides); /// \p Use is the Node that must be rebuilt when \p def changes. /// Record that fact in the graph. @@ -892,7 +930,7 @@ template class DotFileEmitter { } void emitArcs() { g.forEachArc([&](const NodeT *def, const NodeT *use) { - if (includeGraphArc(use, def)) + if (includeGraphArc(def, use)) emitGraphArc(def, use); }); } diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 8d24bd41d831b..87ab90fe2984a 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -282,10 +282,14 @@ namespace swift { /// Whether to verify the parsed syntax tree and emit related diagnostics. bool VerifySyntaxTree = false; - /// Scaffolding to permit experimentation with finer-grained dependencies - /// and faster rebuilds. + /// Emit the newer, finer-grained swiftdeps file. Eventually will support + /// faster rebuilds. bool EnableFineGrainedDependencies = false; - + + /// When using fine-grained dependencies, emit dot files for every swiftdeps + /// file. + bool EmitFineGrainedDependencySourcefileDotFiles = false; + /// To mimic existing system, set to false. /// To experiment with including file-private and private dependency info, /// set to true. diff --git a/include/swift/Driver/Action.h b/include/swift/Driver/Action.h index 4dae1eb336ba0..3f4e5c4edec14 100644 --- a/include/swift/Driver/Action.h +++ b/include/swift/Driver/Action.h @@ -168,6 +168,26 @@ class CompileJobAction : public JobAction { static bool classof(const Action *A) { return A->getKind() == Action::Kind::CompileJob; } + + /// Return a _single_ TY_Swift InputAction, if one exists; + /// if 0 or >1 such inputs exist, return nullptr. + const InputAction *findSingleSwiftInput() const { + auto Inputs = getInputs(); + const InputAction *IA = nullptr; + for (auto const *I : Inputs) { + if (auto const *S = dyn_cast(I)) { + if (S->getType() == file_types::TY_Swift) { + if (IA == nullptr) { + IA = S; + } else { + // Already found one, two is too many. + return nullptr; + } + } + } + } + return IA; + } }; class InterpretJobAction : public JobAction { diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index eaff74cfce578..0d9f56f4abb45 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -521,6 +521,18 @@ class Compilation { /// How many .swift input files? unsigned countSwiftInputs() const; + /// Unfortunately the success or failure of a Swift compilation is currently + /// sensitive to the order in which files are processed, at least in terms of + /// the order of processing extensions (and likely other ways we haven't + /// discovered yet). So long as this is true, we need to make sure any batch + /// job we build names its inputs in an order that's a subsequence of the + /// sequence of inputs the driver was initially invoked with. + /// + /// Also use to write out information in a consistent order. + void sortJobsToMatchCompilationInputs( + ArrayRef unsortedJobs, + SmallVectorImpl &sortedJobs) const; + private: /// Perform all jobs. /// diff --git a/include/swift/Driver/FineGrainedDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h index 237a361784b19..4d3616ff5f93f 100644 --- a/include/swift/Driver/FineGrainedDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -48,9 +48,9 @@ namespace fine_grained_dependencies { /// Keep separate type from Node for type-checking. class ModuleDepGraphNode : public DepGraphNode { - /// The swiftDeps file that holds this entity. + /// The swiftDeps file that holds this entity iff this is a provides node. /// If more than one source file has the same DependencyKey, then there - /// will be one node for each in the driver. + /// will be one node for each in the driver, distinguished by this field. Optional swiftDeps; public: @@ -76,26 +76,18 @@ class ModuleDepGraphNode : public DepGraphNode { const Optional &getSwiftDeps() const { return swiftDeps; } - bool assertImplementationMustBeInAFile() const { - assert((getSwiftDeps().hasValue() || !getKey().isImplementation()) && - "Implementations must be in some file."); - return true; + std::string getSwiftDepsOrEmpty() const { + return getSwiftDeps().getValueOr(std::string()); } - std::string humanReadableName() const { - StringRef where = - !getSwiftDeps().hasValue() - ? "" - : llvm::sys::path::filename(getSwiftDeps().getValue()); - return DepGraphNode::humanReadableName(where); + std::string getSwiftDepsForMapKey() const { + // Use the empty string for nodes whose source file is unknown, + // i.e. depends. (Known depends are represented by arcs, not nodes.) + return getSwiftDepsOrEmpty(); } - SWIFT_DEBUG_DUMP; - - bool assertProvidedEntityMustBeInAFile() const { - assert((getSwiftDeps().hasValue() || !getKey().isImplementation()) && - "Implementations must be in some file."); - return true; + const std::string &getSwiftDepsOfProvides() const { + return getSwiftDeps().getValue(); } /// Nodes can move from file to file when the driver reads the result of a @@ -103,15 +95,28 @@ class ModuleDepGraphNode : public DepGraphNode { void setSwiftDeps(Optional s) { swiftDeps = s; } bool getIsProvides() const { return getSwiftDeps().hasValue(); } -}; -/// A placeholder allowing the experimental system to fit into the driver -/// without changing as much code. -class CoarseGrainedDependencyGraphImpl { -public: - /// Use the status quo LoadResult for now. - using LoadResult = - typename swift::CoarseGrainedDependencyGraphImpl::LoadResult; + /// Return true if this node describes a definition for which the job is known + bool isDefinedInAKnownFile() const { return getIsProvides(); } + + bool doesNodeProvideAnInterface() const { + return getKey().isInterface() && getIsProvides(); + } + + bool assertImplementationMustBeInAFile() const { + assert((isDefinedInAKnownFile() || !getKey().isImplementation()) && + "Implementations must be in some file."); + return true; + } + + std::string humanReadableName() const { + StringRef where = !getIsProvides() + ? "" + : llvm::sys::path::filename(getSwiftDepsOfProvides()); + return DepGraphNode::humanReadableName(where); + } + + SWIFT_DEBUG_DUMP; }; //============================================================================== @@ -159,18 +164,10 @@ class ModuleDepGraph { std::unordered_set externalDependencies; /// The new version of "Marked." - /// Record cascading jobs by swiftDepsFilename because that's what - /// nodes store directly. - /// - /// The status quo system uses "cascade" for the following: - /// Def1 -> def2 -> def3, where arrows are uses, so 3 depends on 2 which - /// depends on 1. The first use is said to "cascade" if when def1 changes, - /// def3 is dirtied. - /// TODO: Move cascadingJobs out of the graph, ultimately. - /// If marked, any Job that depends on me must be rebuilt after compiling me - /// if I have changed. - - std::unordered_set cascadingJobs; + /// Aka "isMarked". Holds the swiftDeps paths for jobs the driver has or will + /// schedule. + /// TODO: Move scheduledJobs out of the graph, ultimately. + std::unordered_set swiftDepsOfJobsThatNeedRunning; /// Keyed by swiftdeps filename, so we can get back to Jobs. std::unordered_map jobsBySwiftDeps; @@ -190,10 +187,12 @@ class ModuleDepGraph { const bool verifyFineGrainedDependencyGraphAfterEveryImport; const bool emitFineGrainedDependencyDotFileAfterEveryImport; - /// If tracing dependencies, holds the current node traversal path + /// If tracing dependencies, holds a vector used to hold the current path + /// def - use/def - use/def - ... Optional> currentPathIfTracing; - /// If tracing dependencies, record the node sequence + /// If tracing dependencies, holds the sequence of defs used to get to the job + /// that is the key std::unordered_multimap> dependencyPathsToJobs; @@ -204,7 +203,7 @@ class ModuleDepGraph { /// Encapsulate the invariant between where the node resides in /// nodesBySwiftDepsFile and the swiftDeps node instance variable here. void addToMap(ModuleDepGraphNode *n) { - nodeMap.insert(n->getSwiftDeps().getValueOr(std::string()), n->getKey(), n); + nodeMap.insert(n->getSwiftDepsForMapKey(), n->getKey(), n); } /// When integrating a SourceFileDepGraph, there might be a node representing @@ -223,8 +222,7 @@ class ModuleDepGraph { /// Remove node from nodeMap, check invariants. ModuleDepGraphNode *eraseNodeFromMap(ModuleDepGraphNode *nodeToErase) { ModuleDepGraphNode *nodeActuallyErased = nodeMap.findAndErase( - nodeToErase->getSwiftDeps().getValueOr(std::string()), - nodeToErase->getKey()); + nodeToErase->getSwiftDepsForMapKey(), nodeToErase->getKey()); (void)nodeActuallyErased; assert( nodeToErase == nodeActuallyErased || @@ -232,6 +230,28 @@ class ModuleDepGraph { return nodeToErase; } + void eraseNodeFromUsesByDef(ModuleDepGraphNode *nodeToErase) { + for (auto &defAndUses : usesByDef) + defAndUses.second.erase(nodeToErase); + } + + void eraseNodeFromCurrentPathIfTracing(ModuleDepGraphNode *nodeToErase) { + if (currentPathIfTracing) + eraseNodeFromVector(currentPathIfTracing.getValue(), nodeToErase); + } + + void eraseNodeFromDependencyPathToJobs(ModuleDepGraphNode *nodeToErase) { + for (auto &jobAndPath : dependencyPathsToJobs) + eraseNodeFromVector(jobAndPath.second, nodeToErase); + } + + static void eraseNodeFromVector(std::vector &v, + const ModuleDepGraphNode *n) { + const auto where = std::find(v.begin(), v.end(), n); + if (where != v.end()) + v.erase(where); + } + static StringRef getSwiftDeps(const driver::Job *cmd) { return cmd->getOutput().getAdditionalOutputForType( file_types::TY_SwiftDeps); @@ -268,6 +288,8 @@ class ModuleDepGraph { assert(verify() && "ModuleDepGraph should be fine when created"); } + ModuleDepGraph() : ModuleDepGraph(false, false, false, nullptr) {} + /// Unlike the standard \c CoarseGrainedDependencyGraph, returns \c /// CoarseGrainedDependencyGraphImpl::LoadResult::AffectsDownstream when /// loading a new file, i.e. when determining the initial set. Caller @@ -275,6 +297,13 @@ class ModuleDepGraph { CoarseGrainedDependencyGraphImpl::LoadResult loadFromPath(const driver::Job *, StringRef, DiagnosticEngine &); + CoarseGrainedDependencyGraphImpl::LoadResult + loadFromString(const driver::Job *cmd, StringRef data); + + CoarseGrainedDependencyGraphImpl::LoadResult + loadFromSourceFileDepGraph(const driver::Job *cmd, + const SourceFileDepGraph &); + /// For the dot file. std::string getGraphID() const { return "driver"; } @@ -302,8 +331,12 @@ class ModuleDepGraph { /// 1. Return value (via visited) is the set of jobs needing recompilation /// after this one, and /// 2. Jobs not previously known to need dependencies reexamined after they - /// are recompiled. Such jobs are added to the \ref cascadingJobs set, and + /// are recompiled. Such jobs are added to the \ref scheduledJobs set, and /// accessed via \ref isMarked. + /// + /// Only return jobs marked that were previously unmarked. Not required for + /// the driver because it won't run a job twice, but required for the unit + /// test. std::vector markTransitive( const driver::Job *jobToBeRecompiled, const void *ignored = nullptr); @@ -315,6 +348,8 @@ class ModuleDepGraph { std::vector getExternalDependencies() const; + /// Find jobs that were previously not known to need compilation but that + /// depend on \c externalDependency. std::vector markExternal(StringRef externalDependency); void forEachUnmarkedJobDirectlyDependentOnExternalSwiftdeps( @@ -428,14 +463,14 @@ class ModuleDepGraph { /// Given a definition node, and a list of already found dependents, /// recursively add transitive closure of dependents of the definition /// into the already found dependents. - /// Also record any dependents that "cascade", i.e. whose dependencies must be - /// recomputed after recompilation so that its dependents can be recompiled. - void findDependentNodesAndRecordCascadingOnes( + void findDependentNodes( std::unordered_set &foundDependents, const ModuleDepGraphNode *definition); - std::vector computeUniqueJobsFromNodes( - const std::unordered_set &nodes); + /// Givien a set of nodes, return the set of swiftDeps for the jobs those + /// nodes are in. + llvm::StringSet<> computeSwiftDepsFromInterfaceNodes( + ArrayRef nodes); /// Record a visit to this node for later dependency printing size_t traceArrival(const ModuleDepGraphNode *visitedNode); @@ -447,9 +482,9 @@ class ModuleDepGraph { const std::vector &pathToJob, const driver::Job *dependentJob); - /// Return true if job did not cascade before - bool rememberThatJobCascades(StringRef swiftDeps) { - return cascadingJobs.insert(swiftDeps).second; + /// Return true if job was not scheduled before + bool recordJobNeedsRunning(StringRef swiftDeps) { + return swiftDepsOfJobsThatNeedRunning.insert(swiftDeps).second; } /// For debugging, write out the graph to a dot file. @@ -469,6 +504,13 @@ class ModuleDepGraph { void printPath(raw_ostream &out, const driver::Job *node) const; private: + /// Get a printable filename, given a node's swiftDeps. + StringRef getProvidingFilename(Optional swiftDeps) const; + + /// Print one node on the dependency path. + static void printOneNodeOfPath(raw_ostream &out, const DependencyKey &key, + const StringRef filename); + bool isCurrentPathForTracingEmpty() const { return !currentPathIfTracing.hasValue() || currentPathIfTracing->empty(); } diff --git a/include/swift/Driver/Job.h b/include/swift/Driver/Job.h index d36d3f3b3ff2d..e57952545bb64 100644 --- a/include/swift/Driver/Job.h +++ b/include/swift/Driver/Job.h @@ -150,6 +150,9 @@ class CommandOutput { public: CommandOutput(file_types::ID PrimaryOutputType, OutputFileMap &Derived); + /// For testing dependency graphs that use Jobs + CommandOutput(StringRef dummyBaseName, OutputFileMap &); + /// Return the primary output type for this CommandOutput. file_types::ID getPrimaryOutputType() const; @@ -319,6 +322,12 @@ class Job { ExtraEnvironment(std::move(ExtraEnvironment)), FilelistFileInfos(std::move(Infos)), ResponseFile(ResponseFile) {} + /// For testing dependency graphs that use Jobs + Job(OutputFileMap &OFM, StringRef dummyBaseName) + : Job(CompileJobAction(file_types::TY_Object), + SmallVector(), + std::make_unique(dummyBaseName, OFM), nullptr, {}) {} + virtual ~Job(); const JobAction &getSource() const { diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index bdb5a2e9e362c..a5a7813b4f17c 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -137,7 +137,11 @@ def driver_always_rebuild_dependents : def enable_fine_grained_dependencies : Flag<["-"], "enable-fine-grained-dependencies">, Flags<[FrontendOption, HelpHidden]>, -HelpText<"Experimental work-in-progress to be more selective about incremental recompilation">; +HelpText<"Be more selective about incremental recompilation">; + +def disable_fine_grained_dependencies : +Flag<["-"], "disable-fine-grained-dependencies">, Flags<[FrontendOption, HelpHidden]>, +HelpText<"Don't be more selective about incremental recompilation">; def enable_only_one_dependency_file : @@ -183,6 +187,11 @@ Flag<["-"], "fine-grained-dependency-include-intrafile">, InternalDebugOpt, HelpText<"Include within-file dependencies.">; +def emit_fine_grained_dependency_sourcefile_dot_files : +Flag<["-"], "emit-fine-grained-dependency-sourcefile-dot-files">, +InternalDebugOpt, +HelpText<"Emit dot files for every source file.">; + def driver_mode : Joined<["--"], "driver-mode=">, Flags<[HelpHidden]>, HelpText<"Set the driver mode to either 'swift' or 'swiftc'">; diff --git a/lib/AST/FineGrainedDependencies.cpp b/lib/AST/FineGrainedDependencies.cpp index c69c4623d79db..e6b6dac802bfc 100644 --- a/lib/AST/FineGrainedDependencies.cpp +++ b/lib/AST/FineGrainedDependencies.cpp @@ -57,11 +57,11 @@ SourceFileDepGraph::getSourceFileNodePair() const { getNode(1)); } -StringRef SourceFileDepGraph::getSwiftDepsFromSourceFileProvide() const { +StringRef SourceFileDepGraph::getSwiftDepsOfJobThatProducedThisGraph() const { return getSourceFileNodePair() .getInterface() ->getKey() - .getSwiftDepsFromSourceFileProvide(); + .getSwiftDepsFromASourceFileProvideNodeKey(); } void SourceFileDepGraph::forEachArc( @@ -77,8 +77,11 @@ void SourceFileDepGraph::forEachArc( InterfaceAndImplementationPair SourceFileDepGraph::findExistingNodePairOrCreateAndAddIfNew( - NodeKind k, StringRef context, StringRef name, - Optional fingerprint) { + NodeKind k, const ContextNameFingerprint &contextNameFingerprint) { + const std::string &context = std::get<0>(contextNameFingerprint); + const std::string &name = std::get<1>(contextNameFingerprint); + const Optional &fingerprint = + std::get<2>(contextNameFingerprint); InterfaceAndImplementationPair nodePair{ findExistingNodeOrCreateIfNew( DependencyKey(k, DeclAspect::interface, context, name), fingerprint, @@ -86,13 +89,16 @@ SourceFileDepGraph::findExistingNodePairOrCreateAndAddIfNew( findExistingNodeOrCreateIfNew( DependencyKey(k, DeclAspect::implementation, context, name), fingerprint, true /* = isProvides */)}; - // if interface changes, have to rebuild implementation - addArc(nodePair.getInterface(), nodePair.getImplementation()); + // if interface changes, have to rebuild implementation. + // But, if an arc is added for this, then *any* change that causes + // a same-named interface to be dirty will dirty this implementation, + // even if that interface is in another file. + // So, make the interface->implementation arc implicit. return nodePair; } SourceFileDepGraphNode *SourceFileDepGraph::findExistingNodeOrCreateIfNew( - DependencyKey key, Optional fingerprint, + DependencyKey key, const Optional &fingerprint, const bool isProvides) { SourceFileDepGraphNode *result = memoizedNodes.findExistingOrCreateIfNew( key, [&](DependencyKey key) -> SourceFileDepGraphNode * { @@ -101,11 +107,22 @@ SourceFileDepGraphNode *SourceFileDepGraph::findExistingNodeOrCreateIfNew( addNode(n); return n; }); + assert(result->getKey() == key && "Keys must match."); + if (!isProvides) + return result; // If have provides and depends with same key, result is one node that // isProvides - if (isProvides) + if (!result->getIsProvides() && fingerprint) { result->setIsProvides(); - assert(result->getKey() == key && "Keys must match."); + assert(!result->getFingerprint() && "Depends should not have fingerprints"); + result->setFingerprint(fingerprint); + return result; + } + // If there are two Decls with same base name but differ only in fingerprint, + // since we won't be able to tell which Decl is depended-upon (is this right?) + // just use the one node, but erase its print: + if (fingerprint != result->getFingerprint()) + result->setFingerprint(None); return result; } @@ -113,6 +130,18 @@ std::string DependencyKey::demangleTypeAsContext(StringRef s) { return swift::Demangle::demangleTypeAsString(s.str()); } +DependencyKey DependencyKey::createTransitiveKeyForWholeSourceFile( + const StringRef swiftDeps) { + assert(!swiftDeps.empty()); + const auto context = DependencyKey::computeContextForProvidedEntity< + NodeKind::sourceFileProvide>(swiftDeps); + const auto name = + DependencyKey::computeNameForProvidedEntity( + swiftDeps); + return DependencyKey(NodeKind::sourceFileProvide, DeclAspect::interface, + context, name); +} + //============================================================================== // MARK: Debugging //============================================================================== @@ -175,9 +204,8 @@ std::string DependencyKey::humanReadableName() const { } std::string DependencyKey::asString() const { - return NodeKindNames[size_t(kind)] + " " + - "aspect: " + DeclAspectNames[size_t(aspect)] + ", " + - humanReadableName(); + return NodeKindNames[size_t(kind)] + " " + "aspect: " + aspectName().str() + + ", " + humanReadableName(); } /// Needed for TwoStageMap::verify: diff --git a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp index 24f7dbb8a3b63..ac4dc54c8a3f0 100644 --- a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp +++ b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp @@ -28,6 +28,7 @@ #include "swift/AST/Types.h" #include "swift/Basic/FileSystem.h" #include "swift/Basic/LLVM.h" +#include "swift/Basic/ReferenceDependencyKeys.h" #include "swift/Demangling/Demangle.h" #include "swift/Frontend/FrontendOptions.h" #include "llvm/ADT/MapVector.h" @@ -214,8 +215,11 @@ struct SourceFileDeclFinder { private: /// Extensions may contain nominals and operators. void findNominalsFromExtensions() { - for (auto *ED : extensions) - findNominalsAndOperatorsIn(ED->getExtendedNominal(), ED); + for (auto *ED : extensions) { + const auto *const NTD = ED->getExtendedNominal(); + if (NTD) + findNominalsAndOperatorsIn(NTD, ED); + } } /// Top-level nominals may contain nominals and operators. void findNominalsInTopNominals() { @@ -260,16 +264,19 @@ struct SourceFileDeclFinder { /// Extensions may contain ValueDecls. void findValuesInExtensions() { for (const auto *ED : extensions) { - if (excludeIfPrivate(ED->getExtendedNominal())) + const auto *const NTD = ED->getExtendedNominal(); + if (!NTD || excludeIfPrivate(NTD)) continue; if (!includePrivateDecls && (!allInheritedProtocolsArePrivate(ED) || allMembersArePrivate(ED))) continue; for (const auto *member : ED->getMembers()) if (const auto *VD = dyn_cast(member)) - if (VD->hasName() && (includePrivateDecls || !declIsPrivate(VD))) - valuesInExtensions.push_back( - std::make_pair(ED->getExtendedNominal(), VD)); + if (VD->hasName() && (includePrivateDecls || !declIsPrivate(VD))) { + const auto *const NTD = ED->getExtendedNominal(); + if (NTD) + valuesInExtensions.push_back(std::make_pair(NTD, VD)); + } } } @@ -424,46 +431,38 @@ std::string DependencyKey::computeNameForProvidedEntity< template <> DependencyKey -DependencyKey::createDependedUponKey( - const DeclBaseName &dbn) { - return DependencyKey(NodeKind::topLevel, DeclAspect::interface, "", - dbn.userFacingName()); +DependencyKey::createDependedUponKey(StringRef name) { + return DependencyKey(NodeKind::topLevel, DeclAspect::interface, "", name); } template <> DependencyKey -DependencyKey::createDependedUponKey( - const DeclBaseName &dbn) { +DependencyKey::createDependedUponKey(StringRef name) { return DependencyKey(NodeKind::dynamicLookup, DeclAspect::interface, "", - dbn.userFacingName()); + name); } template <> -DependencyKey DependencyKey::createDependedUponKey< - NodeKind::nominal, std::pair>( - const std::pair &p) { - return DependencyKey(NodeKind::nominal, DeclAspect::interface, - mangleTypeAsContext(p.first), ""); +DependencyKey +DependencyKey::createDependedUponKey(StringRef name) { + return DependencyKey(NodeKind::externalDepend, DeclAspect::interface, "", + name); } template <> -DependencyKey DependencyKey::createDependedUponKey< - NodeKind::member, std::pair>( - const std::pair &p) { - const bool isMemberBlank = p.second.empty(); - const auto kind = - isMemberBlank ? NodeKind::potentialMember : NodeKind::member; - return DependencyKey(kind, DeclAspect::interface, - mangleTypeAsContext(p.first), - isMemberBlank ? "" : p.second.userFacingName()); +DependencyKey +DependencyKey::createDependedUponKey(StringRef mangledName) { + return DependencyKey(NodeKind::nominal, DeclAspect::interface, mangledName, + ""); } -template <> -DependencyKey -DependencyKey::createDependedUponKey( - const std::string &file) { - return DependencyKey(NodeKind::externalDepend, DeclAspect::interface, "", - file); +DependencyKey DependencyKey::createDependedUponKey(StringRef mangledHolderName, + StringRef memberBaseName) { + const bool isMemberBlank = memberBaseName.empty(); + const auto kind = + isMemberBlank ? NodeKind::potentialMember : NodeKind::member; + return DependencyKey(kind, DeclAspect::interface, mangledHolderName, + isMemberBlank ? "" : memberBaseName); } //============================================================================== @@ -475,12 +474,6 @@ namespace { /// Reads the information provided by the frontend and builds the /// SourceFileDepGraph class SourceFileDepGraphConstructor { - /// The SourceFile containing the Decls. - SourceFile *SF; - - /// Furnishes depended-upon names resulting from lookups. - const DependencyTracker &depTracker; - /// Name of the swiftDeps file, for inclusion in the constructed graph. StringRef swiftDeps; // TODO rm? @@ -491,18 +484,133 @@ class SourceFileDepGraphConstructor { /// If there was an error, cannot get accurate info. const bool hadCompilationError; + /// Functions as the fingerprint of the entire file + const std::string interfaceHash; + + /// Top-level base names of decls that are depended-upon and a flag indicating + /// if the dependency "cascades" + const std::vector> topLevelDepends; + + /// A mangled nominal name and the member base name that are depended-upon, + /// a flag indicating if the member is private to its enclosing file, and + /// a flag indicating if the dependency cascades. + const std::vector, bool>> + memberDepends; + + /// The base name of a class member depended-upon for dynamic lookup, and a + /// cascades flag. + const std::vector> dynamicLookupDepends; + + /// The paths of swiftdeps files of other modules that are depended-upon. + const std::vector externalDependencies; + + /// Provided names + std::vector precedenceGroups; + std::vector memberOperatorDecls; + std::vector operators; + std::vector topNominals; + std::vector topValues; + std::vector allNominals; + std::vector potentialMemberHolders; + std::vector valuesInExtensions; + std::vector classMembers; + /// Graph under construction SourceFileDepGraph g; public: - SourceFileDepGraphConstructor(SourceFile *SF, + /// Expose this layer to enable faking up a constructor for testing. + /// See the instance variable comments for explanation. + // clang-format off + SourceFileDepGraphConstructor( + StringRef swiftDeps, + bool includePrivateDeps, + bool hadCompilationError, + const std::string &interfaceHash, + ArrayRef> topLevelDepends, + ArrayRef, bool>> memberDepends, + ArrayRef> dynamicLookupDepends, + ArrayRef externalDependencies, + + ArrayRef precedenceGroups, + ArrayRef memberOperatorDecls, + ArrayRef operators, + ArrayRef topNominals, + ArrayRef topValues, + ArrayRef allNominals, + ArrayRef potentialMemberHolders, + ArrayRef valuesInExtensions, + ArrayRef classMembers + ) : + swiftDeps(swiftDeps), + includePrivateDeps(includePrivateDeps), + hadCompilationError(hadCompilationError), + + interfaceHash(interfaceHash), + topLevelDepends(topLevelDepends), + memberDepends(memberDepends), + dynamicLookupDepends(dynamicLookupDepends), + externalDependencies(externalDependencies), + + precedenceGroups(precedenceGroups), + memberOperatorDecls(memberOperatorDecls), + operators(operators), + topNominals(topNominals), + topValues(topValues), + allNominals(allNominals), + potentialMemberHolders(potentialMemberHolders), + valuesInExtensions(valuesInExtensions), + classMembers(classMembers) + {} + + SourceFileDepGraphConstructor static forSourceFile(SourceFile *SF, const DependencyTracker &depTracker, StringRef swiftDeps, const bool includePrivateDeps, - const bool hadCompilationError) - : SF(SF), depTracker(depTracker), swiftDeps(swiftDeps), - includePrivateDeps(includePrivateDeps), - hadCompilationError(hadCompilationError) {} + const bool hadCompilationError) { + + SourceFileDeclFinder declFinder(SF, includePrivateDeps); + std::vector> topLevelDepends; + for (const auto p: SF->getReferencedNameTracker()->getTopLevelNames()) + topLevelDepends.push_back(std::make_pair(p.getFirst().userFacingName(), p.getSecond())); + + std::vector> dynamicLookupDepends; + for (const auto p: SF->getReferencedNameTracker()->getDynamicLookupNames()) + dynamicLookupDepends.push_back(std::make_pair(p.getFirst().userFacingName(), p.getSecond())); + + std::vector, bool>> memberDepends; + for (const auto &p: SF->getReferencedNameTracker()->getUsedMembers()) + memberDepends.push_back( + std::make_pair( + std::make_tuple( + mangleTypeAsContext(p.getFirst().first), + p.getFirst().second.userFacingName(), + declIsPrivate(p.getFirst().first)), + p.getSecond())); + + return SourceFileDepGraphConstructor( + swiftDeps, + includePrivateDeps, + hadCompilationError, + + getInterfaceHash(SF), + topLevelDepends, + memberDepends, + dynamicLookupDepends, + depTracker.getDependencies(), + + namesForProvidersOfAGivenType(declFinder.precedenceGroups), + namesForProvidersOfAGivenType(declFinder.memberOperatorDecls), + namesForProvidersOfAGivenType(declFinder.operators), + namesForProvidersOfAGivenType(declFinder.topNominals), + namesForProvidersOfAGivenType(declFinder.topValues), + namesForProvidersOfAGivenType(declFinder.allNominals), + namesForProvidersOfAGivenType(declFinder.potentialMemberHolders), + namesForProvidersOfAGivenType(declFinder.valuesInExtensions), + namesForProvidersOfAGivenType(declFinder.classMembers) + ); + } + // clang-format on /// Construct the graph and return it. SourceFileDepGraph construct() { @@ -517,7 +625,7 @@ class SourceFileDepGraphConstructor { } private: - std::string getSourceFileFingerprint() const { return getInterfaceHash(SF); } + std::string getSourceFileFingerprint() const { return interfaceHash; } static std::string getInterfaceHash(SourceFile *SF) { llvm::SmallString<32> interfaceHash; @@ -533,19 +641,26 @@ class SourceFileDepGraphConstructor { void addDependencyArcsToGraph(); /// Given an array of Decls or pairs of them in \p declsOrPairs - /// create nodes if needed and add the new nodes to the graph. + /// create string pairs for context and name template - void addAllProviderNodesOfAGivenType(std::vector &contentsVec) { - for (const auto declOrPair : contentsVec) { - // No fingerprints for providers (Decls) yet. - // Someday ... - const Optional fingerprint = None; + static std::vector + namesForProvidersOfAGivenType(std::vector &contentsVec) { + std::vector result; + for (const auto declOrPair : contentsVec) + result.push_back( + {DependencyKey::computeContextForProvidedEntity(declOrPair), + DependencyKey::computeNameForProvidedEntity(declOrPair), + Optional()}); + return result; + } + + template + void addAllProviderNodesOfAGivenType( + ArrayRef contextNameFingerprints) { + for (const auto &contextNameFingerprint : contextNameFingerprints) { auto p = g.findExistingNodePairOrCreateAndAddIfNew( - kind, - DependencyKey::computeContextForProvidedEntity(declOrPair), - DependencyKey::computeNameForProvidedEntity(declOrPair), - fingerprint); - // Since we don't have fingerprints yet, must rebuild every provider when + kind, contextNameFingerprint); + // When we don't have a fingerprint yet, must rebuild every provider when // interfaceHash changes. So when interface (i.e. interface hash) of // sourceFile changes, every provides is dirty. And since we don't know // what happened, dirtyness might affect the interface. @@ -557,8 +672,8 @@ class SourceFileDepGraphConstructor { /// Given a map of names and isCascades, add the resulting dependencies to the /// graph. template - void addAllDependenciesFrom(const llvm::DenseMap &map) { - for (const auto &p : map) + void addAllDependenciesFrom(ArrayRef> names) { + for (const auto &p : names) recordThatThisWholeFileDependsOn( DependencyKey::createDependedUponKey(p.first), p.second); } @@ -566,8 +681,7 @@ class SourceFileDepGraphConstructor { /// Given a map of holder-and-member-names and isCascades, add the resulting /// dependencies to the graph. void addAllDependenciesFrom( - const llvm::DenseMap, - bool> &); + ArrayRef, bool>>); /// Given an array of external swiftDeps files, add the resulting external /// dependencies to the graph. @@ -587,28 +701,27 @@ class SourceFileDepGraphConstructor { }; } // namespace -using UsedMembersMap = - llvm::DenseMap, bool>; void SourceFileDepGraphConstructor::addAllDependenciesFrom( - const UsedMembersMap &map) { + ArrayRef, bool>> + members) { - UsedMembersMap filteredMap; - for (const auto &entry : map) - if (includePrivateDeps || !declIsPrivate(entry.first.first)) - filteredMap[entry.getFirst()] = entry.getSecond(); - - std::unordered_set holdersOfCascadingMembers; - for (auto &entry : filteredMap) + llvm::StringSet<> holdersOfCascadingMembers; + for (const auto &entry : members) { + if (!includePrivateDeps && std::get<2>(entry.first)) + continue; if (entry.second) - holdersOfCascadingMembers.insert(entry.first.first); - - for (auto &entry : filteredMap) { - // mangles twice in the name of symmetry + holdersOfCascadingMembers.insert(std::get<0>(entry.first)); + } + for (const auto &entry : members) { + if (!includePrivateDeps && std::get<2>(entry.first)) + continue; recordThatThisWholeFileDependsOn( - DependencyKey::createDependedUponKey(entry.first), - holdersOfCascadingMembers.count(entry.first.first) != 0); + DependencyKey::createDependedUponKey( + std::get<0>(entry.first)), + holdersOfCascadingMembers.count(std::get<0>(entry.first)) != 0); recordThatThisWholeFileDependsOn( - DependencyKey::createDependedUponKey(entry.first), + DependencyKey::createDependedUponKey(std::get<0>(entry.first), + std::get<1>(entry.first)), entry.second); } } @@ -620,47 +733,40 @@ void SourceFileDepGraphConstructor::addAllDependenciesFrom( void SourceFileDepGraphConstructor::addSourceFileNodesToGraph() { g.findExistingNodePairOrCreateAndAddIfNew( NodeKind::sourceFileProvide, - DependencyKey::computeContextForProvidedEntity< - NodeKind::sourceFileProvide>(swiftDeps), - DependencyKey::computeNameForProvidedEntity( - swiftDeps), - getSourceFileFingerprint()); + {DependencyKey::computeContextForProvidedEntity< + NodeKind::sourceFileProvide>(swiftDeps), + DependencyKey::computeNameForProvidedEntity( + swiftDeps), + getSourceFileFingerprint()}); } void SourceFileDepGraphConstructor::addProviderNodesToGraph() { - SourceFileDeclFinder declFinder(SF, includePrivateDeps); // TODO: express the multiple provides and depends streams with variadic // templates // Many kinds of Decls become top-level depends. - addAllProviderNodesOfAGivenType( - declFinder.precedenceGroups); - addAllProviderNodesOfAGivenType( - declFinder.memberOperatorDecls); - addAllProviderNodesOfAGivenType(declFinder.operators); - addAllProviderNodesOfAGivenType(declFinder.topNominals); - addAllProviderNodesOfAGivenType(declFinder.topValues); + addAllProviderNodesOfAGivenType(precedenceGroups); + addAllProviderNodesOfAGivenType(memberOperatorDecls); + addAllProviderNodesOfAGivenType(operators); + addAllProviderNodesOfAGivenType(topNominals); + addAllProviderNodesOfAGivenType(topValues); - addAllProviderNodesOfAGivenType(declFinder.allNominals); + addAllProviderNodesOfAGivenType(allNominals); addAllProviderNodesOfAGivenType( - declFinder.potentialMemberHolders); - addAllProviderNodesOfAGivenType( - declFinder.valuesInExtensions); + potentialMemberHolders); + addAllProviderNodesOfAGivenType(valuesInExtensions); - addAllProviderNodesOfAGivenType( - declFinder.classMembers); + addAllProviderNodesOfAGivenType(classMembers); } void SourceFileDepGraphConstructor::addDependencyArcsToGraph() { // TODO: express the multiple provides and depends streams with variadic // templates - addAllDependenciesFrom( - SF->getReferencedNameTracker()->getTopLevelNames()); - addAllDependenciesFrom(SF->getReferencedNameTracker()->getUsedMembers()); - addAllDependenciesFrom( - SF->getReferencedNameTracker()->getDynamicLookupNames()); - addAllDependenciesFrom(depTracker.getDependencies()); + addAllDependenciesFrom(topLevelDepends); + addAllDependenciesFrom(memberDepends); + addAllDependenciesFrom(dynamicLookupDepends); + addAllDependenciesFrom(externalDependencies); } void SourceFileDepGraphConstructor::recordThatThisWholeFileDependsOn( @@ -676,7 +782,8 @@ void SourceFileDepGraphConstructor::recordThatThisWholeFileDependsOn( bool swift::fine_grained_dependencies::emitReferenceDependencies( DiagnosticEngine &diags, SourceFile *const SF, - const DependencyTracker &depTracker, StringRef outputPath) { + const DependencyTracker &depTracker, StringRef outputPath, + const bool alsoEmitDotFile) { // Before writing to the dependencies file path, preserve any previous file // that may have been there. No error handling -- this is just a nicety, it @@ -685,8 +792,8 @@ bool swift::fine_grained_dependencies::emitReferenceDependencies( const bool includeIntrafileDeps = SF->getASTContext().LangOpts.FineGrainedDependenciesIncludeIntrafileOnes; const bool hadCompilationError = SF->getASTContext().hadError(); - SourceFileDepGraphConstructor gc(SF, depTracker, outputPath, - includeIntrafileDeps, hadCompilationError); + auto gc = SourceFileDepGraphConstructor::forSourceFile( + SF, depTracker, outputPath, includeIntrafileDeps, hadCompilationError); SourceFileDepGraph g = gc.construct(); const bool hadError = @@ -699,10 +806,114 @@ bool swift::fine_grained_dependencies::emitReferenceDependencies( assert(g.verifyReadsWhatIsWritten(outputPath)); - std::string dotFileName = outputPath.str() + ".dot"; - withOutputFile(diags, dotFileName, [&](llvm::raw_pwrite_stream &out) { - DotFileEmitter(out, g, false, false).emit(); - return false; - }); + if (alsoEmitDotFile) { + std::string dotFileName = outputPath.str() + ".dot"; + withOutputFile(diags, dotFileName, [&](llvm::raw_pwrite_stream &out) { + DotFileEmitter(out, g, false, false).emit(); + return false; + }); + } return hadError; } + +//============================================================================== +// Entry point from the unit tests +//============================================================================== + +static std::vector +getBaseNameProvides(ArrayRef simpleNames) { + std::vector result; + for (StringRef n : simpleNames) + result.push_back({"", n.str(), None}); + return result; +} + +static std::vector +getMangledHolderProvides(ArrayRef simpleNames) { + std::vector result; + for (StringRef n : simpleNames) + result.push_back({n.str(), "", None}); + return result; +} + +static std::vector getCompoundProvides( + ArrayRef> compoundNames) { + std::vector result; + for (const auto &p : compoundNames) + result.push_back({p.first, p.second, None}); + return result; +} + +// Use '_' as a prefix indicating non-cascading +static bool cascades(const std::string &s) { return s.empty() || s[0] != '_'; } + +// Use '_' as a prefix for a file-private member +static bool isPrivate(const std::string &s) { + return !s.empty() && s[0] == '_'; +} + +static std::vector> +getSimpleDepends(ArrayRef simpleNames) { + std::vector> result; + for (std::string n : simpleNames) + result.push_back({n, cascades((n))}); + return result; +} + +static std::vector +getExternalDepends(ArrayRef simpleNames) { + return simpleNames; +} + +static std::vector, bool>> +getCompoundDepends( + ArrayRef simpleNames, + ArrayRef> compoundNames) { + std::vector, bool>> + result; + for (std::string n : simpleNames) { + // (On Linux, the compiler needs more verbosity than: + // result.push_back({{n, "", false}, cascades(n)}); + result.push_back( + std::make_pair(std::make_tuple(n, std::string(), false), cascades(n))); + } + for (auto &p : compoundNames) { + // Likewise, for Linux expand the following out: + // result.push_back( + // {{p.first, p.second, isPrivate(p.second)}, cascades(p.first)}); + result.push_back( + std::make_pair(std::make_tuple(p.first, p.second, isPrivate(p.second)), + cascades(p.first))); + } + return result; +} + +SourceFileDepGraph SourceFileDepGraph::simulateLoad( + std::string swiftDepsFilename, const bool includePrivateDeps, + const bool hadCompilationError, std::string interfaceHash, + llvm::StringMap> simpleNamesByRDK, + llvm::StringMap>> + compoundNamesByRDK) { + + using namespace reference_dependency_keys; + + // clang-format off + SourceFileDepGraphConstructor c( + swiftDepsFilename, includePrivateDeps, hadCompilationError, interfaceHash, + getSimpleDepends(simpleNamesByRDK[dependsTopLevel]), + getCompoundDepends(simpleNamesByRDK[dependsNominal], compoundNamesByRDK[dependsMember]), + getSimpleDepends(simpleNamesByRDK[dependsDynamicLookup]), + getExternalDepends(simpleNamesByRDK[dependsExternal]), + {}, // precedence groups + {}, // memberOperatorDecls + {}, // operators + getMangledHolderProvides(simpleNamesByRDK[providesNominal]), // topNominals + getBaseNameProvides(simpleNamesByRDK[providesTopLevel]), // topValues + getMangledHolderProvides(simpleNamesByRDK[providesNominal]), // allNominals + getMangledHolderProvides(simpleNamesByRDK[providesNominal]), // potentialMemberHolders + getCompoundProvides(compoundNamesByRDK[providesMember]), // valuesInExtensions + getBaseNameProvides(simpleNamesByRDK[providesDynamicLookup]) // classMembers + ); + // clang-format on + return c.construct(); +} diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 7cec90ab69e53..b45b217238c09 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -272,7 +272,7 @@ namespace driver { void noteBuilding(const Job *cmd, const bool willBeBuilding, const bool isTentative, const bool forRanges, - StringRef reason) { + StringRef reason) const { if (!Comp.getShowIncrementalBuildDecisions()) return; if (ScheduledCommands.count(cmd)) @@ -300,6 +300,23 @@ namespace driver { }); } + template + void noteBuildingJobs(const JobsCollection &unsortedJobsArg, + const bool forRanges, const StringRef reason) const { + if (!Comp.getShowIncrementalBuildDecisions() && + !Comp.getShowJobLifecycle()) + return; + // Sigh, must manually convert SmallPtrSet to ArrayRef-able container + llvm::SmallVector unsortedJobs; + for (const Job *j : unsortedJobsArg) + unsortedJobs.push_back(j); + llvm::SmallVector sortedJobs; + Comp.sortJobsToMatchCompilationInputs(unsortedJobs, sortedJobs); + for (const Job *j : sortedJobs) + noteBuilding(j, /*willBeBuilding=*/true, /*isTentative=*/false, + forRanges, reason); + } + const Job *findUnfinishedJob(ArrayRef JL) { for (const Job *Cmd : JL) { if (!FinishedCommands.count(Cmd)) @@ -432,7 +449,7 @@ namespace driver { diag::warn_unable_to_load_dependencies, DependenciesFile); Comp.disableIncrementalBuild( - Twine("Malformed swift dependencies file ' ") + DependenciesFile + + Twine("malformed swift dependencies file ' ") + DependenciesFile + "'"); } @@ -468,7 +485,7 @@ namespace driver { // other recompilations. It is possible that the current code marks // things that do not need to be marked. Unecessary compilation would // result if that were the case. - bool wasCascading = isMarkedInDepGraph(FinishedCmd, forRanges); + bool wasKnownToNeedRunning = isMarkedInDepGraph(FinishedCmd, forRanges); switch (loadDepGraphFromPath(FinishedCmd, DependenciesFile, Comp.getDiags(), forRanges)) { @@ -484,7 +501,7 @@ namespace driver { break; case CoarseGrainedDependencyGraph::LoadResult::UpToDate: - if (!wasCascading) + if (!wasKnownToNeedRunning) break; LLVM_FALLTHROUGH; case CoarseGrainedDependencyGraph::LoadResult::AffectsDownstream: @@ -674,14 +691,22 @@ namespace driver { const CommandSet &DependentsInEffect = useRangesForScheduling ? DependentsWithRanges : DependentsWithoutRanges; - for (const Job *Cmd : DependentsInEffect) { + + noteBuildingJobs(DependentsInEffect, useRangesForScheduling, + "because of dependencies discovered later"); + + // Sort dependents for more deterministic behavior + llvm::SmallVector UnsortedDependents; + for (const Job *j : DependentsInEffect) + UnsortedDependents.push_back(j); + llvm::SmallVector SortedDependents; + Comp.sortJobsToMatchCompilationInputs(UnsortedDependents, + SortedDependents); + + for (const Job *Cmd : SortedDependents) { DeferredCommands.erase(Cmd); - noteBuilding(Cmd, /*willBeBuilding=*/true, useRangesForScheduling, - /*isTentative=*/false, - "because of dependencies discovered later"); scheduleCommandIfNecessaryAndPossible(Cmd); } - return TaskFinishedResponse::ContinueExecution; } @@ -692,6 +717,7 @@ namespace driver { // Store this task's ReturnCode as our Result if we haven't stored // anything yet. + if (Result == EXIT_SUCCESS) Result = ReturnCode; @@ -1007,10 +1033,6 @@ namespace driver { const bool isCascading = isCascadingJobAccordingToCondition( Cmd, Cond, HasDependenciesFileName); - - if (Comp.getEnableFineGrainedDependencies()) - assert(getFineGrainedDepGraph(/*forRanges=*/false) - .emitDotFileAndVerify(Comp.getDiags())); return std::make_pair(shouldSched, isCascading); } @@ -1055,6 +1077,10 @@ namespace driver { const Job *const Cmd, const Job::Condition Condition, const bool hasDependenciesFileName, const bool forRanges) { + // When using ranges may still decide not to schedule the job. + const bool isTentative = + Comp.getEnableSourceRangeDependencies() || forRanges; + switch (Condition) { case Job::Condition::Always: case Job::Condition::NewlyAdded: @@ -1070,11 +1096,11 @@ namespace driver { } LLVM_FALLTHROUGH; case Job::Condition::RunWithoutCascading: - noteBuilding(Cmd, /*willBeBuilding=*/true, /*isTentative=*/true, + noteBuilding(Cmd, /*willBeBuilding=*/true, /*isTentative=*/isTentative, forRanges, "(initial)"); return true; case Job::Condition::CheckDependencies: - noteBuilding(Cmd, /*willBeBuilding=*/false, /*isTentative=*/true, + noteBuilding(Cmd, /*willBeBuilding=*/false, /*isTentative=*/isTentative, forRanges, "file is up-to-date and output exists"); return false; } @@ -1118,11 +1144,7 @@ namespace driver { IncrementalTracer)) CascadedJobs.insert(transitiveCmd); } - for (auto *transitiveCmd : CascadedJobs) - noteBuilding(transitiveCmd, /*willBeBuilding=*/true, - /*isTentative=*/false, forRanges, - "because of the initial set"); - + noteBuildingJobs(CascadedJobs, forRanges, "because of the initial set"); return CascadedJobs; } @@ -1138,11 +1160,8 @@ namespace driver { for (const Job * marked: markExternalInDepGraph(dependency, forRanges)) ExternallyDependentJobs.push_back(marked); }); - for (auto *externalCmd : ExternallyDependentJobs) { - noteBuilding(externalCmd, /*willBeBuilding=*/true, - /*isTentative=*/false, forRanges, - "because of external dependencies"); - } + noteBuildingJobs(ExternallyDependentJobs, forRanges, + "because of external dependencies"); return ExternallyDependentJobs; } @@ -1493,11 +1512,11 @@ namespace driver { continue; // Be conservative, in case we use ranges this time but not next. - bool isCascading = true; + bool mightBeCascading = true; if (Comp.getIncrementalBuildEnabled()) - isCascading = isMarkedInDepGraph( + mightBeCascading = isMarkedInDepGraph( Cmd, /*forRanges=*/Comp.getEnableSourceRangeDependencies()); - UnfinishedCommands.insert({Cmd, isCascading}); + UnfinishedCommands.insert({Cmd, mightBeCascading}); } } } @@ -1824,8 +1843,6 @@ int Compilation::performJobsImpl(bool &abnormalExit, CompilationRecordPath + "~moduleonly"); } } - if (getEnableFineGrainedDependencies()) - assert(State.FineGrainedDepGraph.emitDotFileAndVerify(getDiags())); abnormalExit = State.hadAnyAbnormalExit(); return State.getResult(); } @@ -2053,3 +2070,23 @@ void Compilation::addDependencyPathOrCreateDummy( llvm::raw_fd_ostream(depPath, EC, llvm::sys::fs::F_None); } } + +void Compilation::sortJobsToMatchCompilationInputs( + const ArrayRef unsortedJobs, + SmallVectorImpl &sortedJobs) const { + llvm::DenseMap jobsByInput; + for (const Job *J : unsortedJobs) { + const CompileJobAction *CJA = cast(&J->getSource()); + const InputAction *IA = CJA->findSingleSwiftInput(); + auto R = + jobsByInput.insert(std::make_pair(IA->getInputArg().getValue(), J)); + assert(R.second); + (void)R; + } + for (const InputPair &P : getInputFiles()) { + auto I = jobsByInput.find(P.second->getValue()); + if (I != jobsByInput.end()) { + sortedJobs.push_back(I->second); + } + } +} diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index e3a6e2a3361cf..ef99128d3a3a8 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -958,7 +958,8 @@ Driver::buildCompilation(const ToolChain &TC, // relies on the new dependency graph const bool EnableFineGrainedDependencies = - ArgList->hasArg(options::OPT_enable_fine_grained_dependencies); + ArgList->hasFlag(options::OPT_enable_fine_grained_dependencies, + options::OPT_disable_fine_grained_dependencies, false); const bool VerifyFineGrainedDependencyGraphAfterEveryImport = ArgList->hasArg( options:: diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index dc307fea0fab6..a80b1c34b3270 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -42,8 +42,7 @@ using namespace swift::driver; // MARK: Interfacing to Compilation //============================================================================== -using LoadResult = - fine_grained_dependencies::CoarseGrainedDependencyGraphImpl::LoadResult; +using LoadResult = CoarseGrainedDependencyGraphImpl::LoadResult; LoadResult ModuleDepGraph::loadFromPath(const Job *Cmd, StringRef path, DiagnosticEngine &diags) { @@ -66,6 +65,11 @@ LoadResult ModuleDepGraph::loadFromPath(const Job *Cmd, StringRef path, return r; } +LoadResult ModuleDepGraph::loadFromString(const Job *cmd, StringRef data) { + auto buffer = llvm::MemoryBuffer::getMemBuffer(data); + return loadFromBuffer(cmd, *buffer.get()); +} + LoadResult ModuleDepGraph::loadFromBuffer(const Job *job, llvm::MemoryBuffer &buffer) { @@ -73,50 +77,73 @@ LoadResult ModuleDepGraph::loadFromBuffer(const Job *job, SourceFileDepGraph::loadFromBuffer(buffer); if (!sourceFileDepGraph) return CoarseGrainedDependencyGraphImpl::LoadResult::HadError; + return loadFromSourceFileDepGraph(job, sourceFileDepGraph.getValue()); +} + +LoadResult ModuleDepGraph::loadFromSourceFileDepGraph( + const Job *job, const SourceFileDepGraph &sourceFileDepGraph) { addIndependentNode(job); - return integrate(sourceFileDepGraph.getValue()); + return integrate(sourceFileDepGraph); } bool ModuleDepGraph::isMarked(const Job *cmd) const { - return cascadingJobs.count(getSwiftDeps(cmd)); + return swiftDepsOfJobsThatNeedRunning.count(getSwiftDeps(cmd)); } std::vector ModuleDepGraph::markTransitive( const Job *jobToBeRecompiled, const void *ignored) { FrontendStatsTracer tracer(stats, "fine-grained-dependencies-markTransitive"); + assert(jobToBeRecompiled && "Ensure there is really a job"); std::unordered_set dependentNodes; const StringRef swiftDepsToBeRecompiled = getSwiftDeps(jobToBeRecompiled); - // Do the traversal. + assert(!swiftDepsToBeRecompiled.empty() && "Must have a swift deps"); + // Caller already knows to run this job, no need to return it. + recordJobNeedsRunning(swiftDepsToBeRecompiled); + + // Do the traversal for every node in the job to be recompiled. for (auto &fileAndNode : nodeMap[swiftDepsToBeRecompiled]) { assert(isCurrentPathForTracingEmpty()); - findDependentNodesAndRecordCascadingOnes(dependentNodes, - fileAndNode.second); + findDependentNodes(dependentNodes, fileAndNode.second); } - return computeUniqueJobsFromNodes(dependentNodes); + std::vector newJobsToCompile; + // The job containing the interface "cascades", in other words + // whenever that job gets recompiled, anything depending on it + // (since we don't have interface-specific dependency info as of Dec. + // 2018) must be recompiled. + std::vector dependentNodesVec{ + dependentNodes.begin(), dependentNodes.end()}; + for (const auto &entry : + computeSwiftDepsFromInterfaceNodes(dependentNodesVec)) { + const StringRef swiftDeps = entry.getKey(); + if (recordJobNeedsRunning(swiftDeps)) { + const Job *j = getJob(swiftDeps.str()); + newJobsToCompile.push_back(j); + } + } + return newJobsToCompile; } -std::vector ModuleDepGraph::computeUniqueJobsFromNodes( - const std::unordered_set &nodes) { - - std::vectorjobs; +llvm::StringSet<> ModuleDepGraph::computeSwiftDepsFromInterfaceNodes( + const ArrayRef nodes) { - std::unordered_set swiftDepsOfNodes; + llvm::StringSet<> swiftDepsOfNodes; for (const ModuleDepGraphNode *n : nodes) { - if (!n->getSwiftDeps().hasValue()) + // if (!n->doesNodeProvideAnInterface()) + // continue; + if (!n->getIsProvides()) continue; - const std::string &swiftDeps = n->getSwiftDeps().getValue(); + const std::string &swiftDeps = n->getSwiftDepsOfProvides(); if (swiftDepsOfNodes.insert(swiftDeps).second) { assert(n->assertImplementationMustBeInAFile()); - ensureJobIsTracked(swiftDeps); - jobs.push_back(getJob(swiftDeps)); + assert(ensureJobIsTracked(swiftDeps)); } } - return jobs; + return swiftDepsOfNodes; } -bool ModuleDepGraph::markIntransitive(const Job *node) { - return rememberThatJobCascades(getSwiftDeps(node)); +bool ModuleDepGraph::markIntransitive(const Job *job) { + return recordJobNeedsRunning(getSwiftDeps(job)); } void ModuleDepGraph::addIndependentNode(const Job *job) { @@ -150,11 +177,10 @@ void ModuleDepGraph::forEachUnmarkedJobDirectlyDependentOnExternalSwiftdeps( DependencyKey key = DependencyKey::createDependedUponKey( externalSwiftDeps.str()); - // collect answers into useSet - std::unordered_set visitedSet; for (const ModuleDepGraphNode *useNode : usesByDef[key]) { - const Job *job = getJob(useNode->getSwiftDeps()); - if (!isMarked(job)) + const auto swiftDepsOfUse = useNode->getSwiftDepsOfProvides(); + const Job *job = getJob(swiftDepsOfUse); + if (isMarked(job)) continue; fn(job); } @@ -167,7 +193,7 @@ void ModuleDepGraph::forEachUnmarkedJobDirectlyDependentOnExternalSwiftdeps( LoadResult ModuleDepGraph::integrate(const SourceFileDepGraph &g) { FrontendStatsTracer tracer(stats, "fine-grained-dependencies-integrate"); - StringRef swiftDeps = g.getSwiftDepsFromSourceFileProvide(); + StringRef swiftDeps = g.getSwiftDepsOfJobThatProducedThisGraph(); // When done, disappearedNodes contains the nodes which no longer exist. auto disappearedNodes = nodeMap[swiftDeps]; // When done, changeDependencyKeys contains a list of keys that changed @@ -229,10 +255,14 @@ bool ModuleDepGraph::integrateSourceFileDepGraphNode( if (integrand->getKey().getKind() == NodeKind::externalDepend) return externalDependencies.insert(integrand->getKey().getName()).second; + // Since dependencies are modeled as arcs in both SourceFile and Module + // dependency graphs, no more integration need be done for a depends node. The + // information will be obtained front the using node's arcs. if (integrand->isDepends()) - return false; // dependency will be handled by the use node + return false; - StringRef swiftDepsOfSourceFileGraph = g.getSwiftDepsFromSourceFileProvide(); + StringRef swiftDepsOfSourceFileGraph = + g.getSwiftDepsOfJobThatProducedThisGraph(); auto changedAndUseNode = integrateSourceFileDeclNode( integrand, swiftDepsOfSourceFileGraph, preexistingMatch); recordWhatUseDependsUpon(g, integrand, changedAndUseNode.second); @@ -292,6 +322,10 @@ void ModuleDepGraph::recordWhatUseDependsUpon( void ModuleDepGraph::removeNode(ModuleDepGraphNode *n) { eraseNodeFromMap(n); + eraseNodeFromUsesByDef(n); + eraseNodeFromCurrentPathIfTracing(n); + eraseNodeFromDependencyPathToJobs(n); + delete n; } @@ -307,6 +341,15 @@ void ModuleDepGraph::forEachUseOf( return; for (const ModuleDepGraphNode *useNode : iter->second) fn(useNode); + // Add in implicit interface->implementation dependency + if (def->getKey().isInterface() && def->getSwiftDeps()) { + const auto &dk = def->getKey(); + const DependencyKey key(dk.getKind(), DeclAspect::interface, + dk.getContext(), dk.getName()); + if (const auto interfaceNode = + nodeMap.find(def->getSwiftDeps().getValue(), dk)) + fn(interfaceNode.getValue()); + } } void ModuleDepGraph::forEachNode( @@ -340,31 +383,22 @@ void ModuleDepGraph::forEachArc( // Could be faster by passing in a file, not a node, but we are trying for // generality. -void ModuleDepGraph::findDependentNodesAndRecordCascadingOnes( +void ModuleDepGraph::findDependentNodes( std::unordered_set &foundDependents, const ModuleDepGraphNode *definition) { - size_t pathLengthAfterArrival = traceArrival(definition); + size_t pathLengthAfterArrival = traceArrival(definition); // Moved this out of the following loop for effieciency. - assert(definition->getSwiftDeps().hasValue() && - "Should only call me for Decl nodes."); + assert(definition->getIsProvides() && "Should only call me for Decl nodes."); forEachUseOf(definition, [&](const ModuleDepGraphNode *u) { // Cycle recording and check. if (!foundDependents.insert(u).second) return; - if (u->getKey().isInterface() && u->getSwiftDeps().hasValue()) { - // An interface depends on something. Thus, if that something changes - // the interface must be recompiled. But if an interface changes, then - // anything using that interface must also be recompiled. - // So, the job containing the interface "cascades", in other words - // whenever that job gets recompiled, anything depending on it - // (since we don't have interface-specific dependency info as of Dec. - // 2018) must be recompiled. - rememberThatJobCascades(u->getSwiftDeps().getValue()); - findDependentNodesAndRecordCascadingOnes(foundDependents, u); - } + // If this use also provides something, follow it + if (u->getIsProvides()) + findDependentNodes(foundDependents, u); }); traceDeparture(pathLengthAfterArrival); } @@ -373,9 +407,9 @@ size_t ModuleDepGraph::traceArrival(const ModuleDepGraphNode *visitedNode) { if (!currentPathIfTracing.hasValue()) return 0; auto ¤tPath = currentPathIfTracing.getValue(); - recordDependencyPathToJob(currentPath, getJob(visitedNode->getSwiftDeps())); - currentPath.push_back(visitedNode); + const auto visitedSwiftDepsIfAny = visitedNode->getSwiftDeps(); + recordDependencyPathToJob(currentPath, getJob(visitedSwiftDepsIfAny)); return currentPath.size(); } @@ -404,7 +438,8 @@ void ModuleDepGraph::emitDotFileForJob(DiagnosticEngine &diags, void ModuleDepGraph::emitDotFile(DiagnosticEngine &diags, StringRef baseName) { unsigned seqNo = dotFileSequenceNumber[baseName]++; - std::string fullName = baseName.str() + "." + std::to_string(seqNo) + ".dot"; + std::string fullName = + baseName.str() + "-post-integration." + std::to_string(seqNo) + ".dot"; withOutputFile(diags, fullName, [&](llvm::raw_ostream &out) { emitDotFile(out); return false; @@ -422,8 +457,8 @@ void ModuleDepGraph::emitDotFile(llvm::raw_ostream &out) { void ModuleDepGraphNode::dump() const { DepGraphNode::dump(); - if (getSwiftDeps().hasValue()) - llvm::errs() << " swiftDeps: <" << getSwiftDeps().getValue() << ">\n"; + if (getIsProvides()) + llvm::errs() << " swiftDeps: <" << getSwiftDepsOfProvides() << ">\n"; else llvm::errs() << " no swiftDeps\n"; } @@ -478,9 +513,7 @@ void ModuleDepGraph::verifyNodeIsUniqueWithinSubgraph( assert(submapIndex < nodesSeenInNodeMap.size() && "submapIndex is out of bounds."); auto iterInserted = nodesSeenInNodeMap[submapIndex][n->getKey()].insert( - std::make_pair(n->getSwiftDeps().hasValue() ? n->getSwiftDeps().getValue() - : std::string(), - n)); + std::make_pair(n->getSwiftDepsForMapKey(), n)); if (!iterInserted.second) { llvm_unreachable("duplicate driver keys"); } @@ -523,23 +556,75 @@ void ModuleDepGraph::verifyEachJobInGraphIsTracked() const { }); } -bool ModuleDepGraph::emitDotFileAndVerify(DiagnosticEngine &diags) { - if (!driverDotFileBasePath.empty()) - emitDotFile(diags, driverDotFileBasePath); - return verify(); -} - -/// Dump the path that led to \p node. -/// TODO: make output more like existing system's +/// Dump the path(s) that led to \p node. +/// TODO: break up void ModuleDepGraph::printPath(raw_ostream &out, const driver::Job *jobToBeBuilt) const { assert(currentPathIfTracing.hasValue() && "Cannot print paths of paths weren't tracked."); - auto const allPaths = dependencyPathsToJobs.find(jobToBeBuilt); - if (allPaths == dependencyPathsToJobs.cend()) - return; - for (const auto *n : allPaths->second) { - out << n->humanReadableName() << "\n"; + + for (auto paths = dependencyPathsToJobs.find(jobToBeBuilt); + paths != dependencyPathsToJobs.end() && paths->first == jobToBeBuilt; + ++paths) { + const auto &path = paths->second; + bool first = true; + out << "\t"; + for (const ModuleDepGraphNode *n : path) { + if (first) + first = false; + else + out << " -> "; + + const StringRef providerName = getProvidingFilename(n->getSwiftDeps()); + printOneNodeOfPath(out, n->getKey(), providerName); + } + out << "\n"; + } +} + +StringRef ModuleDepGraph::getProvidingFilename( + const Optional swiftDeps) const { + if (!swiftDeps) + return "getFirstSwiftPrimaryInput()); + // FineGrainedDependencyGraphTests work with simulated jobs with empty + // input names. + return !inputName.empty() ? inputName : StringRef(swiftDeps.getValue()); +} + +void ModuleDepGraph::printOneNodeOfPath(raw_ostream &out, + const DependencyKey &key, + const StringRef filename) { + switch (key.getKind()) { + case NodeKind::topLevel: + out << key.aspectName() << " of top-level name '" << key.humanReadableName() + << "' in " << filename; + break; + case NodeKind::nominal: + out << key.aspectName() << " of type '" << key.humanReadableName() + << "' in " << filename; + break; + case NodeKind::potentialMember: + out << key.aspectName() << " of non-private members '" + << key.humanReadableName() << "' in " << filename; + break; + case NodeKind::member: + out << key.aspectName() << " of member '" << key.humanReadableName() + << "' in " << filename; + break; + case NodeKind::dynamicLookup: + out << key.aspectName() << " of AnyObject member '" + << key.humanReadableName() << "' in " << filename; + break; + case NodeKind::externalDepend: + out << filename << " depends on " << key.aspectName() << " of module '" + << key.humanReadableName() << "'"; + break; + case NodeKind::sourceFileProvide: + out << key.aspectName() << " of source file " << key.humanReadableName(); + break; + default: + llvm_unreachable("unknown NodeKind"); } - out << "\n"; } diff --git a/lib/Driver/Job.cpp b/lib/Driver/Job.cpp index a4e21ad0c9b6b..c6362335de7d8 100644 --- a/lib/Driver/Job.cpp +++ b/lib/Driver/Job.cpp @@ -25,6 +25,11 @@ using namespace swift; using namespace swift::driver; +CommandOutput::CommandOutput(StringRef dummyBase, OutputFileMap &dummyOFM) + : Inputs({CommandInputPair(dummyBase, "")}), DerivedOutputMap(dummyOFM) { + setAdditionalOutputForType(file_types::TY_SwiftDeps, dummyBase); +} + StringRef CommandOutput::getOutputForInputAndType(StringRef PrimaryInputFile, file_types::ID Type) const { if (Type == file_types::TY_Nothing) diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index c8b8ab791692b..87398eecadb9b 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -178,26 +178,6 @@ file_types::ID ToolChain::lookupTypeForExtension(StringRef Ext) const { return file_types::lookupTypeForExtension(Ext); } -/// Return a _single_ TY_Swift InputAction, if one exists; -/// if 0 or >1 such inputs exist, return nullptr. -static const InputAction *findSingleSwiftInput(const CompileJobAction *CJA) { - auto Inputs = CJA->getInputs(); - const InputAction *IA = nullptr; - for (auto const *I : Inputs) { - if (auto const *S = dyn_cast(I)) { - if (S->getType() == file_types::TY_Swift) { - if (IA == nullptr) { - IA = S; - } else { - // Already found one, two is too many. - return nullptr; - } - } - } - } - return IA; -} - static bool jobsHaveSameExecutableNames(const Job *A, const Job *B) { // Jobs that get here (that are derived from CompileJobActions) should always // have the same executable name -- it should always be SWIFT_EXECUTABLE_NAME @@ -243,7 +223,7 @@ bool ToolChain::jobIsBatchable(const Compilation &C, const Job *A) const { if (C.OnlyOneDependencyFile && A->getOutput().hasAdditionalOutputForType(file_types::TY_Dependencies)) return false; - return findSingleSwiftInput(CJActA) != nullptr; + return CJActA->findSingleSwiftInput() != nullptr; } bool ToolChain::jobsAreBatchCombinable(const Compilation &C, const Job *A, @@ -305,33 +285,6 @@ mergeBatchInputs(ArrayRef jobs, return false; } -/// Unfortunately the success or failure of a Swift compilation is currently -/// sensitive to the order in which files are processed, at least in terms of -/// the order of processing extensions (and likely other ways we haven't -/// discovered yet). So long as this is true, we need to make sure any batch job -/// we build names its inputs in an order that's a subsequence of the sequence -/// of inputs the driver was initially invoked with. -static void -sortJobsToMatchCompilationInputs(ArrayRef unsortedJobs, - SmallVectorImpl &sortedJobs, - Compilation &C) { - llvm::DenseMap jobsByInput; - for (const Job *J : unsortedJobs) { - const CompileJobAction *CJA = cast(&J->getSource()); - const InputAction *IA = findSingleSwiftInput(CJA); - auto R = - jobsByInput.insert(std::make_pair(IA->getInputArg().getValue(), J)); - assert(R.second); - (void)R; - } - for (const InputPair &P : C.getInputFiles()) { - auto I = jobsByInput.find(P.second->getValue()); - if (I != jobsByInput.end()) { - sortedJobs.push_back(I->second); - } - } -} - /// Construct a \c BatchJob by merging the constituent \p jobs' CommandOutput, /// input \c Job and \c Action members. Call through to \c constructInvocation /// on \p BatchJob, to build the \c InvocationInfo. @@ -343,7 +296,7 @@ ToolChain::constructBatchJob(ArrayRef unsortedJobs, return nullptr; llvm::SmallVector sortedJobs; - sortJobsToMatchCompilationInputs(unsortedJobs, sortedJobs, C); + C.sortJobsToMatchCompilationInputs(unsortedJobs, sortedJobs); // Synthetic OutputInfo is a slightly-modified version of the initial // compilation's OI. diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 087d819cbb9c7..0cb8248242a83 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -230,6 +230,8 @@ static void addCommonFrontendArgs(const ToolChain &TC, const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_AssumeSingleThreaded); inputArgs.AddLastArg(arguments, options::OPT_enable_fine_grained_dependencies); + inputArgs.AddLastArg(arguments, + options::OPT_disable_fine_grained_dependencies); inputArgs.AddLastArg(arguments, options::OPT_fine_grained_dependency_include_intrafile); inputArgs.AddLastArg(arguments, options::OPT_package_description_version); @@ -641,6 +643,12 @@ void ToolChain::JobContext::addFrontendCommandLineInputArguments( if ((!isPrimary || usePrimaryFileList) && !useFileList) arguments.push_back(inputName); } + if (C.getEnableFineGrainedDependencies()) + arguments.push_back("-enable-fine-grained-dependencies"); + + if (Args.hasArg( + options::OPT_emit_fine_grained_dependency_sourcefile_dot_files)) + arguments.push_back("-emit-fine-grained-dependency-sourcefile-dot-files"); } void ToolChain::JobContext::addFrontendSupplementaryOutputArguments( diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 355aeff500586..9add4f3c19bd3 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -357,9 +357,13 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.BuildSyntaxTree = true; Opts.VerifySyntaxTree = true; } - - if (Args.hasArg(OPT_enable_fine_grained_dependencies)) - Opts.EnableFineGrainedDependencies = true; + + Opts.EnableFineGrainedDependencies = + Args.hasFlag(options::OPT_enable_fine_grained_dependencies, + options::OPT_disable_fine_grained_dependencies, false); + + if (Args.hasArg(OPT_emit_fine_grained_dependency_sourcefile_dot_files)) + Opts.EmitFineGrainedDependencySourcefileDotFiles = true; if (Args.hasArg(OPT_fine_grained_dependency_include_intrafile)) Opts.FineGrainedDependenciesIncludeIntrafileOnes = true; diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index fd01c1131faf5..f88d564ab0889 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -979,7 +979,9 @@ static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( if (Invocation.getLangOptions().EnableFineGrainedDependencies) (void)fine_grained_dependencies::emitReferenceDependencies( Instance.getASTContext().Diags, SF, - *Instance.getDependencyTracker(), referenceDependenciesFilePath); + *Instance.getDependencyTracker(), referenceDependenciesFilePath, + Invocation.getLangOptions() + .EmitFineGrainedDependencySourcefileDotFiles); else (void)emitReferenceDependencies(Instance.getASTContext().Diags, SF, *Instance.getDependencyTracker(), diff --git a/test/Driver/batch_mode_dependencies_make_wrong_order.swift b/test/Driver/batch_mode_dependencies_make_wrong_order.swift index 6888ee6461564..8e26f32a3e87e 100644 --- a/test/Driver/batch_mode_dependencies_make_wrong_order.swift +++ b/test/Driver/batch_mode_dependencies_make_wrong_order.swift @@ -16,13 +16,13 @@ // RUN: cd %t && %swiftc_driver -enable-batch-mode -incremental -output-file-map %S/Inputs/abcd_filemap.yaml -module-name main -j 1 d.swift c.swift b.swift a.swift main.swift -driver-show-incremental -driver-show-job-lifecycle >%t/out.txt 2>&1 // RUN: %FileCheck %s <%t/out.txt // -// Check that we saw invalidation happen in alphabetic order -// CHECK: Queuing because of dependencies discovered later: {compile: b.o <= b.swift} -// CHECK: Queuing because of dependencies discovered later: {compile: c.o <= c.swift} +// Check that we saw invalidation happen in command-line argument order // CHECK: Queuing because of dependencies discovered later: {compile: d.o <= d.swift} -// CHECK: Batchable: {compile: b.o <= b.swift} -// CHECK: Batchable: {compile: c.o <= c.swift} +// CHECK: Queuing because of dependencies discovered later: {compile: c.o <= c.swift} +// CHECK: Queuing because of dependencies discovered later: {compile: b.o <= b.swift} // CHECK: Batchable: {compile: d.o <= d.swift} +// CHECK: Batchable: {compile: c.o <= c.swift} +// CHECK: Batchable: {compile: b.o <= b.swift} // -// But check that we still issued the job in reverse-alphabetic order +// Check that we still issued the job in reverse-alphabetic order // CHECK: Adding batch job to task queue: {compile: d.o c.o b.o <= d.swift c.swift b.swift} From 3126142490fb269d8d42c5d0485e95293874bb59 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Sat, 4 Jan 2020 10:05:38 -0800 Subject: [PATCH 266/478] Add fine-grained unit test --- unittests/Driver/CMakeLists.txt | 3 + .../FineGrainedDependencyGraphTests.cpp | 833 ++++++++++++++++++ 2 files changed, 836 insertions(+) create mode 100644 unittests/Driver/FineGrainedDependencyGraphTests.cpp diff --git a/unittests/Driver/CMakeLists.txt b/unittests/Driver/CMakeLists.txt index 425fa364b84e2..fb50d3d15a858 100644 --- a/unittests/Driver/CMakeLists.txt +++ b/unittests/Driver/CMakeLists.txt @@ -1,8 +1,11 @@ add_swift_unittest(SwiftDriverTests CoarseGrainedDependencyGraphTests.cpp + FineGrainedDependencyGraphTests.cpp ) target_link_libraries(SwiftDriverTests PRIVATE swiftDriver + swiftClangImporter + swiftAST ) diff --git a/unittests/Driver/FineGrainedDependencyGraphTests.cpp b/unittests/Driver/FineGrainedDependencyGraphTests.cpp new file mode 100644 index 0000000000000..953ac78885839 --- /dev/null +++ b/unittests/Driver/FineGrainedDependencyGraphTests.cpp @@ -0,0 +1,833 @@ +#include "swift/Basic/ReferenceDependencyKeys.h" +#include "swift/Driver/CoarseGrainedDependencyGraph.h" +#include "swift/Driver/FineGrainedDependencyDriverGraph.h" +#include "swift/Driver/Job.h" +#include "gtest/gtest.h" + +// This file adapts the unit tests from the older, coarse-grained, dependency +// graph to the new fine-grained graph. + +// \c markTransitive and \c markExternal may include jobs in their result +// that would be excluded in the coarse-grained graph. But since these will be +// jobs that have already been scheduled, downstream mechanisms will filter +// them out. + +using namespace swift; +using LoadResult = CoarseGrainedDependencyGraphImpl::LoadResult; +using namespace reference_dependency_keys; +using namespace fine_grained_dependencies; +using Job = driver::Job; + +/// Initial underscore makes non-cascading, on member means private. +static LoadResult +simulateLoad(ModuleDepGraph &dg, const Job *cmd, + llvm::StringMap> simpleNames, + llvm::StringMap>> + compoundNames = {}, + const bool includePrivateDeps = true, + const bool hadCompilationError = false) { + StringRef swiftDeps = + cmd->getOutput().getAdditionalOutputForType(file_types::TY_SwiftDeps); + assert(!swiftDeps.empty()); + StringRef interfaceHash = swiftDeps; + auto sfdg = SourceFileDepGraph::simulateLoad( + swiftDeps, includePrivateDeps, hadCompilationError, interfaceHash, + simpleNames, compoundNames); + + return dg.loadFromSourceFileDepGraph(cmd, sfdg); +} + +LLVM_ATTRIBUTE_UNUSED +static std::vector +printForDebugging(std::vector jobs) { + llvm::errs() << "\nprintForDebugging: "; + for (auto *j : jobs) { + const auto swiftDeps = + j->getOutput().getAdditionalOutputForType(file_types::TY_SwiftDeps); + assert(!swiftDeps.empty()); + llvm::errs() << "job" << swiftDeps << ", "; + } + llvm::errs() << "\n"; + return jobs; +} + +static OutputFileMap OFM; + +static Job job0(OFM, "0"), job1(OFM, "1"), job2(OFM, "2"), job3(OFM, "3"), + job4(OFM, "4"), job5(OFM, "5"), job6(OFM, "6"), job7(OFM, "7"), + job8(OFM, "8"), job9(OFM, "9"), job10(OFM, "10"), job11(OFM, "11"), + job12(OFM, "12"); + +template +static bool contains(const Range &range, const T &value) { + return std::find(std::begin(range), std::end(range), value) != + std::end(range); +} + +TEST(ModuleDepGraph, BasicLoad) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{dependsTopLevel, {"a", "b"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"c", "d"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job2, {{providesTopLevel, {"e", "f"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job3, {{providesNominal, {"g", "h"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job4, {{providesDynamicLookup, {"i", "j"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job5, {{dependsDynamicLookup, {"k", "l"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job6, {}, + {{providesMember, {{"m", "mm"}, {"n", "nn"}}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job7, {}, + {{dependsMember, {{"o", "oo"}, {"p", "pp"}}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job8, {{dependsExternal, {"/foo", "/bar"}}}), + LoadResult::AffectsDownstream); + + EXPECT_EQ(simulateLoad(graph, &job9, + {{providesNominal, {"a", "b"}}, + {providesTopLevel, {"b", "c"}}, + {dependsNominal, {"c", "d"}}, + {dependsTopLevel, {"d", "a"}}}), + LoadResult::AffectsDownstream); +} + +TEST(ModuleDepGraph, IndependentNodes) { + ModuleDepGraph graph; + + EXPECT_EQ( + simulateLoad(graph, &job0, + {{dependsTopLevel, {"a"}}, {providesTopLevel, {"a0"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ( + simulateLoad(graph, &job1, + {{dependsTopLevel, {"b"}}, {providesTopLevel, {"b0"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ( + simulateLoad(graph, &job2, + {{dependsTopLevel, {"c"}}, {providesTopLevel, {"c0"}}}), + LoadResult::AffectsDownstream); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_FALSE(graph.isMarked(&job1)); + EXPECT_FALSE(graph.isMarked(&job2)); + + // Mark 0 again -- should be no change. + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_FALSE(graph.isMarked(&job1)); + EXPECT_FALSE(graph.isMarked(&job2)); + + EXPECT_EQ(0u, graph.markTransitive(&job2).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_FALSE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); + + EXPECT_EQ(0u, graph.markTransitive(&job1).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); +} + +TEST(ModuleDepGraph, IndependentDepKinds) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, + {{dependsNominal, {"a"}}, {providesNominal, {"b"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, + {{dependsTopLevel, {"b"}}, {providesTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_FALSE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, IndependentDepKinds2) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, + {{dependsNominal, {"a"}}, {providesNominal, {"b"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, + {{dependsTopLevel, {"b"}}, {providesTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + + EXPECT_EQ(0u, graph.markTransitive(&job1).size()); + EXPECT_FALSE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, IndependentMembers) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {}, {{providesMember, {{"a", "aa"}}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {}, {{dependsMember, {{"a", "bb"}}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job2, {}, {{dependsMember, {{"a", ""}}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job3, {}, {{dependsMember, {{"b", "aa"}}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job4, {}, {{dependsMember, {{"b", "bb"}}}}), + LoadResult::AffectsDownstream); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_FALSE(graph.isMarked(&job1)); + EXPECT_FALSE(graph.isMarked(&job2)); + EXPECT_FALSE(graph.isMarked(&job3)); + EXPECT_FALSE(graph.isMarked(&job4)); +} + +TEST(ModuleDepGraph, SimpleDependent) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesTopLevel, {"a", "b", "c"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{dependsTopLevel, {"x", "b", "z"}}}), + LoadResult::AffectsDownstream); + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job1, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependentReverse) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{dependsTopLevel, {"a", "b", "c"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{providesTopLevel, {"x", "b", "z"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job1); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job0, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependent2) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a", "b", "c"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"x", "b", "z"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job1, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependent3) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, + {{providesNominal, {"a"}}, {providesTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"a"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job1, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependent4) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, + {{dependsNominal, {"a"}}, {dependsTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job1, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependent5) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, + {{providesNominal, {"a"}}, {providesTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, + {{dependsNominal, {"a"}}, {dependsTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job1, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependent6) { + ModuleDepGraph graph; + + EXPECT_EQ( + simulateLoad(graph, &job0, {{providesDynamicLookup, {"a", "b", "c"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ( + simulateLoad(graph, &job1, {{dependsDynamicLookup, {"x", "b", "z"}}}), + LoadResult::AffectsDownstream); + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job1, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependentMember) { + ModuleDepGraph graph; + + EXPECT_EQ( + simulateLoad(graph, &job0, {}, + {{providesMember, {{"a", "aa"}, {"b", "bb"}, {"c", "cc"}}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ( + simulateLoad(graph, &job1, {}, + {{dependsMember, {{"x", "xx"}, {"b", "bb"}, {"z", "zz"}}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job1, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, MultipleDependentsSame) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a", "b", "c"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"x", "b", "z"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"q", "b", "s"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(2u, marked.size()); + EXPECT_TRUE(contains(marked, &job1)); + EXPECT_TRUE(contains(marked, &job2)); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); +} + +TEST(ModuleDepGraph, MultipleDependentsDifferent) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a", "b", "c"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"x", "b", "z"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"q", "r", "c"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(2u, marked.size()); + EXPECT_TRUE(contains(marked, &job1)); + EXPECT_TRUE(contains(marked, &job2)); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); +} + +TEST(ModuleDepGraph, ChainedDependents) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a", "b", "c"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ( + simulateLoad(graph, &job1, + {{dependsNominal, {"x", "b"}}, {providesNominal, {"z"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"z"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(2u, marked.size()); + EXPECT_TRUE(contains(marked, &job1)); + EXPECT_TRUE(contains(marked, &job2)); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); +} + +TEST(ModuleDepGraph, ChainedNoncascadingDependents) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a", "b", "c"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ( + simulateLoad(graph, &job1, + {{dependsNominal, {"x", "b"}}, {providesNominal, {"_z"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"_z"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(2u, marked.size()); + EXPECT_TRUE(contains(marked, &job1)); + EXPECT_TRUE(contains(marked, &job2)); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); +} + +TEST(ModuleDepGraph, ChainedNoncascadingDependents2) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesTopLevel, {"a", "_b", "c"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ( + simulateLoad(graph, &job1, + {{dependsTopLevel, {"x", "_b"}}, {providesNominal, {"z"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"z"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_TRUE(contains(marked, &job1)); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_FALSE(graph.isMarked(&job2)); +} + +TEST(ModuleDepGraph, MarkTwoNodes) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesTopLevel, {"a", "b"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, + {{dependsTopLevel, {"a"}}, {providesTopLevel, {"z"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job2, {{dependsTopLevel, {"z"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ( + simulateLoad(graph, &job10, + {{providesTopLevel, {"y", "z"}}, {dependsTopLevel, {"q"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job11, {{dependsTopLevel, {"y"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job12, + {{dependsTopLevel, {"q"}}, {providesTopLevel, {"q"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(2u, marked.size()); + EXPECT_TRUE(contains(marked, &job1)); + EXPECT_TRUE(contains(marked, &job2)); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); + EXPECT_FALSE(graph.isMarked(&job10)); + EXPECT_FALSE(graph.isMarked(&job11)); + EXPECT_FALSE(graph.isMarked(&job12)); + + { + auto marked = graph.markTransitive(&job10); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job11, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); + EXPECT_TRUE(graph.isMarked(&job10)); + EXPECT_TRUE(graph.isMarked(&job11)); + EXPECT_FALSE(graph.isMarked(&job12)); +} + +TEST(ModuleDepGraph, MarkOneNodeTwice) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"b"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job1, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_FALSE(graph.isMarked(&job2)); + + // Reload 0. + EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"b"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job2, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); +} + +TEST(ModuleDepGraph, MarkOneNodeTwice2) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"b"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job1, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_FALSE(graph.isMarked(&job2)); + + // Reload 0. + EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a", "b"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job2, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); +} + +TEST(ModuleDepGraph, ReloadDetectsChange) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"b"}}}), + LoadResult::AffectsDownstream); + + EXPECT_EQ(0u, graph.markTransitive(&job1).size()); + EXPECT_FALSE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_FALSE(graph.isMarked(&job2)); + + // Reload 1. + EXPECT_EQ(simulateLoad(graph, &job1, + {{dependsNominal, {"a"}}, {providesNominal, {"b"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job2, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); + + // Re-mark 1. + EXPECT_EQ(0u, graph.markTransitive(&job1).size()); + + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); +} + +TEST(ModuleDepGraph, DependencyLoops) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, + {{providesTopLevel, {"a", "b", "c"}}, + {dependsTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, + {{providesTopLevel, {"x"}}, + {dependsTopLevel, {"x", "b", "z"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job2, {{dependsTopLevel, {"x"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(2u, marked.size()); + EXPECT_TRUE(contains(marked, &job1)); + EXPECT_TRUE(contains(marked, &job2)); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + EXPECT_TRUE(graph.isMarked(&job2)); +} + +TEST(ModuleDepGraph, MarkIntransitive) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesTopLevel, {"a", "b", "c"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{dependsTopLevel, {"x", "b", "z"}}}), + LoadResult::AffectsDownstream); + + EXPECT_TRUE(graph.markIntransitive(&job0)); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_FALSE(graph.isMarked(&job1)); + + { + auto marked = graph.markTransitive(&job0); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job1, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, MarkIntransitiveTwice) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesTopLevel, {"a", "b", "c"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{dependsTopLevel, {"x", "b", "z"}}}), + LoadResult::AffectsDownstream); + + EXPECT_TRUE(graph.markIntransitive(&job0)); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_FALSE(graph.isMarked(&job1)); + + EXPECT_FALSE(graph.markIntransitive(&job0)); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_FALSE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, MarkIntransitiveThenIndirect) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{providesTopLevel, {"a", "b", "c"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ(simulateLoad(graph, &job1, {{dependsTopLevel, {"x", "b", "z"}}}), + LoadResult::AffectsDownstream); + + EXPECT_TRUE(graph.markIntransitive(&job1)); + EXPECT_FALSE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + + EXPECT_EQ(0u, graph.markTransitive(&job0).size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, SimpleExternal) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{dependsExternal, {"/foo", "/bar"}}}), + LoadResult::AffectsDownstream); + + EXPECT_TRUE(contains(graph.getExternalDependencies(), "/foo")); + EXPECT_TRUE(contains(graph.getExternalDependencies(), "/bar")); + + EXPECT_EQ(1u, graph.markExternal("/foo").size()); + EXPECT_TRUE(graph.isMarked(&job0)); + + EXPECT_EQ(0u, graph.markExternal("/foo").size()); + EXPECT_TRUE(graph.isMarked(&job0)); +} + +TEST(ModuleDepGraph, SimpleExternal2) { + ModuleDepGraph graph; + + EXPECT_EQ(simulateLoad(graph, &job0, {{dependsExternal, {"/foo", "/bar"}}}), + LoadResult::AffectsDownstream); + + EXPECT_EQ(1u, graph.markExternal("/bar").size()); + EXPECT_TRUE(graph.isMarked(&job0)); + + EXPECT_EQ(0u, graph.markExternal("/bar").size()); + EXPECT_TRUE(graph.isMarked(&job0)); +} + +TEST(ModuleDepGraph, ChainedExternal) { + ModuleDepGraph graph; + + EXPECT_EQ( + simulateLoad(graph, &job0, + {{dependsExternal, {"/foo"}}, {providesTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ( + simulateLoad(graph, &job1, + {{dependsExternal, {"/bar"}}, {dependsTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + + EXPECT_TRUE(contains(graph.getExternalDependencies(), "/foo")); + EXPECT_TRUE(contains(graph.getExternalDependencies(), "/bar")); + + EXPECT_EQ(2u, graph.markExternal("/foo").size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + + EXPECT_EQ(0u, graph.markExternal("/foo").size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, ChainedExternalReverse) { + ModuleDepGraph graph; + + EXPECT_EQ( + simulateLoad(graph, &job0, + {{dependsExternal, {"/foo"}}, {providesTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ( + simulateLoad(graph, &job1, + {{dependsExternal, {"/bar"}}, {dependsTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + + { + auto marked = graph.markExternal("/bar"); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job1, marked.front()); + } + EXPECT_FALSE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + + EXPECT_EQ(0u, graph.markExternal("/bar").size()); + EXPECT_FALSE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); + + { + auto marked = graph.markExternal("/foo"); + EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(&job0, marked.front()); + } + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_TRUE(graph.isMarked(&job1)); +} + +TEST(ModuleDepGraph, ChainedExternalPreMarked) { + ModuleDepGraph graph; + + EXPECT_EQ( + simulateLoad(graph, &job0, + {{dependsExternal, {"/foo"}}, {providesTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + EXPECT_EQ( + simulateLoad(graph, &job1, + {{dependsExternal, {"/bar"}}, {dependsTopLevel, {"a"}}}), + LoadResult::AffectsDownstream); + + graph.markIntransitive(&job0); + + EXPECT_EQ(0u, graph.markExternal("/foo").size()); + EXPECT_TRUE(graph.isMarked(&job0)); + EXPECT_FALSE(graph.isMarked(&job1)); +} From 896d4fca0c496f9207efa5b2ae0adf63c088ec0f Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sat, 4 Jan 2020 15:01:15 -0800 Subject: [PATCH 267/478] Add efficiency docs for partitionPoint --- test/Prototypes/Algorithms.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/Prototypes/Algorithms.swift b/test/Prototypes/Algorithms.swift index 35f690df2dee3..24d48fa2b14ca 100644 --- a/test/Prototypes/Algorithms.swift +++ b/test/Prototypes/Algorithms.swift @@ -613,6 +613,11 @@ extension Collection { /// The collection must already be partitioned according to the /// predicate, as if `self.partition(by: predicate)` had already /// been called. + /// + /// - Efficiency: At most log(N) invocations of `predicate`, where + /// N is the length of `self`. At most log(N) index offsetting + /// operations if `self` conforms to `RandomAccessCollection`; + /// at most N such operations otherwise. func partitionPoint( where predicate: (Element) throws -> Bool ) rethrows -> Index { From 41cbcfd464582fb76c64775e055a4c5a45e42e39 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Sat, 4 Jan 2020 15:46:26 -0800 Subject: [PATCH 268/478] Fix for the Linux compiler --- ...endenciesSourceFileDepGraphConstructor.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp index ac4dc54c8a3f0..ca821a6af4e11 100644 --- a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp +++ b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp @@ -647,10 +647,10 @@ class SourceFileDepGraphConstructor { namesForProvidersOfAGivenType(std::vector &contentsVec) { std::vector result; for (const auto declOrPair : contentsVec) - result.push_back( - {DependencyKey::computeContextForProvidedEntity(declOrPair), - DependencyKey::computeNameForProvidedEntity(declOrPair), - Optional()}); + result.push_back(ContextNameFingerprint( + DependencyKey::computeContextForProvidedEntity(declOrPair), + DependencyKey::computeNameForProvidedEntity(declOrPair), + Optional())); return result; } @@ -733,11 +733,11 @@ void SourceFileDepGraphConstructor::addAllDependenciesFrom( void SourceFileDepGraphConstructor::addSourceFileNodesToGraph() { g.findExistingNodePairOrCreateAndAddIfNew( NodeKind::sourceFileProvide, - {DependencyKey::computeContextForProvidedEntity< - NodeKind::sourceFileProvide>(swiftDeps), - DependencyKey::computeNameForProvidedEntity( - swiftDeps), - getSourceFileFingerprint()}); + ContextNameFingerprint(DependencyKey::computeContextForProvidedEntity< + NodeKind::sourceFileProvide>(swiftDeps), + DependencyKey::computeNameForProvidedEntity< + NodeKind::sourceFileProvide>(swiftDeps), + getSourceFileFingerprint())); } void SourceFileDepGraphConstructor::addProviderNodesToGraph() { @@ -824,7 +824,7 @@ static std::vector getBaseNameProvides(ArrayRef simpleNames) { std::vector result; for (StringRef n : simpleNames) - result.push_back({"", n.str(), None}); + result.push_back(ContextNameFingerprint("", n.str(), None)); return result; } @@ -832,7 +832,7 @@ static std::vector getMangledHolderProvides(ArrayRef simpleNames) { std::vector result; for (StringRef n : simpleNames) - result.push_back({n.str(), "", None}); + result.push_back(ContextNameFingerprint(n.str(), "", None)); return result; } @@ -840,7 +840,7 @@ static std::vector getCompoundProvides( ArrayRef> compoundNames) { std::vector result; for (const auto &p : compoundNames) - result.push_back({p.first, p.second, None}); + result.push_back(ContextNameFingerprint(p.first, p.second, None)); return result; } From a1bf54af519d8fd3368f65d6df88b5718ecf43f8 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sat, 4 Jan 2020 23:41:14 -0300 Subject: [PATCH 269/478] [CSDiagnostics] Extract diagnostic for CoerceExpr to function --- lib/Sema/CSDiagnostics.cpp | 41 +++++++++++++++++++++++++------------- lib/Sema/CSDiagnostics.h | 3 +++ test/Parse/recovery.swift | 8 +++++--- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 9c84d967ed6c1..d36c8eea96840 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1854,21 +1854,8 @@ bool ContextualFailure::diagnoseAsError() { return true; } - if (auto *coerceExpr = dyn_cast(anchor)) { - auto fromType = getFromType(); - auto toType = getType(coerceExpr->getCastTypeLoc()); - auto diagnostic = - getDiagnosticFor(CTP_CoerceOperand, - /*forProtocol=*/toType->isAnyExistentialType()); - - auto diag = - emitDiagnostic(anchor->getLoc(), *diagnostic, fromType, toType); - diag.highlight(anchor->getSourceRange()); - - (void)tryFixIts(diag); - + if (diagnoseConversionInCoercion()) return true; - } return false; } @@ -2236,6 +2223,28 @@ bool ContextualFailure::diagnoseMissingFunctionCall() const { return true; } +bool ContextualFailure::diagnoseConversionInCoercion() const { + auto *anchor = getAnchor(); + + if (auto *coerceExpr = dyn_cast(anchor)) { + auto fromType = getFromType(); + auto toType = getType(coerceExpr->getCastTypeLoc()); + auto diagnostic = + getDiagnosticFor(CTP_CoerceOperand, + /*forProtocol=*/toType->isAnyExistentialType()); + + auto diag = + emitDiagnostic(anchor->getLoc(), *diagnostic, fromType, toType); + diag.highlight(anchor->getSourceRange()); + + (void)tryFixIts(diag); + + return true; + } + + return false; +} + bool ContextualFailure::diagnoseConversionToBool() const { auto toType = getToType(); if (!toType->isBool()) @@ -2581,6 +2590,10 @@ bool ContextualFailure::trySequenceSubsequenceFixIts( if (getFromType()->isEqual(Substring)) { if (getToType()->isEqual(String)) { auto *anchor = getAnchor()->getSemanticsProvidingExpr(); + if (auto *CE = dyn_cast(anchor)) { + anchor = CE->getSubExpr(); + } + auto range = anchor->getSourceRange(); diagnostic.fixItInsert(range.Start, "String("); diagnostic.fixItInsertAfter(range.End, ")"); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index c8493ff289b19..3e449fac9a1c8 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -554,6 +554,9 @@ class ContextualFailure : public FailureDiagnostic { /// If we're trying to convert something to `nil`. bool diagnoseConversionToNil() const; + + /// Diagnose failed conversion in a `CoerceExpr`. + bool diagnoseConversionInCoercion() const; // If we're trying to convert something of type "() -> T" to T, // then we probably meant to call the value. diff --git a/test/Parse/recovery.swift b/test/Parse/recovery.swift index 6984b1bd85b05..1b683368a5576 100644 --- a/test/Parse/recovery.swift +++ b/test/Parse/recovery.swift @@ -619,9 +619,11 @@ class WrongInheritanceClause6(Int {} class WrongInheritanceClause7(Int where T:AnyObject {} // [swift-crashes 078] parser crash on invalid cast in sequence expr -Base=1 as Base=1 // expected-error {{cannot convert value of type 'Int' to type 'Base' in coercion}} - - +Base=1 as Base=1 // expected-error{{cannot convert value of type 'Int' to type 'Base' in coercion}} +// expected-error@-1 {{cannot assign to immutable expression of type 'Base.Type'}} +// expected-error@-2 {{left side of mutating operator has immutable type 'Base'}} +// expected-error@-3 {{cannot assign value of type '()' to type 'Base.Type'}} +// expected-error@-4 {{cannot assign value of type 'Int' to type 'Base'}} // Parser hangs at swift::Parser::parseType public enum TestA { From f145264fc7c5541defa0c361f2fa3e0c5d2dcae7 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sat, 4 Jan 2020 23:41:44 -0300 Subject: [PATCH 270/478] [tests] Fix parser recover tests --- lib/Sema/CSSimplify.cpp | 7 +++---- test/Parse/recovery.swift | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index fcc12d21892ef..671e410feb575 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2824,11 +2824,10 @@ bool ConstraintSystem::repairFailures( return true; } - // If it has a deep equality restriction defer the diagnostic to a - // GenericMismatch fix. - if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality)) { + // If it has a deep equality restriction, defer the diagnostic to + // GenericMismatch. + if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality)) return false; - } auto *fix = ContextualMismatch::create(*this, lhs, rhs, getConstraintLocator(locator)); diff --git a/test/Parse/recovery.swift b/test/Parse/recovery.swift index 1b683368a5576..c7503c2c97dca 100644 --- a/test/Parse/recovery.swift +++ b/test/Parse/recovery.swift @@ -621,7 +621,7 @@ class WrongInheritanceClause7(Int where T:AnyObject {} // [swift-crashes 078] parser crash on invalid cast in sequence expr Base=1 as Base=1 // expected-error{{cannot convert value of type 'Int' to type 'Base' in coercion}} // expected-error@-1 {{cannot assign to immutable expression of type 'Base.Type'}} -// expected-error@-2 {{left side of mutating operator has immutable type 'Base'}} +// expected-error@-2 {{cannot assign to immutable expression of type 'Base'}} // expected-error@-3 {{cannot assign value of type '()' to type 'Base.Type'}} // expected-error@-4 {{cannot assign value of type 'Int' to type 'Base'}} From 7889bfbcb1fb34e6fddde1bdfc44816480f78ec2 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Sat, 4 Jan 2020 20:54:59 -0800 Subject: [PATCH 271/478] Improvements inspired by review --- include/swift/Driver/Action.h | 25 ++++++++----------- .../Driver/FineGrainedDependencyDriverGraph.h | 2 +- lib/AST/FineGrainedDependencies.cpp | 11 +++++++- .../FineGrainedDependencyDriverGraph.cpp | 5 ++-- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/include/swift/Driver/Action.h b/include/swift/Driver/Action.h index 3f4e5c4edec14..5bc3c26175470 100644 --- a/include/swift/Driver/Action.h +++ b/include/swift/Driver/Action.h @@ -173,20 +173,17 @@ class CompileJobAction : public JobAction { /// if 0 or >1 such inputs exist, return nullptr. const InputAction *findSingleSwiftInput() const { auto Inputs = getInputs(); - const InputAction *IA = nullptr; - for (auto const *I : Inputs) { - if (auto const *S = dyn_cast(I)) { - if (S->getType() == file_types::TY_Swift) { - if (IA == nullptr) { - IA = S; - } else { - // Already found one, two is too many. - return nullptr; - } - } - } - } - return IA; + auto isSwiftInput = [](const Action *A) -> const InputAction* { + if (auto const *S = dyn_cast(A)) + return S->getType() == file_types::TY_Swift ? S : nullptr; + return nullptr; + }; + const auto loc1 = std::find_if(Inputs.begin(), Inputs.end(), isSwiftInput); + if (loc1 == Inputs.end()) + return nullptr; // none found + // Ensure uniqueness + const auto loc2 = std::find_if(loc1 + 1, Inputs.end(), isSwiftInput); + return loc2 == Inputs.end() ? dyn_cast(*loc1) : nullptr; } }; diff --git a/include/swift/Driver/FineGrainedDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h index 4d3616ff5f93f..b07a5f33f9f1f 100644 --- a/include/swift/Driver/FineGrainedDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -487,7 +487,7 @@ class ModuleDepGraph { return swiftDepsOfJobsThatNeedRunning.insert(swiftDeps).second; } - /// For debugging, write out the graph to a dot file. + /// For debugging and visualization, write out the graph to a dot file. /// \p diags may be null if no diagnostics are needed. void emitDotFileForJob(DiagnosticEngine &, const driver::Job *); void emitDotFile(DiagnosticEngine &, StringRef baseName); diff --git a/lib/AST/FineGrainedDependencies.cpp b/lib/AST/FineGrainedDependencies.cpp index e6b6dac802bfc..f61dbd3851dee 100644 --- a/lib/AST/FineGrainedDependencies.cpp +++ b/lib/AST/FineGrainedDependencies.cpp @@ -90,10 +90,19 @@ SourceFileDepGraph::findExistingNodePairOrCreateAndAddIfNew( DependencyKey(k, DeclAspect::implementation, context, name), fingerprint, true /* = isProvides */)}; // if interface changes, have to rebuild implementation. + // This dependency used to be represented by + // addArc(nodePair.getInterface(), nodePair.getImplementation()); + // However, recall that the dependency scheme as of 1/2020 chunks + // declarations together by base name. + // So if the arc were added, a dirtying of a same-based-named interface + // in a different file would dirty the implementation in this file, + // causing the needless recompilation of this file. // But, if an arc is added for this, then *any* change that causes // a same-named interface to be dirty will dirty this implementation, // even if that interface is in another file. - // So, make the interface->implementation arc implicit. + // Therefor no such arc is added here, and any dirtying of either + // the interface or implementation of this declaration will cause + // the driver to recompile this source file. return nodePair; } diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index a80b1c34b3270..6564e17dbf719 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -428,8 +428,9 @@ void ModuleDepGraph::traceDeparture(size_t pathLengthAfterArrival) { currentPath.pop_back(); } -// Emitting Dot file for ModuleDepGraph -// =========================================== +// ============================================================================= +// MARK: Emitting Dot file for ModuleDepGraph +// ============================================================================= void ModuleDepGraph::emitDotFileForJob(DiagnosticEngine &diags, const Job *job) { From ccb3840ae15fdaccc45f00d2638b4c5276aed355 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Sat, 4 Jan 2020 21:17:41 -0800 Subject: [PATCH 272/478] Instantiate a template for the Linux compiler. --- ...FineGrainedDependenciesSourceFileDepGraphConstructor.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp index ca821a6af4e11..28b1c449a7647 100644 --- a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp +++ b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp @@ -361,6 +361,12 @@ std::string DependencyKey::computeContextForProvidedEntity< return mangleTypeAsContext(holderAndMember.first); } +// Linux compiler requires the following: +template +std::string +DependencyKey::computeContextForProvidedEntity(StringRef); + //============================================================================== // MARK: computeNameForProvidedEntity //============================================================================== From 94bbb8f76e3b510253c8c557691ebb69091023e1 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 12 Dec 2019 08:59:09 -0800 Subject: [PATCH 273/478] [ConstraintSystem] Finish porting unresolved member reference failures. This covers member failures where the error is at the declaration, so the result of member lookup is "already diagnosed". --- lib/Sema/CSDiag.cpp | 60 ----------------------------- lib/Sema/CSFix.cpp | 7 ++-- lib/Sema/CSFix.h | 15 ++++++-- lib/Sema/CSSimplify.cpp | 15 ++++---- test/Constraints/diagnostics.swift | 3 +- test/Constraints/rdar46377919.swift | 1 - 6 files changed, 25 insertions(+), 76 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 7b1f26249d3ea..11f6177d1d727 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -240,7 +240,6 @@ class FailureDiagnosis :public ASTVisitor{ bool visitIdentityExpr(IdentityExpr *E); bool visitTryExpr(TryExpr *E); - bool visitUnresolvedMemberExpr(UnresolvedMemberExpr *E); bool visitUnresolvedDotExpr(UnresolvedDotExpr *UDE); bool visitArrayExpr(ArrayExpr *E); bool visitDictionaryExpr(DictionaryExpr *E); @@ -2126,65 +2125,6 @@ bool FailureDiagnosis::visitObjectLiteralExpr(ObjectLiteralExpr *E) { return true; } -bool FailureDiagnosis::visitUnresolvedMemberExpr(UnresolvedMemberExpr *E) { - // If we have no contextual type, there is no way to resolve this. Just - // diagnose this as an ambiguity. - if (!CS.getContextualType()) - return false; - - // OTOH, if we do have a contextual type, we can provide a more specific - // error. Dig out the UnresolvedValueMember constraint for this expr node. - Constraint *memberConstraint = nullptr; - auto checkConstraint = [&](Constraint *C) { - if (C->getKind() == ConstraintKind::UnresolvedValueMember && - simplifyLocatorToAnchor(C->getLocator()) == E) - memberConstraint = C; - }; - - if (CS.failedConstraint) - checkConstraint(CS.failedConstraint); - for (auto &C : CS.getConstraints()) { - if (memberConstraint) break; - checkConstraint(&C); - } - - // If we can't find the member constraint in question, then we failed. - if (!memberConstraint) - return false; - - std::function)> callback = [&]( - ArrayRef candidates) { - bool hasTrailingClosure = callArgHasTrailingClosure(E->getArgument()); - - // Dump all of our viable candidates into a CalleeCandidateInfo & sort it - // out. - CalleeCandidateInfo candidateInfo(Type(), candidates, hasTrailingClosure, - CS); - - // Filter the candidate list based on the argument we may or may not have. - candidateInfo.filterContextualMemberList(E->getArgument()); - - // If we have multiple candidates, then we have an ambiguity. - if (candidateInfo.size() != 1) { - SourceRange argRange; - if (auto arg = E->getArgument()) - argRange = arg->getSourceRange(); - diagnose(E->getNameLoc(), diag::ambiguous_member_overload_set, - E->getName()) - .highlight(argRange); - candidateInfo.suggestPotentialOverloads(E->getNameLoc().getBaseNameLoc()); - return true; - } - - return false; - }; - - return diagnoseMemberFailures(E, nullptr, memberConstraint->getKind(), - memberConstraint->getMember(), - memberConstraint->getFunctionRefKind(), - memberConstraint->getLocator(), callback); -} - bool FailureDiagnosis::diagnoseMemberFailures( Expr *E, Expr *baseExpr, ConstraintKind lookupKind, DeclNameRef memberName, FunctionRefKind funcRefKind, ConstraintLocator *locator, diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index a8ccc6e7e63c1..21abf55ddd143 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -472,14 +472,15 @@ UseSubscriptOperator *UseSubscriptOperator::create(ConstraintSystem &cs, bool DefineMemberBasedOnUse::diagnose(bool asNote) const { auto failure = MissingMemberFailure(getConstraintSystem(), BaseType, Name, getLocator()); - return failure.diagnose(asNote); + return AlreadyDiagnosed || failure.diagnose(asNote); } DefineMemberBasedOnUse * DefineMemberBasedOnUse::create(ConstraintSystem &cs, Type baseType, - DeclNameRef member, ConstraintLocator *locator) { + DeclNameRef member, bool alreadyDiagnosed, + ConstraintLocator *locator) { return new (cs.getAllocator()) - DefineMemberBasedOnUse(cs, baseType, member, locator); + DefineMemberBasedOnUse(cs, baseType, member, alreadyDiagnosed, locator); } AllowMemberRefOnExistential * diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 305e2f488cc15..d8550f6ef09a9 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -806,10 +806,19 @@ class DefineMemberBasedOnUse final : public ConstraintFix { Type BaseType; DeclNameRef Name; + /// Whether or not the member error is already diagnosed. This can happen + /// when referencing an erroneous member, and the error is diagnosed at the + /// member declaration. + /// + /// We still want to define erroneous members based on use in order to find + /// a solution through the new diagnostic infrastructure, but we don't + /// want to report a second error message. + bool AlreadyDiagnosed; + DefineMemberBasedOnUse(ConstraintSystem &cs, Type baseType, DeclNameRef member, - ConstraintLocator *locator) + bool alreadyDiagnosed, ConstraintLocator *locator) : ConstraintFix(cs, FixKind::DefineMemberBasedOnUse, locator), - BaseType(baseType), Name(member) {} + BaseType(baseType), Name(member), AlreadyDiagnosed(alreadyDiagnosed) {} public: std::string getName() const override { @@ -822,7 +831,7 @@ class DefineMemberBasedOnUse final : public ConstraintFix { bool diagnose(bool asNote = false) const override; static DefineMemberBasedOnUse *create(ConstraintSystem &cs, Type baseType, - DeclNameRef member, + DeclNameRef member, bool alreadyDiagnosed, ConstraintLocator *locator); }; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 9c05cf2cd9e20..233af658c7670 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -6121,8 +6121,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( return formUnsolved(); case MemberLookupResult::ErrorAlreadyDiagnosed: - return SolutionKind::Error; - case MemberLookupResult::HasResults: // Keep going! break; @@ -6198,8 +6196,10 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( return type; }); - auto *fix = - DefineMemberBasedOnUse::create(*this, baseTy, member, locator); + bool alreadyDiagnosed = (result.OverallResult == + MemberLookupResult::ErrorAlreadyDiagnosed); + auto *fix = DefineMemberBasedOnUse::create(*this, baseTy, member, + alreadyDiagnosed, locator); // Impact is higher if the base is expected to be inferred from context, // because a failure to find a member ultimately means that base type is // not a match in this case. @@ -6208,9 +6208,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( if (recordFix(fix, impact)) return SolutionKind::Error; - // Allow member type to default to `Any` to make it possible to form - // solutions when contextual type of the result cannot be deduced e.g. - // `let _ = x.foo`. + // Record a hole for memberTy to make it possible to form solutions + // when contextual result type cannot be deduced e.g. `let _ = x.foo`. if (auto *memberTypeVar = memberTy->getAs()) recordPotentialHole(memberTypeVar); @@ -7860,7 +7859,7 @@ ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint( DeclNameRef memberName({ ctx, ctx.Id_dynamicallyCall, {argLabel} }); auto *fix = DefineMemberBasedOnUse::create( - *this, desugar2, memberName, + *this, desugar2, memberName, /*alreadyDiagnosed=*/false, getConstraintLocator(loc, ConstraintLocator::DynamicCallable)); if (recordFix(fix)) diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 0cd9108c3fee8..6b5af5f049e4c 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -251,7 +251,8 @@ struct Toe { let toenail: Nail // expected-error {{use of undeclared type 'Nail'}} func clip() { - toenail.inspect { x in + // FIXME: We shouldn't report this because toenail.inspect is a hole + toenail.inspect { x in // expected-error {{unable to infer closure return type; add explicit type to disambiguate}} toenail.inspect { y in } } } diff --git a/test/Constraints/rdar46377919.swift b/test/Constraints/rdar46377919.swift index ffdaab5b384e9..2f95e1cf28ec6 100644 --- a/test/Constraints/rdar46377919.swift +++ b/test/Constraints/rdar46377919.swift @@ -9,5 +9,4 @@ class Foo { func foo() -> Foo { return Foo(lhs: 2, rhs: 2) - // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '<>'}} } From d9cfbd3e55aced52604589e26fc94767a47a641a Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sun, 5 Jan 2020 18:39:55 -0300 Subject: [PATCH 274/478] [CSDiag] Remove obsolete code block in visitCoerceExpr --- lib/Sema/CSDiag.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 7b1f26249d3ea..c4a492970c09b 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -1888,16 +1888,6 @@ bool FailureDiagnosis::visitCoerceExpr(CoerceExpr *CE) { if (!expr) return true; - auto ref = expr->getReferencedDecl(); - if (auto *decl = ref.getDecl()) { - // Without explicit coercion we might end up - // type-checking sub-expression as unavaible - // declaration, let's try to diagnose that here. - if (AvailableAttr::isUnavailable(decl)) - return diagnoseExplicitUnavailability( - decl, expr->getSourceRange(), CS.DC, dyn_cast(expr)); - } - return false; } From 475917db88b075a87b20f051d4fc2631ee54a9aa Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sun, 5 Jan 2020 14:16:13 -0800 Subject: [PATCH 275/478] Add a pretty printer for small mode SmallBitVectors. Just something I cooked up really quickly b/c I needed it. If you want to use this, lldb-with-tools will auto-import it, so I suggest that you use that. --- utils/lldb/lldbSwiftDataFormatters.py | 47 +++++++++++++++++++++++++++ utils/lldb/lldbToolBox.py | 12 +++++++ 2 files changed, 59 insertions(+) create mode 100644 utils/lldb/lldbSwiftDataFormatters.py diff --git a/utils/lldb/lldbSwiftDataFormatters.py b/utils/lldb/lldbSwiftDataFormatters.py new file mode 100644 index 0000000000000..17d5d2d0817bb --- /dev/null +++ b/utils/lldb/lldbSwiftDataFormatters.py @@ -0,0 +1,47 @@ +""" +LLDB Formatters for LLVM data types for use in the swift project. + +Load into LLDB with 'command script import /path/to/lldbDataFormatters.py' +""" + +import sys + + +def __lldb_init_module(debugger, internal_dict): + tName = 'lldbSwiftDataFormatters.SmallBitVectorSummaryProvider' + debugger.HandleCommand('type summary add -w llvm ' + '-F %s -x "^llvm::SmallBitVector$"' % tName) + + +def SmallBitVectorSummaryProvider(valobj, internal_dict): + underlyingValue = valobj.GetChildMemberWithName('X').GetValueAsUnsigned() + numBaseBits = 32 + is64Bit = sys.maxsize > 2**32 + if is64Bit: + numBaseBits = 64 + smallNumRawBits = numBaseBits - 1 + smallNumSizeBits = None + if numBaseBits == 32: + smallNumSizeBits = 5 + elif numBaseBits == 64: + smallNumSizeBits = 6 + else: + smallNumSizeBits = smallNumRawBits + smallNumDataBits = smallNumRawBits - smallNumSizeBits + + # If our underlying value is not small, print we can not dump large values. + isSmallMask = 1 + if (underlyingValue & isSmallMask) == 0: + return '' + + smallRawBits = underlyingValue >> 1 + smallSize = smallRawBits >> smallNumDataBits + bits = smallRawBits & ((1 << (smallSize + 1)) - 1) + res = "[" + for i in reversed(range(0, smallSize)): + if bool(bits & (1 << i)): + res += '1' + else: + res += '0' + res += "]" + return res diff --git a/utils/lldb/lldbToolBox.py b/utils/lldb/lldbToolBox.py index 61200493590cd..54e1999759cfd 100644 --- a/utils/lldb/lldbToolBox.py +++ b/utils/lldb/lldbToolBox.py @@ -22,6 +22,8 @@ LLVM_REPO = os.path.join(REPO_BASE, "llvm") LLVM_DATAFORMATTER_PATH = os.path.join(LLVM_REPO, "utils", "lldbDataFormatters.py") +SWIFT_DATAFORMATTER_PATH = os.path.join(SWIFT_REPO, "utils", + "lldb", "lldbSwiftDataFormatters.py") def import_llvm_dataformatters(debugger): @@ -33,6 +35,15 @@ def import_llvm_dataformatters(debugger): print("Loaded LLVM data formatters.") +def import_swift_dataformatters(debugger): + if not os.access(SWIFT_DATAFORMATTER_PATH, os.F_OK): + print("WARNING! Could not find Swift data formatters!") + return + cmd = 'command script import {}'.format(SWIFT_DATAFORMATTER_PATH) + debugger.HandleCommand(cmd) + print("Loaded Swift data formatters.") + + VIEWCFG_PATH = os.path.join(SWIFT_REPO, "utils", "viewcfg") BLOCKIFYASM_PATH = os.path.join(SWIFT_REPO, "utils", "dev-scripts", "blockifyasm") @@ -107,6 +118,7 @@ def sequence(debugger, command, exec_ctx, result, internal_dict): def __lldb_init_module(debugger, internal_dict): import_llvm_dataformatters(debugger) + import_swift_dataformatters(debugger) debugger.HandleCommand('command script add disassemble-asm-cfg ' '-f lldbToolBox.disassemble_asm_cfg') debugger.HandleCommand('command script add disassemble-to-file ' From b7e08accb13c8b649989853d71f525649bb81b5d Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Mon, 6 Jan 2020 09:32:30 -0800 Subject: [PATCH 276/478] [stdlib] Slice: customize withContiguous[Mutable]StorageIfAvailable (#28883) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [stdlib] Slice: customize withContiguous[Mutable]StorageIfAvailable We can easily make an UnsafeBufferPointer that slices another UnsafeBufferPointer, so let’s allow Slice to vend a slice of the base collection’s contiguous storage, if it provides access to one. We need to do some index distance calculations to implement this, but those will be constant-time in the usual case where the base collection is a RAC. https://bugs.swift.org/browse/SR-11957 rdar://58090587 * [test] UnsafeBufferPointer: fix some warnings * [stdlib] Slice: don’t calculate index distances unless the base provides contiguous mutable storage --- stdlib/public/core/Slice.swift | 40 +++++++++++++++++++ validation-test/stdlib/Slice.swift.gyb | 25 +++++++----- .../stdlib/UnsafeBufferPointer.swift.gyb | 39 ++++++++++++++---- 3 files changed, 87 insertions(+), 17 deletions(-) diff --git a/stdlib/public/core/Slice.swift b/stdlib/public/core/Slice.swift index 3ad2c2be2ebf3..751e88cd10bbe 100644 --- a/stdlib/public/core/Slice.swift +++ b/stdlib/public/core/Slice.swift @@ -215,6 +215,18 @@ extension Slice: Collection { public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { _base._failEarlyRangeCheck(range, bounds: bounds) } + + @_alwaysEmitIntoClient @inlinable + public func withContiguousStorageIfAvailable( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R? { + try _base.withContiguousStorageIfAvailable { buffer in + let start = _base.distance(from: _base.startIndex, to: _startIndex) + let count = _base.distance(from: _startIndex, to: _endIndex) + let slice = UnsafeBufferPointer(rebasing: buffer[start ..< start + count]) + return try body(slice) + } + } } extension Slice: BidirectionalCollection where Base: BidirectionalCollection { @@ -258,6 +270,34 @@ extension Slice: MutableCollection where Base: MutableCollection { _writeBackMutableSlice(&self, bounds: bounds, slice: newValue) } } + + @_alwaysEmitIntoClient @inlinable + public mutating func withContiguousMutableStorageIfAvailable( + _ body: (inout UnsafeMutableBufferPointer) throws -> R + ) rethrows -> R? { + // We're calling `withContiguousMutableStorageIfAvailable` twice here so + // that we don't calculate index distances unless we know we'll use them. + // The expectation here is that the base collection will make itself + // contiguous on the first try and the second call will be relatively cheap. + guard _base.withContiguousMutableStorageIfAvailable({ _ in }) != nil + else { + return nil + } + let start = _base.distance(from: _base.startIndex, to: _startIndex) + let count = _base.distance(from: _startIndex, to: _endIndex) + return try _base.withContiguousMutableStorageIfAvailable { buffer in + var slice = UnsafeMutableBufferPointer( + rebasing: buffer[start ..< start + count]) + let copy = slice + defer { + _precondition( + slice.baseAddress == copy.baseAddress && + slice.count == copy.count, + "Slice.withUnsafeMutableBufferPointer: replacing the buffer is not allowed") + } + return try body(&slice) + } + } } diff --git a/validation-test/stdlib/Slice.swift.gyb b/validation-test/stdlib/Slice.swift.gyb index 3752fc6e67801..58b01fcd54602 100644 --- a/validation-test/stdlib/Slice.swift.gyb +++ b/validation-test/stdlib/Slice.swift.gyb @@ -113,30 +113,35 @@ SliceTests.test("${Collection}.Slice.{startIndex,endIndex}") { % end % end +SliceTests.test("${Collection}.Slice.withContiguousStorageIfAvailable") { + for test in subscriptRangeTests { + let c = ${Collection}(elements: test.collection) + let bounds = test.bounds(in: c) + var slice = Slice(base: c, bounds: bounds) + let r = slice.withContiguousStorageIfAvailable { _ in } + expectNil(r) // None of the minimal collection types implement this. + } +} + //===----------------------------------------------------------------------===// // MutableSlice //===----------------------------------------------------------------------===// -/* -FIXME: uncomment this test when the following bug is fixed: - - Recast Slice and MutableSlice in terms of Collection -and MutableCollection - -extension MutableSlice { +extension Slice { func _checkBaseSubSequenceElementIsElement() { + expectEqualType( Element.self, Iterator.Element.self) expectEqualType( Element.self, - Iterator.Element.self, Base.Iterator.Element.self) expectEqualType( Element.self, - Iterator.Element.self, + Iterator.Element.self) + expectEqualType( + Element.self, Base.SubSequence.Iterator.Element.self) } } -*/ runAllTests() diff --git a/validation-test/stdlib/UnsafeBufferPointer.swift.gyb b/validation-test/stdlib/UnsafeBufferPointer.swift.gyb index 6a52caa22417c..2ed995338afec 100644 --- a/validation-test/stdlib/UnsafeBufferPointer.swift.gyb +++ b/validation-test/stdlib/UnsafeBufferPointer.swift.gyb @@ -63,7 +63,7 @@ ${SelfName}TestSuite.test("AssociatedTypes") { % if IsRaw: iteratorType: ${SelfType}.Iterator.self, % else: - iteratorType: UnsafeBufferPointerIterator.self, + iteratorType: UnsafeBufferPointer.Iterator.self, % end subSequenceType: Slice<${SelfType}>.self, indexType: Int.self, @@ -361,6 +361,31 @@ UnsafeMutableBufferPointerTestSuite.test("withContiguous(Mutable)StorageIfAvaila expectEqual(result1, result2) } +UnsafeMutableBufferPointerTestSuite.test("Slice.withContiguousStorageIfAvailable") { + guard #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) else { + return + } + + for test in subscriptRangeTests { + let c = test.collection.map { MinimalEquatableValue($0.value) } + let buffer = UnsafeMutableBufferPointer.allocate( + capacity: test.collection.count) + var (it, idx) = buffer.initialize(from: c) + expectEqual(buffer.endIndex, idx) + expectNil(it.next()) + + let expected = test.expected.map { MinimalEquatableValue($0.value) } + let r1: Void? = buffer[test.bounds].withContiguousStorageIfAvailable { b in + expectTrue(expected.elementsEqual(b)) + } + expectNotNil(r1) + let r2: Void? = buffer[test.bounds].withContiguousMutableStorageIfAvailable { b in + expectTrue(expected.elementsEqual(b)) + } + expectNotNil(r2) + } +} + UnsafeMutableBufferPointerTestSuite.test("sort") { var values = (0..<1000).map({ _ in Int.random(in: 0..<100) }) let sortedValues = values.sorted() @@ -389,7 +414,7 @@ for testIndex in (0.. 'read' else 'let'} slice = buffer[sliceRange] if _isDebugAssertConfiguration(), testIndex < sliceRange.lowerBound || @@ -430,7 +455,7 @@ for testRange in testRanges { % Type = 'Unsafe' + ('Mutable' if mutable else '') + ('Raw' if raw else '') + 'BufferPointer' ${Type}TestSuite.test("${action}Slice\(testRange)ViaSlice") { % if raw: - let allocated = UnsafeMutableRawPointer.allocate(bytes: bufCount+2, alignedTo: 8) + let allocated = UnsafeMutableRawPointer.allocate(byteCount: bufCount+2, alignment: 8) for i in 0.. 'read' else 'let'} slice = buffer[sliceRange] if _isDebugAssertConfiguration(), testRange.lowerBound < sliceRange.lowerBound || @@ -937,7 +962,7 @@ UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("subscript/${R UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("subscript/set/overlaps") { % if IsRaw: - let buffer = UnsafeMutableRawBufferPointer.allocate(count: 4) + let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 4, alignment: 1) % else: let buffer = UnsafeMutableBufferPointer.allocate(capacity: 4) % end @@ -970,7 +995,7 @@ UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("subscript/set UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("nonmutating-swapAt") { % if IsRaw: - let allocated = UnsafeMutableRawPointer.allocate(bytes: 4, alignedTo: 8) + let allocated = UnsafeMutableRawPointer.allocate(byteCount: 4, alignment: 8) let buffer = UnsafeMutableRawBufferPointer(start: allocated, count: 3) allocated.storeBytes(of: UInt8.max, toByteOffset: 3, as: UInt8.self) % else: From 64f8a6bd423c21d829034cf34e5dbacfaf8756f9 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Mon, 6 Jan 2020 14:37:27 -0300 Subject: [PATCH 277/478] [CSDiagnostics] Renaming ContextualFailure CoerceExpr method naming --- lib/Sema/CSDiagnostics.cpp | 4 ++-- lib/Sema/CSDiagnostics.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index d36c8eea96840..54b6dad9064ce 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1854,7 +1854,7 @@ bool ContextualFailure::diagnoseAsError() { return true; } - if (diagnoseConversionInCoercion()) + if (diagnoseCoercionToUnrelatedType()) return true; return false; @@ -2223,7 +2223,7 @@ bool ContextualFailure::diagnoseMissingFunctionCall() const { return true; } -bool ContextualFailure::diagnoseConversionInCoercion() const { +bool ContextualFailure::diagnoseCoercionToUnrelatedType() const { auto *anchor = getAnchor(); if (auto *coerceExpr = dyn_cast(anchor)) { diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 3e449fac9a1c8..dfa22d0122f3d 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -556,7 +556,7 @@ class ContextualFailure : public FailureDiagnostic { bool diagnoseConversionToNil() const; /// Diagnose failed conversion in a `CoerceExpr`. - bool diagnoseConversionInCoercion() const; + bool diagnoseCoercionToUnrelatedType() const; // If we're trying to convert something of type "() -> T" to T, // then we probably meant to call the value. From 246d52defec32f90a421ea0326b627a54f0afc94 Mon Sep 17 00:00:00 2001 From: tbkka Date: Mon, 6 Jan 2020 09:52:21 -0800 Subject: [PATCH 278/478] Simplify the floating-point parsing initializers (#28992) The original version scanned the entire input string for whitespace and non-ASCII characters. Both are unnecessary: the C routines we're building on already stop at non-ASCII characters or non-leading whitespace. So we need only check the first character for whitespace and verify that all characters are consumed. This both improves performance and reduces the amount of code that gets inlined into consumers. --- .../core/FloatingPointParsing.swift.gyb | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/stdlib/public/core/FloatingPointParsing.swift.gyb b/stdlib/public/core/FloatingPointParsing.swift.gyb index 1225f297d12d5..b4cddc1a467bd 100644 --- a/stdlib/public/core/FloatingPointParsing.swift.gyb +++ b/stdlib/public/core/FloatingPointParsing.swift.gyb @@ -126,25 +126,38 @@ extension ${Self}: LosslessStringConvertible { /// is `nil`. @inlinable // FIXME(sil-serialize-all) public init?(_ text: S) { - let u8 = text.utf8 - - let (result, n): (${Self}, Int) = text.withCString { chars in + let result: ${Self}? = text.withCString { chars in + // TODO: We should change the ABI for + // _swift_stdlib_strtoX_clocale so that it returns + // a boolean `false` for leading whitespace, empty + // string, or invalid character. Then we could avoid + // inlining these checks into every single client. + switch chars[0] { + case 9, 10, 11, 12, 13, 32: + // Reject any input with leading whitespace. + return nil + case 0: + // Reject the empty string + return nil + default: + break + } var result: ${Self} = 0 let endPtr = withUnsafeMutablePointer(to: &result) { _swift_stdlib_strto${cFuncSuffix2[bits]}_clocale(chars, $0) } - return (result, endPtr == nil ? 0 : endPtr! - chars) + // Verify that all the characters were consumed. + if endPtr == nil || endPtr![0] != 0 { + return nil + } + return result } - if n == 0 || n != u8.count - || u8.contains(where: { codeUnit in - // Check if the code unit is either non-ASCII or if isspace(codeUnit) - // would return nonzero when the current locale is the C locale. - codeUnit > 127 || "\t\n\u{b}\u{c}\r ".utf8.contains(codeUnit) - }) { + if let result = result { + self = result + } else { return nil } - self = result } } From 8e45f44da4c407c406d12d1930f97ffeb84d09a6 Mon Sep 17 00:00:00 2001 From: Harlan Haskins Date: Tue, 19 Nov 2019 16:46:22 -0800 Subject: [PATCH 279/478] [Serialization] Serialize hasMissingDesignatedInitializers Since this is going to be something modules tell clients, rather than something clients discover about modules, serialize it. --- lib/Serialization/Deserialization.cpp | 7 ++++++- lib/Serialization/ModuleFormat.h | 3 ++- lib/Serialization/Serialization.cpp | 7 +++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 891fd9349148b..af1444ae0d962 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -3416,6 +3416,7 @@ class DeclDeserializer { DeclContextID contextID; bool isImplicit, isObjC; bool inheritsSuperclassInitializers; + bool hasMissingDesignatedInits; GenericSignatureID genericSigID; TypeID superclassID; uint8_t rawAccessLevel; @@ -3424,6 +3425,7 @@ class DeclDeserializer { decls_block::ClassLayout::readRecord(scratch, nameID, contextID, isImplicit, isObjC, inheritsSuperclassInitializers, + hasMissingDesignatedInits, genericSigID, superclassID, rawAccessLevel, numConformances, numInheritedTypes, @@ -3466,6 +3468,8 @@ class DeclDeserializer { theClass->setSuperclass(MF.getType(superclassID)); ctx.evaluator.cacheOutput(InheritsSuperclassInitializersRequest{theClass}, std::move(inheritsSuperclassInitializers)); + ctx.evaluator.cacheOutput(HasMissingDesignatedInitializersRequest{theClass}, + std::move(hasMissingDesignatedInits)); handleInherited(theClass, rawInheritedAndDependencyIDs.slice(0, numInheritedTypes)); @@ -5390,7 +5394,8 @@ Decl *handleErrorAndSupplyMissingClassMember(ASTContext &context, Decl *suppliedMissingMember = nullptr; auto handleMissingClassMember = [&](const DeclDeserializationError &error) { if (error.isDesignatedInitializer()) - containingClass->setHasMissingDesignatedInitializers(); + context.evaluator.cacheOutput( + HasMissingDesignatedInitializersRequest{containingClass}, true); if (error.getNumberOfVTableEntries() > 0) containingClass->setHasMissingVTableEntries(); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index b1c96011230b7..601a1c0593f09 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -55,7 +55,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 531; // function parameter noDerivative +const uint16_t SWIFTMODULE_VERSION_MINOR = 532; // @_hasMissingDesignatedInitializers /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1114,6 +1114,7 @@ namespace decls_block { BCFixed<1>, // implicit? BCFixed<1>, // explicitly objc? BCFixed<1>, // inherits convenience initializers from its superclass? + BCFixed<1>, // has missing designated initializers? GenericSignatureIDField, // generic environment TypeIDField, // superclass AccessLevelField, // access level diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 3f9471b7eb565..6862ad669f306 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3139,9 +3139,7 @@ class Serializer::DeclSerializer : public DeclVisitor { uint8_t rawAccessLevel = getRawStableAccessLevel(theClass->getFormalAccess()); - bool inheritsSuperclassInitializers = - const_cast(theClass)-> - inheritsSuperclassInitializers(); + auto mutableClass = const_cast(theClass); unsigned abbrCode = S.DeclTypeAbbrCodes[ClassLayout::Code]; ClassLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, @@ -3149,7 +3147,8 @@ class Serializer::DeclSerializer : public DeclVisitor { contextID.getOpaqueValue(), theClass->isImplicit(), theClass->isObjC(), - inheritsSuperclassInitializers, + mutableClass->inheritsSuperclassInitializers(), + mutableClass->hasMissingDesignatedInitializers(), S.addGenericSignatureRef( theClass->getGenericSignature()), S.addTypeRef(theClass->getSuperclass()), From 283854a01206f926c9dd534a47421ab09540922a Mon Sep 17 00:00:00 2001 From: Harlan Haskins Date: Tue, 19 Nov 2019 16:49:36 -0800 Subject: [PATCH 280/478] [Sema] Requestify hasMissingDesignatedInitializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We’re going to start serializing this for public types that have non-public-or-@usableFromInline initializers, so turn it into a request that we can query and cache it in the existing bit. --- include/swift/AST/Decl.h | 29 +++++++++++---- include/swift/AST/NameLookupRequests.h | 23 ++++++++++++ include/swift/AST/NameLookupTypeIDZone.def | 3 ++ lib/AST/Decl.cpp | 14 +++----- lib/AST/NameLookupRequests.cpp | 41 ++++++++++++++++++++++ lib/ClangImporter/ImportDecl.cpp | 3 +- lib/Sema/CodeSynthesis.cpp | 7 +++- lib/Sema/TypeCheckDeclPrimary.cpp | 1 + 8 files changed, 103 insertions(+), 18 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index c3bdc2e01836d..c8c579931f1a2 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -539,7 +539,7 @@ class alignas(1 << DeclAlignInBits) Decl { RawForeignKind : 2, /// \see ClassDecl::getEmittedMembers() - HasForcedEmittedMembers : 1, + HasForcedEmittedMembers : 1, HasMissingDesignatedInitializers : 1, ComputedHasMissingDesignatedInitializers : 1, @@ -3833,6 +3833,25 @@ class ClassDecl final : public NominalTypeDecl { return None; } + Optional getCachedHasMissingDesignatedInitializers() const { + if (!Bits.ClassDecl.ComputedHasMissingDesignatedInitializers) { + // Force loading all the members, which will add this attribute if any of + // members are determined to be missing while loading. + auto mutableThis = const_cast(this); + (void)mutableThis->lookupDirect(DeclBaseName::createConstructor()); + } + + if (Bits.ClassDecl.ComputedHasMissingDesignatedInitializers) + return Bits.ClassDecl.HasMissingDesignatedInitializers; + + return None; + } + + void setHasMissingDesignatedInitializers(bool value) { + Bits.ClassDecl.HasMissingDesignatedInitializers = value; + Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = true; + } + /// Marks that this class inherits convenience initializers from its /// superclass. void setInheritsSuperclassInitializers(bool value) { @@ -3843,6 +3862,7 @@ class ClassDecl final : public NominalTypeDecl { friend class SuperclassDeclRequest; friend class SuperclassTypeRequest; friend class EmittedMembersRequest; + friend class HasMissingDesignatedInitializersRequest; friend class InheritsSuperclassInitializersRequest; friend class TypeChecker; @@ -3949,11 +3969,6 @@ class ClassDecl final : public NominalTypeDecl { /// initializers that cannot be represented in Swift. bool hasMissingDesignatedInitializers() const; - void setHasMissingDesignatedInitializers(bool newValue = true) { - Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = 1; - Bits.ClassDecl.HasMissingDesignatedInitializers = newValue; - } - /// Returns true if the class has missing members that require vtable entries. /// /// In this case, the class cannot be subclassed, because we cannot construct @@ -3992,7 +4007,7 @@ class ClassDecl final : public NominalTypeDecl { /// Determine whether this class inherits the convenience initializers /// from its superclass. - bool inheritsSuperclassInitializers(); + bool inheritsSuperclassInitializers() const; /// Walks the class hierarchy starting from this class, checking various /// conditions. diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index 47f7a96a82d1f..dbc9ddb796200 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -161,6 +161,29 @@ class SuperclassDeclRequest : void cacheResult(ClassDecl *value) const; }; +/// Requests whether or not this class has designated initializers that are +/// not public or @usableFromInline. +class HasMissingDesignatedInitializersRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, ClassDecl *subject) const; + +public: + // Caching + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(bool) const; +}; + /// Request the nominal declaration extended by a given extension declaration. class ExtendedNominalRequest : public SimpleRequest(this); - mutableThis->Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = 1; - (void)mutableThis->lookupDirect(DeclBaseName::createConstructor()); - } - - return Bits.ClassDecl.HasMissingDesignatedInitializers; + return evaluateOrDefault( + getASTContext().evaluator, + HasMissingDesignatedInitializersRequest{const_cast(this)}, + false); } bool ClassDecl::hasMissingVTableEntries() const { @@ -4158,7 +4154,7 @@ bool ClassDecl::isIncompatibleWithWeakReferences() const { return false; } -bool ClassDecl::inheritsSuperclassInitializers() { +bool ClassDecl::inheritsSuperclassInitializers() const { // If there's no superclass, there's nothing to inherit. if (!getSuperclass()) return false; diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index 5eb06e0bafa6d..d6013f2c10bc0 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -67,6 +67,47 @@ void SuperclassDeclRequest::cacheResult(ClassDecl *value) const { protocolDecl->LazySemanticInfo.SuperclassDecl.setPointerAndInt(value, true); } +//----------------------------------------------------------------------------// +// Missing designated initializers computation +//----------------------------------------------------------------------------// + +Optional HasMissingDesignatedInitializersRequest::getCachedResult() const { + auto classDecl = std::get<0>(getStorage()); + return classDecl->getCachedHasMissingDesignatedInitializers(); +} + +void HasMissingDesignatedInitializersRequest::cacheResult(bool result) const { + auto classDecl = std::get<0>(getStorage()); + classDecl->setHasMissingDesignatedInitializers(result); +} + +llvm::Expected +HasMissingDesignatedInitializersRequest::evaluate(Evaluator &evaluator, + ClassDecl *subject) const { + // Short-circuit and check for the attribute here. + if (subject->getAttrs().hasAttribute()) + return true; + + AccessScope scope = + subject->getFormalAccessScope(/*useDC*/nullptr, + /*treatUsableFromInlineAsPublic*/true); + // This flag only makes sense for public types that will be written in the + // module. + if (!scope.isPublic()) + return false; + + auto constructors = subject->lookupDirect(DeclBaseName::createConstructor()); + return llvm::any_of(constructors, [&](ValueDecl *decl) { + auto init = cast(decl); + if (!init->isDesignatedInit()) + return false; + AccessScope scope = + init->getFormalAccessScope(/*useDC*/nullptr, + /*treatUsableFromInlineAsPublic*/true); + return !scope.isPublic(); + }); +} + //----------------------------------------------------------------------------// // Extended nominal computation. //----------------------------------------------------------------------------// diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 6459d0e9cbf04..5d727c9c55e0e 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -7695,7 +7695,8 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl, if (getClangModuleForDecl(theClass) == getClangModuleForDecl(method)) { if (auto swiftClass = castIgnoringCompatibilityAlias( importDecl(theClass, CurrentVersion))) { - swiftClass->setHasMissingDesignatedInitializers(); + SwiftContext.evaluator.cacheOutput( + HasMissingDesignatedInitializersRequest{swiftClass}, true); } } } diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 35139a903e797..9556bc555a660 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -1002,13 +1002,18 @@ static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) { llvm::Expected InheritsSuperclassInitializersRequest::evaluate(Evaluator &eval, ClassDecl *decl) const { + // Check if we parsed the @_inheritsConvenienceInitializers attribute. + if (decl->getAttrs().hasAttribute()) + return true; + auto superclass = decl->getSuperclass(); assert(superclass); // If the superclass has known-missing designated initializers, inheriting // is unsafe. auto *superclassDecl = superclass->getClassOrBoundGenericClass(); - if (superclassDecl->hasMissingDesignatedInitializers()) + if (superclassDecl->getModuleContext() != decl->getParentModule() && + superclassDecl->hasMissingDesignatedInitializers()) return false; // If we're allowed to inherit designated initializers, then we can inherit diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 4d6ccb0ea8c0d..100a1b30f7401 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1103,6 +1103,7 @@ static void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) { auto *superclassDecl = classDecl->getSuperclassDecl(); if (superclassDecl && + superclassDecl->getModuleContext() != classDecl->getModuleContext() && superclassDecl->hasMissingDesignatedInitializers()) return; From 511db0c90af7d2ffba8141ed5da38c7cee1e004c Mon Sep 17 00:00:00 2001 From: Harlan Haskins Date: Tue, 19 Nov 2019 16:53:43 -0800 Subject: [PATCH 281/478] [ModuleInterface] Add printing for new attributes Specially print @_hasMissingDesignatedInitializers and @_inheritsConvenienceInitializers in module interfaces Fixes rdar://51249311 --- lib/AST/ASTPrinter.cpp | 16 +++++ test/IDE/print_ast_tc_decls.swift | 14 ++--- ...erits-superclass-initializers-client.swift | 41 +++++++++++++ .../inherits-superclass-initializers.swift | 60 +++++++++++++++++++ .../non-public-designated-inits-client.swift | 26 ++++++++ .../non-public-designated-inits.swift | 21 +++++++ test/ModuleInterface/nsmanaged-attr.swift | 2 +- test/attr/attr_objc.swift | 8 +-- 8 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 test/ModuleInterface/inherits-superclass-initializers-client.swift create mode 100644 test/ModuleInterface/inherits-superclass-initializers.swift create mode 100644 test/ModuleInterface/non-public-designated-inits-client.swift create mode 100644 test/ModuleInterface/non-public-designated-inits.swift diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index e4e038947fc01..aafb4e11c8ab3 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -990,6 +990,22 @@ void PrintAST::printAttributes(const Decl *D) { Printer << " "; } } + + // If the declaration has designated inits that won't be visible to + // clients, or if it inherits superclass convenience initializers, + // then print those attributes specially. + if (auto CD = dyn_cast(D)) { + if (Options.PrintImplicitAttrs) { + if (CD->inheritsSuperclassInitializers()) { + Printer.printAttrName("@_inheritsConvenienceInitializers"); + Printer << " "; + } + if (CD->hasMissingDesignatedInitializers()) { + Printer.printAttrName("@_hasMissingDesignatedInitializers"); + Printer << " "; + } + } + } } D->getAttrs().print(Printer, Options, D); diff --git a/test/IDE/print_ast_tc_decls.swift b/test/IDE/print_ast_tc_decls.swift index 48ded10a61b5f..6e58881b90b4f 100644 --- a/test/IDE/print_ast_tc_decls.swift +++ b/test/IDE/print_ast_tc_decls.swift @@ -452,7 +452,7 @@ class d0120_TestClassBase { } class d0121_TestClassDerived : d0120_TestClassBase { -// PASS_COMMON-LABEL: {{^}}class d0121_TestClassDerived : d0120_TestClassBase {{{$}} +// PASS_COMMON-LABEL: {{^}}@_inheritsConvenienceInitializers {{()?}}class d0121_TestClassDerived : d0120_TestClassBase {{{$}} required init() { super.init() } // PASS_COMMON-NEXT: {{^}} required init(){{$}} @@ -611,8 +611,8 @@ struct d0200_EscapedIdentifiers { // PASS_ONE_LINE_TYPEREPR-DAG: {{^}} typealias `protocol` = `class`{{$}} class `extension` : `class` {} -// PASS_ONE_LINE_TYPE-DAG: {{^}} class `extension` : d0200_EscapedIdentifiers.`class` {{{$}} -// PASS_ONE_LINE_TYPEREPR-DAG: {{^}} class `extension` : `class` {{{$}} +// PASS_ONE_LINE_TYPE-DAG: {{^}} @_inheritsConvenienceInitializers class `extension` : d0200_EscapedIdentifiers.`class` {{{$}} +// PASS_ONE_LINE_TYPEREPR-DAG: {{^}} @_inheritsConvenienceInitializers class `extension` : `class` {{{$}} // PASS_COMMON: {{^}} @objc deinit{{$}} // PASS_COMMON-NEXT: {{^}} {{(override )?}}init(){{$}} // PASS_COMMON-NEXT: {{^}} }{{$}} @@ -748,7 +748,7 @@ class d0260_ExplodePattern_TestClassBase { } class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase { -// PASS_EXPLODE_PATTERN-LABEL: {{^}}class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase {{{$}} +// PASS_EXPLODE_PATTERN-LABEL: {{^}}@_inheritsConvenienceInitializers class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase {{{$}} override final var baseProp2: Int { get { @@ -791,13 +791,13 @@ class ClassWithInheritance2 : FooProtocol, BarProtocol {} // PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance2 : FooProtocol, BarProtocol {{{$}} class ClassWithInheritance3 : FooClass {} -// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance3 : FooClass {{{$}} +// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance3 : FooClass {{{$}} class ClassWithInheritance4 : FooClass, FooProtocol {} -// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance4 : FooClass, FooProtocol {{{$}} +// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance4 : FooClass, FooProtocol {{{$}} class ClassWithInheritance5 : FooClass, FooProtocol, BarProtocol {} -// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance5 : FooClass, FooProtocol, BarProtocol {{{$}} +// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance5 : FooClass, FooProtocol, BarProtocol {{{$}} class ClassWithInheritance6 : QuxProtocol, SubFooProtocol { typealias Qux = Int diff --git a/test/ModuleInterface/inherits-superclass-initializers-client.swift b/test/ModuleInterface/inherits-superclass-initializers-client.swift new file mode 100644 index 0000000000000..53880c5c89d7d --- /dev/null +++ b/test/ModuleInterface/inherits-superclass-initializers-client.swift @@ -0,0 +1,41 @@ +// Compile the imported module to a .swiftinterface and ensure the convenience +// init delegates through the subclasses correctly. + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(Module)) %S/inherits-superclass-initializers.swift -emit-module-path %t/Module.swiftmodule -emit-module-interface-path %t/Module.swiftinterface -module-name Module -enable-library-evolution +// RUN: rm %t/Module.swiftmodule +// RUN: %target-build-swift %s -I %t -L %t -lModule -o %t/main %target-rpath(%t) +// RUN: %target-codesign %t/main %t/%target-library-name(Module) +// RUN: %target-run %t/main %t/%target-library-name(Module) | %FileCheck %s + +// Make sure the same error is emitted when importing a .swiftmodule + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(Module)) %S/inherits-superclass-initializers.swift -emit-module-path %t/Module.swiftmodule -module-name Module -enable-library-evolution +// RUN: %target-build-swift %s -I %t -L %t -lModule -o %t/main %target-rpath(%t) +// RUN: %target-codesign %t/main %t/%target-library-name(Module) +// RUN: %target-run %t/main %t/%target-library-name(Module) | %FileCheck %s + +import Module + +_ = Base() +// CHECK: secret init from Base + +_ = Sub() +// CHECK: secret init from Sub +// CHECK: secret init from Base + +_ = SubSub() +// CHECK: secret init from SubSub +// CHECK: secret init from Sub +// CHECK: secret init from Base + +test() +// CHECK: secret init from Sub +// CHECK: secret init from Base + +// CHECK: secret init from SubSub +// CHECK: secret init from Sub +// CHECK: secret init from Base + +// CHECK-NOT: public init diff --git a/test/ModuleInterface/inherits-superclass-initializers.swift b/test/ModuleInterface/inherits-superclass-initializers.swift new file mode 100644 index 0000000000000..e2489d58a936e --- /dev/null +++ b/test/ModuleInterface/inherits-superclass-initializers.swift @@ -0,0 +1,60 @@ +// Note: this test has a client: inherits-superclass-initializers-client.swift + +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %s -emit-module-interface-path %t/Module.swiftinterface -module-name Module -enable-library-evolution +// RUN: %FileCheck %s < %t/Module.swiftinterface + +// CHECK: @_hasMissingDesignatedInitializers open class Base { +open class Base { + // CHECK-NEXT: public init(arg: Swift.Int) + public init(arg: Int) { + print("public init from Base") + } + // CHECK-NOT: init(secret: Swift.Int) + internal init(secret: Int) { + print("secret init from Base") + } + + // CHECK: convenience public init() + public convenience init() { + self.init(secret: 4) + } + +// CHECK: } +} + +// CHECK: @_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers public class Sub : Module.Base { +public class Sub : Base { + // CHECK: override public init(arg: Swift.Int) + public override init(arg: Int) { + print("public init from Sub") + super.init(arg: arg) + } + // CHECK-NOT: init(secret: Swift.Int) + internal override init(secret: Int) { + print("secret init from Sub") + super.init(secret: secret) + } +// CHECK: } +} + +// CHECK: @_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers public class SubSub : Module.Sub { +public class SubSub: Sub { + // CHECK: override public init(arg: Swift.Int) + public override init(arg: Int) { + print("public init from SubSub") + super.init(arg: arg) + } + // CHECK-NOT: init(secret: Swift.Int) + internal override init(secret: Int) { + print("secret init from SubSub") + super.init(secret: secret) + } +// CHECK: } +} + +@inlinable public func test() { + _ = Sub() + _ = SubSub() +} diff --git a/test/ModuleInterface/non-public-designated-inits-client.swift b/test/ModuleInterface/non-public-designated-inits-client.swift new file mode 100644 index 0000000000000..a0e84cb52cbf5 --- /dev/null +++ b/test/ModuleInterface/non-public-designated-inits-client.swift @@ -0,0 +1,26 @@ +// Compile the imported module to a .swiftinterface and ensure the convenience +// init cannot be called. + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -typecheck %S/non-public-designated-inits.swift -emit-module-interface-path %t/Module.swiftinterface -module-name Module -enable-library-evolution +// RUN: %target-swift-frontend -typecheck -verify %s -I %t + +// Make sure the same error is emitted when importing a .swiftmodule + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -o %t/Module.swiftmodule %S/non-public-designated-inits.swift -module-name Module -enable-library-evolution +// RUN: %target-swift-frontend -typecheck -verify %s -I %t + +import Module + +open class B : A { + var x: Int + + public override init(_ x: Int) { + self.x = x + super.init(x) + } +} + +print(B(hi: ())) // expected-error {{cannot convert value of type '()' to expected argument type 'Int'}} +// expected-error @-1 {{extraneous argument label 'hi:' in call}} diff --git a/test/ModuleInterface/non-public-designated-inits.swift b/test/ModuleInterface/non-public-designated-inits.swift new file mode 100644 index 0000000000000..c8b00df92e677 --- /dev/null +++ b/test/ModuleInterface/non-public-designated-inits.swift @@ -0,0 +1,21 @@ +// Note: This test has a client: non-public-designated-inits-client.swift + +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %s -emit-module-interface-path %t/Module.swiftinterface -module-name Module -enable-library-evolution +// RUN: %FileCheck %s < %t/Module.swiftinterface + +// CHECK: @_hasMissingDesignatedInitializers open class A { +open class A { + // This is a non-public designated init, which means the convenience + // init should not be inheritable. + init() {} + + // CHECK-NEXT: public init(_: Swift.Int) + public init(_: Int) {} + + // CHECK-NEXT: convenience public init(hi: ()) + public convenience init(hi: ()) { self.init() } + +// CHECK: } +} diff --git a/test/ModuleInterface/nsmanaged-attr.swift b/test/ModuleInterface/nsmanaged-attr.swift index c199e163910fe..2312c8a926294 100644 --- a/test/ModuleInterface/nsmanaged-attr.swift +++ b/test/ModuleInterface/nsmanaged-attr.swift @@ -16,7 +16,7 @@ import CoreData import Foundation -// CHECK: @objc public class MyObject : CoreData.NSManagedObject { +// CHECK: @objc @_inheritsConvenienceInitializers public class MyObject : CoreData.NSManagedObject { public class MyObject: NSManagedObject { // CHECK: @objc @NSManaged dynamic public var myVar: Swift.String { // CHECK-NEXT: @objc get diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index ff125d501f35f..fb87402898c3b 100644 --- a/test/attr/attr_objc.swift +++ b/test/attr/attr_objc.swift @@ -1534,7 +1534,7 @@ class infer_instanceVar2< } class infer_instanceVar3 : Class_ObjC1 { -// CHECK-LABEL: @objc class infer_instanceVar3 : Class_ObjC1 { +// CHECK-LABEL: @objc @_inheritsConvenienceInitializers class infer_instanceVar3 : Class_ObjC1 { var v1: Int = 0 // CHECK-LABEL: @objc @_hasInitialValue var v1: Int @@ -1599,13 +1599,13 @@ protocol infer_throughConformanceProto1 { } class infer_class1 : PlainClass {} -// CHECK-LABEL: {{^}}class infer_class1 : PlainClass { +// CHECK-LABEL: {{^}}@_inheritsConvenienceInitializers class infer_class1 : PlainClass { class infer_class2 : Class_ObjC1 {} -// CHECK-LABEL: @objc class infer_class2 : Class_ObjC1 { +// CHECK-LABEL: @objc @_inheritsConvenienceInitializers class infer_class2 : Class_ObjC1 { class infer_class3 : infer_class2 {} -// CHECK-LABEL: @objc class infer_class3 : infer_class2 { +// CHECK-LABEL: @objc @_inheritsConvenienceInitializers class infer_class3 : infer_class2 { class infer_class4 : Protocol_Class1 {} // CHECK-LABEL: {{^}}class infer_class4 : Protocol_Class1 { From 4d731735d23c40ef28afd1e3c5a044d409b925e6 Mon Sep 17 00:00:00 2001 From: Harlan Haskins Date: Tue, 19 Nov 2019 16:57:02 -0800 Subject: [PATCH 282/478] [api-digester] Teach the api-digester about hasMissingDesignatedInitializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because we won’t be serializing this attribute, add custom diagnostics for the cases where: - We add @_hasMissingDesignatedInits to an open class, which means subclasses won’t be able to inherit its inits - We remove @_inheritsConvenienceInitializers, which means APIs are removed --- include/swift/AST/DiagnosticsModuleDiffer.def | 4 +++ include/swift/IDE/DigesterEnums.def | 2 ++ .../Inputs/cake_baseline/cake.swift | 21 +++++++++++++++- .../Inputs/cake_current/cake.swift | 25 ++++++++++++++++++- test/api-digester/Outputs/Cake-abi.txt | 8 ++++-- test/api-digester/Outputs/Cake.txt | 2 ++ test/api-digester/Outputs/cake-abi.json | 18 +++++++++---- test/api-digester/Outputs/cake.json | 15 ++++++++--- .../Outputs/clang-module-dump.txt | 19 ++++++++++++++ test/api-digester/compare-dump.swift | 4 +-- .../ModuleAnalyzerNodes.cpp | 10 +++++++- .../swift-api-digester/ModuleAnalyzerNodes.h | 9 +++++++ .../ModuleDiagsConsumer.cpp | 2 ++ .../swift-api-digester/swift-api-digester.cpp | 16 ++++++++++++ 14 files changed, 139 insertions(+), 16 deletions(-) diff --git a/include/swift/AST/DiagnosticsModuleDiffer.def b/include/swift/AST/DiagnosticsModuleDiffer.def index df4ddf42b9a89..d97909581b1b9 100644 --- a/include/swift/AST/DiagnosticsModuleDiffer.def +++ b/include/swift/AST/DiagnosticsModuleDiffer.def @@ -98,6 +98,10 @@ ERROR(objc_name_change,none,"%0 has ObjC name change from %1 to %2", (StringRef, ERROR(desig_init_added,none,"%0 has been added as a designated initializer to an open class", (StringRef)) +ERROR(added_invisible_designated_init,none,"%0 has new designated initializers that are not visible to clients", (StringRef)) + +ERROR(not_inheriting_convenience_inits,none,"%0 no longer inherits convenience inits from its superclass", (StringRef)) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/include/swift/IDE/DigesterEnums.def b/include/swift/IDE/DigesterEnums.def index b1566ce894dff..d00a337f4eb21 100644 --- a/include/swift/IDE/DigesterEnums.def +++ b/include/swift/IDE/DigesterEnums.def @@ -132,6 +132,8 @@ KEY_BOOL(HasStorage, hasStorage) KEY_BOOL(ReqNewWitnessTableEntry, reqNewWitnessTableEntry) KEY_BOOL(IsABIPlaceholder, isABIPlaceholder) KEY_BOOL(IsExternal, isExternal) +KEY_BOOL(HasMissingDesignatedInitializers, hasMissingDesignatedInitializers) +KEY_BOOL(InheritsConvenienceInitializers, inheritsConvenienceInitializers) KEY(kind) diff --git a/test/api-digester/Inputs/cake_baseline/cake.swift b/test/api-digester/Inputs/cake_baseline/cake.swift index fd57d1823f41e..8ac2884bca90a 100644 --- a/test/api-digester/Inputs/cake_baseline/cake.swift +++ b/test/api-digester/Inputs/cake_baseline/cake.swift @@ -105,7 +105,26 @@ public protocol DerivedProtocolRequiementChanges: RequiementChanges {} public class SuperClassRemoval: C3 {} -public class ClassToStruct {} +public class ClassToStruct { + public init() {} +} + +open class ClassWithMissingDesignatedInits { + internal init() {} + public convenience init(x: Int) { self.init() } +} + +open class ClassWithoutMissingDesignatedInits { + public init() {} + public convenience init(x: Int) { self.init() } +} + +public class SubclassWithMissingDesignatedInits: ClassWithMissingDesignatedInits { +} + +public class SubclassWithoutMissingDesignatedInits: ClassWithoutMissingDesignatedInits { +} + public protocol ProtocolToEnum {} public class SuperClassChange: C7 {} diff --git a/test/api-digester/Inputs/cake_current/cake.swift b/test/api-digester/Inputs/cake_current/cake.swift index d9dee4768a10f..1f9a0a772298e 100644 --- a/test/api-digester/Inputs/cake_current/cake.swift +++ b/test/api-digester/Inputs/cake_current/cake.swift @@ -114,7 +114,30 @@ public protocol DerivedProtocolRequiementChanges: RequiementChanges {} public class SuperClassRemoval {} -public struct ClassToStruct {} +public struct ClassToStruct { + public init() {} +} + +open class ClassWithMissingDesignatedInits { + // Remove the @_hasMissingDesignatedInitializers attribute + public init() {} + public convenience init(x: Int) { self.init() } +} + +open class ClassWithoutMissingDesignatedInits { + // Add the @_hasMissingDesignatedInitializers attribute by adding an inaccessible + // init + public init() {} + public convenience init(x: Int) { self.init() } + internal init(y: Int) {} +} + +public class SubclassWithMissingDesignatedInits: ClassWithMissingDesignatedInits { +} + +public class SubclassWithoutMissingDesignatedInits: ClassWithoutMissingDesignatedInits { +} + public enum ProtocolToEnum {} public class SuperClassChange: C8 {} diff --git a/test/api-digester/Outputs/Cake-abi.txt b/test/api-digester/Outputs/Cake-abi.txt index f19c8589fdd62..fb4d8c7a8926f 100644 --- a/test/api-digester/Outputs/Cake-abi.txt +++ b/test/api-digester/Outputs/Cake-abi.txt @@ -60,6 +60,8 @@ cake: Class C0 is a new API without @available attribute cake: Class C5 is now without @objc cake: Class C8 is a new API without @available attribute cake: Constructor C1.init(_:) is a new API without @available attribute +cake: Constructor ClassWithMissingDesignatedInits.init() is a new API without @available attribute +cake: Constructor SubclassWithMissingDesignatedInits.init() is a new API without @available attribute cake: Enum IceKind is now without @frozen cake: EnumElement FrozenKind.AddedCase is a new API without @available attribute cake: Func C1.foo1() is now not static @@ -102,10 +104,10 @@ cake: Struct fixedLayoutStruct has added a conformance to an existing protocol P cake: Struct fixedLayoutStruct has removed conformance to P1 /* Protocol Requirement Change */ -cake: Accessor HasMutatingMethodClone.bar.Get() now requires new witness table entry +cake: Accessor HasMutatingMethodClone.bar.Get() now requires new witness table entry cake: AssociatedType AssociatedTypePro.T1 has removed default type Swift.Int cake: AssociatedType RequiementChanges.addedTypeWithoutDefault has been added as a protocol requirement -cake: Func HasMutatingMethodClone.foo() now requires new witness table entry +cake: Func HasMutatingMethodClone.foo() now requires new witness table entry cake: Func RequiementChanges.addedFunc() has been added as a protocol requirement cake: Var RequiementChanges.addedVar has been added as a protocol requirement @@ -113,4 +115,6 @@ cake: Var RequiementChanges.addedVar has been added as a protocol requirement cake: Class C4 has changed its super class from APINotesTest.OldType to APINotesTest.NewType cake: Class SubGenericClass has changed its super class from cake.GenericClass to cake.GenericClass cake: Class SuperClassRemoval has removed its super class cake.C3 +cake: Class SuperClassRemoval no longer inherits convenience inits from its superclass cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class +cake: Constructor ClassWithMissingDesignatedInits.init() has been added as a designated initializer to an open class diff --git a/test/api-digester/Outputs/Cake.txt b/test/api-digester/Outputs/Cake.txt index 97f48480f01cf..0dab4e4589584 100644 --- a/test/api-digester/Outputs/Cake.txt +++ b/test/api-digester/Outputs/Cake.txt @@ -64,7 +64,9 @@ cake: Accessor ClassWithOpenMember.property.Get() is no longer open for subclass cake: Class C4 has changed its super class from APINotesTest.OldType to APINotesTest.NewType cake: Class SubGenericClass has changed its super class from cake.GenericClass to cake.GenericClass cake: Class SuperClassRemoval has removed its super class cake.C3 +cake: Class SuperClassRemoval no longer inherits convenience inits from its superclass cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class +cake: Constructor ClassWithMissingDesignatedInits.init() has been added as a designated initializer to an open class cake: Func ClassWithOpenMember.bar() is no longer open for subclassing cake: Func ClassWithOpenMember.foo() is no longer open for subclassing cake: Var ClassWithOpenMember.property is no longer open for subclassing diff --git a/test/api-digester/Outputs/cake-abi.json b/test/api-digester/Outputs/cake-abi.json index ac91076d139a7..a6f9baf090553 100644 --- a/test/api-digester/Outputs/cake-abi.json +++ b/test/api-digester/Outputs/cake-abi.json @@ -199,7 +199,8 @@ "usr": "s:4cake2C0C", "moduleName": "cake", "genericSig": "<τ_0_0, τ_0_1, τ_0_2>", - "sugared_genericSig": "" + "sugared_genericSig": "", + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -428,6 +429,8 @@ "usr": "s:4cake2C1C", "moduleName": "cake", "superclassUsr": "s:4cake2C0C", + "hasMissingDesignatedInitializers": true, + "inheritsConvenienceInitializers": true, "superclassNames": [ "cake.C0" ] @@ -1330,7 +1333,8 @@ "declAttributes": [ "FixedLayout", "UsableFromInline" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1387,6 +1391,7 @@ "declKind": "Class", "usr": "s:4cake15FutureContainerC", "moduleName": "cake", + "hasMissingDesignatedInitializers": true, "conformances": [ { "kind": "Conformance", @@ -1418,7 +1423,8 @@ "Available", "Available", "Available" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1430,7 +1436,8 @@ "intro_swift": "5", "declAttributes": [ "Available" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1482,7 +1489,8 @@ "objc_name": "NewObjCClass", "declAttributes": [ "ObjC" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", diff --git a/test/api-digester/Outputs/cake.json b/test/api-digester/Outputs/cake.json index a2e3fa2407b12..13fd939c1e866 100644 --- a/test/api-digester/Outputs/cake.json +++ b/test/api-digester/Outputs/cake.json @@ -201,7 +201,8 @@ "declKind": "Class", "usr": "s:4cake2C0C", "moduleName": "cake", - "genericSig": "" + "genericSig": "", + "hasMissingDesignatedInitializers": true }, { "kind": "TypeAlias", @@ -425,6 +426,8 @@ "usr": "s:4cake2C1C", "moduleName": "cake", "superclassUsr": "s:4cake2C0C", + "hasMissingDesignatedInitializers": true, + "inheritsConvenienceInitializers": true, "superclassNames": [ "cake.C0" ] @@ -1235,6 +1238,7 @@ "declKind": "Class", "usr": "s:4cake15FutureContainerC", "moduleName": "cake", + "hasMissingDesignatedInitializers": true, "conformances": [ { "kind": "Conformance", @@ -1266,7 +1270,8 @@ "Available", "Available", "Available" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1278,7 +1283,8 @@ "intro_swift": "5", "declAttributes": [ "Available" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1330,7 +1336,8 @@ "objc_name": "NewObjCClass", "declAttributes": [ "ObjC" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", diff --git a/test/api-digester/Outputs/clang-module-dump.txt b/test/api-digester/Outputs/clang-module-dump.txt index 80da5e122c3ba..d6d9a22418752 100644 --- a/test/api-digester/Outputs/clang-module-dump.txt +++ b/test/api-digester/Outputs/clang-module-dump.txt @@ -119,6 +119,7 @@ "Dynamic" ], "superclassUsr": "c:objc(cs)NSObject", + "inheritsConvenienceInitializers": true, "superclassNames": [ "ObjectiveC.NSObject" ], @@ -134,6 +135,24 @@ "name": "NSObjectProtocol", "printedName": "NSObjectProtocol", "usr": "c:objc(pl)NSObject" + }, + { + "kind": "Conformance", + "name": "Equatable", + "printedName": "Equatable", + "usr": "s:SQ" + }, + { + "kind": "Conformance", + "name": "Hashable", + "printedName": "Hashable", + "usr": "s:SH" + }, + { + "kind": "Conformance", + "name": "CVarArg", + "printedName": "CVarArg", + "usr": "s:s7CVarArgP" } ] }, diff --git a/test/api-digester/compare-dump.swift b/test/api-digester/compare-dump.swift index 54574cbb1a7e4..c37cecb5d424f 100644 --- a/test/api-digester/compare-dump.swift +++ b/test/api-digester/compare-dump.swift @@ -4,8 +4,8 @@ // RUN: %empty-directory(%t.module-cache) // RUN: %swift -emit-module -o %t.mod1/cake.swiftmodule %S/Inputs/cake_baseline/cake.swift -parse-as-library -enable-library-evolution -I %S/Inputs/APINotesLeft %clang-importer-sdk-nosource -module-name cake // RUN: %swift -emit-module -o %t.mod2/cake.swiftmodule %S/Inputs/cake_current/cake.swift -parse-as-library -enable-library-evolution -I %S/Inputs/APINotesRight %clang-importer-sdk-nosource -module-name cake -// RUN: %api-digester -dump-sdk -module cake -o - -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod1 -I %S/Inputs/APINotesLeft > %t.dump1.json -// RUN: %api-digester -dump-sdk -module cake -o - -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod2 -I %S/Inputs/APINotesLeft > %t.dump2.json +// RUN: %api-digester -dump-sdk -module cake -o %t.dump1.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod1 -I %S/Inputs/APINotesLeft +// RUN: %api-digester -dump-sdk -module cake -o %t.dump2.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod2 -I %S/Inputs/APINotesRight // RUN: %api-digester -diagnose-sdk -print-module --input-paths %t.dump1.json -input-paths %t.dump2.json -o %t.result // RUN: %clang -E -P -x c %S/Outputs/Cake.txt -o - | sed '/^\s*$/d' > %t.expected diff --git a/tools/swift-api-digester/ModuleAnalyzerNodes.cpp b/tools/swift-api-digester/ModuleAnalyzerNodes.cpp index 402d59493adbd..fc6fe99b0b5f7 100644 --- a/tools/swift-api-digester/ModuleAnalyzerNodes.cpp +++ b/tools/swift-api-digester/ModuleAnalyzerNodes.cpp @@ -126,7 +126,9 @@ SDKNodeTypeAlias::SDKNodeTypeAlias(SDKNodeInitInfo Info): SDKNodeDeclType::SDKNodeDeclType(SDKNodeInitInfo Info): SDKNodeDecl(Info, SDKNodeKind::DeclType), SuperclassUsr(Info.SuperclassUsr), SuperclassNames(Info.SuperclassNames), - EnumRawTypeName(Info.EnumRawTypeName), IsExternal(Info.IsExternal) {} + EnumRawTypeName(Info.EnumRawTypeName), IsExternal(Info.IsExternal), + HasMissingDesignatedInitializers(Info.HasMissingDesignatedInitializers), + InheritsConvenienceInitializers(Info.InheritsConvenienceInitializers) {} SDKNodeConformance::SDKNodeConformance(SDKNodeInitInfo Info): SDKNode(Info, SDKNodeKind::Conformance), @@ -1403,6 +1405,8 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD) SuperclassNames.push_back(getPrintedName(Ctx, T->getCanonicalType())); } } + HasMissingDesignatedInitializers = CD->hasMissingDesignatedInitializers(); + InheritsConvenienceInitializers = CD->inheritsSuperclassInitializers(); } if (auto *FD = dyn_cast(VD)) { @@ -1975,6 +1979,10 @@ void SDKNodeDeclType::jsonize(json::Output &out) { output(out, KeyKind::KK_superclassUsr, SuperclassUsr); output(out, KeyKind::KK_enumRawTypeName, EnumRawTypeName); output(out, KeyKind::KK_isExternal, IsExternal); + output(out, KeyKind::KK_hasMissingDesignatedInitializers, + HasMissingDesignatedInitializers); + output(out, KeyKind::KK_inheritsConvenienceInitializers, + InheritsConvenienceInitializers); out.mapOptional(getKeyContent(Ctx, KeyKind::KK_superclassNames).data(), SuperclassNames); out.mapOptional(getKeyContent(Ctx, KeyKind::KK_conformances).data(), Conformances); } diff --git a/tools/swift-api-digester/ModuleAnalyzerNodes.h b/tools/swift-api-digester/ModuleAnalyzerNodes.h index 8e5feb76f1016..db19837d46ebc 100644 --- a/tools/swift-api-digester/ModuleAnalyzerNodes.h +++ b/tools/swift-api-digester/ModuleAnalyzerNodes.h @@ -524,6 +524,8 @@ class SDKNodeDeclType: public SDKNodeDecl { // Check whether the type declaration is pulled from an external module so we // can incorporate extensions in the interested module. bool IsExternal; + bool HasMissingDesignatedInitializers; + bool InheritsConvenienceInitializers; public: SDKNodeDeclType(SDKNodeInitInfo Info); static bool classof(const SDKNode *N); @@ -548,6 +550,13 @@ class SDKNodeDeclType: public SDKNodeDecl { return EnumRawTypeName; } + bool hasMissingDesignatedInitializers() const { + return HasMissingDesignatedInitializers; + }; + bool inheritsConvenienceInitializers() const { + return InheritsConvenienceInitializers; + }; + Optional getSuperclass() const; /// Finding the node through all children, including the inheritted ones, diff --git a/tools/swift-api-digester/ModuleDiagsConsumer.cpp b/tools/swift-api-digester/ModuleDiagsConsumer.cpp index dcb9902633de3..2964709fc4ae4 100644 --- a/tools/swift-api-digester/ModuleDiagsConsumer.cpp +++ b/tools/swift-api-digester/ModuleDiagsConsumer.cpp @@ -73,6 +73,8 @@ static StringRef getCategoryName(uint32_t ID) { case LocalDiagID::super_class_changed: case LocalDiagID::no_longer_open: case LocalDiagID::desig_init_added: + case LocalDiagID::added_invisible_designated_init: + case LocalDiagID::not_inheriting_convenience_inits: return "/* Class Inheritance Change */"; default: return StringRef(); diff --git a/tools/swift-api-digester/swift-api-digester.cpp b/tools/swift-api-digester/swift-api-digester.cpp index dcc6e06131126..6253f69a9e8df 100644 --- a/tools/swift-api-digester/swift-api-digester.cpp +++ b/tools/swift-api-digester/swift-api-digester.cpp @@ -789,6 +789,22 @@ void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) { emitDiag(Loc, diag::super_class_changed, LSuperClass, RSuperClass); } } + + // Check for @_hasMissingDesignatedInitializers and + // @_inheritsConvenienceInitializers changes. + if (isOpen() && R->isOpen()) { + // It's not safe to add new, invisible designated inits to open + // classes. + if (!hasMissingDesignatedInitializers() && + R->hasMissingDesignatedInitializers()) + R->emitDiag(R->getLoc(), diag::added_invisible_designated_init); + } + + // It's not safe to stop inheriting convenience inits, it changes + // the set of initializers that are available. + if (inheritsConvenienceInitializers() && + !R->inheritsConvenienceInitializers()) + R->emitDiag(R->getLoc(), diag::not_inheriting_convenience_inits); break; } default: From 93d07c6c30b20a8813f3ae7a1cb38d8981bdef43 Mon Sep 17 00:00:00 2001 From: tbkka Date: Mon, 6 Jan 2020 10:29:36 -0800 Subject: [PATCH 283/478] _isOptional(type(of: value)) Does Not Do What You Think It Does (#28994) In particular, if value is `Any` in a generic context, then `type(of: value)` is `Any.Protocol` which is never considered optional. As a result, the first clause here was never actually being used for `print()` or other similar paths. (Curiously, it _was_ used for string interpolation.) This changes how we test for an optional type so that the first clause is consistently used for all optionals, even when they are wrapped in `Any` containers. Fortunately? `print()` was producing the right results for optionals because of a dynamic cast bug that failed to unwrap optionals in these same contexts. --- stdlib/public/core/OutputStream.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/OutputStream.swift b/stdlib/public/core/OutputStream.swift index a00bab337d49f..358696961007b 100644 --- a/stdlib/public/core/OutputStream.swift +++ b/stdlib/public/core/OutputStream.swift @@ -384,7 +384,9 @@ internal func _print_unlocked( // string. Check for Optional first, before checking protocol // conformance below, because an Optional value is convertible to a // protocol if its wrapped type conforms to that protocol. - if _isOptional(type(of: value)) { + // Note: _isOptional doesn't work here when T == Any, hence we + // use a more elaborate formulation: + if _openExistential(type(of: value as Any), do: _isOptional) { let debugPrintable = value as! CustomDebugStringConvertible debugPrintable.debugDescription.write(to: &target) return From dfda7aca7ff06a6d1929f4e1e7f5dcce949e1044 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 6 Jan 2020 11:31:06 -0800 Subject: [PATCH 284/478] [Constraint solver] Generalize "target" of solution application. Chip away at the expression-centric nature of the constraint system by generalizing the "target" of solution application from a single expression to a a "SolutionApplicationTarget", which can be an expression or a function body. The latter will be used for applying function builders to an entire function body. --- lib/Sema/CSApply.cpp | 88 +++++++++++++++++++++++++------------ lib/Sema/CSDiag.cpp | 34 +++++++------- lib/Sema/CSSolver.cpp | 2 +- lib/Sema/ConstraintSystem.h | 65 +++++++++++++++++++++++++-- 4 files changed, 140 insertions(+), 49 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 9a8c38ff25d10..7dbec1839b0cd 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7206,10 +7206,9 @@ bool ConstraintSystem::applySolutionFixes(const Solution &solution) { /// Apply a given solution to the expression, producing a fully /// type-checked expression. -Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, - Type convertType, - bool discardedExpr, - bool performingDiagnostics) { +llvm::PointerUnion ConstraintSystem::applySolutionImpl( + Solution &solution, SolutionApplicationTarget target, Type convertType, + bool discardedExpr, bool performingDiagnostics) { // If any fixes needed to be applied to arrive at this solution, resolve // them to specific expressions. if (!solution.Fixes.empty()) { @@ -7228,7 +7227,7 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, // If we didn't manage to diagnose anything well, so fall back to // diagnosing mining the system to construct a reasonable error message. - diagnoseFailureForExpr(expr); + diagnoseFailureFor(target); return nullptr; } } @@ -7236,10 +7235,24 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, ExprRewriter rewriter(*this, solution, shouldSuppressDiagnostics()); ExprWalker walker(rewriter); - // Apply the solution to the expression. - auto result = expr->walk(walker); - if (!result) - return nullptr; + // Apply the solution to the target. + llvm::PointerUnion result; + if (auto expr = target.getAsExpr()) { + result = expr->walk(walker); + } else { + // FIXME: Implement this! + llvm_unreachable("Not yet implemented"); + +#if false + auto fn = *target.getAsFunction(); + auto transform = rewriter.getAppliedBuilderTransform(fn); + assert(transform && "Not applying builder transform?"); + result = walker.applyFunctionBuilderBodyTransform(fn, transform); +#endif + } + + if (result.isNull()) + return result; // If we're re-typechecking an expression for diagnostics, don't // visit closures that have non-single expression bodies. @@ -7261,28 +7274,36 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, return nullptr; } - // We are supposed to use contextual type only if it is present and - // this expression doesn't represent the implicit return of the single - // expression function which got deduced to be `Never`. - auto shouldCoerceToContextualType = [&]() { - return convertType && !(getType(result)->isUninhabited() && - getContextualTypePurpose() == CTP_ReturnSingleExpr); - }; + if (auto resultExpr = result.dyn_cast()) { + Expr *expr = target.getAsExpr(); + assert(expr && "Can't have expression result without expression target"); + // We are supposed to use contextual type only if it is present and + // this expression doesn't represent the implicit return of the single + // expression function which got deduced to be `Never`. + auto shouldCoerceToContextualType = [&]() { + return convertType && + !(getType(resultExpr)->isUninhabited() && + getContextualTypePurpose() == CTP_ReturnSingleExpr); + }; - // If we're supposed to convert the expression to some particular type, - // do so now. - if (shouldCoerceToContextualType()) { - result = rewriter.coerceToType(result, convertType, - getConstraintLocator(expr)); - if (!result) - return nullptr; - } else if (getType(result)->hasLValueType() && !discardedExpr) { - // We referenced an lvalue. Load it. - result = rewriter.coerceToType(result, getType(result)->getRValueType(), - getConstraintLocator(expr)); + // If we're supposed to convert the expression to some particular type, + // do so now. + if (shouldCoerceToContextualType()) { + result = rewriter.coerceToType(resultExpr, convertType, + getConstraintLocator(expr)); + if (!result) + return nullptr; + } else if (getType(resultExpr)->hasLValueType() && !discardedExpr) { + // We referenced an lvalue. Load it. + result = rewriter.coerceToType(resultExpr, + getType(resultExpr)->getRValueType(), + getConstraintLocator(expr)); + } + + if (resultExpr) + solution.setExprTypes(resultExpr); } - solution.setExprTypes(result); rewriter.finalize(); return result; @@ -7431,3 +7452,14 @@ ArrayRef SolutionResult::getAmbiguousSolutions() const { assert(getKind() == Ambiguous); return makeArrayRef(solutions, numSolutions); } + +llvm::PointerUnion SolutionApplicationTarget::walk( + ASTWalker &walker) { + switch (kind) { + case Kind::expression: + return getAsExpr()->walk(walker); + + case Kind::function: + return getAsFunction()->getBody()->walk(walker); + } +} diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 11f6177d1d727..ccdcf68523974 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -2430,30 +2430,32 @@ bool FailureDiagnosis::diagnoseExprFailure() { /// /// This is guaranteed to always emit an error message. /// -void ConstraintSystem::diagnoseFailureForExpr(Expr *expr) { +void ConstraintSystem::diagnoseFailureFor(SolutionApplicationTarget target) { setPhase(ConstraintSystemPhase::Diagnostics); SWIFT_DEFER { setPhase(ConstraintSystemPhase::Finalization); }; - // Look through RebindSelfInConstructorExpr to avoid weird Sema issues. - if (auto *RB = dyn_cast(expr)) - expr = RB->getSubExpr(); + if (auto expr = target.getAsExpr()) { + // Look through RebindSelfInConstructorExpr to avoid weird Sema issues. + if (auto *RB = dyn_cast(expr)) + expr = RB->getSubExpr(); - FailureDiagnosis diagnosis(expr, *this); + FailureDiagnosis diagnosis(expr, *this); - // Now, attempt to diagnose the failure from the info we've collected. - if (diagnosis.diagnoseExprFailure()) - return; + // Now, attempt to diagnose the failure from the info we've collected. + if (diagnosis.diagnoseExprFailure()) + return; - // If this is a contextual conversion problem, dig out some information. - if (diagnosis.diagnoseContextualConversionError(expr, getContextualType(), - getContextualTypePurpose())) - return; + // If this is a contextual conversion problem, dig out some information. + if (diagnosis.diagnoseContextualConversionError(expr, getContextualType(), + getContextualTypePurpose())) + return; - // If no one could find a problem with this expression or constraint system, - // then it must be well-formed... but is ambiguous. Handle this by diagnostic - // various cases that come up. - diagnosis.diagnoseAmbiguity(expr); + // If no one could find a problem with this expression or constraint system, + // then it must be well-formed... but is ambiguous. Handle this by diagnostic + // various cases that come up. + diagnosis.diagnoseAmbiguity(expr); + } } std::pair diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index f991117946808..4f1dafe71d9e1 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1127,7 +1127,7 @@ bool ConstraintSystem::solve(Expr *&expr, return true; case SolutionResult::Kind::UndiagnosedError: - diagnoseFailureForExpr(expr); + diagnoseFailureFor(expr); salvagedResult.markAsDiagnosed(); return true; diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index d41003d3ae303..e34d97a6499b1 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1100,6 +1100,54 @@ struct DynamicCallableMethods { } }; +/// Describes the target to which a constraint system's solution can be +/// applied. +class SolutionApplicationTarget { + enum class Kind { + expression, + function + } kind; + + union { + Expr *expression; + AnyFunctionRef function; + }; + +public: + SolutionApplicationTarget(Expr *expr) { + kind = Kind::expression; + expression = expr; + } + + SolutionApplicationTarget(AnyFunctionRef fn) { + kind = Kind::function; + function = fn; + } + + Expr *getAsExpr() const { + switch (kind) { + case Kind::expression: + return expression; + + case Kind::function: + return nullptr; + } + } + + Optional getAsFunction() const { + switch (kind) { + case Kind::expression: + return None; + + case Kind::function: + return function; + } + } + + /// Walk the contents of the application target. + llvm::PointerUnion walk(ASTWalker &walker); +}; + enum class ConstraintSystemPhase { ConstraintGeneration, Solving, @@ -2243,12 +2291,12 @@ class ConstraintSystem { /// system to generate a plausible diagnosis of why the system could not be /// solved. /// - /// \param expr The expression whose constraints we're investigating for a - /// better diagnostic. + /// \param target The solution target whose constraints we're investigating + /// for a better diagnostic. /// /// Assuming that this constraint system is actually erroneous, this *always* /// emits an error message. - void diagnoseFailureForExpr(Expr *expr); + void diagnoseFailureFor(SolutionApplicationTarget target); bool diagnoseAmbiguity(ArrayRef solutions); bool diagnoseAmbiguityWithFixes(SmallVectorImpl &solutions); @@ -3922,6 +3970,12 @@ class ConstraintSystem { findBestSolution(SmallVectorImpl &solutions, bool minimize); +private: + llvm::PointerUnion applySolutionImpl( + Solution &solution, SolutionApplicationTarget target, + Type convertType, bool discardedExpr, bool performingDiagnostics); + +public: /// Apply a given solution to the expression, producing a fully /// type-checked expression. /// @@ -3934,7 +3988,10 @@ class ConstraintSystem { Expr *applySolution(Solution &solution, Expr *expr, Type convertType, bool discardedExpr, - bool performingDiagnostics); + bool performingDiagnostics) { + return applySolutionImpl(solution, expr, convertType, discardedExpr, + performingDiagnostics).get(); + } /// Reorder the disjunctive clauses for a given expression to /// increase the likelihood that a favored constraint will be successfully From fffe1291fa8ed7395a803864013982b0c5183bdf Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 6 Jan 2020 11:39:11 -0800 Subject: [PATCH 285/478] [Constraint system] Generalize record of transformed function builders. --- include/swift/AST/AnyFunctionRef.h | 8 ++++++++ lib/Sema/BuilderTransform.cpp | 10 +++++----- lib/Sema/CSApply.cpp | 5 ++--- lib/Sema/CSSolver.cpp | 16 ++++++++-------- lib/Sema/ConstraintSystem.h | 14 +++++++------- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/include/swift/AST/AnyFunctionRef.h b/include/swift/AST/AnyFunctionRef.h index 5c52e3bd053bb..73f99f0f775cc 100644 --- a/include/swift/AST/AnyFunctionRef.h +++ b/include/swift/AST/AnyFunctionRef.h @@ -208,6 +208,14 @@ class AnyFunctionRef { llvm_unreachable("unexpected AnyFunctionRef representation"); } + friend bool operator==(AnyFunctionRef lhs, AnyFunctionRef rhs) { + return lhs.TheFunction == rhs.TheFunction; + } + + friend bool operator!=(AnyFunctionRef lhs, AnyFunctionRef rhs) { + return lhs.TheFunction != rhs.TheFunction; + } + private: ArrayRef getYieldResultsImpl(SmallVectorImpl &buffer, diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 33e351a1f851b..9f655c89cfa26 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -604,13 +604,13 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( // Record the transformation. assert(std::find_if( - builderTransformedClosures.begin(), - builderTransformedClosures.end(), - [&](const std::pair &elt) { + functionBuilderTransformed.begin(), + functionBuilderTransformed.end(), + [&](const std::pair &elt) { return elt.first == closure; - }) == builderTransformedClosures.end() && + }) == functionBuilderTransformed.end() && "already transformed this closure along this path!?!"); - builderTransformedClosures.push_back( + functionBuilderTransformed.push_back( std::make_pair(closure, AppliedBuilderTransform{builderType, singleExpr})); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 7dbec1839b0cd..760dcc5f2d71c 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7041,9 +7041,8 @@ namespace { // If this closure had a function builder applied, rewrite it to a // closure with a single expression body containing the builder // invocations. - auto builder = - Rewriter.solution.builderTransformedClosures.find(closure); - if (builder != Rewriter.solution.builderTransformedClosures.end()) { + auto builder = Rewriter.solution.functionBuilderTransformed.find(closure); + if (builder != Rewriter.solution.functionBuilderTransformed.end()) { auto singleExpr = builder->second.singleExpr; auto returnStmt = new (ctx) ReturnStmt( singleExpr->getStartLoc(), singleExpr, /*implicit=*/true); diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 4f1dafe71d9e1..5f26aa246e5b7 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -171,13 +171,13 @@ Solution ConstraintSystem::finalize() { for (auto &e : CheckedConformances) solution.Conformances.push_back({e.first, e.second}); - for (const auto &transformed : builderTransformedClosures) { + for (const auto &transformed : functionBuilderTransformed) { auto known = - solution.builderTransformedClosures.find(transformed.first); - if (known != solution.builderTransformedClosures.end()) { + solution.functionBuilderTransformed.find(transformed.first); + if (known != solution.functionBuilderTransformed.end()) { assert(known->second.singleExpr == transformed.second.singleExpr); } - solution.builderTransformedClosures.insert(transformed); + solution.functionBuilderTransformed.insert(transformed); } return solution; @@ -241,8 +241,8 @@ void ConstraintSystem::applySolution(const Solution &solution) { for (auto &conformance : solution.Conformances) CheckedConformances.push_back(conformance); - for (const auto &transformed : solution.builderTransformedClosures) { - builderTransformedClosures.push_back(transformed); + for (const auto &transformed : solution.functionBuilderTransformed) { + functionBuilderTransformed.push_back(transformed); } // Register any fixes produced along this path. @@ -436,7 +436,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numCheckedConformances = cs.CheckedConformances.size(); numDisabledConstraints = cs.solverState->getNumDisabledConstraints(); numFavoredConstraints = cs.solverState->getNumFavoredConstraints(); - numBuilderTransformedClosures = cs.builderTransformedClosures.size(); + numFunctionBuilderTransformed = cs.functionBuilderTransformed.size(); numResolvedOverloads = cs.ResolvedOverloads.size(); PreviousScore = cs.CurrentScore; @@ -503,7 +503,7 @@ ConstraintSystem::SolverScope::~SolverScope() { truncate(cs.CheckedConformances, numCheckedConformances); /// Remove any builder transformed closures. - truncate(cs.builderTransformedClosures, numBuilderTransformedClosures); + truncate(cs.functionBuilderTransformed, numFunctionBuilderTransformed); // Reset the previous score. cs.CurrentScore = PreviousScore; diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index e34d97a6499b1..67ff1cb53f523 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -823,9 +823,9 @@ class Solution { std::vector> Conformances; - /// The set of closures that have been transformed by a function builder. - llvm::MapVector - builderTransformedClosures; + /// The set of functions that have been transformed by a function builder. + llvm::MapVector + functionBuilderTransformed; /// Simplify the given type by substituting all occurrences of /// type variables for their fixed types. @@ -1341,9 +1341,9 @@ class ConstraintSystem { std::vector> CheckedConformances; - /// The set of closures that have been transformed by a function builder. - std::vector> - builderTransformedClosures; + /// The set of functions that have been transformed by a function builder. + std::vector> + functionBuilderTransformed; public: /// The locators of \c Defaultable constraints whose defaults were used. @@ -1840,7 +1840,7 @@ class ConstraintSystem { unsigned numFavoredConstraints; - unsigned numBuilderTransformedClosures; + unsigned numFunctionBuilderTransformed; /// The length of \c ResolvedOverloads. unsigned numResolvedOverloads; From 8bd3e2d40dfc9846b6be16b88fcd72190b33029e Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Mon, 6 Jan 2020 11:56:23 -0800 Subject: [PATCH 286/478] [AutoDiff] Upstream minor changes from `tensorflow` branch. (#29018) Upstream derivative function lookup changes from https://github.com/apple/swift/pull/28935. --- lib/Sema/TypeCheckAttr.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 3dae0bf29bf89..abaf821355a92 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3182,20 +3182,15 @@ static AbstractFunctionDecl *findAbstractFunctionDecl( // Perform lookup. LookupResult results; + // If `baseType` is not null but `lookupContext` is a type context, set + // `baseType` to the `self` type of `lookupContext` to perform member lookup. + if (!baseType && lookupContext->isTypeContext()) + baseType = lookupContext->getSelfTypeInContext(); if (baseType) { results = TypeChecker::lookupMember(lookupContext, baseType, funcName); } else { results = TypeChecker::lookupUnqualified(lookupContext, funcName, funcNameLoc, lookupOptions); - - // If looking up an operator within a type context, look specifically within - // the type context. - // This tries to resolve unqualified operators, like `+`. - if (funcName.isOperator() && lookupContext->isTypeContext()) { - if (auto tmp = TypeChecker::lookupMember( - lookupContext, lookupContext->getSelfTypeInContext(), funcName)) - results = tmp; - } } // Initialize error flags. From afc6332289ba35e86630a2173d971ebf2d235211 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Fri, 3 Jan 2020 16:44:22 -0800 Subject: [PATCH 287/478] [Sema] Parse a Clang function type from the cType argument. --- include/swift/AST/ClangModuleLoader.h | 7 ++++ include/swift/AST/DiagnosticsSema.def | 7 ++++ include/swift/ClangImporter/ClangImporter.h | 4 ++ lib/ClangImporter/ClangImporter.cpp | 17 ++++++++ lib/Sema/TypeCheckType.cpp | 43 ++++++++++++++++++++- test/Parse/c_function_pointers.swift | 4 +- test/attr/attr_convention.swift | 4 ++ 7 files changed, 82 insertions(+), 4 deletions(-) diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index a3d53be0584a7..b6dd3089ca54e 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -21,6 +21,7 @@ class CompilerInstance; class Preprocessor; class Sema; class TargetInfo; +class Type; } // namespace clang namespace swift { @@ -100,6 +101,12 @@ class ClangModuleLoader : public ModuleLoader { lookupRelatedEntity(StringRef clangName, ClangTypeKind kind, StringRef relatedEntityKind, llvm::function_ref receiver) = 0; + + /// Try to parse the string as a Clang function type. + /// + /// Returns null if there was a parsing failure. + virtual const clang::Type *parseClangFunctionType(StringRef type, + SourceLoc loc) const = 0; }; } // namespace swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 990e072795f72..af4de2c836114 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3924,6 +3924,13 @@ ERROR(unsupported_convention,none, "convention '%0' not supported", (StringRef)) ERROR(unreferenced_generic_parameter,none, "generic parameter '%0' is not used in function signature", (StringRef)) +ERROR(unexpected_ctype_for_non_c_convention,none, + "convention '%0' does not support the 'cType' argument label, did you " + "mean @convention(c, cType: \"%1\") or @convention(block, cType: \"%1\") " + "instead?", (StringRef, StringRef)) +ERROR(unable_to_parse_c_function_type,none, + "unable to parse '%0'; it should be a C function pointer type or a " + "block pointer type", (StringRef)) // Opaque types ERROR(unsupported_opaque_type,none, diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 9150852c0088a..ae54664345357 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -40,6 +40,7 @@ namespace clang { class NamedDecl; class Sema; class TargetInfo; + class Type; class VisibleDeclConsumer; class DeclarationName; } @@ -416,6 +417,9 @@ class ClangImporter final : public ClangModuleLoader { /// with -import-objc-header option. getPCHFilename(const ClangImporterOptions &ImporterOptions, StringRef SwiftPCHHash, bool &isExplicit); + + const clang::Type *parseClangFunctionType(StringRef type, + SourceLoc loc) const override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index e74b691817ed7..8f10eec6923ee 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3275,6 +3275,23 @@ void ClangImporter::verifyAllModules() { #endif } +const clang::Type * +ClangImporter::parseClangFunctionType(StringRef typeStr, + SourceLoc loc) const { + auto &sema = Impl.getClangSema(); + StringRef filename = Impl.SwiftContext.SourceMgr.getDisplayNameForLoc(loc); + // TODO: Obtain a clang::SourceLocation from the swift::SourceLoc we have + auto parsedType = sema.ParseTypeFromStringCallback(typeStr, filename, {}); + if (!parsedType.isUsable()) + return nullptr; + clang::QualType resultType = clang::Sema::GetTypeFromParser(parsedType.get()); + auto *typePtr = resultType.getTypePtrOrNull(); + if (typePtr && (typePtr->isFunctionPointerType() + || typePtr->isBlockPointerType())) + return typePtr; + return nullptr; +} + //===----------------------------------------------------------------------===// // ClangModule Implementation //===----------------------------------------------------------------------===// diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 7f3563f4f6b0a..a53d8c2724498 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1799,6 +1799,8 @@ namespace { AnyFunctionType::Representation representation = AnyFunctionType::Representation::Swift, bool noescape = false, + const clang::Type *parsedClangFunctionType + = nullptr, DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable); bool @@ -2166,7 +2168,31 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Function attributes require a syntactic function type. auto *fnRepr = dyn_cast(repr); + auto tryParseClangType = [this](TypeAttributes::Convention &conv, + bool hasConventionCOrBlock) + -> const clang::Type * { + if (conv.ClangType.empty()) + return nullptr; + if (!hasConventionCOrBlock) { + diagnose(conv.ClangTypeLoc, + diag::unexpected_ctype_for_non_c_convention, + conv.Name, conv.ClangType); + return nullptr; + } + + StringRef filename = + Context.SourceMgr.getDisplayNameForLoc(conv.ClangTypeLoc); + const clang::Type *type = Context.getClangModuleLoader() + ->parseClangFunctionType(conv.ClangType, + conv.ClangTypeLoc); + if (!type) + diagnose(conv.ClangTypeLoc, diag::unable_to_parse_c_function_type, + conv.ClangType); + return type; + }; + if (fnRepr && hasFunctionAttr) { + const clang::Type *parsedClangFunctionType = nullptr; if (options & TypeResolutionFlags::SILType) { SILFunctionType::Representation rep; TypeRepr *witnessMethodProtocol = nullptr; @@ -2214,6 +2240,11 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, rep = SILFunctionType::Representation::Thin; } else { rep = *parsedRep; + bool isCOrBlock = + rep == SILFunctionTypeRepresentation::CFunctionPointer + || rep == SILFunctionTypeRepresentation::Block; + parsedClangFunctionType = + tryParseClangType(attrs.ConventionArguments.getValue(), isCOrBlock); } if (rep == SILFunctionType::Representation::WitnessMethod) { @@ -2264,6 +2295,11 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, rep = FunctionType::Representation::Swift; } else { rep = *parsedRep; + + bool isCOrBlock = rep == FunctionTypeRepresentation::CFunctionPointer + || rep == FunctionTypeRepresentation::Block; + parsedClangFunctionType = + tryParseClangType(attrs.ConventionArguments.getValue(), isCOrBlock); } } @@ -2280,6 +2316,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, } ty = resolveASTFunctionType(fnRepr, options, rep, /*noescape=*/false, + parsedClangFunctionType, diffKind); if (!ty || ty->hasError()) return ty; @@ -2593,6 +2630,7 @@ Type TypeResolver::resolveOpaqueReturnType(TypeRepr *repr, Type TypeResolver::resolveASTFunctionType( FunctionTypeRepr *repr, TypeResolutionOptions parentOptions, AnyFunctionType::Representation representation, bool noescape, + const clang::Type *parsedClangFunctionType, DifferentiabilityKind diffKind) { TypeResolutionOptions options = None; @@ -2631,8 +2669,9 @@ Type TypeResolver::resolveASTFunctionType( noescape, repr->throws(), diffKind, /*clangFunctionType*/nullptr); - const clang::Type *clangFnType = nullptr; - if (representation == AnyFunctionType::Representation::CFunctionPointer) + const clang::Type *clangFnType = parsedClangFunctionType; + if (representation == AnyFunctionType::Representation::CFunctionPointer + && !clangFnType) clangFnType = Context.getClangFunctionType( params, outputTy, incompleteExtInfo, AnyFunctionType::Representation::CFunctionPointer); diff --git a/test/Parse/c_function_pointers.swift b/test/Parse/c_function_pointers.swift index 3d51e63098b5f..188634808dfac 100644 --- a/test/Parse/c_function_pointers.swift +++ b/test/Parse/c_function_pointers.swift @@ -50,8 +50,8 @@ let f: @convention(c) (Int) -> Int = genericFunc // expected-error{{cannot be fo func ct1() -> () { print("") } -let ct1ref0 : @convention(c, cType: "void *(void)") () -> () = ct1 -let ct1ref1 : @convention(c, cType: "void *(void)") = ct1 // expected-error{{expected type}} +let ct1ref0 : @convention(c, cType: "void (*)(void)") () -> () = ct1 +let ct1ref1 : @convention(c, cType: "void (*)(void)") = ct1 // expected-error{{expected type}} let ct1ref2 : @convention(c, ) () -> () = ct1 // expected-error{{expected 'cType' label in 'convention' attribute}} let ct1ref3 : @convention(c, cType) () -> () = ct1 // expected-error{{expected ':' after 'cType' for 'convention' attribute}} let ct1ref4 : @convention(c, cType: ) () -> () = ct1 // expected-error{{expected string literal containing clang type for 'cType' in 'convention' attribute}} diff --git a/test/attr/attr_convention.swift b/test/attr/attr_convention.swift index 7ee849bdb3079..83048435363fc 100644 --- a/test/attr/attr_convention.swift +++ b/test/attr/attr_convention.swift @@ -2,8 +2,12 @@ let f1: (Int) -> Int = { $0 } let f2: @convention(swift) (Int) -> Int = { $0 } +let f2a: @convention(swift, cType: "int *(int)") (Int32) -> Int32 = { $0 } // expected-error{{convention 'swift' does not support the 'cType' argument label, did you mean @convention(c, cType: "int *(int)") or @convention(block, cType: "int *(int)") instead?}} let f3: @convention(block) (Int) -> Int = { $0 } let f4: @convention(c) (Int) -> Int = { $0 } +let f4a: @convention(c, cType: "int (int)") (Int32) -> Int32 = { $0 } // expected-error{{unable to parse 'int (int)'; it should be a C function pointer type or a block pointer type}} +let f4b: @convention(c, cType: "void *") (Int32) -> Int32 = { $0 } // expected-error{{unable to parse 'void *'; it should be a C function pointer type or a block pointer type}} +let f4c: @convention(c, cType: "int (*)(int)") (Int32) -> Int32 = { $0 } let f5: @convention(INTERCAL) (Int) -> Int = { $0 } // expected-error{{convention 'INTERCAL' not supported}} From 96604470ae1a618a315ba490d4a73d07b0af7504 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Thu, 12 Dec 2019 17:54:42 -0800 Subject: [PATCH 288/478] [AST] Add printing for Clang function types in the AST. --- include/swift/AST/ASTPrinter.h | 3 ++ include/swift/AST/ClangModuleLoader.h | 4 ++ include/swift/AST/PrintOptions.h | 21 ++++++-- include/swift/AST/Types.h | 6 +++ include/swift/ClangImporter/ClangImporter.h | 2 + .../swift/Frontend/ModuleInterfaceSupport.h | 4 ++ include/swift/Option/FrontendOptions.td | 5 ++ lib/AST/ASTPrinter.cpp | 53 ++++++++++++++++--- lib/AST/Type.cpp | 6 +++ lib/ClangImporter/ClangImporter.cpp | 6 +++ lib/Frontend/CompilerInvocation.cpp | 2 + lib/Frontend/ModuleInterfaceSupport.cpp | 2 +- lib/IDE/CodeCompletionResultBuilder.h | 3 +- lib/IDE/IDETypeChecking.cpp | 3 +- test/ClangImporter/clang-function-types.swift | 12 +++++ .../clang-importer-sdk/usr/include/ctypes.h | 10 ++++ test/ModuleInterface/full-convention.swift | 32 +++++++++++ 17 files changed, 160 insertions(+), 14 deletions(-) create mode 100644 test/ClangImporter/clang-function-types.swift create mode 100644 test/ModuleInterface/full-convention.swift diff --git a/include/swift/AST/ASTPrinter.h b/include/swift/AST/ASTPrinter.h index a9e9b89a99005..98d665b1fd728 100644 --- a/include/swift/AST/ASTPrinter.h +++ b/include/swift/AST/ASTPrinter.h @@ -14,6 +14,7 @@ #define SWIFT_AST_ASTPRINTER_H #include "swift/Basic/LLVM.h" +#include "swift/Basic/QuotedString.h" #include "swift/Basic/UUID.h" #include "swift/AST/Identifier.h" #include "llvm/ADT/StringRef.h" @@ -185,6 +186,8 @@ class ASTPrinter { return *this; } + ASTPrinter &operator<<(QuotedString s); + ASTPrinter &operator<<(unsigned long long N); ASTPrinter &operator<<(UUID UU); diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index b6dd3089ca54e..0e3d109226fd0 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -107,6 +107,10 @@ class ClangModuleLoader : public ModuleLoader { /// Returns null if there was a parsing failure. virtual const clang::Type *parseClangFunctionType(StringRef type, SourceLoc loc) const = 0; + + /// Print the Clang type. + virtual void printClangType(const clang::Type *type, + llvm::raw_ostream &os) const = 0; }; } // namespace swift diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 329136a29b222..6a142e02da510 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -306,8 +306,21 @@ struct PrintOptions { /// List of decls that should be printed even if they are implicit and \c SkipImplicit is set to true. std::vector TreatAsExplicitDeclList; + enum class FunctionRepresentationMode : uint8_t { + /// Print the entire convention, including an arguments. + /// For example, this will print a cType argument label if applicable. + Full, + /// Print only the name of the convention, skipping extra argument labels. + NameOnly, + /// Skip printing the @convention(..) altogether. + None + }; + /// Whether to print function @convention attribute on function types. - bool PrintFunctionRepresentationAttrs = true; + // FIXME: [clang-function-type-serialization] Once we start serializing Clang + // types, we should also start printing the full type in the swiftinterface. + FunctionRepresentationMode PrintFunctionRepresentationAttrs = + FunctionRepresentationMode::NameOnly; /// Whether to print storage representation attributes on types, e.g. /// '@sil_weak', '@sil_unmanaged'. @@ -502,7 +515,8 @@ struct PrintOptions { /// consistent and well-formed. /// /// \see swift::emitSwiftInterface - static PrintOptions printSwiftInterfaceFile(bool preferTypeRepr); + static PrintOptions printSwiftInterfaceFile(bool preferTypeRepr, + bool printFullConvention); /// Retrieve the set of options suitable for "Generated Interfaces", which /// are a prettified representation of the public API of a module, to be @@ -585,7 +599,8 @@ struct PrintOptions { PO.SkipUnderscoredKeywords = true; PO.EnumRawValues = EnumRawValueMode::Print; PO.PrintImplicitAttrs = false; - PO.PrintFunctionRepresentationAttrs = false; + PO.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; PO.PrintDocumentationComments = false; PO.ExcludeAttrList.push_back(DAK_Available); PO.SkipPrivateStdlibDecls = true; diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index c03a523d0ae40..2e36e3e7fd001 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -59,6 +59,7 @@ class AssociatedTypeDecl; class ASTContext; enum BufferPointerTypeKind : unsigned; class ClassDecl; +class ClangModuleLoader; class DependentMemberType; class GenericTypeParamDecl; class GenericTypeParamType; @@ -2950,6 +2951,11 @@ class AnyFunctionType : public TypeBase { bool empty() const { return !ClangFunctionType; } Uncommon(const clang::Type *type) : ClangFunctionType(type) {} + + public: + /// Use the ClangModuleLoader to print the Clang type as a string. + void printClangFunctionType(ClangModuleLoader *cml, + llvm::raw_ostream &os); }; private: diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index ae54664345357..ee9f94c406d85 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -420,6 +420,8 @@ class ClangImporter final : public ClangModuleLoader { const clang::Type *parseClangFunctionType(StringRef type, SourceLoc loc) const override; + void printClangType(const clang::Type *type, + llvm::raw_ostream &os) const override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/include/swift/Frontend/ModuleInterfaceSupport.h b/include/swift/Frontend/ModuleInterfaceSupport.h index 7a3e54ee8bc64..4289150c14554 100644 --- a/include/swift/Frontend/ModuleInterfaceSupport.h +++ b/include/swift/Frontend/ModuleInterfaceSupport.h @@ -31,6 +31,10 @@ struct ModuleInterfaceOptions { /// interface, or should we fully-qualify them? bool PreserveTypesAsWritten = false; + /// Should we emit the cType when printing @convention(c) or no? + /// FIXME: [clang-function-type-serialization] This check should go away. + bool PrintFullConvention = false; + /// Copy of all the command-line flags passed at .swiftinterface /// generation time, re-applied to CompilerInvocation when reading /// back .swiftinterface and reconstructing .swiftmodule. diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 711dbd7dcf0a4..a19bd5f383b96 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -602,6 +602,11 @@ def module_interface_preserve_types_as_written : HelpText<"When emitting a module interface, preserve types as they were " "written in the source">; +def experimental_print_full_convention : + Flag<["-"], "experimental-print-full-convention">, + HelpText<"When emitting a module interface, emit additional @convention " + "arguments, regardless of whether they were written in the source">; + def prebuilt_module_cache_path : Separate<["-"], "prebuilt-module-cache-path">, HelpText<"Directory of prebuilt modules for loading module interfaces">; diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index e4e038947fc01..8d1b364eed758 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTMangler.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/Attr.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/Comment.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" @@ -98,7 +99,8 @@ static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) { return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic(); } -PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr) { +PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr, + bool printFullConvention) { PrintOptions result; result.PrintLongAttrsOnSeparateLines = true; result.TypeDefinitions = true; @@ -115,6 +117,9 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr) { result.OpaqueReturnTypePrinting = OpaqueReturnTypePrintingMode::StableReference; result.PreferTypeRepr = preferTypeRepr; + if (printFullConvention) + result.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::Full; // We should print __consuming, __owned, etc for the module interface file. result.SkipUnderscoredKeywords = false; @@ -321,6 +326,14 @@ void ASTPrinter::callPrintDeclPre(const Decl *D, printDeclPre(D, Bracket); } +ASTPrinter &ASTPrinter::operator<<(QuotedString s) { + llvm::SmallString<32> Str; + llvm::raw_svector_ostream OS(Str); + OS << s; + printTextImpl(OS.str()); + return *this; +} + ASTPrinter &ASTPrinter::operator<<(unsigned long long N) { llvm::SmallString<32> Str; llvm::raw_svector_ostream OS(Str); @@ -3462,6 +3475,15 @@ void Pattern::print(llvm::raw_ostream &OS, const PrintOptions &Options) const { // Type Printing //===----------------------------------------------------------------------===// +template +void printCType(ASTContext &Ctx, ASTPrinter &Printer, ExtInfo &info) { + auto *cml = Ctx.getClangModuleLoader(); + SmallString<64> buf; + llvm::raw_svector_ostream os(buf); + info.getUncommonInfo().getValue().printClangFunctionType(cml, os); + Printer << ", cType: " << QuotedString(os.str()); +} + namespace { class TypePrinter : public TypeVisitor { using super = TypeVisitor; @@ -3824,7 +3846,7 @@ class TypePrinter : public TypeVisitor { visit(staticSelfT); } - void printFunctionExtInfo(AnyFunctionType::ExtInfo info) { + void printFunctionExtInfo(ASTContext &Ctx, AnyFunctionType::ExtInfo info) { if (Options.SkipAttributes) return; @@ -3837,9 +3859,18 @@ class TypePrinter : public TypeVisitor { } } - if (Options.PrintFunctionRepresentationAttrs && - !Options.excludeAttrKind(TAK_convention) && - info.getSILRepresentation() != SILFunctionType::Representation::Thick) { + SmallString<64> buf; + switch (Options.PrintFunctionRepresentationAttrs) { + case PrintOptions::FunctionRepresentationMode::None: + return; + case PrintOptions::FunctionRepresentationMode::Full: + case PrintOptions::FunctionRepresentationMode::NameOnly: + if (Options.excludeAttrKind(TAK_convention) || + info.getSILRepresentation() == SILFunctionType::Representation::Thick) + return; + + bool printNameOnly = Options.PrintFunctionRepresentationAttrs == + PrintOptions::FunctionRepresentationMode::NameOnly; Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); Printer << "("; @@ -3855,6 +3886,11 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; + // FIXME: [clang-function-type-serialization] Once we start serializing + // Clang function types, we should be able to remove the second check. + if (printNameOnly || !info.getUncommonInfo().hasValue()) + break; + printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::Method: Printer << "method"; @@ -3889,7 +3925,8 @@ class TypePrinter : public TypeVisitor { } } - if (Options.PrintFunctionRepresentationAttrs && + if ((Options.PrintFunctionRepresentationAttrs != + PrintOptions::FunctionRepresentationMode::None) && !Options.excludeAttrKind(TAK_convention) && info.getRepresentation() != SILFunctionType::Representation::Thick) { Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); @@ -3975,7 +4012,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getExtInfo()); + printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); // If we're stripping argument labels from types, do it when printing. visitAnyFunctionTypeParams(T->getParams(), /*printLabels*/false); @@ -4012,7 +4049,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getExtInfo()); + printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); printGenericSignature(T->getGenericSignature(), PrintAST::PrintParams | PrintAST::PrintRequirements); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index d63b809756ac4..31f898b14addc 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -17,6 +17,7 @@ #include "swift/AST/Types.h" #include "ForeignRepresentationInfo.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/ReferenceCounting.h" #include "swift/AST/TypeCheckRequests.h" @@ -3239,6 +3240,11 @@ Type ProtocolCompositionType::get(const ASTContext &C, return build(C, CanTypes, HasExplicitAnyObject); } +void AnyFunctionType::ExtInfo::Uncommon::printClangFunctionType( + ClangModuleLoader *cml, llvm::raw_ostream &os) { + cml->printClangType(ClangFunctionType, os); +} + void AnyFunctionType::ExtInfo::assertIsFunctionType(const clang::Type *type) { #ifndef NDEBUG diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 8f10eec6923ee..c7120ea0eb835 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3292,6 +3292,12 @@ ClangImporter::parseClangFunctionType(StringRef typeStr, return nullptr; } +void ClangImporter::printClangType(const clang::Type *type, + llvm::raw_ostream &os) const { + auto policy = clang::PrintingPolicy(getClangASTContext().getLangOpts()); + clang::QualType(type, 0).print(os, policy); +} + //===----------------------------------------------------------------------===// // ClangModule Implementation //===----------------------------------------------------------------------===// diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 355aeff500586..2eeee7f96f54d 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -227,6 +227,8 @@ static void ParseModuleInterfaceArgs(ModuleInterfaceOptions &Opts, Opts.PreserveTypesAsWritten |= Args.hasArg(OPT_module_interface_preserve_types_as_written); + Opts.PrintFullConvention |= + Args.hasArg(OPT_experimental_print_full_convention); } /// Save a copy of any flags marked as ModuleInterfaceOption, if running diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 7fa40c3a94b5f..a17f33f3bfe87 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -440,7 +440,7 @@ bool swift::emitSwiftInterface(raw_ostream &out, printImports(out, M); const PrintOptions printOptions = PrintOptions::printSwiftInterfaceFile( - Opts.PreserveTypesAsWritten); + Opts.PreserveTypesAsWritten, Opts.PrintFullConvention); InheritedProtocolCollector::PerTypeMap inheritedProtocolMap; SmallVector topLevelDecls; diff --git a/lib/IDE/CodeCompletionResultBuilder.h b/lib/IDE/CodeCompletionResultBuilder.h index a56e8eee1dada..cc4793d9eb1ba 100644 --- a/lib/IDE/CodeCompletionResultBuilder.h +++ b/lib/IDE/CodeCompletionResultBuilder.h @@ -400,7 +400,8 @@ class CodeCompletionResultBuilder { if (auto AFT = Ty->getAs()) { // If this is a closure type, add ChunkKind::CallParameterClosureType. PrintOptions PO; - PO.PrintFunctionRepresentationAttrs = false; + PO.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; PO.SkipAttributes = true; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; diff --git a/lib/IDE/IDETypeChecking.cpp b/lib/IDE/IDETypeChecking.cpp index 0b719b59c85e8..6d4050743df8f 100644 --- a/lib/IDE/IDETypeChecking.cpp +++ b/lib/IDE/IDETypeChecking.cpp @@ -85,7 +85,8 @@ PrintOptions PrintOptions::printDocInterface() { PrintOptions::ArgAndParamPrintingMode::BothAlways; result.PrintDocumentationComments = false; result.PrintRegularClangComments = false; - result.PrintFunctionRepresentationAttrs = false; + result.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; return result; } diff --git a/test/ClangImporter/clang-function-types.swift b/test/ClangImporter/clang-function-types.swift new file mode 100644 index 0000000000000..88b50655eb237 --- /dev/null +++ b/test/ClangImporter/clang-function-types.swift @@ -0,0 +1,12 @@ +// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -sdk %clang-importer-sdk -enable-library-evolution %s -experimental-print-full-convention | tee ~/tmp.si | %FileCheck %s + +import ctypes + +// CHECK: f1: (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)? +public let f1 = getFunctionPointer_() + +// CHECK: f2: (@convention(c, cType: "int (*(*)(int (*)(int)))(int)") ((@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?) -> (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?)? +public let f2 = getHigherOrderFunctionPointer() + +// CHECK: f3: () -> (@convention(c, cType: "Dummy *(*)(Dummy *)") (Swift.UnsafeMutablePointer?) -> Swift.UnsafeMutablePointer?)? +public let f3 = getFunctionPointer3 diff --git a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h index c055b48ebff88..4d4a7cf018575 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h @@ -205,6 +205,8 @@ typedef int (*fptr)(int); fptr getFunctionPointer(void); void useFunctionPointer(fptr); +int (*getFunctionPointer_(void))(int); + struct FunctionPointerWrapper { fptr a; fptr b; @@ -214,6 +216,14 @@ typedef void (*fptr2)(int, long, void *); fptr2 getFunctionPointer2(void); void useFunctionPointer2(fptr2); +int (*(*getHigherOrderFunctionPointer(void))(int (*)(int)))(int); + +typedef struct Dummy { + int x; +} Dummy; + +Dummy * (*getFunctionPointer3(void))(Dummy *); + //===--- // Unions //===--- diff --git a/test/ModuleInterface/full-convention.swift b/test/ModuleInterface/full-convention.swift new file mode 100644 index 0000000000000..e65c2fe427ca6 --- /dev/null +++ b/test/ModuleInterface/full-convention.swift @@ -0,0 +1,32 @@ +// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -enable-library-evolution %s -experimental-print-full-convention | %FileCheck %s + +public func f( + // CHECK: g: @convention(c, cType: "void (*)(void)") + g: @convention(c) () -> (), + + // CHECK: h0: @convention(c, cType: "int (*)(long long)") + h0: @convention(c) (Int64) -> Int32, + // CHECK: h1: @convention(c, cType: "int (*)(long long)") + h1: @convention(c, cType: "int (*)(long long)") (Int64) -> Int32, + + // CHECK: i0: @convention(c, cType: "int *(*)(long long, int)") + i0: @convention(c) (Int64, Int32) -> Optional>, + // CHECK: i1: @convention(c, cType: "int *(*)(long long, int)") + i1: @convention(c, cType: "int *(*)(long long, int)") (Int64, Int32) -> Optional>, + + // CHECK: p0: @convention(c, cType: "void (*)(void (*)(long))") + // CHECK: @convention(c, cType: "void (*)(long)") + p0: @convention(c) (@convention(c) (Int) -> Void) -> Void, + + // CHECK: p1: @convention(c, cType: "void (*)(void (*)(long))") + // CHECK: @convention(c, cType: "void (*)(long)") + p1: @convention(c, cType: "void (*)(void (*)(long))") (@convention(c) (Int) -> Void) -> Void, + + // CHECK: p2: @convention(c, cType: "void (*)(void (*)(long))") + // CHECK: @convention(c, cType: "void (*)(long)") + p2: @convention(c) (@convention(c, cType: "void (*)(long)") (Int) -> Void) -> Void, + + // CHECK: p3: @convention(c, cType: "void (*)(void (*)(long))") + // CHECK: @convention(c, cType: "void (*)(long)") + p3: @convention(c, cType: "void (*)(void (*)(long))") (@convention(c, cType: "void (*)(long)") (Int) -> Void) -> Void +) {} From 8d6781a40fe4aca03c26229cfda4b18399ad8514 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Wed, 11 Dec 2019 23:46:34 -0800 Subject: [PATCH 289/478] [NFC] Add missing SILFunctionType::ExtInfo::getUncommonInfo() method. --- include/swift/AST/Types.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 2e36e3e7fd001..455f377b800b7 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -3953,6 +3953,11 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, return getSILFunctionLanguage(getRepresentation()); } + /// Return the underlying Uncommon value if it is not the default value. + Optional getUncommonInfo() const { + return Other.empty() ? Optional() : Other; + } + bool hasSelfParam() const { switch (getRepresentation()) { case Representation::Thick: From 7bdd72ae11b5d20e54f19e81148d9551d518e518 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Wed, 11 Dec 2019 23:48:02 -0800 Subject: [PATCH 290/478] [AST] Add printing for Clang function types in SIL. --- include/swift/AST/Types.h | 5 +++++ lib/AST/ASTContext.cpp | 5 +++++ lib/AST/ASTPrinter.cpp | 27 +++++++++++++++++++++------ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 455f377b800b7..bf066910de272 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -3900,6 +3900,11 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, bool empty() const { return !ClangFunctionType; } Uncommon(const clang::FunctionType *type) : ClangFunctionType(type) {} + + public: + /// Analog of AnyFunctionType::ExtInfo::Uncommon::printClangFunctionType. + void printClangFunctionType(ClangModuleLoader *cml, + llvm::raw_ostream &os) const; }; Uncommon Other; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index ec5c403dc5775..aa419a463a9ee 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3156,6 +3156,11 @@ ArrayRef GenericFunctionType::getRequirements() const { return Signature->getRequirements(); } +void SILFunctionType::ExtInfo::Uncommon::printClangFunctionType( + ClangModuleLoader *cml, llvm::raw_ostream &os) const { + cml->printClangType(ClangFunctionType, os); +} + void SILFunctionType::Profile( llvm::FoldingSetNodeID &id, GenericSignature genericParams, diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 8d1b364eed758..63e138ee686f6 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3911,7 +3911,8 @@ class TypePrinter : public TypeVisitor { } } - void printFunctionExtInfo(SILFunctionType::ExtInfo info, + void printFunctionExtInfo(ASTContext &Ctx, + SILFunctionType::ExtInfo info, ProtocolConformanceRef witnessMethodConformance) { if (Options.SkipAttributes) return; @@ -3925,10 +3926,19 @@ class TypePrinter : public TypeVisitor { } } - if ((Options.PrintFunctionRepresentationAttrs != - PrintOptions::FunctionRepresentationMode::None) && - !Options.excludeAttrKind(TAK_convention) && - info.getRepresentation() != SILFunctionType::Representation::Thick) { + + SmallString<64> buf; + switch (Options.PrintFunctionRepresentationAttrs) { + case PrintOptions::FunctionRepresentationMode::None: + break; + case PrintOptions::FunctionRepresentationMode::NameOnly: + case PrintOptions::FunctionRepresentationMode::Full: + if (Options.excludeAttrKind(TAK_convention) || + info.getRepresentation() == SILFunctionType::Representation::Thick) + break; + + bool printNameOnly = Options.PrintFunctionRepresentationAttrs == + PrintOptions::FunctionRepresentationMode::NameOnly; Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); Printer << "("; @@ -3943,6 +3953,11 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; + // FIXME: [clang-function-type-serialization] Once we start serializing + // Clang function types, we should be able to remove the second check. + if (printNameOnly || !info.getUncommonInfo().hasValue()) + break; + printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::Method: Printer << "method"; @@ -4102,7 +4117,7 @@ class TypePrinter : public TypeVisitor { void visitSILFunctionType(SILFunctionType *T) { printSILCoroutineKind(T->getCoroutineKind()); - printFunctionExtInfo(T->getExtInfo(), + printFunctionExtInfo(T->getASTContext(), T->getExtInfo(), T->getWitnessMethodConformanceOrInvalid()); printCalleeConvention(T->getCalleeConvention()); From 6784575de119869a756ef6207ed90a6fc184b085 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 6 Jan 2020 14:51:08 -0800 Subject: [PATCH 291/478] Introduce a special LLDB-only way to prepend decls SwiftASTManipulator is installing declarations in the wrong order, and currently fails a REPL test if the right API is used. rdar://58355191 is tracking the removal of this entrypoint. --- include/swift/AST/SourceFile.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index a9078e67defbb..61173f8efefe4 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -137,6 +137,16 @@ class SourceFile final : public FileUnit { Decls.push_back(d); } + /// Prepends a declaration to the top-level decls list. + /// + /// FIXME: This entrypoint exists to support LLDB. Calls to this function are + /// always a mistake, and additional uses should not be added. + /// + /// See rdar://58355191 + void prependTopLevelDecl(Decl *d) { + Decls.insert(Decls.begin(), d); + } + /// Retrieves an immutable view of the list of top-level decls in this file. ArrayRef getTopLevelDecls() const { return Decls; From ddd714e5b332310626c6c51e443531cd4386c28e Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Wed, 20 Nov 2019 12:58:02 -0800 Subject: [PATCH 292/478] [swift-ide-test] Mimic SourceKit's setup behavior when testing syntactic requests SourceKit doesn't set up a compiler instance, just a parser, for its syntactic requests (document structure and syntax coloring). This updates swift-ide-test to minic this setup to ensure the StructureAnnotator/PrintSyntaxColorWalker and underlying SyntaxModelWalker handle this setup (which has no type-checker installed) for all our swift-ide-test based tests. Resolves rdar://problem/57202584 --- tools/swift-ide-test/swift-ide-test.cpp | 189 ++++++++++++++++-------- 1 file changed, 125 insertions(+), 64 deletions(-) diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index adfd9d0ee73a2..d54206e0d286c 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -46,6 +46,7 @@ #include "swift/IDE/IDERequests.h" #include "swift/Index/Index.h" #include "swift/Sema/IDETypeChecking.h" +#include "swift/SyntaxParse/SyntaxTreeCreator.h" #include "swift/Markup/Markup.h" #include "swift/Config.h" #include "clang/Rewrite/Core/RewriteBuffer.h" @@ -717,6 +718,20 @@ removeCodeCompletionTokens(llvm::MemoryBuffer *Input, Input->getBufferIdentifier())); } +/// Returns true on error +static bool setBufferForFile(StringRef SourceFilename, + std::unique_ptr &Buffer) { + llvm::ErrorOr> FileBufOrErr = + llvm::MemoryBuffer::getFile(SourceFilename); + if (!FileBufOrErr) { + llvm::errs() << "error opening input file '" << SourceFilename << "':\n" + << " " << FileBufOrErr.getError().message() << '\n'; + return true; + } + Buffer = std::move(FileBufOrErr.get()); + return false; +} + static bool doCodeCompletionImpl( CodeCompletionCallbacksFactory *callbacksFactory, const CompilerInvocation &InitInvok, @@ -724,18 +739,14 @@ static bool doCodeCompletionImpl( StringRef SecondSourceFileName, StringRef CodeCompletionToken, bool CodeCompletionDiagnostics) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) return 1; - } unsigned Offset; std::unique_ptr CleanFile(removeCodeCompletionTokens( - FileBufOrErr.get().get(), CodeCompletionToken, &Offset)); + FileBuf.get(), CodeCompletionToken, &Offset)); if (Offset == ~0U) { llvm::errs() << "could not find code completion token \"" @@ -843,15 +854,11 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok, static int doREPLCodeCompletion(const CompilerInvocation &InitInvok, StringRef SourceFilename) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) return 1; - } - StringRef BufferText = FileBufOrErr.get()->getBuffer(); + StringRef BufferText = FileBuf->getBuffer(); // Drop a single newline character from the buffer. if (BufferText.endswith("\n")) BufferText = BufferText.drop_back(1); @@ -1057,38 +1064,74 @@ static int doSyntaxColoring(const CompilerInvocation &InitInvok, CompilerInvocation Invocation(InitInvok); Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename); Invocation.getLangOptions().DisableAvailabilityChecking = false; - - CompilerInstance CI; - - // Display diagnostics to stderr. - PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); Invocation.getLangOptions().Playground = Playground; Invocation.getLangOptions().CollectParsedToken = true; Invocation.getLangOptions().BuildSyntaxTree = true; - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - if (!RunTypeChecker) - CI.performParseOnly(); - else + + // Display diagnostics to stderr. + PrintingDiagnosticConsumer PrintDiags; + + if (RunTypeChecker) { + CompilerInstance CI; + CI.addDiagnosticConsumer(&PrintDiags); + if (CI.setup(Invocation)) + return 1; CI.performSema(); - unsigned BufID = CI.getInputBufferIDs().back(); - SourceFile *SF = nullptr; - for (auto Unit : CI.getMainModule()->getFiles()) { - SF = dyn_cast(Unit); - if (SF) - break; - } - assert(SF && "no source file?"); + unsigned BufID = CI.getInputBufferIDs().back(); + SourceFile *SF = nullptr; + for (auto Unit : CI.getMainModule()->getFiles()) { + SF = dyn_cast(Unit); + if (SF) + break; + } + assert(SF && "no source file?"); + + ide::SyntaxModelContext ColorContext(*SF); + PrintSyntaxColorWalker ColorWalker(CI.getSourceMgr(), BufID, llvm::outs(), + TerminalOutput); + ColorContext.walk(ColorWalker); + ColorWalker.finished(); + } else { + // SourceKit doesn't set up a compiler instance at all for its syntactic + // requests, just the parser. We try to mimic that setup here to help catch + // any cases where the walker might inadvertently rely on the name lookup or + // other semantic functionality via the request evaluator. + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) + return 1; + + SourceManager SM; + unsigned BufferID = SM.addNewSourceBuffer(std::move(FileBuf)); - ide::SyntaxModelContext ColorContext(*SF); - PrintSyntaxColorWalker ColorWalker(CI.getSourceMgr(), BufID, llvm::outs(), - TerminalOutput); - ColorContext.walk(ColorWalker); - ColorWalker.finished(); + RC syntaxArena{new syntax::SyntaxArena()}; + std::shared_ptr SynTreeCreator = + std::make_shared( + SM, BufferID, Invocation.getMainFileSyntaxParsingCache(), + syntaxArena); + ParserUnit Parser(SM, SourceFileKind::Main, BufferID, + Invocation.getLangOptions(), + Invocation.getTypeCheckerOptions(), + Invocation.getModuleName(), + SynTreeCreator, + Invocation.getMainFileSyntaxParsingCache()); + + registerParseRequestFunctions(Parser.getParser().Context.evaluator); + registerTypeCheckerRequestFunctions(Parser.getParser().Context.evaluator); + + // Collecting syntactic information shouldn't evaluate # conditions. + Parser.getParser().State->PerformConditionEvaluation = false; + Parser.getDiagnosticEngine().addConsumer(PrintDiags); + + (void)Parser.parse(); + + ide::SyntaxModelContext ColorContext(Parser.getSourceFile()); + PrintSyntaxColorWalker ColorWalker(SM, BufferID, llvm::outs(), + TerminalOutput); + ColorContext.walk(ColorWalker); + ColorWalker.finished(); + } return 0; } @@ -1280,25 +1323,51 @@ class StructureAnnotator : public ide::SyntaxModelWalker { static int doStructureAnnotation(const CompilerInvocation &InitInvok, StringRef SourceFilename) { + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) + return 1; + CompilerInvocation Invocation(InitInvok); Invocation.getLangOptions().BuildSyntaxTree = true; Invocation.getLangOptions().CollectParsedToken = true; Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename); - CompilerInstance CI; + // Structure annotation is run as a purely syntactic request by SourceKit. It + // doesn't set up a compiler instance at all, just the parser. We try to mimic + // that setup here to help catch any cases where the walker might inadvertently + // rely on the name lookup or other semantic functionality via the request + // evaluator. + SourceManager SM; + unsigned BufferID = SM.addNewSourceBuffer(std::move(FileBuf)); + + RC syntaxArena{new syntax::SyntaxArena()}; + std::shared_ptr SynTreeCreator = + std::make_shared( + SM, BufferID, Invocation.getMainFileSyntaxParsingCache(), + syntaxArena); + + ParserUnit Parser(SM, SourceFileKind::Main, BufferID, + Invocation.getLangOptions(), + Invocation.getTypeCheckerOptions(), + Invocation.getModuleName(), + SynTreeCreator, + Invocation.getMainFileSyntaxParsingCache()); + + registerParseRequestFunctions(Parser.getParser().Context.evaluator); + registerTypeCheckerRequestFunctions( + Parser.getParser().Context.evaluator); + + // Collecting syntactic information shouldn't evaluate # conditions. + Parser.getParser().State->PerformConditionEvaluation = false; // Display diagnostics to stderr. PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performParseOnly(); + Parser.getDiagnosticEngine().addConsumer(PrintDiags); - unsigned BufID = CI.getInputBufferIDs().back(); - ide::SyntaxModelContext StructureContext( - CI.getMainModule()->getMainSourceFile(SourceFileKind::Main)); - StructureAnnotator Annotator(CI.getSourceMgr(), BufID); + (void)Parser.parse(); + + ide::SyntaxModelContext StructureContext(Parser.getSourceFile()); + StructureAnnotator Annotator(SM, BufferID); StructureContext.walk(Annotator); Annotator.printResult(llvm::outs()); return 0; @@ -1561,17 +1630,13 @@ static int doSemanticAnnotation(const CompilerInvocation &InitInvok, } static int doInputCompletenessTest(StringRef SourceFilename) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) return 1; - } llvm::raw_ostream &OS = llvm::outs(); OS << SourceFilename << ": "; - if (isSourceInputComplete(std::move(FileBufOrErr.get()), + if (isSourceInputComplete(std::move(FileBuf), SourceFileKind::REPL).IsComplete) { OS << "IS_COMPLETE\n"; } else { @@ -3102,16 +3167,12 @@ static int doTestCreateCompilerInvocation(ArrayRef Args) { } static int doTestCompilerInvocationFromModule(StringRef ModuleFilePath) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(ModuleFilePath); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; - return -1; - } + std::unique_ptr FileBuf; + if (setBufferForFile(ModuleFilePath, FileBuf)) + return 1; CompilerInvocation CI; - StringRef Data = FileBufOrErr.get()->getBuffer(); + StringRef Data = FileBuf->getBuffer(); static_assert(static_cast(serialization::Status::Valid) == 0, "Status::Valid should be a successful exit"); return static_cast(CI.loadFromSerializedAST(Data)); From 040050a6c766e8d1719111bfb13c5616a6236dac Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 6 Jan 2020 15:48:48 -0800 Subject: [PATCH 293/478] Basic: out-of-line equality operator (NFCI) MSVC did not like the original code and would fail to build as: ``` swift\include\swift/Basic/Located.h(50): error C2995: 'bool swift::operator ==(const swift::Located &,const swift::Located &)': function template has already been defined swift\include\swift/Basic/Located.h(50): note: see declaration of 'swift::operator ==' llvm\include\llvm/Support/TrailingObjects.h(76): note: see reference to class template instantiation 'swift::Located' being compiled llvm\include\llvm/Support/TrailingObjects.h(233): note: see reference to class template instantiation 'llvm::trailing_objects_internal::AlignmentCalcHelper>' being compiled swift\include\swift/AST/Decl.h(1512): note: see reference to class template instantiation 'llvm::TrailingObjects>' being compiled ``` The original code is odd. There appears to be some unnecessary complexity. First, the member function is marked as a friend of a `struct` type which does not change the member's visibility, thus all the members are `public`, and the function need not be friended. Second, the function is templated over the same parameter type, which means that the original template parameter could be used and the standard member equality operator could be used rather than the free-standing form. It is unclear why the member equality operator is insufficient, and the extraneous template instatiations here seem wasteful. Out-of-line the free-standing form and not mark it as a friend to restore the build. Switching to a member form can be a follow up change. --- include/swift/Basic/Located.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/include/swift/Basic/Located.h b/include/swift/Basic/Located.h index 79721d257b041..6b3430b365124 100644 --- a/include/swift/Basic/Located.h +++ b/include/swift/Basic/Located.h @@ -30,9 +30,8 @@ namespace swift { /// the ClangImporter needs to keep track of where imports were originally written. /// Located makes it easy to do so while making the code more readable, compared to /// using `std::pair`. -template +template struct Located { - /// The main item whose source location is being tracked. T Item; @@ -45,13 +44,13 @@ struct Located { SWIFT_DEBUG_DUMP; void dump(raw_ostream &os) const; - - template - friend bool operator ==(const Located &lhs, const Located &rhs) { - return lhs.Item == rhs.Item && lhs.Loc == rhs.Loc; - } }; +template +bool operator ==(const Located &lhs, const Located &rhs) { + return lhs.Item == rhs.Item && lhs.Loc == rhs.Loc; +} + } // end namespace swift namespace llvm { From d69e892f039d9d2347cda50de7d173ca9d4c6bbb Mon Sep 17 00:00:00 2001 From: Marc Rasi Date: Mon, 6 Jan 2020 15:32:21 -0800 Subject: [PATCH 294/478] [AutoDiff upstream] forbid @derivative of protocol req --- lib/Sema/TypeCheckAttr.cpp | 3 ++ .../Sema/derivative_attr_type_checking.swift | 34 +++++++++---------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 3dae0bf29bf89..86b8fa6f90761 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3503,6 +3503,9 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, }; auto isValidOriginal = [&](AbstractFunctionDecl *originalCandidate) { + // TODO(TF-982): Allow derivatives on protocol requirements. + if (isa(originalCandidate->getDeclContext())) + return false; return checkFunctionSignature( cast(originalFnType->getCanonicalType()), originalCandidate->getInterfaceType()->getCanonicalType(), diff --git a/test/AutoDiff/Sema/derivative_attr_type_checking.swift b/test/AutoDiff/Sema/derivative_attr_type_checking.swift index 1e1b70c7cd341..27db38dc34c4e 100644 --- a/test/AutoDiff/Sema/derivative_attr_type_checking.swift +++ b/test/AutoDiff/Sema/derivative_attr_type_checking.swift @@ -185,6 +185,11 @@ protocol StaticMethod: Differentiable { static func generic(_ x: T) -> T } +extension StaticMethod { + static func foo(_ x: Float) -> Float { x } + static func generic(_ x: T) -> T { x } +} + extension StaticMethod { @derivative(of: foo) static func jvpFoo(x: Float) -> (value: Float, differential: (Float) -> Float) @@ -215,11 +220,16 @@ extension StaticMethod { // Test instance methods. protocol InstanceMethod: Differentiable { - // expected-note @+1 {{'foo' defined here}} func foo(_ x: Self) -> Self + func generic(_ x: T) -> Self +} + +extension InstanceMethod { + // expected-note @+1 {{'foo' defined here}} + func foo(_ x: Self) -> Self { x } // expected-note @+1 {{'generic' defined here}} - func generic(_ x: T) -> Self + func generic(_ x: T) -> Self { self } } extension InstanceMethod { @@ -539,24 +549,14 @@ extension HasStoredProperty { // Test cross-file derivative registration. Currently unsupported. // TODO(TF-1021): Lift this restriction. -extension AdditiveArithmetic where Self: Differentiable { +extension FloatingPoint where Self: Differentiable { // expected-error @+1 {{derivative not in the same file as the original function}} - @derivative(of: +) - static func vjpPlus(x: Self, y: Self) -> ( + @derivative(of: rounded) + func vjpRounded() -> ( value: Self, - pullback: (Self.TangentVector) -> (Self.TangentVector, Self.TangentVector) - ) { - return (x + y, { v in (v, v) }) - } -} - -extension FloatingPoint where Self: Differentiable, Self == Self.TangentVector { - // expected-error @+1 {{derivative not in the same file as the original function}} - @derivative(of: +) - static func vjpPlus(x: Self, y: Self) -> ( - value: Self, pullback: (Self) -> (Self, Self) + pullback: (Self.TangentVector) -> (Self.TangentVector) ) { - return (x + y, { v in (v, v) }) + fatalError() } } From 2e7e81e22a9e0b249bbbece45bc8bc9ba2d79e62 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Mon, 6 Jan 2020 16:05:38 -0800 Subject: [PATCH 295/478] Make utils/build-toolchain compatible with Xcode new build system. (#29025) Xcode's new build system requires the Version entry in Info.plist to have a particular date-based format. Toolchains built using utils/build-toolchain now work with Xcode's new build system. --- utils/build-toolchain | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/utils/build-toolchain b/utils/build-toolchain index 4edcc957bc40e..f89f30f3faf1d 100755 --- a/utils/build-toolchain +++ b/utils/build-toolchain @@ -98,15 +98,15 @@ set -x YEAR=$(date +"%Y") MONTH=$(date +"%m") DAY=$(date +"%d") -TOOLCHAIN_VERSION="swift-LOCAL-${YEAR}-${MONTH}-${DAY}-a" +TOOLCHAIN_VERSION="5.0.${YEAR}${MONTH}${DAY}" +TOOLCHAIN_NAME="swift-LOCAL-${YEAR}-${MONTH}-${DAY}-a" DARWIN_TOOLCHAIN_VERSION="0.0.${YEAR}${MONTH}${DAY}" -ARCHIVE="${TOOLCHAIN_VERSION}-${OS_SUFFIX}.tar.gz" -SYM_ARCHIVE="${TOOLCHAIN_VERSION}-${OS_SUFFIX}-symbols.tar.gz" +ARCHIVE="${TOOLCHAIN_NAME}-${OS_SUFFIX}.tar.gz" +SYM_ARCHIVE="${TOOLCHAIN_NAME}-${OS_SUFFIX}-symbols.tar.gz" BUNDLE_PREFIX=${BUNDLE_PREFIX:?Please specify a bundle prefix} BUNDLE_IDENTIFIER="${BUNDLE_PREFIX}.${YEAR}${MONTH}${DAY}" DISPLAY_NAME_SHORT="Local Swift Development Snapshot" DISPLAY_NAME="${DISPLAY_NAME_SHORT} ${YEAR}-${MONTH}-${DAY}" -TOOLCHAIN_NAME="${TOOLCHAIN_VERSION}" SWIFT_INSTALLABLE_PACKAGE="${RESULT_DIR}/${ARCHIVE}" SWIFT_INSTALL_DIR="${RESULT_DIR}/swift-nightly-install" @@ -126,5 +126,5 @@ DISTCC_FLAG="${DISTCC_FLAG}" darwin_toolchain_display_name="${DISPLAY_NAME}" \ darwin_toolchain_display_name_short="${DISPLAY_NAME_SHORT}" \ darwin_toolchain_xctoolchain_name="${TOOLCHAIN_NAME}" \ - darwin_toolchain_version="${DARWIN_TOOLCHAIN_VERSION}" \ + darwin_toolchain_version="${TOOLCHAIN_VERSION}" \ darwin_toolchain_alias="Local" From e805fe486e0eb13fac64c2825abf00300a1b59b1 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 6 Jan 2020 16:26:08 -0800 Subject: [PATCH 296/478] Revert "Clang function types v2: Electric Boogaloo (parsing + printing)" --- include/swift/AST/ASTPrinter.h | 3 - include/swift/AST/ClangModuleLoader.h | 11 --- include/swift/AST/DiagnosticsSema.def | 7 -- include/swift/AST/PrintOptions.h | 21 +---- include/swift/AST/Types.h | 16 ---- include/swift/ClangImporter/ClangImporter.h | 6 -- .../swift/Frontend/ModuleInterfaceSupport.h | 4 - include/swift/Option/FrontendOptions.td | 5 -- lib/AST/ASTContext.cpp | 5 -- lib/AST/ASTPrinter.cpp | 76 +++---------------- lib/AST/Type.cpp | 6 -- lib/ClangImporter/ClangImporter.cpp | 23 ------ lib/Frontend/CompilerInvocation.cpp | 2 - lib/Frontend/ModuleInterfaceSupport.cpp | 2 +- lib/IDE/CodeCompletionResultBuilder.h | 3 +- lib/IDE/IDETypeChecking.cpp | 3 +- lib/Sema/TypeCheckType.cpp | 43 +---------- test/ClangImporter/clang-function-types.swift | 12 --- .../clang-importer-sdk/usr/include/ctypes.h | 10 --- test/ModuleInterface/full-convention.swift | 32 -------- test/Parse/c_function_pointers.swift | 4 +- test/attr/attr_convention.swift | 4 - 22 files changed, 22 insertions(+), 276 deletions(-) delete mode 100644 test/ClangImporter/clang-function-types.swift delete mode 100644 test/ModuleInterface/full-convention.swift diff --git a/include/swift/AST/ASTPrinter.h b/include/swift/AST/ASTPrinter.h index 98d665b1fd728..a9e9b89a99005 100644 --- a/include/swift/AST/ASTPrinter.h +++ b/include/swift/AST/ASTPrinter.h @@ -14,7 +14,6 @@ #define SWIFT_AST_ASTPRINTER_H #include "swift/Basic/LLVM.h" -#include "swift/Basic/QuotedString.h" #include "swift/Basic/UUID.h" #include "swift/AST/Identifier.h" #include "llvm/ADT/StringRef.h" @@ -186,8 +185,6 @@ class ASTPrinter { return *this; } - ASTPrinter &operator<<(QuotedString s); - ASTPrinter &operator<<(unsigned long long N); ASTPrinter &operator<<(UUID UU); diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index 0e3d109226fd0..a3d53be0584a7 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -21,7 +21,6 @@ class CompilerInstance; class Preprocessor; class Sema; class TargetInfo; -class Type; } // namespace clang namespace swift { @@ -101,16 +100,6 @@ class ClangModuleLoader : public ModuleLoader { lookupRelatedEntity(StringRef clangName, ClangTypeKind kind, StringRef relatedEntityKind, llvm::function_ref receiver) = 0; - - /// Try to parse the string as a Clang function type. - /// - /// Returns null if there was a parsing failure. - virtual const clang::Type *parseClangFunctionType(StringRef type, - SourceLoc loc) const = 0; - - /// Print the Clang type. - virtual void printClangType(const clang::Type *type, - llvm::raw_ostream &os) const = 0; }; } // namespace swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index af4de2c836114..990e072795f72 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3924,13 +3924,6 @@ ERROR(unsupported_convention,none, "convention '%0' not supported", (StringRef)) ERROR(unreferenced_generic_parameter,none, "generic parameter '%0' is not used in function signature", (StringRef)) -ERROR(unexpected_ctype_for_non_c_convention,none, - "convention '%0' does not support the 'cType' argument label, did you " - "mean @convention(c, cType: \"%1\") or @convention(block, cType: \"%1\") " - "instead?", (StringRef, StringRef)) -ERROR(unable_to_parse_c_function_type,none, - "unable to parse '%0'; it should be a C function pointer type or a " - "block pointer type", (StringRef)) // Opaque types ERROR(unsupported_opaque_type,none, diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 6a142e02da510..329136a29b222 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -306,21 +306,8 @@ struct PrintOptions { /// List of decls that should be printed even if they are implicit and \c SkipImplicit is set to true. std::vector TreatAsExplicitDeclList; - enum class FunctionRepresentationMode : uint8_t { - /// Print the entire convention, including an arguments. - /// For example, this will print a cType argument label if applicable. - Full, - /// Print only the name of the convention, skipping extra argument labels. - NameOnly, - /// Skip printing the @convention(..) altogether. - None - }; - /// Whether to print function @convention attribute on function types. - // FIXME: [clang-function-type-serialization] Once we start serializing Clang - // types, we should also start printing the full type in the swiftinterface. - FunctionRepresentationMode PrintFunctionRepresentationAttrs = - FunctionRepresentationMode::NameOnly; + bool PrintFunctionRepresentationAttrs = true; /// Whether to print storage representation attributes on types, e.g. /// '@sil_weak', '@sil_unmanaged'. @@ -515,8 +502,7 @@ struct PrintOptions { /// consistent and well-formed. /// /// \see swift::emitSwiftInterface - static PrintOptions printSwiftInterfaceFile(bool preferTypeRepr, - bool printFullConvention); + static PrintOptions printSwiftInterfaceFile(bool preferTypeRepr); /// Retrieve the set of options suitable for "Generated Interfaces", which /// are a prettified representation of the public API of a module, to be @@ -599,8 +585,7 @@ struct PrintOptions { PO.SkipUnderscoredKeywords = true; PO.EnumRawValues = EnumRawValueMode::Print; PO.PrintImplicitAttrs = false; - PO.PrintFunctionRepresentationAttrs = - PrintOptions::FunctionRepresentationMode::None; + PO.PrintFunctionRepresentationAttrs = false; PO.PrintDocumentationComments = false; PO.ExcludeAttrList.push_back(DAK_Available); PO.SkipPrivateStdlibDecls = true; diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index bf066910de272..c03a523d0ae40 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -59,7 +59,6 @@ class AssociatedTypeDecl; class ASTContext; enum BufferPointerTypeKind : unsigned; class ClassDecl; -class ClangModuleLoader; class DependentMemberType; class GenericTypeParamDecl; class GenericTypeParamType; @@ -2951,11 +2950,6 @@ class AnyFunctionType : public TypeBase { bool empty() const { return !ClangFunctionType; } Uncommon(const clang::Type *type) : ClangFunctionType(type) {} - - public: - /// Use the ClangModuleLoader to print the Clang type as a string. - void printClangFunctionType(ClangModuleLoader *cml, - llvm::raw_ostream &os); }; private: @@ -3900,11 +3894,6 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, bool empty() const { return !ClangFunctionType; } Uncommon(const clang::FunctionType *type) : ClangFunctionType(type) {} - - public: - /// Analog of AnyFunctionType::ExtInfo::Uncommon::printClangFunctionType. - void printClangFunctionType(ClangModuleLoader *cml, - llvm::raw_ostream &os) const; }; Uncommon Other; @@ -3958,11 +3947,6 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, return getSILFunctionLanguage(getRepresentation()); } - /// Return the underlying Uncommon value if it is not the default value. - Optional getUncommonInfo() const { - return Other.empty() ? Optional() : Other; - } - bool hasSelfParam() const { switch (getRepresentation()) { case Representation::Thick: diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 9ac0d73ef7b9b..9cace98692d33 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -40,7 +40,6 @@ namespace clang { class NamedDecl; class Sema; class TargetInfo; - class Type; class VisibleDeclConsumer; class DeclarationName; } @@ -417,11 +416,6 @@ class ClangImporter final : public ClangModuleLoader { /// with -import-objc-header option. getPCHFilename(const ClangImporterOptions &ImporterOptions, StringRef SwiftPCHHash, bool &isExplicit); - - const clang::Type *parseClangFunctionType(StringRef type, - SourceLoc loc) const override; - void printClangType(const clang::Type *type, - llvm::raw_ostream &os) const override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/include/swift/Frontend/ModuleInterfaceSupport.h b/include/swift/Frontend/ModuleInterfaceSupport.h index 4289150c14554..7a3e54ee8bc64 100644 --- a/include/swift/Frontend/ModuleInterfaceSupport.h +++ b/include/swift/Frontend/ModuleInterfaceSupport.h @@ -31,10 +31,6 @@ struct ModuleInterfaceOptions { /// interface, or should we fully-qualify them? bool PreserveTypesAsWritten = false; - /// Should we emit the cType when printing @convention(c) or no? - /// FIXME: [clang-function-type-serialization] This check should go away. - bool PrintFullConvention = false; - /// Copy of all the command-line flags passed at .swiftinterface /// generation time, re-applied to CompilerInvocation when reading /// back .swiftinterface and reconstructing .swiftmodule. diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index a19bd5f383b96..711dbd7dcf0a4 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -602,11 +602,6 @@ def module_interface_preserve_types_as_written : HelpText<"When emitting a module interface, preserve types as they were " "written in the source">; -def experimental_print_full_convention : - Flag<["-"], "experimental-print-full-convention">, - HelpText<"When emitting a module interface, emit additional @convention " - "arguments, regardless of whether they were written in the source">; - def prebuilt_module_cache_path : Separate<["-"], "prebuilt-module-cache-path">, HelpText<"Directory of prebuilt modules for loading module interfaces">; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 060f2e60a2cab..a9bda93b6538f 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3156,11 +3156,6 @@ ArrayRef GenericFunctionType::getRequirements() const { return Signature->getRequirements(); } -void SILFunctionType::ExtInfo::Uncommon::printClangFunctionType( - ClangModuleLoader *cml, llvm::raw_ostream &os) const { - cml->printClangType(ClangFunctionType, os); -} - void SILFunctionType::Profile( llvm::FoldingSetNodeID &id, GenericSignature genericParams, diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 99a0ca8c982a8..94461dc2473db 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -19,7 +19,6 @@ #include "swift/AST/ASTMangler.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/Attr.h" -#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/Comment.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" @@ -99,8 +98,7 @@ static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) { return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic(); } -PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr, - bool printFullConvention) { +PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr) { PrintOptions result; result.PrintLongAttrsOnSeparateLines = true; result.TypeDefinitions = true; @@ -117,9 +115,6 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr, result.OpaqueReturnTypePrinting = OpaqueReturnTypePrintingMode::StableReference; result.PreferTypeRepr = preferTypeRepr; - if (printFullConvention) - result.PrintFunctionRepresentationAttrs = - PrintOptions::FunctionRepresentationMode::Full; // We should print __consuming, __owned, etc for the module interface file. result.SkipUnderscoredKeywords = false; @@ -326,14 +321,6 @@ void ASTPrinter::callPrintDeclPre(const Decl *D, printDeclPre(D, Bracket); } -ASTPrinter &ASTPrinter::operator<<(QuotedString s) { - llvm::SmallString<32> Str; - llvm::raw_svector_ostream OS(Str); - OS << s; - printTextImpl(OS.str()); - return *this; -} - ASTPrinter &ASTPrinter::operator<<(unsigned long long N) { llvm::SmallString<32> Str; llvm::raw_svector_ostream OS(Str); @@ -3491,15 +3478,6 @@ void Pattern::print(llvm::raw_ostream &OS, const PrintOptions &Options) const { // Type Printing //===----------------------------------------------------------------------===// -template -void printCType(ASTContext &Ctx, ASTPrinter &Printer, ExtInfo &info) { - auto *cml = Ctx.getClangModuleLoader(); - SmallString<64> buf; - llvm::raw_svector_ostream os(buf); - info.getUncommonInfo().getValue().printClangFunctionType(cml, os); - Printer << ", cType: " << QuotedString(os.str()); -} - namespace { class TypePrinter : public TypeVisitor { using super = TypeVisitor; @@ -3862,7 +3840,7 @@ class TypePrinter : public TypeVisitor { visit(staticSelfT); } - void printFunctionExtInfo(ASTContext &Ctx, AnyFunctionType::ExtInfo info) { + void printFunctionExtInfo(AnyFunctionType::ExtInfo info) { if (Options.SkipAttributes) return; @@ -3875,18 +3853,9 @@ class TypePrinter : public TypeVisitor { } } - SmallString<64> buf; - switch (Options.PrintFunctionRepresentationAttrs) { - case PrintOptions::FunctionRepresentationMode::None: - return; - case PrintOptions::FunctionRepresentationMode::Full: - case PrintOptions::FunctionRepresentationMode::NameOnly: - if (Options.excludeAttrKind(TAK_convention) || - info.getSILRepresentation() == SILFunctionType::Representation::Thick) - return; - - bool printNameOnly = Options.PrintFunctionRepresentationAttrs == - PrintOptions::FunctionRepresentationMode::NameOnly; + if (Options.PrintFunctionRepresentationAttrs && + !Options.excludeAttrKind(TAK_convention) && + info.getSILRepresentation() != SILFunctionType::Representation::Thick) { Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); Printer << "("; @@ -3902,11 +3871,6 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; - // FIXME: [clang-function-type-serialization] Once we start serializing - // Clang function types, we should be able to remove the second check. - if (printNameOnly || !info.getUncommonInfo().hasValue()) - break; - printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::Method: Printer << "method"; @@ -3927,8 +3891,7 @@ class TypePrinter : public TypeVisitor { } } - void printFunctionExtInfo(ASTContext &Ctx, - SILFunctionType::ExtInfo info, + void printFunctionExtInfo(SILFunctionType::ExtInfo info, ProtocolConformanceRef witnessMethodConformance) { if (Options.SkipAttributes) return; @@ -3942,19 +3905,9 @@ class TypePrinter : public TypeVisitor { } } - - SmallString<64> buf; - switch (Options.PrintFunctionRepresentationAttrs) { - case PrintOptions::FunctionRepresentationMode::None: - break; - case PrintOptions::FunctionRepresentationMode::NameOnly: - case PrintOptions::FunctionRepresentationMode::Full: - if (Options.excludeAttrKind(TAK_convention) || - info.getRepresentation() == SILFunctionType::Representation::Thick) - break; - - bool printNameOnly = Options.PrintFunctionRepresentationAttrs == - PrintOptions::FunctionRepresentationMode::NameOnly; + if (Options.PrintFunctionRepresentationAttrs && + !Options.excludeAttrKind(TAK_convention) && + info.getRepresentation() != SILFunctionType::Representation::Thick) { Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); Printer << "("; @@ -3969,11 +3922,6 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; - // FIXME: [clang-function-type-serialization] Once we start serializing - // Clang function types, we should be able to remove the second check. - if (printNameOnly || !info.getUncommonInfo().hasValue()) - break; - printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::Method: Printer << "method"; @@ -4043,7 +3991,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); + printFunctionExtInfo(T->getExtInfo()); // If we're stripping argument labels from types, do it when printing. visitAnyFunctionTypeParams(T->getParams(), /*printLabels*/false); @@ -4080,7 +4028,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); + printFunctionExtInfo(T->getExtInfo()); printGenericSignature(T->getGenericSignature(), PrintAST::PrintParams | PrintAST::PrintRequirements); @@ -4133,7 +4081,7 @@ class TypePrinter : public TypeVisitor { void visitSILFunctionType(SILFunctionType *T) { printSILCoroutineKind(T->getCoroutineKind()); - printFunctionExtInfo(T->getASTContext(), T->getExtInfo(), + printFunctionExtInfo(T->getExtInfo(), T->getWitnessMethodConformanceOrInvalid()); printCalleeConvention(T->getCalleeConvention()); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 31f898b14addc..d63b809756ac4 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -17,7 +17,6 @@ #include "swift/AST/Types.h" #include "ForeignRepresentationInfo.h" #include "swift/AST/ASTContext.h" -#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/ReferenceCounting.h" #include "swift/AST/TypeCheckRequests.h" @@ -3240,11 +3239,6 @@ Type ProtocolCompositionType::get(const ASTContext &C, return build(C, CanTypes, HasExplicitAnyObject); } -void AnyFunctionType::ExtInfo::Uncommon::printClangFunctionType( - ClangModuleLoader *cml, llvm::raw_ostream &os) { - cml->printClangType(ClangFunctionType, os); -} - void AnyFunctionType::ExtInfo::assertIsFunctionType(const clang::Type *type) { #ifndef NDEBUG diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 5363d6c94ac2a..8a8b5d1781619 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3275,29 +3275,6 @@ void ClangImporter::verifyAllModules() { #endif } -const clang::Type * -ClangImporter::parseClangFunctionType(StringRef typeStr, - SourceLoc loc) const { - auto &sema = Impl.getClangSema(); - StringRef filename = Impl.SwiftContext.SourceMgr.getDisplayNameForLoc(loc); - // TODO: Obtain a clang::SourceLocation from the swift::SourceLoc we have - auto parsedType = sema.ParseTypeFromStringCallback(typeStr, filename, {}); - if (!parsedType.isUsable()) - return nullptr; - clang::QualType resultType = clang::Sema::GetTypeFromParser(parsedType.get()); - auto *typePtr = resultType.getTypePtrOrNull(); - if (typePtr && (typePtr->isFunctionPointerType() - || typePtr->isBlockPointerType())) - return typePtr; - return nullptr; -} - -void ClangImporter::printClangType(const clang::Type *type, - llvm::raw_ostream &os) const { - auto policy = clang::PrintingPolicy(getClangASTContext().getLangOpts()); - clang::QualType(type, 0).print(os, policy); -} - //===----------------------------------------------------------------------===// // ClangModule Implementation //===----------------------------------------------------------------------===// diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 2eeee7f96f54d..355aeff500586 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -227,8 +227,6 @@ static void ParseModuleInterfaceArgs(ModuleInterfaceOptions &Opts, Opts.PreserveTypesAsWritten |= Args.hasArg(OPT_module_interface_preserve_types_as_written); - Opts.PrintFullConvention |= - Args.hasArg(OPT_experimental_print_full_convention); } /// Save a copy of any flags marked as ModuleInterfaceOption, if running diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 3369917df62b9..b1b6b73673822 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -440,7 +440,7 @@ bool swift::emitSwiftInterface(raw_ostream &out, printImports(out, M); const PrintOptions printOptions = PrintOptions::printSwiftInterfaceFile( - Opts.PreserveTypesAsWritten, Opts.PrintFullConvention); + Opts.PreserveTypesAsWritten); InheritedProtocolCollector::PerTypeMap inheritedProtocolMap; SmallVector topLevelDecls; diff --git a/lib/IDE/CodeCompletionResultBuilder.h b/lib/IDE/CodeCompletionResultBuilder.h index cc4793d9eb1ba..a56e8eee1dada 100644 --- a/lib/IDE/CodeCompletionResultBuilder.h +++ b/lib/IDE/CodeCompletionResultBuilder.h @@ -400,8 +400,7 @@ class CodeCompletionResultBuilder { if (auto AFT = Ty->getAs()) { // If this is a closure type, add ChunkKind::CallParameterClosureType. PrintOptions PO; - PO.PrintFunctionRepresentationAttrs = - PrintOptions::FunctionRepresentationMode::None; + PO.PrintFunctionRepresentationAttrs = false; PO.SkipAttributes = true; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; diff --git a/lib/IDE/IDETypeChecking.cpp b/lib/IDE/IDETypeChecking.cpp index 6d4050743df8f..0b719b59c85e8 100644 --- a/lib/IDE/IDETypeChecking.cpp +++ b/lib/IDE/IDETypeChecking.cpp @@ -85,8 +85,7 @@ PrintOptions PrintOptions::printDocInterface() { PrintOptions::ArgAndParamPrintingMode::BothAlways; result.PrintDocumentationComments = false; result.PrintRegularClangComments = false; - result.PrintFunctionRepresentationAttrs = - PrintOptions::FunctionRepresentationMode::None; + result.PrintFunctionRepresentationAttrs = false; return result; } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index a53d8c2724498..7f3563f4f6b0a 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1799,8 +1799,6 @@ namespace { AnyFunctionType::Representation representation = AnyFunctionType::Representation::Swift, bool noescape = false, - const clang::Type *parsedClangFunctionType - = nullptr, DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable); bool @@ -2168,31 +2166,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Function attributes require a syntactic function type. auto *fnRepr = dyn_cast(repr); - auto tryParseClangType = [this](TypeAttributes::Convention &conv, - bool hasConventionCOrBlock) - -> const clang::Type * { - if (conv.ClangType.empty()) - return nullptr; - if (!hasConventionCOrBlock) { - diagnose(conv.ClangTypeLoc, - diag::unexpected_ctype_for_non_c_convention, - conv.Name, conv.ClangType); - return nullptr; - } - - StringRef filename = - Context.SourceMgr.getDisplayNameForLoc(conv.ClangTypeLoc); - const clang::Type *type = Context.getClangModuleLoader() - ->parseClangFunctionType(conv.ClangType, - conv.ClangTypeLoc); - if (!type) - diagnose(conv.ClangTypeLoc, diag::unable_to_parse_c_function_type, - conv.ClangType); - return type; - }; - if (fnRepr && hasFunctionAttr) { - const clang::Type *parsedClangFunctionType = nullptr; if (options & TypeResolutionFlags::SILType) { SILFunctionType::Representation rep; TypeRepr *witnessMethodProtocol = nullptr; @@ -2240,11 +2214,6 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, rep = SILFunctionType::Representation::Thin; } else { rep = *parsedRep; - bool isCOrBlock = - rep == SILFunctionTypeRepresentation::CFunctionPointer - || rep == SILFunctionTypeRepresentation::Block; - parsedClangFunctionType = - tryParseClangType(attrs.ConventionArguments.getValue(), isCOrBlock); } if (rep == SILFunctionType::Representation::WitnessMethod) { @@ -2295,11 +2264,6 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, rep = FunctionType::Representation::Swift; } else { rep = *parsedRep; - - bool isCOrBlock = rep == FunctionTypeRepresentation::CFunctionPointer - || rep == FunctionTypeRepresentation::Block; - parsedClangFunctionType = - tryParseClangType(attrs.ConventionArguments.getValue(), isCOrBlock); } } @@ -2316,7 +2280,6 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, } ty = resolveASTFunctionType(fnRepr, options, rep, /*noescape=*/false, - parsedClangFunctionType, diffKind); if (!ty || ty->hasError()) return ty; @@ -2630,7 +2593,6 @@ Type TypeResolver::resolveOpaqueReturnType(TypeRepr *repr, Type TypeResolver::resolveASTFunctionType( FunctionTypeRepr *repr, TypeResolutionOptions parentOptions, AnyFunctionType::Representation representation, bool noescape, - const clang::Type *parsedClangFunctionType, DifferentiabilityKind diffKind) { TypeResolutionOptions options = None; @@ -2669,9 +2631,8 @@ Type TypeResolver::resolveASTFunctionType( noescape, repr->throws(), diffKind, /*clangFunctionType*/nullptr); - const clang::Type *clangFnType = parsedClangFunctionType; - if (representation == AnyFunctionType::Representation::CFunctionPointer - && !clangFnType) + const clang::Type *clangFnType = nullptr; + if (representation == AnyFunctionType::Representation::CFunctionPointer) clangFnType = Context.getClangFunctionType( params, outputTy, incompleteExtInfo, AnyFunctionType::Representation::CFunctionPointer); diff --git a/test/ClangImporter/clang-function-types.swift b/test/ClangImporter/clang-function-types.swift deleted file mode 100644 index 88b50655eb237..0000000000000 --- a/test/ClangImporter/clang-function-types.swift +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -sdk %clang-importer-sdk -enable-library-evolution %s -experimental-print-full-convention | tee ~/tmp.si | %FileCheck %s - -import ctypes - -// CHECK: f1: (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)? -public let f1 = getFunctionPointer_() - -// CHECK: f2: (@convention(c, cType: "int (*(*)(int (*)(int)))(int)") ((@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?) -> (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?)? -public let f2 = getHigherOrderFunctionPointer() - -// CHECK: f3: () -> (@convention(c, cType: "Dummy *(*)(Dummy *)") (Swift.UnsafeMutablePointer?) -> Swift.UnsafeMutablePointer?)? -public let f3 = getFunctionPointer3 diff --git a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h index 4d4a7cf018575..c055b48ebff88 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h @@ -205,8 +205,6 @@ typedef int (*fptr)(int); fptr getFunctionPointer(void); void useFunctionPointer(fptr); -int (*getFunctionPointer_(void))(int); - struct FunctionPointerWrapper { fptr a; fptr b; @@ -216,14 +214,6 @@ typedef void (*fptr2)(int, long, void *); fptr2 getFunctionPointer2(void); void useFunctionPointer2(fptr2); -int (*(*getHigherOrderFunctionPointer(void))(int (*)(int)))(int); - -typedef struct Dummy { - int x; -} Dummy; - -Dummy * (*getFunctionPointer3(void))(Dummy *); - //===--- // Unions //===--- diff --git a/test/ModuleInterface/full-convention.swift b/test/ModuleInterface/full-convention.swift deleted file mode 100644 index e65c2fe427ca6..0000000000000 --- a/test/ModuleInterface/full-convention.swift +++ /dev/null @@ -1,32 +0,0 @@ -// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -enable-library-evolution %s -experimental-print-full-convention | %FileCheck %s - -public func f( - // CHECK: g: @convention(c, cType: "void (*)(void)") - g: @convention(c) () -> (), - - // CHECK: h0: @convention(c, cType: "int (*)(long long)") - h0: @convention(c) (Int64) -> Int32, - // CHECK: h1: @convention(c, cType: "int (*)(long long)") - h1: @convention(c, cType: "int (*)(long long)") (Int64) -> Int32, - - // CHECK: i0: @convention(c, cType: "int *(*)(long long, int)") - i0: @convention(c) (Int64, Int32) -> Optional>, - // CHECK: i1: @convention(c, cType: "int *(*)(long long, int)") - i1: @convention(c, cType: "int *(*)(long long, int)") (Int64, Int32) -> Optional>, - - // CHECK: p0: @convention(c, cType: "void (*)(void (*)(long))") - // CHECK: @convention(c, cType: "void (*)(long)") - p0: @convention(c) (@convention(c) (Int) -> Void) -> Void, - - // CHECK: p1: @convention(c, cType: "void (*)(void (*)(long))") - // CHECK: @convention(c, cType: "void (*)(long)") - p1: @convention(c, cType: "void (*)(void (*)(long))") (@convention(c) (Int) -> Void) -> Void, - - // CHECK: p2: @convention(c, cType: "void (*)(void (*)(long))") - // CHECK: @convention(c, cType: "void (*)(long)") - p2: @convention(c) (@convention(c, cType: "void (*)(long)") (Int) -> Void) -> Void, - - // CHECK: p3: @convention(c, cType: "void (*)(void (*)(long))") - // CHECK: @convention(c, cType: "void (*)(long)") - p3: @convention(c, cType: "void (*)(void (*)(long))") (@convention(c, cType: "void (*)(long)") (Int) -> Void) -> Void -) {} diff --git a/test/Parse/c_function_pointers.swift b/test/Parse/c_function_pointers.swift index 188634808dfac..3d51e63098b5f 100644 --- a/test/Parse/c_function_pointers.swift +++ b/test/Parse/c_function_pointers.swift @@ -50,8 +50,8 @@ let f: @convention(c) (Int) -> Int = genericFunc // expected-error{{cannot be fo func ct1() -> () { print("") } -let ct1ref0 : @convention(c, cType: "void (*)(void)") () -> () = ct1 -let ct1ref1 : @convention(c, cType: "void (*)(void)") = ct1 // expected-error{{expected type}} +let ct1ref0 : @convention(c, cType: "void *(void)") () -> () = ct1 +let ct1ref1 : @convention(c, cType: "void *(void)") = ct1 // expected-error{{expected type}} let ct1ref2 : @convention(c, ) () -> () = ct1 // expected-error{{expected 'cType' label in 'convention' attribute}} let ct1ref3 : @convention(c, cType) () -> () = ct1 // expected-error{{expected ':' after 'cType' for 'convention' attribute}} let ct1ref4 : @convention(c, cType: ) () -> () = ct1 // expected-error{{expected string literal containing clang type for 'cType' in 'convention' attribute}} diff --git a/test/attr/attr_convention.swift b/test/attr/attr_convention.swift index 83048435363fc..7ee849bdb3079 100644 --- a/test/attr/attr_convention.swift +++ b/test/attr/attr_convention.swift @@ -2,12 +2,8 @@ let f1: (Int) -> Int = { $0 } let f2: @convention(swift) (Int) -> Int = { $0 } -let f2a: @convention(swift, cType: "int *(int)") (Int32) -> Int32 = { $0 } // expected-error{{convention 'swift' does not support the 'cType' argument label, did you mean @convention(c, cType: "int *(int)") or @convention(block, cType: "int *(int)") instead?}} let f3: @convention(block) (Int) -> Int = { $0 } let f4: @convention(c) (Int) -> Int = { $0 } -let f4a: @convention(c, cType: "int (int)") (Int32) -> Int32 = { $0 } // expected-error{{unable to parse 'int (int)'; it should be a C function pointer type or a block pointer type}} -let f4b: @convention(c, cType: "void *") (Int32) -> Int32 = { $0 } // expected-error{{unable to parse 'void *'; it should be a C function pointer type or a block pointer type}} -let f4c: @convention(c, cType: "int (*)(int)") (Int32) -> Int32 = { $0 } let f5: @convention(INTERCAL) (Int) -> Int = { $0 } // expected-error{{convention 'INTERCAL' not supported}} From 96a0c7931db0dee61564ae14b4b56426cf831bdc Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 6 Jan 2020 12:03:20 -0800 Subject: [PATCH 297/478] [basic] Add a simple vector backed 2 stage multi map. I have been using this in a bunch of places in the compiler and rather than implement it by hand over and over (and maybe messing up), this commit just commits a correct implementation. This data structure is a map backed by a vector like data structure. It has two phases: 1. An insertion phase when the map is mutable and one inserts (key, value) pairs into the map. These are just appeneded into the storage array. 2. A frozen stage when the map is immutable and one can now perform map queries on the multimap. The map transitions from the mutable, thawed phase to the immutable, frozen phase by performing a stable_sort of its internal storage by only the key. Since this is a stable_sort, we know that the relative insertion order of values is preserved if their keys equal. Thus the sorting will have created contiguous regions in the array of values, all mapped to the same key, that are insertion order. Thus by finding the lower_bound for a given key, we are guaranteed to get the first element in that continguous range. We can then do a forward search to find the end of the region, allowing us to then return an ArrayRef to these internal values. The reason why I keep on finding myself using this is that this map enables one to map a key to an array of values without needing to store small vectors in a map or use heap allocated memory, all key, value pairs are stored inline (in potentially a single SmallVector given that one is using SmallFrozenMultiMap). --- include/swift/Basic/FrozenMultiMap.h | 187 +++++++++++++++++++++++++ unittests/Basic/CMakeLists.txt | 1 + unittests/Basic/FrozenMultiMapTest.cpp | 187 +++++++++++++++++++++++++ 3 files changed, 375 insertions(+) create mode 100644 include/swift/Basic/FrozenMultiMap.h create mode 100644 unittests/Basic/FrozenMultiMapTest.cpp diff --git a/include/swift/Basic/FrozenMultiMap.h b/include/swift/Basic/FrozenMultiMap.h new file mode 100644 index 0000000000000..c4e497f7f5d6c --- /dev/null +++ b/include/swift/Basic/FrozenMultiMap.h @@ -0,0 +1,187 @@ +//===--- FrozenMultiMap.h ----------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// A 2 stage multi-map. Initially the multimap is mutable and can only be +/// initialized. Once complete, the map is frozen and can be only used for map +/// operations. It is guaranteed that all values are still in insertion order. +/// +/// DISCUSSION: These restrictions flow from the internal implementation of the +/// multi-map being a pair of keys, values. We form the map property by +/// performing a stable_sort of the (key, value) in the process of freezing the +/// map. +/// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BASIC_FROZENMULTIMAP_H +#define SWIFT_BASIC_FROZENMULTIMAP_H + +#include "swift/Basic/LLVM.h" +#include "swift/Basic/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include + +namespace swift { + +template >> +class FrozenMultiMap { + VectorStorage storage; + bool frozen = false; + +private: + struct PairToSecondElt; + +public: + using PairToSecondEltRange = + TransformRange>, PairToSecondElt>; + + FrozenMultiMap() = default; + + void insert(const Key &key, const Value &value) { + assert(!isFrozen() && "Can not insert new keys once map is frozen"); + storage.emplace_back(key, value); + } + + Optional find(const Key &key) const { + assert(isFrozen() && + "Can not perform a find operation until the map is frozen"); + // Since our array is sorted, we need to first find the first pair with our + // inst as the first element. + auto start = std::lower_bound( + storage.begin(), storage.end(), std::make_pair(key, Value()), + [&](const std::pair &p1, const std::pair &p2) { + return p1.first < p2.first; + }); + if (start == storage.end() || start->first != key) { + return None; + } + + // Ok, we found our first element. Now scan forward until we find a pair + // whose instruction is not our own instruction. + auto end = find_if_not( + start, storage.end(), + [&](const std::pair &pair) { return pair.first == key; }); + unsigned count = std::distance(start, end); + ArrayRef> slice(&*start, count); + return PairToSecondEltRange(slice, PairToSecondElt()); + } + + bool isFrozen() const { return frozen; } + + /// Set this map into its frozen state when we + void setFrozen() { + std::stable_sort(storage.begin(), storage.end(), + [&](const std::pair &lhs, + const std::pair &rhs) { + // Only compare the first entry so that we preserve + // insertion order. + return lhs.first < rhs.first; + }); + frozen = true; + } + + unsigned size() const { return storage.size(); } + bool empty() const { return storage.empty(); } + + struct iterator : std::iterator>> { + using base_iterator = typename decltype(storage)::iterator; + + FrozenMultiMap ↦ + base_iterator baseIter; + Optional> currentValue; + + iterator(FrozenMultiMap &map, base_iterator iter) + : map(map), baseIter(iter), currentValue() { + // If we are end, just return. + if (iter == map.storage.end()) { + return; + } + + // Otherwise, prime our first range. + updateCurrentValue(); + } + + void updateCurrentValue() { + base_iterator end = map.storage.end(); + auto rangeEnd = std::find_if_not(std::next(baseIter), end, + [&](const std::pair &elt) { + return elt.first == baseIter->first; + }); + unsigned count = std::distance(baseIter, rangeEnd); + ArrayRef> slice(&*baseIter, count); + currentValue = {baseIter->first, + PairToSecondEltRange(slice, PairToSecondElt())}; + } + + iterator &operator++() { + baseIter = std::find_if_not(std::next(baseIter), map.storage.end(), + [&](const std::pair &elt) { + return elt.first == baseIter->first; + }); + updateCurrentValue(); + return *this; + } + + iterator operator++(int) { + auto tmp = *this; + baseIter = std::find_if_not(std::next(baseIter), map.storage.end(), + [&](const std::pair &elt) { + return elt.first == baseIter->first; + }); + updateCurrentValue(); + return tmp; + } + + std::pair operator*() const { + return *currentValue; + } + + bool operator==(const iterator &RHS) const { + return baseIter == RHS.baseIter; + } + + bool operator!=(const iterator &RHS) const { + return baseIter != RHS.baseIter; + } + }; + + /// Return a range of (key, ArrayRef) pairs. The keys are guaranteed to + /// be in key sorted order and the ArrayRef are in insertion order. + llvm::iterator_range getRange() const { + assert(isFrozen() && + "Can not create range until data structure is frozen?!"); + auto *self = const_cast(this); + iterator iter1 = iterator(*self, self->storage.begin()); + iterator iter2 = iterator(*self, self->storage.end()); + return llvm::make_range(iter1, iter2); + } +}; + +template +struct FrozenMultiMap::PairToSecondElt { + PairToSecondElt() {} + + Value operator()(const std::pair &pair) const { + return pair.second; + } +}; + +template +using SmallFrozenMultiMap = + FrozenMultiMap, SmallSize>>; + +} // namespace swift + +#endif diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt index 488dc579d239c..b26199c3897ce 100644 --- a/unittests/Basic/CMakeLists.txt +++ b/unittests/Basic/CMakeLists.txt @@ -15,6 +15,7 @@ add_swift_unittest(SwiftBasicTests EncodedSequenceTest.cpp ExponentialGrowthAppendingBinaryByteStreamTests.cpp FileSystemTest.cpp + FrozenMultiMapTest.cpp ImmutablePointerSetTest.cpp JSONSerialization.cpp OptionSetTest.cpp diff --git a/unittests/Basic/FrozenMultiMapTest.cpp b/unittests/Basic/FrozenMultiMapTest.cpp new file mode 100644 index 0000000000000..40207ef59c3b0 --- /dev/null +++ b/unittests/Basic/FrozenMultiMapTest.cpp @@ -0,0 +1,187 @@ +//===--- FrozenMultiMapTest.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 +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "swift-frozen-multi-map-test" + +#include "swift/Basic/FrozenMultiMap.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/Lazy.h" +#include "swift/Basic/NullablePtr.h" +#include "swift/Basic/Range.h" +#include "swift/Basic/STLExtras.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" +#include +#include +#include + +using namespace swift; + +namespace { + +class Canary { + static unsigned currentID; + unsigned id; + +public: + static void resetIDs() { currentID = 0; } + Canary(unsigned id) : id(id) {} + Canary() { + id = currentID; + ++currentID; + } + + unsigned getID() const { return id; } + bool operator<(const Canary &other) const { return id < other.id; } + + bool operator==(const Canary &other) const { return id == other.id; } + + bool operator!=(const Canary &other) const { return !(*this == other); } +}; + +unsigned Canary::currentID = 0; + +} // namespace + +TEST(FrozenMultiMapCustomTest, SimpleFind) { + Canary::resetIDs(); + FrozenMultiMap map; + + auto key1 = Canary(); + auto key2 = Canary(); + map.insert(key1, Canary()); + map.insert(key1, Canary()); + map.insert(key1, Canary()); + map.insert(key2, Canary()); + map.insert(key2, Canary()); + + map.setFrozen(); + + EXPECT_EQ(map.size(), 5u); + { + auto r = map.find(key1); + EXPECT_TRUE(r.hasValue()); + EXPECT_EQ(r->size(), 3u); + EXPECT_EQ((*r)[0].getID(), 2u); + EXPECT_EQ((*r)[1].getID(), 3u); + EXPECT_EQ((*r)[2].getID(), 4u); + } + + { + auto r = map.find(key2); + EXPECT_TRUE(r.hasValue()); + EXPECT_EQ(r->size(), 2u); + EXPECT_EQ((*r)[0].getID(), 5u); + EXPECT_EQ((*r)[1].getID(), 6u); + } +} + +TEST(FrozenMultiMapCustomTest, SimpleIter) { + Canary::resetIDs(); + FrozenMultiMap map; + + auto key1 = Canary(); + auto key2 = Canary(); + map.insert(key1, Canary()); + map.insert(key1, Canary()); + map.insert(key1, Canary()); + map.insert(key2, Canary()); + map.insert(key2, Canary()); + + map.setFrozen(); + + EXPECT_EQ(map.size(), 5u); + + auto range = map.getRange(); + + EXPECT_EQ(std::distance(range.begin(), range.end()), 2); + + auto iter = range.begin(); + { + auto p = *iter; + EXPECT_EQ(p.first.getID(), key1.getID()); + EXPECT_EQ(p.second.size(), 3u); + EXPECT_EQ(p.second[0].getID(), 2u); + EXPECT_EQ(p.second[1].getID(), 3u); + EXPECT_EQ(p.second[2].getID(), 4u); + } + + ++iter; + { + auto p = *iter; + EXPECT_EQ(p.first.getID(), key2.getID()); + EXPECT_EQ(p.second.size(), 2u); + EXPECT_EQ(p.second[0].getID(), 5u); + EXPECT_EQ(p.second[1].getID(), 6u); + } +} + +TEST(FrozenMultiMapCustomTest, RandomAgainstStdMultiMap) { + Canary::resetIDs(); + FrozenMultiMap map; + std::multimap stdMultiMap; + + auto seed = + std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::mt19937 mt_rand(seed); + + std::vector keyIdList; + for (unsigned i = 0; i < 1024; ++i) { + unsigned keyID = mt_rand() % 20; + keyIdList.push_back(keyID); + for (unsigned valueID = (mt_rand()) % 15; valueID < 15; ++valueID) { + map.insert(keyID, valueID); + stdMultiMap.emplace(keyID, valueID); + } + } + + map.setFrozen(); + + // Then for each key. + for (unsigned i : keyIdList) { + // Make sure that we have the same elements in the same order for each key. + auto range = *map.find(i); + auto stdRange = stdMultiMap.equal_range(i); + EXPECT_EQ(std::distance(range.begin(), range.end()), + std::distance(stdRange.first, stdRange.second)); + auto modernStdRange = llvm::make_range(stdRange.first, stdRange.second); + for (auto p : llvm::zip(range, modernStdRange)) { + unsigned lhs = std::get<0>(p); + unsigned rhs = std::get<1>(p).second; + EXPECT_EQ(lhs, rhs); + } + } + + // Then check that when we iterate over both ranges, we get the same order. + { + auto range = map.getRange(); + auto rangeIter = range.begin(); + auto stdRangeIter = stdMultiMap.begin(); + + while (rangeIter != range.end()) { + auto rangeElt = *rangeIter; + + for (unsigned i : indices(rangeElt.second)) { + EXPECT_EQ(rangeElt.first, stdRangeIter->first); + EXPECT_EQ(rangeElt.second[i], stdRangeIter->second); + ++stdRangeIter; + } + + ++rangeIter; + } + } +} From 29465f82add36c3a964e2f8bd1557c7d35934f81 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Mon, 6 Jan 2020 18:29:26 -0800 Subject: [PATCH 298/478] Add standalone test. --- .../Sema/derivative_attr_type_checking.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/AutoDiff/Sema/derivative_attr_type_checking.swift b/test/AutoDiff/Sema/derivative_attr_type_checking.swift index 27db38dc34c4e..babb8fad87aa5 100644 --- a/test/AutoDiff/Sema/derivative_attr_type_checking.swift +++ b/test/AutoDiff/Sema/derivative_attr_type_checking.swift @@ -546,6 +546,23 @@ extension HasStoredProperty { } } +// Test derivative registration for protocol requirements. Currently unsupported. +// TODO(TF-982): Lift this restriction and add proper support. + +protocol ProtocolRequirementDerivative { + func requirement(_ x: Float) -> Float +} +extension ProtocolRequirementDerivative { + // NOTE: the error is misleading because `findAbstractFunctionDecl` in + // TypeCheckAttr.cpp is not setup to show customized error messages for + // invalid original function candidates. + // expected-error @+1 {{could not find function 'requirement' with expected type ' (Self) -> (Float) -> Float'}} + @derivative(of: requirement) + func vjpRequirement(_ x: Float) -> (value: Float, pullback: (Float) -> Float) { + fatalError() + } +} + // Test cross-file derivative registration. Currently unsupported. // TODO(TF-1021): Lift this restriction. From 509735ea66a42cb9d610845abb223eced1d43aa0 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 6 Jan 2020 16:01:20 -0800 Subject: [PATCH 299/478] IRGen: Work around RemoteMirror bug generating reflection info for empty builtin types. The RemoteMirror library in shipping versions of macOS/iOS/tvOS/watchOS crashes if the compiler emits a BuiltinTypeDescriptor with size zero. Although this is fixed in top-of-tree RemoteMirror, we want binaries built with the new compiler to still be inspectable when run on older OSes. Generate the metadata as an empty struct with no fields when deploying back to these older platforms, which should be functionally equivalent for most purposes. Fixes rdar://problem/57924984. --- lib/IRGen/GenReflection.cpp | 75 ++++++++++++++++++- .../Reflection/reflect_empty_struct.swift | 3 +- .../reflect_empty_struct_compat.swift | 75 +++++++++++++++++++ 3 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 validation-test/Reflection/reflect_empty_struct_compat.swift diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index 4415b5b6d6e86..558439d743391 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -671,7 +671,10 @@ class AssociatedTypeMetadataBuilder : public ReflectionMetadataBuilder { }; class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { - const uint32_t fieldRecordSize = 12; +public: + static const uint32_t FieldRecordSize = 12; + +private: const NominalTypeDecl *NTD; void addFieldDecl(const ValueDecl *value, Type type, @@ -714,7 +717,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { } B.addInt16(uint16_t(kind)); - B.addInt16(fieldRecordSize); + B.addInt16(FieldRecordSize); auto properties = NTD->getStoredProperties(); B.addInt32(properties.size()); @@ -737,7 +740,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { } B.addInt16(uint16_t(kind)); - B.addInt16(fieldRecordSize); + B.addInt16(FieldRecordSize); B.addInt32(strategy.getElementsWithPayload().size() + strategy.getElementsWithNoPayload().size()); @@ -764,7 +767,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { else Kind = FieldDescriptorKind::Protocol; B.addInt16(uint16_t(Kind)); - B.addInt16(fieldRecordSize); + B.addInt16(FieldRecordSize); B.addInt32(0); } @@ -825,6 +828,57 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { } }; +static bool +deploymentTargetHasRemoteMirrorZeroSizedTypeDescriptorBug(IRGenModule &IGM) { + auto target = IGM.Context.LangOpts.Target; + + if (target.isMacOSX() && target.isMacOSXVersionLT(10, 16, 0)) { + return true; + } + if (target.isiOS() && target.isOSVersionLT(14)) { // includes tvOS + return true; + } + if (target.isWatchOS() && target.isOSVersionLT(7)) { + return true; + } + + return false; +} + +/// Metadata builder that emits a fixed-layout empty type as an empty struct, as +/// a workaround for a RemoteMirror crash in older OSes. +class EmptyStructMetadataBuilder : public ReflectionMetadataBuilder { + const NominalTypeDecl *NTD; + + void layout() override { + addNominalRef(NTD); + B.addInt32(0); + B.addInt16(uint16_t(FieldDescriptorKind::Struct)); + B.addInt16(FieldTypeMetadataBuilder::FieldRecordSize); + B.addInt32(0); + } + +public: + EmptyStructMetadataBuilder(IRGenModule &IGM, + const NominalTypeDecl *NTD) + : ReflectionMetadataBuilder(IGM), NTD(NTD) { + assert(IGM.getTypeInfoForUnlowered( + NTD->getDeclaredTypeInContext()->getCanonicalType()) + .isKnownEmpty(ResilienceExpansion::Maximal) + && "should only be used for known empty types"); + } + + llvm::GlobalVariable *emit() { + auto section = IGM.getFieldTypeMetadataSectionName(); + return ReflectionMetadataBuilder::emit( + [&](IRGenModule &IGM, ConstantInit definition) -> llvm::Constant* { + return IGM.getAddrOfReflectionFieldDescriptor( + NTD->getDeclaredType()->getCanonicalType(), definition); + }, + section); + } +}; + class FixedTypeMetadataBuilder : public ReflectionMetadataBuilder { ModuleDecl *module; CanType type; @@ -1338,6 +1392,19 @@ void IRGenModule::emitFieldDescriptor(const NominalTypeDecl *D) { } if (needsOpaqueDescriptor) { + // Work around an issue in the RemoteMirror library that ships in + // macOS 10.15/iOS 13 and earlier that causes it to crash on a + // BuiltinTypeDescriptor with zero size. If the type has zero size, emit it + // as an empty struct instead, which will have the same impact on the + // encoded type layout. + auto &TI = getTypeInfoForUnlowered(T); + if (deploymentTargetHasRemoteMirrorZeroSizedTypeDescriptorBug(*this) + && TI.isKnownEmpty(ResilienceExpansion::Maximal)) { + EmptyStructMetadataBuilder builder(*this, D); + builder.emit(); + return; + } + FixedTypeMetadataBuilder builder(*this, D); builder.emit(); } diff --git a/validation-test/Reflection/reflect_empty_struct.swift b/validation-test/Reflection/reflect_empty_struct.swift index 32427344a3fc9..df2e365172082 100644 --- a/validation-test/Reflection/reflect_empty_struct.swift +++ b/validation-test/Reflection/reflect_empty_struct.swift @@ -1,11 +1,12 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct +// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.99.0 // RUN: %target-codesign %t/reflect_empty_struct // RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail // REQUIRES: objc_interop // REQUIRES: executable_test +// REQUIRES: OS=macosx import SwiftReflectionTest diff --git a/validation-test/Reflection/reflect_empty_struct_compat.swift b/validation-test/Reflection/reflect_empty_struct_compat.swift new file mode 100644 index 0000000000000..93188e9f1010e --- /dev/null +++ b/validation-test/Reflection/reflect_empty_struct_compat.swift @@ -0,0 +1,75 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.15.0 +// RUN: %target-codesign %t/reflect_empty_struct +// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail + +// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.14.0 +// RUN: %target-codesign %t/reflect_empty_struct +// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail + +// REQUIRES: objc_interop +// REQUIRES: executable_test +// REQUIRES: OS=macosx + +import SwiftReflectionTest + +import EmptyStruct + +@_alignment(1) struct EmptyStruct { } +class Class { + var a = EmptyStruct() + var b: Any = EmptyStruct() + var c = EmptyStructC() + var d: Any = EmptyStructC() +} + +var obj = Class() + +reflect(object: obj) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_empty_struct.Class) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=80 alignment=8 stride=80 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=a offset=16 +// CHECK-64: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64: (field name=b offset=16 +// CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1 +// CHECK-64: (field name=metadata offset=24 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=2147483647 bitwise_takable=1)))) +// CHECK-64: (field name=c offset=48 +// CHECK-64: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64: (field name=d offset=48 +// CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1 +// CHECK-64: (field name=metadata offset=24 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=2147483647 bitwise_takable=1))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_empty_struct.Class) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=40 alignment=4 stride=40 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=a offset=8 +// CHECK-32: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-32: (field name=b offset=8 +// CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32: (field name=metadata offset=12 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1)))) +// CHECK-32: (field name=c offset=24 +// CHECK-32: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-32: (field name=d offset=24 +// CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32: (field name=metadata offset=12 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. From 133e8a186fe67aeebae8410257a005155f031acc Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Fri, 20 Dec 2019 14:12:58 -0800 Subject: [PATCH 300/478] ModuleInterface: preserve AutolinkForceLoad option when generating .swiftmodule from .swiftinterface This change ensures using .swiftmodule built from source has the same behavior as using .swiftmodule built from .swiftinterface. A swift-ide-test utility is added to print linked libraries from a Swift module for testing purposes. rdar://58057556 --- lib/Frontend/ModuleInterfaceBuilder.cpp | 2 + .../ModuleInterface/force-load-autolink.swift | 8 ++ tools/swift-ide-test/swift-ide-test.cpp | 78 ++++++++++++++++++- 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 test/ModuleInterface/force-load-autolink.swift diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index f1bfcea821bc6..59af6d226681e 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -348,6 +348,8 @@ bool ModuleInterfaceBuilder::buildSwiftModule( std::string OutPathStr = OutPath; SerializationOpts.OutputPath = OutPathStr.c_str(); SerializationOpts.ModuleLinkName = FEOpts.ModuleLinkName; + SerializationOpts.AutolinkForceLoad = + !subInvocation.getIRGenOptions().ForceLoadSymbolName.empty(); // Record any non-SDK module interface files for the debug info. StringRef SDKPath = SubInstance.getASTContext().SearchPathOpts.SDKPath; diff --git a/test/ModuleInterface/force-load-autolink.swift b/test/ModuleInterface/force-load-autolink.swift new file mode 100644 index 0000000000000..3cef9dd75b7ef --- /dev/null +++ b/test/ModuleInterface/force-load-autolink.swift @@ -0,0 +1,8 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -typecheck -module-name Foo -emit-module-interface-path %t/Foo.swiftinterface %s -module-link-name Foo -enable-library-evolution -autolink-force-load +// RUN: %target-swift-frontend -compile-module-from-interface -o %t/Foo.swiftmodule %t/Foo.swiftinterface +// RUN: %target-swift-ide-test -print-module-metadata -module-to-print Foo -I %t -source-filename %s | %FileCheck %s + +public func foo() {} + +// CHECK: link library: Foo, force load: true \ No newline at end of file diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index d54206e0d286c..995eaaaab5eef 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -86,6 +86,7 @@ enum class ActionType { PrintASTNotTypeChecked, PrintASTTypeChecked, PrintModule, + PrintModuleMetadata, PrintHeader, PrintSwiftFileInterface, PrintDecl, @@ -181,6 +182,8 @@ Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None), "print-ast-typechecked", "Print the typechecked AST"), clEnumValN(ActionType::PrintModule, "print-module", "Print visible declarations in a module"), + clEnumValN(ActionType::PrintModuleMetadata, + "print-module-metadata", "Print meta-data in a module"), clEnumValN(ActionType::PrintHeader, "print-header", "Print visible declarations in a header file"), clEnumValN(ActionType::PrintSwiftFileInterface, @@ -2037,6 +2040,76 @@ static int doPrintModuleGroups(const CompilerInvocation &InitInvok, return ExitCode; } +static void printModuleMetadata(ModuleDecl *MD) { + auto &OS = llvm::outs(); + MD->collectLinkLibraries([&](LinkLibrary lib) { + OS << "link library: " << lib.getName() + << ", force load: " << (lib.shouldForceLoad() ? "true" : "false") << "\n"; + }); +} + +static int doPrintModuleMetaData(const CompilerInvocation &InitInvok, + const std::vector ModulesToPrint) { + CompilerInvocation Invocation(InitInvok); + + CompilerInstance CI; + // Display diagnostics to stderr. + PrintingDiagnosticConsumer PrintDiags; + CI.addDiagnosticConsumer(&PrintDiags); + if (CI.setup(Invocation)) + return 1; + registerIDERequestFunctions(CI.getASTContext().evaluator); + auto &Context = CI.getASTContext(); + + // Load standard library so that Clang importer can use it. + auto *Stdlib = getModuleByFullName(Context, Context.StdlibModuleName); + if (!Stdlib) { + llvm::errs() << "Failed loading stdlib\n"; + return 1; + } + int ExitCode = 0; + for (StringRef ModuleToPrint : ModulesToPrint) { + if (ModuleToPrint.empty()) { + ExitCode = 1; + continue; + } + + // Get the (sub)module to print. + auto *M = getModuleByFullName(Context, ModuleToPrint); + if (!M) { + llvm::errs() << "error: could not find module '" << ModuleToPrint + << "'\n"; + ExitCode = 1; + continue; + } + + // Split the module path. + std::vector ModuleName; + while (!ModuleToPrint.empty()) { + StringRef SubModuleName; + std::tie(SubModuleName, ModuleToPrint) = ModuleToPrint.split('.'); + ModuleName.push_back(SubModuleName); + } + assert(!ModuleName.empty()); + + // FIXME: If ModuleToPrint is a submodule, get its top-level module, which + // will be the DeclContext for all of its Decls since we don't have first- + // class submodules. + if (ModuleName.size() > 1) { + M = getModuleByFullName(Context, ModuleName[0]); + if (!M) { + llvm::errs() << "error: could not find module '" << ModuleName[0] + << "'\n"; + ExitCode = 1; + continue; + } + } + printModuleMetadata(M); + } + + return ExitCode; +} + static int doPrintModules(const CompilerInvocation &InitInvok, const std::vector ModulesToPrint, const std::vector GroupsToPrint, @@ -3543,7 +3616,10 @@ int main(int argc, char *argv[]) { } break; } - + case ActionType::PrintModuleMetadata: { + ExitCode = doPrintModuleMetaData(InitInvok, options::ModuleToPrint); + break; + } case ActionType::PrintHeader: { ExitCode = doPrintHeaders( InitInvok, options::HeaderToPrint, PrintOpts, From 7a1ac6b5a4af3e98379703a9abd861eeede00e7c Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Mon, 6 Jan 2020 14:04:37 -0800 Subject: [PATCH 301/478] cmake: add SDK library search path for overlays unavailable in the source When force linking auto-linked libraries, an overlay will fail to link if the dependence libraries are missing from the source. This change provides linker flags to search overlay libraries from the SDK. --- cmake/modules/AddSwift.cmake | 4 ++++ stdlib/public/Darwin/ARKit/CMakeLists.txt | 1 + stdlib/public/Darwin/AVFoundation/CMakeLists.txt | 2 +- stdlib/public/Darwin/MediaPlayer/CMakeLists.txt | 3 ++- stdlib/public/Darwin/Photos/CMakeLists.txt | 2 +- stdlib/public/Darwin/Vision/CMakeLists.txt | 2 +- stdlib/public/Darwin/WatchKit/CMakeLists.txt | 2 +- 7 files changed, 11 insertions(+), 5 deletions(-) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index b40bd4d881ed4..2be88f8b615be 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -1721,6 +1721,7 @@ function(add_swift_target_library name) SWIFT_MODULE_DEPENDS_TVOS SWIFT_MODULE_DEPENDS_WATCHOS SWIFT_MODULE_DEPENDS_WINDOWS + SWIFT_MODULE_DEPENDS_FROM_SDK TARGET_SDKS) cmake_parse_arguments(SWIFTLIB @@ -1960,6 +1961,9 @@ function(add_swift_target_library name) set(swiftlib_c_compile_flags_all ${SWIFTLIB_C_COMPILE_FLAGS}) if(sdk IN_LIST SWIFT_APPLE_PLATFORMS AND SWIFTLIB_IS_SDK_OVERLAY) set(swiftlib_swift_compile_private_frameworks_flag "-Fsystem" "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}/System/Library/PrivateFrameworks/") + foreach(tbd_lib ${SWIFTLIB_SWIFT_MODULE_DEPENDS_FROM_SDK}) + list(APPEND swiftlib_link_flags_all "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}/usr/lib/swift/libswift${tbd_lib}.tbd") + endforeach() endif() list(APPEND swiftlib_c_compile_flags_all "-DSWIFT_TARGET_LIBRARY_NAME=${name}") diff --git a/stdlib/public/Darwin/ARKit/CMakeLists.txt b/stdlib/public/Darwin/ARKit/CMakeLists.txt index df79968511cb8..f8e3eeff16388 100644 --- a/stdlib/public/Darwin/ARKit/CMakeLists.txt +++ b/stdlib/public/Darwin/ARKit/CMakeLists.txt @@ -11,6 +11,7 @@ add_swift_target_library(swiftARKit ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_ TARGET_SDKS IOS IOS_SIMULATOR SWIFT_MODULE_DEPENDS_IOS Darwin CoreImage CoreGraphics Metal UIKit Dispatch GLKit SceneKit simd Foundation AVFoundation SpriteKit CoreMedia QuartzCore ModelIO CoreFoundation CoreAudio ObjectiveC # auto-updated FRAMEWORK_DEPENDS_WEAK ARKit + SWIFT_MODULE_DEPENDS_FROM_SDK CoreMIDI DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_ARKIT_IOS} INSTALL_IN_COMPONENT sdk-overlay diff --git a/stdlib/public/Darwin/AVFoundation/CMakeLists.txt b/stdlib/public/Darwin/AVFoundation/CMakeLists.txt index ad2afdf90eced..e77e6e3618d0b 100644 --- a/stdlib/public/Darwin/AVFoundation/CMakeLists.txt +++ b/stdlib/public/Darwin/AVFoundation/CMakeLists.txt @@ -16,7 +16,7 @@ add_swift_target_library(swiftAVFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYP TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" SWIFT_MODULE_DEPENDS_OSX Darwin CoreImage CoreGraphics Metal Dispatch IOKit simd Foundation CoreMedia QuartzCore XPC CoreFoundation CoreAudio ObjectiveC # auto-updated SWIFT_MODULE_DEPENDS_IOS Darwin CoreGraphics Metal Dispatch simd Foundation CoreMedia QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated SWIFT_MODULE_DEPENDS_TVOS Darwin CoreGraphics Metal Dispatch simd Foundation CoreMedia QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated diff --git a/stdlib/public/Darwin/MediaPlayer/CMakeLists.txt b/stdlib/public/Darwin/MediaPlayer/CMakeLists.txt index 2befab06cdfef..0700ae75b0d6b 100644 --- a/stdlib/public/Darwin/MediaPlayer/CMakeLists.txt +++ b/stdlib/public/Darwin/MediaPlayer/CMakeLists.txt @@ -7,10 +7,11 @@ add_swift_target_library(swiftMediaPlayer ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPE "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" TARGET_SDKS IOS IOS_SIMULATOR SWIFT_MODULE_DEPENDS_IOS Darwin CoreImage CoreGraphics Metal UIKit Dispatch simd Foundation AVFoundation CoreMedia QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated + SWIFT_MODULE_DEPENDS_FROM_SDK CoreMIDI FRAMEWORK_DEPENDS_WEAK MediaPlayer DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_MEDIAPLAYER_IOS} diff --git a/stdlib/public/Darwin/Photos/CMakeLists.txt b/stdlib/public/Darwin/Photos/CMakeLists.txt index e59dc5b2445af..7d8a8aae1e786 100644 --- a/stdlib/public/Darwin/Photos/CMakeLists.txt +++ b/stdlib/public/Darwin/Photos/CMakeLists.txt @@ -8,7 +8,7 @@ add_swift_target_library(swiftPhotos ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" TARGET_SDKS IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR OSX SWIFT_MODULE_DEPENDS_IOS Darwin CoreImage CoreGraphics Metal Dispatch simd Foundation AVFoundation CoreMedia CoreLocation QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated SWIFT_MODULE_DEPENDS_TVOS Darwin CoreImage CoreGraphics Metal Dispatch simd Foundation AVFoundation CoreMedia CoreLocation QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated diff --git a/stdlib/public/Darwin/Vision/CMakeLists.txt b/stdlib/public/Darwin/Vision/CMakeLists.txt index c956ccd3ef2c6..5fd9cb4202f1e 100644 --- a/stdlib/public/Darwin/Vision/CMakeLists.txt +++ b/stdlib/public/Darwin/Vision/CMakeLists.txt @@ -7,7 +7,7 @@ add_swift_target_library(swiftVision ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR SWIFT_MODULE_DEPENDS_OSX Darwin CoreImage CoreGraphics Metal Dispatch IOKit simd Foundation XPC CoreFoundation ObjectiveC # auto-updated SWIFT_MODULE_DEPENDS_IOS Darwin CoreImage CoreGraphics Metal Dispatch simd Foundation CoreFoundation ObjectiveC # auto-updated diff --git a/stdlib/public/Darwin/WatchKit/CMakeLists.txt b/stdlib/public/Darwin/WatchKit/CMakeLists.txt index dbe216c09d61c..37431bd22ceff 100644 --- a/stdlib/public/Darwin/WatchKit/CMakeLists.txt +++ b/stdlib/public/Darwin/WatchKit/CMakeLists.txt @@ -7,7 +7,7 @@ add_swift_target_library(swiftWatchKit ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" TARGET_SDKS WATCHOS WATCHOS_SIMULATOR SWIFT_MODULE_DEPENDS_WATCHOS Darwin HomeKit CoreGraphics UIKit Dispatch SceneKit simd Foundation MapKit CoreLocation CoreFoundation ObjectiveC # auto-updated FRAMEWORK_DEPENDS_WEAK WatchKit From 659a37c1226e8b699dd56a9427b9936305df5c3d Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sun, 10 Nov 2019 20:32:58 -0800 Subject: [PATCH 302/478] Rewrite AliasAnalysis may-release/may-decrement queries. Use the new EscapeAnalysis infrastructure to make ARC code motion and ARC sequence opts much more powerful and fix a latent bug in AliasAnalysis. Adds a new API `EscapeAnalysis::mayReleaseContent()`. This replaces all uses if `EscapeAnalysis::canEscapeValueTo()`, which affects `AliasAnalysis::can[Apply|Builtin]DecrementRefCount()`. Also rewrite `AliasAnalysis::mayValueReleaseInterferWithInstruction` to directly use `EscapeAnalysis::mayReleaseContent`. The new implementation in `EscapeAnalysis::mayReleaseContent()` generalizes the logic to handle more cases while avoiding an incorrect assumption in the prior code. In particular, it adds support for disambiguating local references from accessed addresses. This helps handle cases in which inlining was defeating ARC optimization. The incorrect assumption was that a non-escaping address is never reachable via a reference. However, if a reference does not escape, then an address into its object also does not escape. The bug in `AliasAnalysis::mayValueReleaseInterfereWithInstruction()` appears not to have broken anything yet because it is always called by `AliasAnalysis::mayHaveSymmetricInteference()`, which later checks whether the accessed address may alias with the released reference using a separate query, `EscapeAnalysis::canPointToSameMemory()`. This happens to work because an address into memory that is directly released when destroying a reference necesasarilly points to the same memory object. For this reason, I couldn't figure out a simple way to hand-code SIL tests to expose this bug. The changes in diff order: Replace EscapeAnalysis `canEscapeToValue` with `mayReleaseContent` to make the semantics clear. It queries: "Can the given reference release the content pointed to the given address". Change `AliasAnalysis::canApplyDecrementRefCount` to use `mayReleaseContent` instead if 'canEscapeToValue'. Change `AliasAnalysis::mayValueReleaseInterferWithInstruction`: after getting the memory address accessed by the instruction, simply call `EscapeAnalysis::mayReleaseContent`, which now implements all the logic. This avoids the bad assumption made by AliasAnalysis. Handle two cases in mayReleaseContent: non-escaping instruction addresses and non-escaping referenecs. Fix the non-escaping address case by following all content nodes to determine whether the address is reachable from the released reference. Introduce a new optimization for the case in which the reference being released is allocated locally. The following test case is now optimized in arcsequenceopts.sil: remove_as_local_object_indirectly_escapes_to_callee. It was trying to test that ARC optimization was not too aggressive when it removed a retain/release of a child object whose parent container is still in use. But the retain/release should be removed. The original example already over-releases the parent object. Add new unit tests to late_release_hoisting.sil. --- .../SILOptimizer/Analysis/AliasAnalysis.h | 6 +- .../SILOptimizer/Analysis/EscapeAnalysis.h | 20 +- lib/SILOptimizer/Analysis/AliasAnalysis.cpp | 71 +++--- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 151 ++++++++--- test/SILOptimizer/arcsequenceopts.sil | 46 +++- test/SILOptimizer/escape_analysis.sil | 72 +++--- test/SILOptimizer/late_release_hoisting.sil | 234 ++++++++++++++++++ 7 files changed, 467 insertions(+), 133 deletions(-) create mode 100644 test/SILOptimizer/late_release_hoisting.sil diff --git a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h index fe0837c9549ba..7c743f9ec45b9 100644 --- a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h @@ -194,10 +194,10 @@ class AliasAnalysis : public SILAnalysis { return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::MayAlias; } - /// \returns True if the release of the \p Ptr can access memory accessed by - /// \p User. + /// \returns True if the release of the \p releasedReference can access or + /// free memory accessed by \p User. bool mayValueReleaseInterfereWithInstruction(SILInstruction *User, - SILValue Ptr); + SILValue releasedReference); /// Use the alias analysis to determine the memory behavior of Inst with /// respect to V. diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index 0899435b279a2..aa32d361535e5 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -672,10 +672,10 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// isInterior is always false for non-content nodes and is set for content /// nodes based on the type and origin of the pointer. CGNode *allocNode(ValueBase *V, NodeType Type, bool isInterior, - bool isReference) { + bool hasReferenceOnly) { assert((Type == NodeType::Content) || !isInterior); CGNode *Node = new (NodeAllocator.Allocate()) - CGNode(V, Type, isInterior, isReference); + CGNode(V, Type, isInterior, hasReferenceOnly); Nodes.push_back(Node); return Node; } @@ -871,10 +871,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { template bool forwardTraverseDefer(CGNode *startNode, CGNodeVisitor &&visitor); - /// Return true if \p pointer may indirectly point to \pointee via pointers - /// and object references. - bool mayReach(CGNode *pointer, CGNode *pointee); - public: /// Gets or creates a node for a value \p V. /// If V is a projection(-path) then the base of the projection(-path) is @@ -1164,16 +1160,16 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Note that if \p RI is a retain-instruction always false is returned. bool canEscapeTo(SILValue V, RefCountingInst *RI); - /// Returns true if the value \p V can escape to any other pointer \p To. - /// This means that either \p To is the same as \p V or contains a reference - /// to \p V. - bool canEscapeToValue(SILValue V, SILValue To); + /// Return true if \p releasedReference deinitialization may release memory + /// pointed to by \p accessedAddress. + bool mayReleaseContent(SILValue releasedReference, SILValue accessedAddress); /// Returns true if the pointers \p V1 and \p V2 can possibly point to the /// same memory. /// - /// If at least one of the pointers refers to a local object and the - /// connection-graph-nodes of both pointers do not point to the same content + /// First checks that the pointers are known not to alias outside this + /// function, then checks the connection graph to determine that their content + /// is not in the same points-to chain based on access inside this function. bool canPointToSameMemory(SILValue V1, SILValue V2); /// Invalidate all information in this analysis. diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index a6ab4c4ea36a5..8dece001a856a 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -678,7 +678,7 @@ bool AliasAnalysis::canApplyDecrementRefCount(FullApplySite FAS, SILValue Ptr) { if (ArgEffect.mayRelease()) { // The function may release this argument, so check if the pointer can // escape to it. - if (EA->canEscapeToValue(Ptr, FAS.getArgument(Idx))) + if (EA->mayReleaseContent(FAS.getArgument(Idx), Ptr)) return true; } } @@ -696,54 +696,51 @@ bool AliasAnalysis::canBuiltinDecrementRefCount(BuiltinInst *BI, SILValue Ptr) { // A builtin can only release an object if it can escape to one of the // builtin's arguments. - if (EA->canEscapeToValue(Ptr, Arg)) + if (EA->mayReleaseContent(Arg, Ptr)) return true; } return false; } +// If the deinit for releasedReference can release any values used by User, then +// this is an interference. (The retains that originally forced liveness of +// those values may have already been eliminated). Note that we only care about +// avoiding a dangling pointer. The memory side affects of Release are +// unordered. +// +// \p releasedReference must be a value that directly contains the references +// being released. It cannot be an address or other kind of pointer that +// indirectly releases a reference. Otherwise, the escape analysis query is +// invalid. +bool AliasAnalysis::mayValueReleaseInterfereWithInstruction( + SILInstruction *User, SILValue releasedReference) { + assert(!releasedReference->getType().isAddress() + && "an address is never a reference"); -bool AliasAnalysis::mayValueReleaseInterfereWithInstruction(SILInstruction *User, - SILValue Ptr) { - // TODO: Its important to make this as precise as possible. - // - // TODO: Eventually we can plug in some analysis on the what the release of - // the Ptr can do, i.e. be more precise about Ptr's deinit. - // - // TODO: If we know the specific release instruction, we can potentially do - // more. - // // If this instruction can not read or write any memory. Its OK. if (!User->mayReadOrWriteMemory()) return false; - // These instructions do read or write memory, get memory directly - // accessed. 'V' must be the only memory accessed by User, and it must be - // directly accessed. Any memory indirectly accessed via 'User' may have - // escaped. - SILValue V = getDirectlyAccessedMemory(User); - if (!V) - return true; - - // If the 'User' instruction's memory is uniquely identified and does not - // escape in the local scope, then it can't be accessed by a deinit in the - // local scope. Note that an exclusive argument's content may have escaped in - // the caller, but the argument value itself can't be accessed via aliasing - // references and we know that User doesn't see through any indirection. - if (!isUniquelyIdentified(V)) + // Get a pointer to the memory directly accessed by 'Users' (either via an + // address or heap reference operand). If additional memory may be indirectly + // accessed by 'User', such as via an inout argument, then stop here because + // mayReleaseContent can only reason about one level of memory access. + // + // TODO: Handle @inout arguments by iterating over the apply arguments. For + // each argument find out if any reachable content can be released. This is + // slightly more involved than mayReleaseContent because it needs to check all + // connection graph nodes reachable from accessedPointer that don't pass + // through another stored reference. + SILValue accessedPointer = getDirectlyAccessedMemory(User); + if (!accessedPointer) return true; - // This is a scoped allocation. - // The most important check: does the object escape the current function? - auto LO = getUnderlyingObject(V); - auto *ConGraph = EA->getConnectionGraph(User->getFunction()); - auto *Content = ConGraph->getValueContent(LO); - if (Content && !Content->escapes()) - return false; - - // This is either a non-local allocation or a scoped allocation that escapes. - // We failed to prove anything, it could be read or written by the deinit. - return true; + // If releasedReference can reach the first refcounted object reachable from + // accessedPointer, then releasing it early may destroy the object accessed by + // accessedPointer. Access to any objects beyond the first released refcounted + // object are irrelevant--they must already have sufficient refcount that they + // won't be released when releasing Ptr. + return EA->mayReleaseContent(releasedReference, accessedPointer); } bool swift::isLetPointer(SILValue V) { diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index c6c122164e997..ecadc10197eb4 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -72,7 +72,8 @@ EscapeAnalysis::findRecursivePointerKind(SILType Ty, }; if (auto *Str = Ty.getStructOrBoundGenericStruct()) { for (auto *Field : Str->getStoredProperties()) { - SILType fieldTy = Ty.getFieldType(Field, M, F.getTypeExpansionContext()); + SILType fieldTy = Ty.getFieldType(Field, M, F.getTypeExpansionContext()) + .getObjectType(); meetAggregateKind(findCachedPointerKind(fieldTy, F)); } return aggregateKind; @@ -925,8 +926,10 @@ EscapeAnalysis::ConnectionGraph::getOrCreateReferenceContent(SILValue refVal, if (auto *C = refType.getClassOrBoundGenericClass()) { PointerKind aggregateKind = NoPointer; for (auto *field : C->getStoredProperties()) { - SILType fieldType = refType.getFieldType(field, F->getModule(), - F->getTypeExpansionContext()); + SILType fieldType = refType + .getFieldType(field, F->getModule(), + F->getTypeExpansionContext()) + .getObjectType(); PointerKind fieldKind = EA->findCachedPointerKind(fieldType, *F); if (fieldKind > aggregateKind) aggregateKind = fieldKind; @@ -1161,19 +1164,6 @@ bool EscapeAnalysis::ConnectionGraph::forwardTraverseDefer( return true; } -bool EscapeAnalysis::ConnectionGraph::mayReach(CGNode *pointer, - CGNode *pointee) { - if (pointer == pointee) - return true; - - // This query is successful when the traversal halts and returns false. - return !backwardTraverse(pointee, [pointer](Predecessor pred) { - if (pred.getPredNode() == pointer) - return Traversal::Halt; - return Traversal::Follow; - }); -} - void EscapeAnalysis::ConnectionGraph::removeFromGraph(ValueBase *V) { CGNode *node = Values2Nodes.lookup(V); if (!node) @@ -1193,10 +1183,7 @@ void EscapeAnalysis::ConnectionGraph::removeFromGraph(ValueBase *V) { /// This makes iterating over the edges easier. struct CGForDotView { - enum EdgeTypes { - PointsTo, - Deferred - }; + enum EdgeTypes { PointsTo, Reference, Deferred }; struct Node { EscapeAnalysis::CGNode *OrigNode; @@ -1248,7 +1235,10 @@ CGForDotView::CGForDotView(const EscapeAnalysis::ConnectionGraph *CG) : Nd.OrigNode = OrigNode; if (auto *PT = OrigNode->getPointsToEdge()) { Nd.Children.push_back(Orig2Node[PT]); - Nd.ChildrenTypes.push_back(PointsTo); + if (OrigNode->hasReferenceOnly()) + Nd.ChildrenTypes.push_back(Reference); + else + Nd.ChildrenTypes.push_back(PointsTo); } for (auto *Def : OrigNode->defersTo) { Nd.Children.push_back(Orig2Node[Def]); @@ -1396,8 +1386,12 @@ namespace llvm { const CGForDotView *Graph) { unsigned ChildIdx = I - Node->Children.begin(); switch (Node->ChildrenTypes[ChildIdx]) { - case CGForDotView::PointsTo: return ""; - case CGForDotView::Deferred: return "color=\"gray\""; + case CGForDotView::PointsTo: + return ""; + case CGForDotView::Reference: + return "color=\"green\""; + case CGForDotView::Deferred: + return "color=\"gray\""; } llvm_unreachable("Unhandled CGForDotView in switch."); @@ -2570,24 +2564,6 @@ static SILFunction *getCommonFunction(SILValue V1, SILValue V2) { return F; } -bool EscapeAnalysis::canEscapeToValue(SILValue V, SILValue To) { - if (!isUniquelyIdentified(V)) - return true; - - SILFunction *F = getCommonFunction(V, To); - if (!F) - return true; - auto *ConGraph = getConnectionGraph(F); - - CGNode *valueContent = ConGraph->getValueContent(V); - if (!valueContent) - return true; - CGNode *userContent = ConGraph->getValueContent(To); - if (!userContent) - return true; - return ConGraph->mayReach(userContent, valueContent); -} - bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { // At least one of the values must be a non-escaping local object. bool isUniq1 = isUniquelyIdentified(V1); @@ -2642,6 +2618,99 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { return true; } +// Return true if deinitialization of \p releasedReference may release memory +// directly pointed to by \p accessAddress. +// +// Note that \p accessedAddress could be a reference itself, an address of a +// local/argument that contains a reference, or even a pointer to the middle of +// an object (even if it is an exclusive argument). +// +// This is almost the same as asking "is the content node for accessedAddress +// reachable via releasedReference", with three subtle differences: +// +// (1) A locally referenced object can only be freed when deinitializing +// releasedReference if it is the same object. Indirect references will be kept +// alive by their distinct local references--ARC can't remove those without +// inserting a mark_dependence/end_dependence scope. +// +// (2) the content of exclusive arguments may be indirectly reachable via +// releasedReference, but the exclusive argument must have it's own reference +// count, so cannot be freed via the locally released reference. +// +// (3) Objects may contain raw pointers into themselves or into other +// objects. Any access to the raw pointer is not considered a use of the object +// because that access must be "guarded" by a fix_lifetime or +// mark_dependence/end_dependence that acts as a placeholder. +// +// There are two interesting cases in which a connection graph query can +// determine that the accessed memory cannot be released: +// +// Case #1: accessedAddress points to a uniquely identified object that does not +// escape within this function. +// +// Note: A "uniquely identified object" is either a locally allocated object, +// which is obviously not reachable outside this function, or an exclusive +// address argument, which *is* reachable outside this function, but must +// have its own reference count so cannot be released locally. +// +// Case #2: The released reference points to a local object and no connection +// graph path exists from the referenced object to a global-escaping or +// argument-escaping node without traversing a non-interior edge. +// +// In both cases, the connection graph is sufficient to determine if the +// accessed content may be released. To prove that the accessed memory is +// distinct from any released memory it is now sufficient to check that no +// connection graph path exists from the released object's node to the accessed +// content node without traversing a non-interior edge. +bool EscapeAnalysis::mayReleaseContent(SILValue releasedReference, + SILValue accessedAddress) { + assert(!releasedReference->getType().isAddress() + && "an address is never a reference"); + + SILFunction *f = getCommonFunction(releasedReference, accessedAddress); + if (!f) + return true; + + auto *conGraph = getConnectionGraph(f); + + CGNode *addrContentNode = conGraph->getValueContent(accessedAddress); + if (!addrContentNode) + return true; + + // Case #1: Unique accessedAddress whose content does not escape. + bool isAccessUniq = + isUniquelyIdentified(accessedAddress) + && !addrContentNode->valueEscapesInsideFunction(accessedAddress); + + // Case #2: releasedReference points to a local object. + if (!isAccessUniq && !pointsToLocalObject(releasedReference)) + return true; + + CGNode *releasedObjNode = conGraph->getValueContent(releasedReference); + // Make sure we have at least one value CGNode for releasedReference. + if (!releasedObjNode) + return true; + + // Check for reachability from releasedObjNode to addrContentNode. + // A pointsTo cycle is equivalent to a null pointsTo. + CGNodeWorklist worklist(conGraph); + for (CGNode *releasedNode = releasedObjNode; + releasedNode && worklist.tryPush(releasedNode); + releasedNode = releasedNode->getContentNodeOrNull()) { + // A path exists from released content to accessed content. + if (releasedNode == addrContentNode) + return true; + + // A path exists to an escaping node. + if (!isAccessUniq && releasedNode->escapesInsideFunction()) + return true; + + if (!releasedNode->isInterior()) + break; + } + return false; // no path to escaping memory that may be freed. +} + void EscapeAnalysis::invalidate() { Function2Info.clear(); Allocator.DestroyAll(); diff --git a/test/SILOptimizer/arcsequenceopts.sil b/test/SILOptimizer/arcsequenceopts.sil index 1cc48079154a1..b6e024270a495 100644 --- a/test/SILOptimizer/arcsequenceopts.sil +++ b/test/SILOptimizer/arcsequenceopts.sil @@ -571,10 +571,16 @@ bb0(%0 : $Cls): return %r : $() } -// CHECK-LABEL: sil @dont_remove_as_local_object_indirectly_escapes_to_callee -// CHECK: retain_value -// CHECK: release_value -sil @dont_remove_as_local_object_indirectly_escapes_to_callee : $@convention(thin) (Cls) -> () { +// Remove a retain release "pair" for the local reference if a child object. +// +// The local 'Cls' reference is now effectively "moved" into the parent +// 'Container' object, so the child will be destroyed with the parent. +// The original SIL already has an over-release of the parent. +// +// CHECK-LABEL: sil @remove_as_local_object_indirectly_escapes_to_callee +// CHECK-NOT: retain_value +// CHECK-NOT: release_value +sil @remove_as_local_object_indirectly_escapes_to_callee : $@convention(thin) (Cls) -> () { bb0(%0 : $Cls): %1 = alloc_ref $Cls %2 = alloc_ref $Container @@ -589,6 +595,30 @@ bb0(%0 : $Cls): return %r : $() } +// Remove a retain release "pair" for the local reference if a child object. +// +// The local 'Cls' reference is now effectively "moved" into the parent +// 'Container' object, so the child will be destroyed with the parent. +// +// CHECK-LABEL: sil @local_object_indirectly_escapes_to_use_and_owned_arg +// CHECK-NOT: retain_value +// CHECK-NOT: release_value +sil @local_object_indirectly_escapes_to_use_and_owned_arg : $@convention(thin) (Cls) -> () { +bb0(%0 : $Cls): + %1 = alloc_ref $Cls + %2 = alloc_ref $Container + %3 = ref_element_addr %2 : $Container, #Container.c + store %1 to %3 : $*Cls + retain_value %1 : $Cls + %f1 = function_ref @use_container : $@convention(thin) (@guaranteed Container) -> () + apply %f1 (%2) : $@convention(thin) (@guaranteed Container) -> () + %f2 = function_ref @release_container : $@convention(thin) (Container) -> () + apply %f2 (%2) : $@convention(thin) (Container) -> () + release_value %1 : $Cls + %r = tuple() + return %r : $() +} + sil @release_arg1 : $@convention(thin) (Cls, Cls) -> () { bb0(%0 : $Cls, %1 : $Cls): strong_release %1 : $Cls @@ -596,6 +626,14 @@ bb0(%0 : $Cls, %1 : $Cls): return %r : $() } +sil @use_container : $@convention(thin) (@guaranteed Container) -> () { +bb0(%0 : $Container): + %1 = ref_element_addr %0 : $Container, #Container.c + %2 = load %1 : $*Cls + %r = tuple() + return %r : $() +} + sil @release_container : $@convention(thin) (Container) -> () { bb0(%0 : $Container): strong_release %0 : $Container diff --git a/test/SILOptimizer/escape_analysis.sil b/test/SILOptimizer/escape_analysis.sil index 13e43a122f4db..e137f3ab5515c 100644 --- a/test/SILOptimizer/escape_analysis.sil +++ b/test/SILOptimizer/escape_analysis.sil @@ -100,7 +100,7 @@ bb0(%0 : $Int): // CHECK-NEXT: Con %1.1 Esc: G, Succ: // CHECK-NEXT: Val [ref] %2 Esc: A, Succ: (%3) // CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: A, Succ: %1 +// CHECK-NEXT: Con [ref] %3.1 Esc: A, Succ: %1 // CHECK-NEXT: Con [ref] %5 Esc: A, Succ: %2 // CHECK-NEXT: End sil @test_simple : $@convention(thin) (@inout Y, @owned X) -> () { @@ -122,7 +122,7 @@ bb0(%0 : $*Y, %1 : $X): // CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: // CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%4), %0 // CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: A, Succ: %1 +// CHECK-NEXT: Con [ref] %4.1 Esc: A, Succ: %1 // CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @deferringEdge : $@convention(thin) (@owned LinkedNode, @owned LinkedNode) -> @owned LinkedNode { @@ -158,7 +158,7 @@ bb0: // CHECK-NEXT: Val [ref] %4 Esc: A, Succ: (%2) // CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%2) // CHECK-NEXT: Val [ref] %11 Esc: , Succ: (%2), %7, %13 -// CHECK-NEXT: Con %13 Esc: A, Succ: %0, %1, %4 +// CHECK-NEXT: Con [ref] %13 Esc: A, Succ: %0, %1, %4 // CHECK-NEXT: Ret [ref] return Esc: , Succ: %13 // CHECK-NEXT: End sil @test_linked_list : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { @@ -192,7 +192,7 @@ bb2: // CHECK-NEXT: Val [ref] %4 Esc: A, Succ: (%8) // CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%8) // CHECK-NEXT: Con [int] %8 Esc: A, Succ: (%8.1) -// CHECK-NEXT: Con %8.1 Esc: A, Succ: %0, %1, %4 +// CHECK-NEXT: Con [ref] %8.1 Esc: A, Succ: %0, %1, %4 // CHECK-NEXT: Val [ref] %11 Esc: , Succ: (%8), %8.1 // CHECK-NEXT: Ret [ref] return Esc: , Succ: %11 // CHECK-NEXT: End @@ -217,7 +217,7 @@ bb0(%0 : $LinkedNode): // CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%3) // CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%3), %0, %4 // CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %4 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [ref] %4 Esc: A, Succ: (%3) // CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @loadNext : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { @@ -238,14 +238,14 @@ bb2: // CHECK-LABEL: CG of call_load_next3 // CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) // CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: (%0.3) // CHECK-NEXT: Con [int] %0.3 Esc: A, Succ: (%0.4) -// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%0.5) +// CHECK-NEXT: Con [ref] %0.4 Esc: A, Succ: (%0.5) // CHECK-NEXT: Con [int] %0.5 Esc: A, Succ: (%0.6) -// CHECK-NEXT: Con %0.6 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %0.6 Esc: A, Succ: // CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1), %0.6 // CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %2.2 Esc: A, Succ: // CHECK-NEXT: Ret [ref] return Esc: , Succ: %2 // CHECK-NEXT: End sil @call_load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { @@ -258,13 +258,13 @@ bb0(%0 : $LinkedNode): // CHECK-LABEL: CG of load_next3 // CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) // CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [ref] %2 Esc: A, Succ: (%3) // CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %4 Esc: A, Succ: (%5) +// CHECK-NEXT: Con [ref] %4 Esc: A, Succ: (%5) // CHECK-NEXT: Con [int] %5 Esc: A, Succ: (%6) -// CHECK-NEXT: Con %6 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %6 Esc: A, Succ: // CHECK-NEXT: Con [int] %6.1 Esc: A, Succ: (%6.2) -// CHECK-NEXT: Con %6.2 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %6.2 Esc: A, Succ: // CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { @@ -593,10 +593,10 @@ sil_global @global_ln : $LinkedNode // CHECK-LABEL: CG of load_next_recursive // CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) // CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: G, Succ: +// CHECK-NEXT: Con [ref] %2 Esc: G, Succ: // CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1), %2 // CHECK-NEXT: Con [int] %4.1 Esc: G, Succ: (%4.2) -// CHECK-NEXT: Con %4.2 Esc: G, Succ: +// CHECK-NEXT: Con [ref] %4.2 Esc: G, Succ: // CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @load_next_recursive : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { @@ -611,7 +611,7 @@ bb0(%0 : $LinkedNode): // CHECK-LABEL: CG of let_escape // CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: (%0.1) // CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: // CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) // CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %0 // CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%0.1), %0 @@ -631,7 +631,7 @@ bb0(%0 : $LinkedNode): // CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%5.1), %5.2 // CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%5.1), %0, %3 // CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2) -// CHECK-NEXT: Con %5.2 Esc: G, Succ: (%5.1) +// CHECK-NEXT: Con [ref] %5.2 Esc: G, Succ: (%5.1) // CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @return_same : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { @@ -652,12 +652,12 @@ bb2(%5 : $LinkedNode): // CHECK-LABEL: CG of loadNext2 // CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) // CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [ref] %2 Esc: A, Succ: (%2.1) // CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con [ref] %2.2 Esc: A, Succ: (%4.1) // CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1), %2.2, %4.2 // CHECK-NEXT: Con [int] %4.1 Esc: A, Succ: (%4.2) -// CHECK-NEXT: Con %4.2 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con [ref] %4.2 Esc: A, Succ: (%4.1) // CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @loadNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { @@ -673,10 +673,10 @@ bb0(%0 : $LinkedNode): // CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%5) // CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%8.1), %8.2 // CHECK-NEXT: Con [int] %5 Esc: A, Succ: (%6) -// CHECK-NEXT: Con %6 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Con [ref] %6 Esc: A, Succ: (%8.1) // CHECK-NEXT: Val [ref] %8 Esc: , Succ: (%8.1), %3, %6 // CHECK-NEXT: Con [int] %8.1 Esc: A, Succ: (%8.2) -// CHECK-NEXT: Con %8.2 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Con [ref] %8.2 Esc: A, Succ: (%8.1) // CHECK-NEXT: Ret [ref] return Esc: , Succ: %8 // CHECK-NEXT: End sil @returnNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { @@ -702,7 +702,7 @@ bb3(%8 : $LinkedNode): // CHECK-LABEL: CG of single_cycle_recursion // CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) // CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [ref] %3 Esc: A, Succ: (%2) // CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%2), %3 // CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%2), %0, %5 // CHECK-NEXT: Ret [ref] return Esc: , Succ: %7 @@ -823,7 +823,7 @@ sil @take_y_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () // CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: // CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%3) // CHECK-NEXT: Con [int] %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: %0 +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @store_to_unknown_reference : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -838,7 +838,7 @@ bb0(%0 : $X): // CHECK-LABEL: CG of get_reference // CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1) // CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Con [ref] %1.2 Esc: G, Succ: // CHECK-NEXT: Ret [ref] return Esc: , Succ: %1 // CHECK-NEXT: End sil @get_reference : $@convention(thin) () -> @owned Y { @@ -858,7 +858,7 @@ sil @unknown_set_y : $@convention(thin) () -> @out Y // CHECK-NEXT: Val %0 Esc: , Succ: (%3) // CHECK-NEXT: Con [ref] %3 Esc: G, Succ: // CHECK-NEXT: Con [int] %3.1 Esc: G, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: G, Succ: +// CHECK-NEXT: Con [ref] %3.2 Esc: G, Succ: // CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @get_y : $@convention(thin) () -> @owned Y { @@ -875,7 +875,7 @@ bb0: // CHECK-NEXT: Val [ref] %0 Esc: G, Succ: // CHECK-NEXT: Val [ref] %2 Esc: G, Succ: (%3) // CHECK-NEXT: Con [int] %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: %0 +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @create_and_store_x : $@convention(thin) () -> () { bb0: @@ -919,7 +919,7 @@ bb1(%7 : $(Pointer, Pointer)): // CHECK-NEXT: Con [ref] %2 Esc: A, Succ: (%6), %4 // CHECK-NEXT: Con [ref] %4 Esc: A, Succ: %2 // CHECK-NEXT: Con [int] %6 Esc: A, Succ: (%7) -// CHECK-NEXT: Con %7 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %7 Esc: A, Succ: // CHECK-NEXT: End sil @defer_edge_cycle : $@convention(thin) (@inout Y, @inout Y) -> () { entry(%0 : $*Y, %1 : $*Y): @@ -1457,7 +1457,7 @@ bb0: // CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_strong_release // CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) // CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @check_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () { bb0: @@ -1474,7 +1474,7 @@ bb0: // CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_release_value // CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) // CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @check_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () { bb0: @@ -1544,7 +1544,7 @@ bb0(%0 : $X): // CHECK-LABEL: CG of $s4main1ZCfD // CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) // CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: G, Succ: +// CHECK-NEXT: Con [ref] %2 Esc: G, Succ: // CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) // CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: %2 // CHECK-NEXT: End @@ -1608,7 +1608,7 @@ bb2: // CHECK-LABEL: CG of call_coroutine // CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) // CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: // CHECK-NEXT: Con %0.3 Esc: G, Succ: // CHECK-NEXT: Val %2 Esc: , Succ: (%2.1) // CHECK-NEXT: Con [ref] %2.1 Esc: G, Succ: %4 @@ -1631,13 +1631,13 @@ bb0: // CHECK-LABEL: CG of testInitializePointsToLeaf // CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) // CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%13) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: (%13) // CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%13), %0.2 // CHECK-NEXT: Val [ref] %4 Esc: , Succ: %2 // CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%13), %0.2 // CHECK-NEXT: Val [ref] %12 Esc: , Succ: (%13), %7 // CHECK-NEXT: Con [int] %13 Esc: A, Succ: (%14) -// CHECK-NEXT: Con %14 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %14 Esc: A, Succ: // CHECK-LABEL: End class C { var c: C @@ -1688,7 +1688,7 @@ bb10(%arg2 : $LinkedNode): // CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) // CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%2) // CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %3 Esc: A, Succ: // CHECK-NEXT: Val [ref] %7 Esc: , Succ: %0 // CHECK-NEXT: Val [ref] %12 Esc: , Succ: %1 // CHECK-NEXT: Val [ref] %14 Esc: , Succ: (%2), %1, %12 @@ -1833,7 +1833,7 @@ sil [_semantics "array.uninitialized"] @init_any_array_with_buffer : $@conventio // CHECK-LABEL: CG of testBadArrayUninit // CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1) // CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: G, Succ: +// CHECK-NEXT: Con [ref] %2.2 Esc: G, Succ: // CHECK-NEXT: Val %5 Esc: , Succ: (%5.1) // CHECK-NEXT: Con %5.1 Esc: G, Succ: %10 // CHECK-NEXT: Val [ref] %10 Esc: G, Succ: (%10.1) diff --git a/test/SILOptimizer/late_release_hoisting.sil b/test/SILOptimizer/late_release_hoisting.sil new file mode 100644 index 0000000000000..9fd9653b12c26 --- /dev/null +++ b/test/SILOptimizer/late_release_hoisting.sil @@ -0,0 +1,234 @@ +// RUN: %target-sil-opt -enable-sil-verify-all -late-release-hoisting %s | %FileCheck %s + +import Builtin +import Swift + +sil_stage canonical + +//===----------------------------------------------------------------------===// +// Unit tests for reachability to and from local objects. +//===----------------------------------------------------------------------===// + +class HasObj { + var o : AnyObject +} + +class HasInt64 { + var i : Int64 +} + +class HasHasObj { + var ho : HasObj +} + +// The release of %lo can be hoisted over the load because it does not +// point to any escaping references. +// +// CHECK-LABEL: sil @testLocalNotReachesEscaped : $@convention(thin) (Int64, @owned HasObj) -> AnyObject { +// CHECK: bb0(%0 : $Int64, %1 : $HasObj): +// CHECK: [[LO:%.*]] = alloc_ref $HasInt +// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasInt64, #HasInt64.i +// CHECK: store %0 to [[IADR]] : $*Int64 +// CHECK: strong_release [[LO]] : $HasInt +// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalNotReachesEscaped' +sil @testLocalNotReachesEscaped : $@convention(thin) (Int64, @owned HasObj) -> AnyObject { +bb0(%0 : $Int64, %1 : $HasObj): + %lo = alloc_ref $HasInt64 + %iadr = ref_element_addr %lo : $HasInt64, #HasInt64.i + store %0 to %iadr : $*Int64 + + %oadr = ref_element_addr %1 : $HasObj, #HasObj.o + %o = load %oadr : $*AnyObject + strong_release %lo : $HasInt64 + return %o : $AnyObject +} + +// The release of %lo cannot be hoisted over the load. +// +// CHECK-LABEL: sil @testLocalReachesEscaped : $@convention(thin) (@owned AnyObject) -> AnyObject { +// CHECK: bb0(%0 : $AnyObject): +// CHECK: [[LO:%.*]] = alloc_ref $HasObj +// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasObj, #HasObj.o +// CHECK: store %0 to [[IADR]] : $*AnyObject +// CHECK: [[OADR:%.*]] = ref_element_addr [[LO]] : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject +// CHECK: strong_release [[LO]] : $HasObj +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalReachesEscaped' +sil @testLocalReachesEscaped : $@convention(thin) (@owned AnyObject) -> AnyObject { +bb0(%0 : $AnyObject): + %lo = alloc_ref $HasObj + %iadr = ref_element_addr %lo : $HasObj, #HasObj.o + store %0 to %iadr : $*AnyObject + + %oadr = ref_element_addr %lo : $HasObj, #HasObj.o + %o = load %oadr : $*AnyObject + strong_release %lo : $HasObj + return %o : $AnyObject +} + +// The release of %lo can be hoisted above the load from %oadr. There +// is a points-to relation between %lo and %oadr. However, the +// points-to path from %lo to %oadr reaches another reference counted +// object before reaching $oadr. +// +// CHECK-LABEL: sil @testLocalReachesRCEscaped : $@convention(thin) (@owned HasObj) -> AnyObject { +// CHECK: bb0(%0 : $HasObj): +// CHECK: [[LO:%.*]] = alloc_ref $HasHasObj +// CHECK: [[HOADR:%.*]] = ref_element_addr [[LO]] : $HasHasObj, #HasHasObj.ho +// CHECK: strong_retain %0 : $HasObj +// CHECK: store %0 to [[HOADR]] : $*HasObj +// CHECK: [[HO:%.*]] = load [[HOADR]] : $*HasObj +// CHECK: strong_release [[LO]] : $HasHasObj +// CHECK: [[OADR:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalReachesRCEscaped' +sil @testLocalReachesRCEscaped : $@convention(thin) (@owned HasObj) -> AnyObject { +bb0(%0 : $HasObj): + %lo = alloc_ref $HasHasObj + %hoadr = ref_element_addr %lo : $HasHasObj, #HasHasObj.ho + strong_retain %0 : $HasObj + store %0 to %hoadr : $*HasObj + %ho = load %hoadr : $*HasObj + %oadr = ref_element_addr %ho : $HasObj, #HasObj.o + %o = load %oadr : $*AnyObject + // Normally a retain of %o would precede this release of %c. But + // let's assume some aggressive optimization happened. + strong_release %lo : $HasHasObj + return %o : $AnyObject +} + +// Two local references, one reachable from the other. +// +// TODO: We do not currently hoist strong_release %hho above +// strong_retain %o because %hho is marked as escaping. However, it is +// only stored to another local object, so in theory it does not need +// to be marked escaping. +// +// CHECK-LABEL: sil @testLocalReachesEscapingLocal : $@convention(thin) (AnyObject) -> AnyObject { +// CHECK: bb0(%0 : $AnyObject): +// CHECK: [[LO:%.*]] = alloc_ref $HasObj +// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o +// CHECK: strong_retain %0 : $AnyObject +// CHECK: store %0 to [[OADR]] : $*AnyObject +// CHECK: [[HHO:%.*]] = alloc_ref $HasHasObj +// CHECK: [[HOADR:%.*]] = ref_element_addr [[HHO]] : $HasHasObj, #HasHasObj.ho +// CHECK: store [[LO]] to [[HOADR]] : $*HasObj +// CHECK: [[HO:%.*]] = load [[HOADR]] : $*HasObj +// CHECK: [[OADR2:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR2]] : $*AnyObject +// CHECK: strong_retain [[O]] : $AnyObject +// CHECK: strong_release [[HHO]] : $HasHasObj +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalReachesEscapingLocal' +sil @testLocalReachesEscapingLocal : $@convention(thin) (AnyObject) -> AnyObject { +bb0(%0 : $AnyObject): + %ho = alloc_ref $HasObj + %oadr = ref_element_addr %ho : $HasObj, #HasObj.o + strong_retain %0 : $AnyObject + store %0 to %oadr : $*AnyObject + + %hho = alloc_ref $HasHasObj + %hoadr = ref_element_addr %hho : $HasHasObj, #HasHasObj.ho + store %ho to %hoadr : $*HasObj + + %ho2 = load %hoadr : $*HasObj + %oadr2 = ref_element_addr %ho2 : $HasObj, #HasObj.o + %o = load %oadr2 : $*AnyObject + strong_retain %o : $AnyObject + strong_release %hho : $HasHasObj + return %o : $AnyObject +} + +// Two local references, one reachable from the other. We assume that +// the reachable one has its own reference count and hoist +// strong_release %hho above load %oadr. +// +// CHECK-LABEL: sil @testLocalReachesRCLocal : $@convention(thin) (AnyObject) -> AnyObject { +// CHECK: bb0(%0 : $AnyObject): +// CHECK: [[LO:%.*]] = alloc_ref $HasObj +// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o +// CHECK: strong_retain %0 : $AnyObject +// CHECK: store %0 to [[OADR]] : $*AnyObject +// CHECK: [[HHO:%.*]] = alloc_ref $HasHasObj +// CHECK: [[HOADR:%.*]] = ref_element_addr [[HHO]] : $HasHasObj, #HasHasObj.ho +// CHECK: store [[LO]] to [[HOADR]] : $*HasObj +// CHECK: [[HO:%.*]] = load [[HOADR]] : $*HasObj +// CHECK: strong_release [[HHO]] : $HasHasObj +// CHECK: [[OADR2:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR2]] : $*AnyObject +// CHECK: strong_retain [[O]] : $AnyObject +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalReachesRCLocal' +sil @testLocalReachesRCLocal : $@convention(thin) (AnyObject) -> AnyObject { +bb0(%0 : $AnyObject): + %ho = alloc_ref $HasObj + %oadr = ref_element_addr %ho : $HasObj, #HasObj.o + strong_retain %0 : $AnyObject + store %0 to %oadr : $*AnyObject + + %hho = alloc_ref $HasHasObj + %hoadr = ref_element_addr %hho : $HasHasObj, #HasHasObj.ho + store %ho to %hoadr : $*HasObj + + %ho2 = load %hoadr : $*HasObj + %oadr2 = ref_element_addr %ho2 : $HasObj, #HasObj.o + %o = load %oadr2 : $*AnyObject + strong_release %hho : $HasHasObj + strong_retain %o : $AnyObject + return %o : $AnyObject +} + +// Hoist the release of an escaping object above memory operations on +// a local object that never escapes. +// +// CHECK-LABEL: sil @testEscapeNotReachesLocal : $@convention(thin) (Int64, @owned HasObj) -> Int64 { +// CHECK: bb0(%0 : $Int64, %1 : $HasObj): +// CHECK: strong_release %1 : $HasObj +// CHECK: [[LO:%.*]] = alloc_ref $HasInt64 +// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasInt64, #HasInt64.i +// CHECK: store %0 to [[IADR]] : $*Int64 +// CHECK: [[I:%.*]] = load [[IADR]] : $*Int64 +// CHECK: return [[I]] : $Int64 +// CHECK-LABEL: } // end sil function 'testEscapeNotReachesLocal' +sil @testEscapeNotReachesLocal : $@convention(thin) (Int64, @owned HasObj) -> Int64 { +bb0(%0 : $Int64, %1 : $HasObj): + %lo = alloc_ref $HasInt64 + %iadr = ref_element_addr %lo : $HasInt64, #HasInt64.i + store %0 to %iadr : $*Int64 + %i = load %iadr : $*Int64 + strong_release %1 : $HasObj + return %i : $Int64 +} + +// EscapeAnalysis must consider the local object %lo as globally +// escaping because it is stored into an object that escapes via an +// incoming argument. This creates an aliasing relationship preventing +// the strong_release from being hoisted. +// +// CHECK-LABEL: sil @testEscapeReachesLocal : $@convention(thin) (@owned AnyObject, @owned HasHasObj) -> AnyObject { +// CHECK: bb0(%0 : $AnyObject, %1 : $HasHasObj): +// CHECK: [[LO:%.*]] = alloc_ref $HasObj +// CHECK: [[OADR:%.*]] = ref_element_addr [[LO]] : $HasObj, #HasObj.o +// CHECK: store %0 to [[OADR]] : $*AnyObject +// CHECK: [[HOADR:%.*]] = ref_element_addr %1 : $HasHasObj, #HasHasObj.ho +// CHECK: store [[LO]] to [[HOADR]] : $*HasObj +// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject +// CHECK: strong_release %1 : $HasHasObj +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testEscapeReachesLocal' +sil @testEscapeReachesLocal : $@convention(thin) (@owned AnyObject, @owned HasHasObj) -> AnyObject { +bb0(%0 : $AnyObject, %1 : $HasHasObj): + %lo = alloc_ref $HasObj + %oadr = ref_element_addr %lo : $HasObj, #HasObj.o + store %0 to %oadr : $*AnyObject + %hoadr = ref_element_addr %1 : $HasHasObj, #HasHasObj.ho + store %lo to %hoadr : $*HasObj + %o = load %oadr : $*AnyObject + strong_release %1 : $HasHasObj + return %o : $AnyObject +} From d7856914806bf00403e012c2dbdd22e8cf8c0282 Mon Sep 17 00:00:00 2001 From: futurejones Date: Tue, 7 Jan 2020 22:49:36 +0900 Subject: [PATCH 303/478] change cmark repo to swift-5.1-branch --- utils/update_checkout/update-checkout-config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index 071e45d4ab152..476432e55bffa 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -228,7 +228,7 @@ "repos": { "llvm-project": "swift/swift-5.1-branch", "swift": "swift-5.1-branch", - "cmark": "master", + "cmark": "swift-5.1-branch", "llbuild": "swift-5.1-branch", "swiftpm": "swift-5.1-branch", "swift-syntax": "swift-5.1-branch", From b5c02ee9e82a47c18923845d9b976966c8b5edc0 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 3 Jan 2020 12:03:50 -0800 Subject: [PATCH 304/478] build: simplify version tracking logic --- lib/Basic/CMakeLists.txt | 58 ++++++++++++---------------------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt index 6cd7577ce3f93..fd60da8ff5379 100644 --- a/lib/Basic/CMakeLists.txt +++ b/lib/Basic/CMakeLists.txt @@ -11,59 +11,32 @@ else() set(UUID_INCLUDE "${UUID_INCLUDE_DIRS}") endif() -# Figure out if we can track VC revisions. -# FIXME: Copied from Clang. -function(find_first_existing_file out_var) - foreach(file ${ARGN}) - if(EXISTS "${file}") - set(${out_var} "${file}" PARENT_SCOPE) - return() - endif() - endforeach() -endfunction() +function(generate_revision_inc revision_inc_var name dir) + find_first_existing_vc_file("${dir}" ${name}_vc) -macro(find_first_existing_vc_file out_var path) - find_first_existing_file(${out_var} - "${path}/.git/logs/HEAD" # Git - "${path}/.svn/wc.db" # SVN 1.7 - "${path}/.svn/entries" # SVN 1.6 - ) -endmacro() + # Create custom target to generate the VC revision include. + set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") -set(generate_vcs_version_script "${LLVM_MAIN_SRC_DIR}/cmake/modules/GenerateVersionFromVCS.cmake") + set(generate_vcs_version_script "${LLVM_MAIN_SRC_DIR}/cmake/modules/GenerateVersionFromVCS.cmake") -function(generate_revision_inc revision_inc_var name dir) - find_first_existing_vc_file(dep_file "${dir}") - # Create custom target to generate the VC revision include. - set(revision_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") - string(TOUPPER ${name} upper_name) - if(DEFINED dep_file) - add_custom_command(OUTPUT "${revision_inc}" - DEPENDS "${dep_file}" "${generate_vcs_version_script}" - COMMAND - ${CMAKE_COMMAND} "-DNAMES=${upper_name}" - "-D${upper_name}_SOURCE_DIR=${dir}" - "-DHEADER_FILE=${revision_inc}" - -P "${generate_vcs_version_script}") - else() - # Generate an empty Revision.inc file if we are not using git or SVN. - file(WRITE "${revision_inc}" "") - endif() + add_custom_command(OUTPUT "${version_inc}" + DEPENDS "${${name}_vc}" "${generate_vcs_version_script}" + COMMAND ${CMAKE_COMMAND} "-DNAMES=$" + "-D$_SOURCE_DIR=${dir}" + "-DHEADER_FILE=${version_inc}" + -P "${generate_vcs_version_script}") # Mark the generated header as being generated. - set_source_files_properties("${revision_inc}" + set_source_files_properties("${version_inc}" PROPERTIES GENERATED TRUE HEADER_FILE_ONLY TRUE) - set(${revision_inc_var} ${revision_inc} PARENT_SCOPE) + set(${revision_inc_var} ${version_inc} PARENT_SCOPE) endfunction() generate_revision_inc(llvm_revision_inc LLVM "${LLVM_MAIN_SRC_DIR}") generate_revision_inc(clang_revision_inc Clang "${CLANG_MAIN_SRC_DIR}") generate_revision_inc(swift_revision_inc Swift "${SWIFT_SOURCE_DIR}") -set(version_inc_files - ${llvm_revision_inc} ${clang_revision_inc} ${swift_revision_inc}) - add_swift_host_library(swiftBasic STATIC AnyValue.cpp Cache.cpp @@ -95,7 +68,10 @@ add_swift_host_library(swiftBasic STATIC Unicode.cpp UUID.cpp Version.cpp - ${version_inc_files} + + ${llvm_revision_inc} + ${clang_revision_inc} + ${swift_revision_inc} # Platform-specific TaskQueue implementations Unix/TaskQueue.inc From e19ef090b4ee909fac5c459ce850c0f3a3a58b6b Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 7 Jan 2020 09:11:32 -0800 Subject: [PATCH 305/478] [ConstraintSystem] Add a new locator element to denote branches of ternary operator `TernaryBranch` with a boolean flag to identify "then" (true) or "else" (false) branches of the ternary operator expression. --- lib/Sema/ConstraintLocator.cpp | 14 +++++++++++--- lib/Sema/ConstraintLocator.h | 15 +++++++++++++++ lib/Sema/ConstraintLocatorPathElts.def | 3 +++ lib/Sema/ConstraintSystem.cpp | 9 +++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index cfd69c73e988c..bba1582f764df 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -61,7 +61,8 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor, case ConditionalRequirement: case TypeParameterRequirement: case ContextualType: - case SynthesizedArgument: { + case SynthesizedArgument: + case TernaryBranch: { auto numValues = numNumericValuesInPathElement(elt.getKind()); for (unsigned i = 0; i < numValues; ++i) id.AddInteger(elt.getValue(i)); @@ -69,8 +70,8 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor, } #define SIMPLE_LOCATOR_PATH_ELT(Name) case Name : #include "ConstraintLocatorPathElts.def" - // Nothing to do for simple locator elements. - break; + // Nothing to do for simple locator elements. + break; } } } @@ -115,6 +116,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::Condition: case ConstraintLocator::DynamicCallable: case ConstraintLocator::ImplicitCallAsFunction: + case ConstraintLocator::TernaryBranch: return 0; case ConstraintLocator::FunctionArgument: @@ -463,6 +465,12 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { case ImplicitCallAsFunction: out << "implicit reference to callAsFunction"; break; + + case TernaryBranch: + auto branchElt = elt.castTo(); + out << (branchElt.forThen() ? "'then'" : "'else'") + << " branch of a ternary operator"; + break; } } out << ']'; diff --git a/lib/Sema/ConstraintLocator.h b/lib/Sema/ConstraintLocator.h index 9939a878c52b3..e7cfd9eaf2132 100644 --- a/lib/Sema/ConstraintLocator.h +++ b/lib/Sema/ConstraintLocator.h @@ -78,6 +78,7 @@ class ConstraintLocator : public llvm::FoldingSetNode { case KeyPathComponent: case SynthesizedArgument: case KeyPathDynamicMember: + case TernaryBranch: return 1; case TypeParameterRequirement: @@ -782,6 +783,20 @@ class LocatorPathElt::KeyPathDynamicMember final : public LocatorPathElt { } }; +class LocatorPathElt::TernaryBranch final : public LocatorPathElt { +public: + TernaryBranch(bool side) + : LocatorPathElt(ConstraintLocator::TernaryBranch, side) {} + + bool forThen() const { return bool(getValue(0)); } + + bool forElse() const { return !bool(getValue(0)); } + + static bool classof(const LocatorPathElt *elt) { + return elt->getKind() == ConstraintLocator::TernaryBranch; + } +}; + /// A simple stack-only builder object that constructs a /// constraint locator without allocating memory. /// diff --git a/lib/Sema/ConstraintLocatorPathElts.def b/lib/Sema/ConstraintLocatorPathElts.def index 5342ce5edef33..54912bbfa9953 100644 --- a/lib/Sema/ConstraintLocatorPathElts.def +++ b/lib/Sema/ConstraintLocatorPathElts.def @@ -172,6 +172,9 @@ SIMPLE_LOCATOR_PATH_ELT(Condition) SIMPLE_LOCATOR_PATH_ELT(DynamicCallable) +/// The 'true' or 'false' branch of a ternary operator. +CUSTOM_LOCATOR_PATH_ELT(TernaryBranch) + #undef LOCATOR_PATH_ELT #undef CUSTOM_LOCATOR_PATH_ELT #undef SIMPLE_LOCATOR_PATH_ELT diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 2977ffa3737e6..b3d6a4cd47c21 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3313,6 +3313,15 @@ void constraints::simplifyLocator(Expr *&anchor, continue; } + case ConstraintLocator::TernaryBranch: { + auto branch = path[0].castTo(); + auto *ifExpr = cast(anchor); + + anchor = branch.forThen() ? ifExpr->getThenExpr() : ifExpr->getElseExpr(); + path = path.slice(1); + continue; + } + default: // FIXME: Lots of other cases to handle. break; From 8c2854f8f2181a7ae385dcc52afd4638d384e5e0 Mon Sep 17 00:00:00 2001 From: yostane Date: Tue, 7 Jan 2020 19:03:43 +0100 Subject: [PATCH 306/478] Added more details to "Building Swift on Windows" (#28496) * Added more details to "Building Swift on Windows" --- docs/WindowsBuild.md | 53 ++++++++++++++++++++++++++----------- docs/visual-studio-cmd.png | Bin 0 -> 63763 bytes 2 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 docs/visual-studio-cmd.png diff --git a/docs/WindowsBuild.md b/docs/WindowsBuild.md index ab8c0afa3af0e..0fb39e7d87c07 100644 --- a/docs/WindowsBuild.md +++ b/docs/WindowsBuild.md @@ -1,19 +1,25 @@ # Building Swift on Windows -Visual Studio 2017 or newer is needed to build swift on Windows. The following must take place in the developer command prompt (provided by Visual Studio). This shows up as "x64 Native Tools Command Prompt for VS2017" (or VS2019, VS2019 Preview depending on the Visual Studio that you are using) in the Start Menu. +Visual Studio 2017 or newer is needed to build swift on Windows. + +The following must take place in the **developer command prompt** (provided by Visual Studio). This shows up as "x64 Native Tools Command Prompt for VS2017" (or VS2019, VS2019 Preview depending on the Visual Studio that you are using) in the Start Menu. ## Install dependencies + - Install the latest version of [Visual Studio](https://www.visualstudio.com/downloads/) -- Make sure to include "Programming Languages|Visual C++" and "Windows and Web Development|Universal Windows App Development|Windows SDK" in your installation. The following components are required: +- Make sure to include "Programming Languages|Visual C++" and "Windows and Web Development|Universal Windows App Development|Windows SDK" in your installation. The following components are required: 1. Microsoft.VisualStudio.Component.Windows10SDK -1. Microsoft.VisualStudio.Component.Windows10SDK.17763 -1. Microsoft.VisualStudio.Component.VC.Tools.x86.x64 +2. Microsoft.VisualStudio.Component.Windows10SDK.17763 +3. Microsoft.VisualStudio.Component.VC.Tools.x86.x64 + +The following [link](https://docs.microsoft.com/visualstudio/install/workload-component-id-vs-build-tools?view=vs-2019)) helps in finding the component name given its ID for Visual Studio 2019. ## Clone the repositories + 1. Clone `apple/llvm-project` into a directory for the toolchain -1. Clone `apple/swift-cmark`, `apple/swift`, `apple/swift-corelibs-libdispatch`, `apple/swift-corelibs-foundation`, `apple/swift-corelibs-xctest`, `apple/swift-llbuild`, `apple/swift-package-manager` into the toolchain directory -1. Clone `compnerd/windows-swift` as a peer of the toolchain directory +2. Clone `apple/swift-cmark`, `apple/swift`, `apple/swift-corelibs-libdispatch`, `apple/swift-corelibs-foundation`, `apple/swift-corelibs-xctest`, `apple/swift-llbuild`, `apple/swift-package-manager` into the toolchain directory +3. Clone `compnerd/windows-swift` as a peer of the toolchain directory - Currently, other repositories in the Swift project have not been tested and may not be supported. @@ -37,8 +43,17 @@ git clone -c core.autocrlf=input https://github.com/apple/swift-package-manager git clone https://github.com/compnerd/windows-swift windows-swift ``` -## Acquire ICU, SQLite3, curl, libxml2, zlib -1. Go to https://dev.azure.com/compnerd/windows-swift and scroll down to "Dependencies" where you'll see bots (hopefully green) for icu, SQLite, curl, and libxml2. Download each of the zip files and copy their contents into S:/Library. The directory structure should resemble: +## Acquire ICU, SQLite3, curl, libxml2 and zlib + +Go to [compnerd's windows-swift azure page](https://dev.azure.com/compnerd/windows-swift/_build) and open [Pipelines](https://dev.azure.com/compnerd/windows-swift/_build) where you'll see bots (hopefully green) for: + +- [ICU](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=9) +- [SQLite](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=12&_a=summary) +- [curl](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=11&_a=summary) +- [libxml2](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=10&_a=summary) +- [zlib](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=16&_a=summary) + +Download each of the zip files and copy their contents into S:/Library. The directory structure should resemble: ``` /Library @@ -55,9 +70,14 @@ git clone https://github.com/compnerd/windows-swift windows-swift ``` ## One-time Setup (re-run on Visual Studio upgrades) -- Set up the `ucrt`, `visualc`, and `WinSDK` modules by copying `ucrt.modulemap` located at - `swift/stdlib/public/Platform/ucrt.modulemap` into - `${UniversalCRTSdkDir}/Include/${UCRTVersion}/ucrt` as `module.modulemap`, copying `visualc.modulemap` located at `swift/stdlib/public/Platform/visualc.modulemap` into `${VCToolsInstallDir}/include` as `module.modulemap`, and copying `winsdk.modulemap` located at `swift/stdlib/public/Platform/winsdk.modulemap` into `${UniversalCRTSdkDir}/Include/${UCRTVersion}/um` and setup the `visualc.apinotes` located at `swift/stdlib/public/Platform/visualc.apinotes` into `${VCToolsInstallDir}/include` as `visualc.apinotes` + +Set up the `ucrt`, `visualc`, and `WinSDK` modules by: + +- copying `ucrt.modulemap` located at `swift/stdlib/public/Platform/ucrt.modulemap` into + `${UniversalCRTSdkDir}/Include/${UCRTVersion}/ucrt` as `module.modulemap` +- copying `visualc.modulemap` located at `swift/stdlib/public/Platform/visualc.modulemap` into `${VCToolsInstallDir}/include` as `module.modulemap` +- copying `winsdk.modulemap` located at `swift/stdlib/public/Platform/winsdk.modulemap` into `${UniversalCRTSdkDir}/Include/${UCRTVersion}/um` +- and setup the `visualc.apinotes` located at `swift/stdlib/public/Platform/visualc.apinotes` into `${VCToolsInstallDir}/include` as `visualc.apinotes` ```cmd mklink "%UniversalCRTSdkDir%\Include\%UCRTVersion%\ucrt\module.modulemap" S:\toolchain\swift\stdlib\public\Platform\ucrt.modulemap @@ -104,6 +124,7 @@ ninja -C S:\b\foundation ``` - Add Foundation to your path: + ```cmd path S:\b\foundation\Foundation;%PATH% ``` @@ -116,6 +137,7 @@ ninja -C S:\b\xctest ``` - Add XCTest to your path: + ```cmd path S:\b\xctest;%PATH% ``` @@ -137,7 +159,7 @@ ninja -C S:\b\foundation ```cmd cmake --build S:\b\foundation -ninja -C S:\b\foundation test +ninja -C S:\b\foundation test ``` ## Build llbuild @@ -148,7 +170,8 @@ cmake -B S:\b\llbuild -G Ninja -S S:\toolchain\llbuild -DCMAKE_BUILD_TYPE=RelWit ninja -C S:\b\llbuild ``` - - Add llbuild to your path: +- Add llbuild to your path: + ```cmd path S:\b\llbuild\bin;%PATH% ``` @@ -171,8 +194,8 @@ ninja -C S:\b\spm - Run ninja install: -```cmd +```cmd ninja -C S:\b\toolchain install ``` -- Add the Swift on Windows binaries path (`C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin`) to the `PATH` environment variable. +- Add the Swift on Windows binaries path (`C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin`) to the `PATH` environment variable. diff --git a/docs/visual-studio-cmd.png b/docs/visual-studio-cmd.png new file mode 100644 index 0000000000000000000000000000000000000000..aefe732e57e05e6c9735bcd2061b5007d82863a9 GIT binary patch literal 63763 zcmYhiXc$ zZrki@-Tm_#e9IQykN;h2z0lXUwrr8UKJ&ZRrGy~e z+*!W zk-$9RG*<$T9}Ps@av({@BN20@2-b3OWG$l($qk1EX zI!diap))1Hd>|K9=oX2df+Wq(WMLj8x#P^kglSH)^a&9iq_Ry=CmIPC)H#sF9z9#r z%rxPrO3=o9G+hYB!`!KEg`Im{-#fs)Vc|+RON3jCvdwSX2U+8+ndS}XCX4-78tva! z8pN<36@6Q4Omi?d&Z#;F|2YbKx8r6b(yermtZAbg#5K>|c%W_R#PM^X%XOQAdiZh3109qWW3y_3aA}oKbUs9;sEx z!|)~#p+5}@{tYvuSbkVYHO;&UJ=@~J)mV76Jl384Ufb3{+HVtTb%h=^4|hIy=fc;y ztqa-<;g%7lLk29)jmaBRQ12Y?l6&Yyi-KMtLM}>y#l6MmTsmYI6w+Xv+6G(9#Kfwp z4kQTC^r-=LG!SjQ9ZNTZOO ze-yduRm-w9n@)0(yVLFQO-4gG<)pCWBh6A_dGbs{KU9~~vrEYlf_waTUMi?DA@uUVyWaNtcFP$^z1f_MS+feUjFv3=OBltg(XoY>7(xanjija|h%S zG4-b;%N0TQHlrJ>D6=h-8xl<@@)=tH$6tAE8)rn}*3I*aDG@tQlyshp47BdG=DIr5 zswTbqwg;CRrfX^jLKvTK_WLyCx4!#AE;tq{+yk1M4Rr*HAAM9d?cPGoqK2Tk7KzD8vR$yy07eS7o9qr zUfE&Un0R&lDIWhC;x1YspRTe2^QD}B=2|B*My~7(T;jcVv$NC#3t+|htskl{FY#-t z6eHVbpNs^e>&biA9WD=be1x{1+YYte3?LGvFCeYN8PQ4;xUPO(f2uu+JGv~p8}km| zu%cS)(y(VFEZO^O_geu|9Bu|v1{6gN1N*6Mr*pVv+<*OaKP~G{I^nu~#=y$n}_31B9^w$!w4?KW2ADpA_%$JSIUzz5l!hmN{TSYw?1k$IOFyQQA z&gXF51$*7ts#67#x;ba+hw!+LC0S#injpvPtch=F?C_zRlox;Xo&n%HOB7uG~2XNOVTIY5BuaP7ph! zIow}aPFgu(6X7K{d5eFQYBe`moE=C=!I_Y0#ZkQ0_l4DNRXRr8Pp7>}yc+>}F~8x8 z>v;QsG%hysDm0zk#6z#T4VYUl-{Sq^8|6r8V>xkHi?+s@PjbIogs3b$Ynh;pHC01j zUjk9f(Cm@&%-}zvBC%0L=|f5lu*=@TLVy5N&~8%fHIvtyWB5k?0UHLcy|$sTWF6gG z^)~`KTCMXS^>9+`CC!n-2v6|=h$x&8c0rffK6js(z~ze$7IgwSx(o|VLtx+$T6t5S zX6KAHIbYWK>G$TgfeiV}QPV}WF|2WmUP7wu*}UxwdXUb>J)c|0=57UD$jD!P4C~kh zSW{-Ox8;3Q?SqQ)kAaeg?pNYMF(M7M5cfH}Lm2}rYU17SDa+s1bMGK<)@&S@EHJO2 z6?li5SwhnG}>g(ryk^)@^+qVYT$BEAf1LTxNU zx=<8bTs_(3fxZ|2S(B;p)Uq~lMTT2pL*Tx-(M&mQMl!`a-LS35 zlYAN}`C_Zto2U4cSMn`s^`%)SBP`&$9$y^A*Z9(pm)T-Mhd}EMAQUIfl`R{<=6y3e zF>1sJs{tuKf+iBu-6>ErHRx7hg@!WD%H=7o3Ya_KS0h&sv6;OLQc`SLg%;#BO67J; zWTx#m0LtU%mF!YUB9;CkRIdS8M(!zn28ppDPxqOIva$ihIcJw$502wLN-fmpB{OWZ zBaZi@lMge;bUqZaW-7e(_CIS3B9P<@OBqrcTR|h@mGgKOXJPaaW|YW^L$KFbh_&i4 z_sF%c1*ChTIBWCTk)6>-Kh6zYo*T`wfi)m!4wVvRr*akkU(fxasqJzmUG;S!aQ(er@{i?V zLb8|wVdG+bF2SP0wRMw5=#Cpv6vedf*=Bj-oGZJHxYoJSDI>cJGQbn}l3#~@SPA{Z zQLpu*o2B_khbir&qD1~94TN4h1zA1CoWN!XTv>wL8bA2~J>;hmcB<)W*<=<(!O=Gb zNY|3sYtbRSo~3Mg4SU>DzCJPMf!$zsZ7^Fp3-YPTa`A)YyKlk*@)W~`i~O;yEcNWH zB1uHUxfD*;8^Xh`#EbXA=wxmpP$Yd^N_s^Zo<=G>vi zVSehL@gcGUJ{NRsfS8{Kga0<}C8+^a@wX#a?_@(&pg)+Z{WsZz&N)z{2+}FF7$DLE zc-u^URkhfqTx8~`b$FmY3o-<^MAz3}pLUPnDr#5cwfUn+8yqLbd}@AVvDPj5=WJM` z3^Q-F@o)Ypr)P+L>ify}0ue|h0usdcx%iC+O!IoVnY=uw-z3j8u&&I}16K2|6X8d~ zobexSuh4alLDQ2OEcr;wGD#BI6`vKS9(+jQtNRIiSERAAm++!wAwKFIy*??U!Q{FX zWA0qFc6Wy~hym=$D4zvS(I7Rg>L|q-`-+(CLqTP-@42e%oQ4DsN#d`V5v$w;A!yE- z&TwA}2Qz2d=t;}*`cL%8l~vAhh1?oFb+6(WHAk&p!p6ns?YT;f3beiVT`y$gpMC`7 zE(6#iIG3UmqkjErR`|^zV&EMfA!#YHWmH9+n6(7fo3r&9`8m?7{~eX2td$K`eu87F zN0xV@=_=4|T8oI672z3{*}Wc;dhBHHE&|ok!A!>u=8Y6@3F;4|t|YCXTG~}oGZnYy zMQhs&P6vq_cj&IDcyX8GTufq&UAKoUJ4|t%&SI&QYwVa?1k~iB1K#+8((<8GXswOe z)U{~J{(kevdqQ>wzXL|iximoyxXYG~D(Ny)#ZkpY5n)pYmcQpzPJS?z)TJ#YbAZXgi?Vaj#G!caBf8^z zpg-XThqlj;l+fpRBy;$IT3^&jT-NWnO52QMfi>216iaLTkZT)ho!EsE4g3Fi!F{}) zW2%z&LLGKhwZxj-iA)gY<2z8_z0KMg_q+6sf86qa&ogX&pTu=c=1N9xmda*!C!t*E z`FIFOnlpddlUhBQ5bKT72ToV|DP^`|lT`le^4qiyHGHs^Fcut@cia0o$Qx!PT_-}dg*xTu) zWrLPh@|m5qH6(rwNyPlnQ@+pW#B#IM<1d(g$Ljcy)XPTCb$Id5NK_34?ngOGIa7w~ zvh#030-&)&Wq_QM?#-{u=mcpdVa$E@D41dS0J(cFb7C$4NC2K_bxQpAQ9;MdFIdXa zX7^8~W+y83AFnp*MzjvbAc)K*<&>Us>hPK_qV?nQ=1+C>iDa~98-5?AaNc8^;nd^Bd&g=4NyReWl}9kKJdfRB=_p8+R+H6Q@n z$6~Ye&`$z8_YGalOp1Ma?WL3=_}ZYP?k#XvQp_KpSiWIt5p&i4*z$KAeV zUw1_qp&1viyy|;ad1KmA5;VX58?CAV+9g8fchA8)fC&P`IB_KFG-dl6>n| zrY}LHO!p<3pehUQ=ashhr31kj`A~~}%cw$fQZWJedCVoZ3XB^KP5zk^0#Dt76buIM z7~}_9$Ic~gg~)d4o#VxMa#U+p) z3))RrzAr`4(US$sRk&;4!++#GIEfuP=t*Ft&@CDt8;n^D(qs&CQY#j`M>`)FbOe(d zmKUWDdnnI+IU#(j+0Yy3hRpUu#Vx|*M*4sd8OONSMr7f)@t_Si*Qdoux_1Llvah`= zX@k(R0Ghm4@M*jB49v)#Ntj~=>_GwmZo|7L3fHEH6jb~bh{ud3sqdrdY4>wDdkf=b zKI8KwvsTsa+nqC=k8gfo%<5O3m;xoQHWM8&Gr611_su5hyY6N)B_TQ{Pl*svX?HY& zprWH>FPVHLSJR4-mdx8P+qLRFQm=F|^zCxk9jbyKx&wzX{tkBC{q9R}^psn-;HyTl zH?kZSVCzsDJd@c6tr71O`}%|=%I8`)BKC8tzGQfg6@Q$u4;n21v9r@Gk4UuadHTCU zOQr4B3uX%t1;6x}zsar2hCbWSDv&6#T(*E2ndj(5<#);TVZCa!s!pB(w#KPA5Zb+6 z@Zs||1w#~VFTrQEV0OKt)pqA}swo)$$-!&jE(s`xy}a_*vZ-7fQojzyp!=SR8n&p+ z|LpX1!3MQ#Gs!|)1H$sNBEK;1Y)UG|X?*goHp$!k)EfO;v}rtv0d{zeA2!f5^oDm( zh8LtMvg-4ebwvgKOHEeoEKyjV+2>TW^?Lc-qeC&d-fdz7pDIl>#~VpLI}lr`b7kEG zTt_EmHC%DQafJB`gkG(lfBU0xmZLT)k|Vhp!uuFu;ixeq-dwY>(D);B4X}Plwd9T< zkQ5_t`M24t=ROhN)OE}MH==GZZha&TCmB4mzjtEikzVx3WAyC9#K3L*unQhNTC<`s z$SBB}CK`I>R0tb-Q93Xx7{GVx=PV5-`e(rsh1HSo3r@K9=!eGGSG{-WHLtijo@fN7 zihs;nrSY&$TCZTQ@m5DY2CR`kzLsJV7MiYJ*ocw=PjfRy>S7K3Yy+U6mM^Az^Y8O3 zrnr8kEvT&H+@_SQ%$!9<;n2j-X=}@spi%Z|O{>PB-0PwG5#B+Kl_8 z^O4II9y}xi8S>S_PLCq2`sI%tj@C0r?x&du54gE23;CeZI6ru7rYWA3AbcKXQeb0P znLiBR-?S-xwxsrqIM9D!K^^5T`=Cc%w9*N$UnuIup3tGq_2IhYWIG5qMW@H2+iXq) zgn}txHElduzdASfSR=5svb5{4ZlQ@RpMO6xM+ePc(uxWMKT!P~&_ZXoz}(jP|7;r| zH@{}I2+EztALKCaI|s%6hK#zSnW{_fdsLKfp^))f)lYrKC$gx#47D_Q>E>y;+9ubh z(cID!7;3+{8=9uWCBD@z@STDI?K&&9uo&)}O2~>6%-+%9;t6XoUE$rWEw$1TDm?(_d$uLjdV-CHN6Sy0dxJ>K z!53nG3U=KxG}`x5w8teqOna*DB!6}vQ1EUp)hY8S7ApKQ8~@uEe&0_??^lNeJp7^~ z4t1gCc~#K6N9ghdv~60hekd)N8=ZGQewmkLoS8z24U1|Yx9z>2qEl%YauB#*57TRp z>3yZ(b>ltkwmXY0tdvpv^JHJVPkcDt>t`mmAO#Hs8Xud2tDUIccFtJq#IAQOQ-g&D zwsG|}%o1qKC7WR_*?a_j1DpKY=)q*kpq5WI_ZpJ8KFW4$TbwL$0FUX4x63xAnIhP4 zs6NN{S8}dtf7>DGu67rdw?}b4hroqceNvMpszWi`@LYc_QB6H?GNFCyisg4{JsEH% z2<~?8L(o~X@D%89EO|tmS}gDk7*16%q3YZJ5bdB_te%H9^5%m5&uWL;o+LSCrWW}J zDIMyOiLvwqYoFEA*V{EuC3et!-5mBA?uH1(Yz+Y?HA|3RnPzx zjNtx6M!7aIUfIF%XY%!Co;Z~WW=aFJK!F|^kWMj&=0|HP=u0Vx3CM}?w&BC+p*!uM zdYMNZ>)s5}j@|@>MX?3n{KiNc*$K8LKz#J~Gauz|9XmfXdS$)x|BHL{2_febVC}lC z`}yNN=Lf)zlYnXx)s0!VRk5vU8#{7o;s2h1z{eWwuim46~)HUb@DBMv#61xbR{ zJq_M5)|$9AIlW=RHz+{%b(St4)Y7+lRjvpg5%e5z_3tzqHKxVnObYsM6S|^e%iRi_ z-ky6K5`E9z5KP+F#VZ=Um~28h{4L9E3hNNkCiBac0>{eGSGi$l3*%fdJ+v{(E->fK zaXo6)KF9`D^c$)5yR#+YWK8EC;>dQ7dZ21=rOKfDhQS>Z>Kw;gqJl_KlNwi&KQ+fp zt#W7S09{(c7pBEZ@5mR^3Px6>+mc|fenn0UCRwJ zMWCiR31-4XY}x(A0CFFBtMrI3Iz;m7@dM}SEL%gzIV_Ua4@@jHncw%(VFhgs@9D|1 zTGuf9vG*@TaQ8P(W_TWzsrShz3+mzAPe4ds89jsq)Q|L=K||218vb!Y@+0G~oHBX$ z6*IC|hq(hDwwUn|9lL8YArmG3YWDV`%GN{o0_%EJ!ln#4drO<&;G?){3VNLu1>ZUR zzme8`kpXUogh(nNR5=q>vj0b^0<|T*oW|qB&zw48aIl1X*l-m6sLK29n_{?2S_-efR)^$zT`%A2`TR4V=HnS}13PI{;a=zmr2X;s`FB8en9tu`6A`pH zAF5&U_Xp2E0tj(mVjsXz2b)7K2ifrKASM@2x_?c`sd7D^W9@tZm&gq}H0y(E>oNKK zi!})M+)t1E%yqrbV4Ur{5&O_nQk#r9g zW(ivVCK&J-JL7&bi58%+o6#>Y`yA3=;677TpcS8#eiTu2)JoU7Hk7+hn_}KOcKR_d zU;8bjw69GFXpvk96h%0s6`5t`ymS*S>6EDliGH#30Te7n1>;^NMql*5`qje6wXPw~ z*Syvl8W5woTBPK4PA31b@7IaH54j--T72$T;Ast zX9IsTiauFDk9${BqCWydvzq}$-fBFMJq-Fo)(PdD9Qx&8RDFDrF30D!crH=O?v zUp{+1z<#Uom)@S4Y^ZQRoG<@%E)lxrX^P~&i$8aL1x+yCgE^D7&>wBW48 zgV>E!SpBZkNJ@=%Z$K98PC#y9L~V)aq-~la;Voe7&korSJKW-4W(TPPGxFJ|s-F@@ z9Vzw-k}Q~V`VhkXo&!I9jLM$u*oKRylKk&pU9V*D-tQPuw*_IxjwXD25FA=^E z?qPrTo8PW}2o>#;emg5|`PH`eu_rZgV2FE9I^K=x>KR}?b8#kx6(KjW|?m^x#GgxdWtB|{#ks8O>E79+-s>i z!Y}+;tYgLd1ffR#D|lJLRW3QjIlQLQ>#U;TF}#+=dGOrq-QGx&d^ALddHTLZ_nYqu z>i-TO{9X(9Ze=_lU5bGbL0y~Vj&#Qg!3soIS&dEdUmzv|?}?ZzmL2?NqPAt5cv+FP z=9r#k$t*=olZkM8*B!(_8-S0@1E)u>5k~Q(2n1~yKM@F@-AjbmI*V+;g+hbnhj{UjXaVs?-Kf|2$$;x9sJ{=nsRsFMQ0lw|s%G33|&k6c+r| zG7wzWN)cOg4{WB|ohK}2>8zAVww4XP6Q{_(@%|tqean%AzsB@0&)5aTDrtGA*C*WR zsmnWkEiY*IUPH$lr-eENu}GTf)QRA<|wX( z$i}-u*E-qghRJ^c%YcL^P@B{-nlxcDCK1kS(M(vsQWYj5|gIPz97Q>0%5q4^PM02^)S9Qqfakq2=*`{Eirx5X7TTmcjvn% zoFx8Ke7*3xru$OeNHKa{YKwhpP}aSfuVgsu0Qevks%eE2cfMaUP9%!r!I-ga z#7K{gH?-s#Knvkt23Hymnkx~-t4Bao}*6e8NosgnwF(+yZDu-^*s>3bh=PRk=VGVeA9T3Bc2xCzI*{7lWE_1yeGyGRk~7 zR5gm7-?LcTbxkw_(fG%|@qS$DF92f9=bmkTD^}^)Nsja+%dByOtcdJ_&n{~w3-qxn z9HR5y+PAdH&=saAw{R%~6Dz1(1CDM3ta#L~&%7X>1odzhj9soNaDiP-r{#?0w%xpF z6IU$6IQc&uCmYwFIt1S+wIeq}?}M$P$`wv27|M^n;WV}cFCIz`86+5nVQ*CVz)g~S z8rfQ_%m>NGoA#rw=DevNlbQ!foFB*GX|4rjt@mWd9806+Ih7#gLbHQS zLqY77bN5*!b?fw3`Q@^mx+i9KSvo^it87TO<$`-3>9zLy_#{Co0j>$(oZu1A^JL-* z`g=`oFmD^E7t30U-Hb0)1Xp?3zkc8Q{?Qc|(efbj%TxVUU1-%VL9r~)<1`yH5&3|9q@;Paw9;jjb#M1%aeaH#!PoU z!^g#=V=2W(M|NY$(f`wSj%QA;SI4Z>X7oSp8B0ijcTtXC0bM-l@m*n&AS?C*jO9mF z%I*?ilRY~wu@!?PPnkI>2-_dB=+vWC^KYxS!kRnr!(sa$M^enwkC>fh9tcQWzP&z` z^i;5CWnQW9OTM*s4CGpR8$LJvVhw9grgDz!pjR$5n z38HH=WKulDAX81aLSfwb+9@oybbGskVos`0-NBoU+xq!Xxw;PkguI#S#a^hXzh@%h z{Kp|21~f|(A`IAC7uhwpN%13V{02qD!;#*wlCC^Wjj`daT~ms;ew`7?)pGO%8p%yy)|QAD zJZqdp%Q@}t^kvl7jbrN_Pf+UCY~V6Z1vi`6P_EaGbGnp4j_k>eW(=CgVJ|LyU~{6! zoQYMItx8Iofwp+)LnXa%f6wHc#~LHqg=jCkdbiSbGJHLKszrFfgFp>>E@Me4jxi2h z(<>jsp$a~NP{9l8U;i}yN9xBja~eMuO6d7xkyRz5oy&39no*Jb&WYsQ^#KG)G*Sta zJPg?uG}pgYVVig0^$>b|_;Q!~6P0$?4{hLjMFBtPZf%ha{f1SQD@n2|;_MxX-#>c<1bfCv4^=kBH@ z0t2RpcT+zz8BkVceP-|HFqRf#2Oywf4U({`-91YDkvIEyRxQ(g9ft2BGWT0fa6s|o z=R^>C6xCd4sVrLT?>R4lfPf3)DJg7t~~l?q|v;&<`-e>9@mnZ2=s zQPGsFH4cT--^a;$sbS6aHl zE~1qpd>135sf8pdlPy6p>yv<_FMw#kbMbfjHlqTkiiqI^VZhCA@D2 zZ9?fJ@B(q)g4)E6r7rvFztc1)q+$CVPxk`rp8g+}iTJ#D-4-~4*ufxMOb*Uo1Di0u z$9}f*Sx!`tuda)ejgDVBd`Y7siAIqUf=S5;(JNZq;sI0-}K5lzU7apPpn z+Ek^of3ml>bJ76L(FO4pn3ZJA!!7QKr#~Ydc-EL0h9wo=YXpX)kzBSB^yAQZN`yu)B zMO^G}nE&|Sd?MVoox6Xf)%{;j6JmR!U2UGsz?^+*{qIe#G}Fg>g*Yghf7z6(>`D0B z`DS4E)WOU6{Uy~4Zkxc@^%^8H%}knNHs(aU+9P#iZSroi@RTMqAOzRKX&gp$@}+OU*+ zZV>fNG^rXN%npKd3IY&cJSwzhRI&;|QW`MxRbR2CM0YV>WEIq~zO34aCT4l8;MTLy zDoR}*L+Id~tUi`UX#c+2l4DuY<97Ad;vfOJxccqK!=004eZaNFY+Dj@aycF-ez%uN zYAut7>V?a!*DjajuRs+!UG?ta6Gtq}J{9Qa0dGFB8mX#L`sV==0HR_Sq$#|D_#O_%WZV2K?NkLicUTS;B< zU8u1QOJ=Z=>&og{H&cG0uPABE0p=vf0l^MlFZ@wtW?ah*Oq#OVvMs5xu+8&MzF)#` z3I794Yk|i&?z2gMG%|P@0l=2Z-A;^>dn}WZT?8a8ve{605hT9opKB`Zlr&?4{>_4i z_+|yVg9p1;=Ci_*`?ksN!irEen~I>o%d|C|cHP(GR@h(qNsW2KDl$YtKGy&aYS!97 zMcx&pZv(hoeOYB!AlFDB^JfL<#z3*Wmge%vB;{U0&x`$tV1&lTsPG?`dR^li1 zEh#M0uQX|HQ{ng`r{T(^NT_+PRzRSYj4>|aEQV}DE$(Js^+<^*9D)rLdquh3Q!tl4 zJe0mX>ZAD}|HP&HILX~F!|R~~U4M#`CX!Q^lc$&Q$|dIVa{T&B5I1SwiWIl@a85~_ zOry(0L`~JdM1J!8H4JR6pH7-v_QRo8CJ7!Z{fI6}*CcdJyhb9wAgb`220(Vi+M7r{ zl}z=MJ1xIbXDi{G?kn}3K?VK%7wI&-QQ)ws{UPrpHN&1_#qu zeG*-{i?+5YBjvT&oR*Zq>-?9a=s1%_blDswg{G`s1dR5!jj&yq#;Ac&8TX~O$DgUN zCT3TLLe#o_E}%_uZ>!yakc!&E>(^sOssv~zd!wpfS&mwb*t)Gk?fi^=z4B#Qi*jZg zkvYNlS#RAqC;^L3gQnhfPM}e<7@nR=xO@(iFunZMKY5ykp)K!*h>=8~o7AVbC~WHG zdn=OcyU!fRJaO$`Gj`zO~^ReE#cglEpet)u?x~j zkqQ6VBxtAyUTu{*T&#N-w1Y%DX_Fted>bY+&U%A!ybRTdxdWrQTMvYu6P$G)HJa(B zW@SBtl*q!lzI1EnG86d}M%T?a<25X&#__&uvhdyHFMvt8^s;h%X+?qg2$4#ce<39H zFDtO@r1g_f(gH;A6tU9Fa*`N#%`p?l;B5Zi%osFaayf>Gz^jze=p9{(G1rpSZyEc? z1S!d)dQIi9pASh2&vK+v_dK?@r9liLqcN_R`E%CSe@}rxdZl}sl z^QqhCcQ)jSKb`yl)?wW?n*14`wAc2$i6sPw{2x(<%>QZyVu!cMDnKN@*k+w(Py$>6 zyBFZCf<6H{N)xI?+b@vDoU-sHyQ~Jq&b>V;;ACeHI)OU_=cVr<{0 z-vB=C!OEZheo!?AB(OHB90TfzQqf97fMT_Ym<3H<@0@@k`hqM;QJX`?0|B4_c*90- zhVgyYpIEgMPR!NewGuk5Q1hs1Jw;U3{f!@;fT5fXQ1Ftg`aLWHYyyArDkYK14Q%s!cN; z^@gbyp_E#C?-J3uEs0er2^ocw`-b}Lg+*5ZjLHmfCRcDvO=P0y+M6yqJ*of4nPMA? z(0m{!SmaXk*f8BzB^kP5u{YrSK`Ld`@xGO!=S2KmDQXp|0%vaA99!~t!7a(wlF&am z#}Ix2>-8c1VIM#{wnp!ao<7lPPpel}Vh-jGdZqs#wTI`82xh`n6`k#3r=*-SK-+Yv zNiP)}AlBxU9!}${*1#R5MSl9y9cMjmK-u$>cp2o_SM!G5|;y08pp z%$8n}&QtpD?fSs41e5@kXFH(FLu`Rhi^{x|=n*vvbGr#sPcUPJkJ&l=x3+ECjFsav zP@6?)r~Qb{9UaEEm3c}iYuo9u#ci1(+&rM`QQhl+Jwt_b6L51k3we=0lM?P9LKEeW z^f;_rK9&d~2Ihipr`?NGtB2PHq0_oq?foD9m#$UG$=-`aFtXkDwud!Wt#bhuYvW*9 zgH>}rX7`aph6TIMXJlUb5f$uSH=mIhomA6HeplPyVc6`@kg7rch1@%AQek$N>Rdsc^$#u8rkF#^+D0q!!%frpQ(G>(6CkWrse+5*KPhj zE_?D2BaS#xIKbabyoYlP)U%gL{yC{7H{$Rx4KbehzSSA8;4G>4>85c+=aPhZ4<5HY z*-K{mQt<}Y-5Pzb=b>{J<0QDAr+LfwMH%lyN+wHwWDo`(N?V2b@`KjkT3_(0q}Q%j zAf6yQMAoGRzddh4#c@+3iu^#PuYEj>P6v2q5e)X^M=u1QNgCEG><13(klUW78a8{& z#4gC5q85vKUp+LX%H6WC1iimsQI3oYO@{xxR(&z4*>~qrixfL*F-P>gg|Pv?Lv7pc z_XEBy@eMw);l}r^=I|4op2mIfb7UG4P+;TJR@yq`S1P=p12OT}+)M4F>P2~3LKgqg zN7f9XdE0FChkph;23b)uV#oTZFW(nFGxI~fcVmyM*Lh3ga5<}!-PyK~w&E;!K6V%8 zNE)p$CaCM!%4$l;3n=9WAoXQip^%Zot-d0Fn>tcEkjJY|FGe0@icj9-GMjNlFwn0oU6#P~@xZS+GeRT$hzQ`?Z%3-Xxe#omV9tw3E z{g%I*a|^GCOacUiznYL~%GpW*Z+$ z00mR2JhiZnVGWz%H2%nt;X;jv=y_TLtVs*y?j`o@K&BVGCb0RoF?htKI|g9D#MY5V zCFj0Z;k+Q?%^?$o^p0SE{nE3#?{LpvbvT9Juwq>eOdd};g4=SUR@*VA-Yx}c?oR8W zZ7$iCiD09&V3C@t!2+B7o{*wW(YK>ZS0~=YCG<@1#X2EO!o0FNN+4D1c zXnO;zdI%O$pvkMMp39dyWz=6+Fp2@14e_1tXO1){-w^ISWIEnegjz6DH@JF;QeGGr zU^@BuHuhZ88NQFMq=<=&>Ht@Ti>laKf0e{do?191KOrD|?Eg(;#gK@9(!%Q25a|x194!cjkUUB+- zf{25M^>#-s1EB7a~nJ%PGwiepQ1S0B(i$fAA z+(LIhTOI!4Wvyn}ZXgY3kvMhe7+4hHspOO^Gk>uT@dw*@w*2AZ%0k-}A#=!#26^Fz zhKVuWaZ6SwgBBC-{&g4Qtj9xpLP9fRu>5g9@qli~R}SL`4S7WGbE(l4X3i{?6&^Qt z=0Y-;QxUx%ToM`G}GYJLjyIB5A@(T{6$V z_wJqK*D<@8>lm*UE~doScwTFfVHHUv#*>u7EaCH~#rrG%w#)=NPbr zq8^D>hF<{U~njI_QnsA{6o4U!jKNzzw-PSHE7^n9`& zc{h9SvXw>M4N0$TwI|t7OET@Rm1M4tB(W} zmXHZB+t5xeD);JH?(?}_BZWLW6Cq9T*6$W!CHN*!0D+PBVsX$a^7~D1Ks)nfZrnv9 zOl{dCPi=hjH=Go4F z6qg-w-`)E4c(#_P%}T?H+$?ILcRFgJC_iSYJfW}+j^2^Kwo7y7FSE@4u3+&{Ei_U7 zNm3v1uAFbADmDL!~88Kx!_vQ8x#&`kOmhmQCfdjxt&3ajMNTy(+zT zmk|`iO?q#ilrM>lId8(^=CrvtE|lgdgJoc^E(mg zPjC|Yw(pU_ODbPFZ+6xeA9>bd#J!B^8iF<9;Z7Qg68m_vk^1aOl6RAL{d`7INC0)F2swN$4|vw{2(*bH&fH`iL20*U ze*-wun>d-J&YhD8>W)1P^|v%AXACP!J(z{=RF6f}=ie81$o!bafQm`4tK4EluRTQX z_#Q`5SipA<7DnS?MCF zmktT5yn*^d2%{_Paw$(RyyAjKZki{{``qZw82!PiuYXy&Yq3mH_fUVyOc8_CmQe#t z1fpH;-rGRe^=KmASzDoRXcO~F;q#;vTZG$H)t)UQ&bP20Tx3cuDK6k_p-q~#s9fP4 z|Ea*8+F_5${dho?iQK72zMX~0oo=z< zK2k7+W;2gYEn-V|%|+Pa=VCrBXMm6!?)U9;rDOEuPUG%S!@iVgoD&5iJERIXPOD@o zLHy;l=a42@jA2;98nxG`mbrn&tUaDAz{~?yYC9GdCZVb{HhXf-6fx;O=f`eCt!+cF zO5yLts+{gocfoS+OS;<;aq_rRHS$>KGDhpIvwd~*f~U=0?>4xb2pBpM?$o25k6NI- zPmsT(wNk1EG#SPp7y6Sg*2QiUA@z$VDu?lsd4H~jrkxc52C$Cys29F)v~eCOEb3p$ zQ#(L(bJTLYe?qlc@W|dwL}Y-J8z%z3P}Wq_D!PL%JO0aJyqUav7B%-&RqS4gQHaJD zb5*}C*q@dcM-rU(K|gfiG4f*YN^Se53-`HSP}_e2s!bLXSLMIrAtdWR%8kER5QnK248Y)Z8fD0uFGR<95*n+ zi)s;_k?W_uCb#ecZ*Q+4h6rlR)J)@+2urM0IeS9U6Zm-Q!*En&khW9@{fH!noOx~x}pSWWv-MsYe>A^ zYg_Bb#emUv&B)R|NBBv-`GkPKRWMG-9aO_r@iw|Sy=D_GghPT5F;4ykO1fEsuT*o_18xpoFL(fj-p~S`n zR9KK=g5b;&Ry-|#mBHWZyr*HKyVu5aeXMsm2HEewjFW$>j1fWQ70~gRpAcQ^lL(09 zHl&%Mj4aptt(R6V8((Y$gObLbZs8pY>z!_n+;_%ZZ41qY{XfdyJ)Y@4j{C19bqSS< zoR+JyuB&pm#2hE7TwTd@b)Xz3isZ0H%wddlr3jVE7#mkbW!w9eKJy~+KyjecFAC0 z{75BeJQ<=J_B?W`Y^&3m-Gd~`yOoYCeNUMy$I61`9g^L~0X}(-qtE?5KDO{##YW@U z+~{ZWMf!LYyW3}n_vBGi7F09v7b#DbjF~_%rET4?gyk8OT=wTF#75{++yc0z^ryr@ zagnBb(k!-=I0O=ZGg?nm!*W{yZfre&Uc+oQ;}{Mnwdq6>Tq>sR!42AqqkxB;}uG} zkN8sr<&?`#ko&%)n~ZvHDMMO^@K{<$$9qPt*j0xl&gzEsERHU6VVpQ|fOu#)MllNAgUNC0N3A#kpAWEW4R2XNhy=Z!mWSAX zoR45=WN{FR1#fiRQWch$1c9kqMQGBuNaea*Q16qp9!zAW968GGje&~x(juN0_K64%j#0g!bnS~r)Pqcv~O%bzap=%UV;M3!6v{iu<7o zuUng1NdsV>g?Edr#DTS+hv3VHaPmr!v~nS3uX_w1H_;?UY$pY*IqC!~O`^t{&)np% ztXw%^XOZvL?Dv8tXaV0Q4tI2FrM?*(6vcU~;kD6LvH1(&(jQ_vMt&Br?UqF=IcPuO zBTA5UVPsQh-$uo6j{kD*eV|6^n9g@|iaTBM_?$^BG0@?s^}tp?q!Zb2x7h>7t0TT5t;EyR}h*V4zF;PzP*&(Rf1uJT;l1}0>E z@JZmH5&~Bz)=-Ku24OVpJ{lvW%ZC=Gj_)PHq?hy&uJf_-2ME!Fh3xRo;SysV@KMj| z`M}oIm12i@))Ut2E-uBFKEz6?UyKYzFAKaz9b_qrJ6{xF#S|f=OC1(0c93T(SW|I3 zy^Mcf<6nKH`Q-Bbw3)1%t)Uw71@c;LBYD|f z{`n=xuX%mCy6fD$qUme&XOeB#wP5bwod1T*@_GO-Uhog`Sbgi2ix~EmbPKoi`uc^P zKx}|)fk_}Oy!1v5^rTYPOGQmDDlP98Qj>&%7!d;}Pu+36n}TwzyVcZe^;1}QzRR$s z5h{KG7rzLlX%Z6zCAF$e<9e57{31>OV@N5Y2JN~Pg!4LRzb*e^SkaI5qgTK%8yHs} z8%}>zJl3?evA+3-8e)(grWb!Evi-L!iv#gv1EFzymNMltnPIGT%G47-a4|~j8TeN9 z*d@VIW>R6o_k~y9*1oX+#uENW;~P&fjDT5K5&_+I1uaWG;@UE+=sU4?e`u*APdDy8baJDHRb}J3M(MYX z&e{9OTGX%6&!{Hhc86kr{lq2BoQkInyOwV=6Y~Y%=ennZYEG*EHqkww_Aff)0Q{ML zu2j7I1o3a3)%$_ye?6kK6t6&QGi4aW22+qxaog$It`OEwRInCG-|%t$IrFEKZKC|!++iL_-)LN zOB)|fMQjv&+oTIqEBnGN0;`AG8{>UWCks*queHEN^>YV{NE?nEs1A z%y{g%I?@#=W+4tBzrhHw96Q!t60BuI)O>mFY;t;zXUo&$icWd_lE%0bnF{R6oBy4&G0e&ogNIa0);?M7Iq_-n_u2lTV-vVVQ% z{{fSl+?Gr&W(Q%Vf-f6k7|^@#b9)p!0x|?~%0v`5acDrVU5^O;8N1}y7Dy0$*KIe% z>oRLITAHK;1xF#CV$XiL5s&hr1s^_s#nZv>QOYTh&4j0>K{nF1@n;^BhM*@Bj>wSxq9mTDeNPh@91s7^P7diV{jk~_-R=x9hGH~MaRmm zVd~e@VeN(^9fdksCON`8Ivm4K`8?8=DTf;x6z>8`pGqI2Zm^u}rdQ{ihSM@}QI9Rl zZ%%W-2yOun(xtEKx0>R0mg9|mg;l~(uZi@5NUHdhFvr16!^IT38@MK0^mw`KO6Bf% z#Rhr;r~$G1I{OOA2Mms@?40&|itwYEAkt|X=ITYw$h;@<;Zry};9_~hb_zFIC@%eq zMNmM@`%Zf1FXIJ=G&?K%xaV)4t{h1obKZ~z{o-;;n;pGIPg+`?tz8V`^HB3WwyQ2w zwI5CVp0Ru~!WVuWMdM+9ZwdqA_L=GddHtBPNlV}2XqvHgjX|M8FIxC|;?dsNt>zj5 zN5^QUQ*djIe(b0@SZb*2Vjy(Vg1X>MLyM6Q`VlSc^Dc07L`#OokPhsIgBqm6JOW3Q zB_S4X@}@^UjDHf%#763!c@#AWJ7u4FaFqpt)ykF{3a#=6g#SVk7g8LpOzNke={84f zM#6c?WR#ul$H@QoIl&GMvvi%~3G&m8eB&1JVjkbv%F>`st>E+<_lovHT9Q*tF|y1m z-P{H5gq)^huh!;Sg!wJI;veOZC6gbT_LyVXrNV9!g~ofF>{R3W{SlKP5^3;8wVAeR zeFUEf_5P^=#ba}5nU65sH7&np?%UYZH$Pb<+BdoTEJAS*o20uzP5FM#hEEKWjjJ1R zA~M}bcnomo|BMMr<>NtbSDxcH!cUxm;n{t2dEv$Kx} zzD~2H)w0{>13QD78Rz&1<0^xxsX;iiyLP}*ui5nZpD!B2r*Sn_SG>KzCR54Q_=hOw zld?3EJmG#{_cRlQo|8W^YoKZLGCd+XJSpaz9uxP;()w{=UQGPW3Aw=uh1H3NpVF`I zDjqm*>pA1w@5z^JXJ2f;offF9c)kaS!`!be8hYks0cr0Gf5CZ&<0-PfEk_0(U-ft5 zl{my%9mAC6gbssq`DJ<2r7htz-H@*R95)wXFR+o&{WZG!MYeip85Vfa%9?u7KBM9< zk)d}raWxBSzG@GGifboXjW2jZXVz%=fyk-i;wch7sL^mTr|CKa*iLTN8M-oY8!o+0 z4?rL85xG%=Jc3IPW9)-Ids>uQ;QB}qQ-Yp(t!RE5waLkfHqdDnV=LDbe|lUd?|7Va zci^n`kVdCg)&r(ad#<_v$H&cuSmDQHOq^2Uy@C6^|4x3uI)qa3-Ld}fC*J8g@%k*k z+RVmOmD(Li#qDqqtIw)aA1kv$wvD=J@T14HI}R1v2=3}I@>*MZVC_1pokYE%*_fsQ znbTp+i||=%4fc@8Nql6GE#B1mg2di)4u+}L=nTYF2766LwDIXp3tM~-%Q|M2 ze&&tbW+z)`4!{x#EqxW?G&EKvx}V?S75p`edt7==W3UQr2)Y4ZI}#u>Y{A{T8r-}e z=`w!2w0ZAH^lhx(P-FL@BN*iDEWyrv)$OQf)ZuJb-{An+2M8=sm0O?XQVK!wmy6;T zoTwkZu1A0?jT>IKwmLu>c{+{k(!*eFn5VV*mei}(f%5l-qIog<+Oqz&iBrhyJwbd6 zqY5DIBbpT>|LLEQ9mmfb;#h&rKiondns?~4n|SZRVW~7d;7hiHlgj*<+qhhsAlbnX zJ#vaki5{rylL7H^>lvBZWbBXRN>_Go{s+K!GLNUfg7UjnxW&!38rJaN#i zH(WH8V*MpcI{k?tdkn;e@nfuhBEFkT8QlnFA(c!sUtUe#<5Zy zQ@_-%?U`&w;8wS|wWjL+-fQLk^{OvBfSWjt1NbH|F~%B7ub|f>?U21g0p}ZH{ILRG zL0NE`L-}z%owQ7qWfC0?fYO`a_YJ4L0TU+t52J=>7-!s70FnWK`QF>cH5=SG9{ z3KI%>!(ao&jrwcT-^>Fx!JtTit+bG(d8Df@lAmOD9V|&4deu}X3VKL_``<&G)+;%9 zz74FNkv$q^WFM<|8hgwru98OThgqAhmAwIYPGXGIu|VEA6^ti@GN>w@Uk*hOieG9( zkcX&XNV*A@Q$l7=SG7^!UEyN6PcNRgv=euP3ecN<-%SwXaHjG56Ux8VS@A8?sXB(buZL2*;XX2w5j937Vp>^l<%kyrDY0%V*UVWI z73)*;zpwqPIiYh77FNxOHH9@SU#ZAGrv4&!LYSSheCk$YZYXKixVpv zT(w7c)$pNm<=BZoBi72LNsV<*qn#h;^&f-bQ2(wNZr#)`7B96gn2G6@D;E(-$C7lp z7)lx~AnQ+ES5^-1$uWI~{p~)MD&KcU0{8^fBSupwp$2-P(fuA5#)NO{^C*CEqo9k@ z3YvRF_;jR}H-!V z7PH+q)q}8=n*GWR2A#|Gz3EBte6(k$PFd2PLrTkiO!@J`l%W&@_aBJ9he+KU>S9h2 zrKngncik4wk8<@BL2sKX#U+w~yI-v1UkD1S%tM;kXh*)Nswmq>Q^_Qrs z$m|0}kYB7Wy{dE5Kb(<)TRh)cXL0_WE3X6}r_`U7h4|NsyNMtD`L`_r@y&d0tth;2 zh+_@0F0g+eWtXx!Rm=StD<)xHAb_9#{Z>e&Z0OJlvp~(zVXuQ9-2Ns%Q47d03)Q$5 z{OxQ=yNG2xP?s07a5JI7j>SCbUtL5_J_v*2kX^5>{*dDJ=BH^MzTDTD*~ThGVO~)C zRY;>CStPlV(%~S@sZ0l6HHd#&&~~;dE3p|`1PoKl@~LZO?uYTaahsusK>@DZ9Kmu* zI2fgqno07C;Ya_xjF<+8jClO5%sz8ysPoA)y5(H4k4~Q>afoxRKx}7bDE%(eKhEDg z$Tp~PU~k^O`XVG<_|9M`6LqT(;75k+&c zzrGxaO+=JeHoZ6yHe=O;efQ|vSzq@f=kM$x%|#~~g`A+9&5%U(_}Ha7*N4R7+b-{1 z|DF(K?bD?e>RE~1lg#h@S|F}!KL|`4v`+m^49<+uyapm8TlRs5FPTxyUsAJdTiz8@ z>nJxCEnaiY-de!Ip=M)V_pmX4KjL31%G!q*Z~4kT0bBF~!f`;S#xNSUifiSd4ixHi zp8tL^_yPOL73Ywy*Z6=?|0pDH8WBIV4DQ@lT-+SpFxO zOYPNf!3qZ1=n9VQuY?n(k7g)-=b*S=dc)2#r=`%pRJTN!eX!_o%<3J~e6RVJmzNcF z_V%HnfYDMVN?V<1$XO?KfKN?F3EkGNHX>tx(S3(UM*e(aH4|bEp^CQgdveh0QPkuH zOPSzV05?E8^`B+&&@81try=q?{c+iuh+U}{5yFA4RtYvHgvL)63F9R}mYdkE&Q(sx zd{RVaM~#D($WkFoJF)5azmA_$G3%7SAk@hQB{_9JKL)7BKX zS6w>&^H6Pz8YZGwtnQsMGvBqjl=6w#fRxDb04!Zlx$Ya~7QD>Tr*aDuPFD8gsuKYq z$muOqUxRAa;WTGBK1WfW)fH`JLY|-y9~vFH<2B^ z?hY+BshW)E#{u`^h@NKOiSIm$S0m;H5BAu5t$WPE-KWx)llv;&!|=+I#3c}+H2m5W zQWklffMoL`6H}A@X#HXMFxZ!4z|7pNvnRUwK@emS7sv#iV2@ha`k7Aa!wUnZl+aBZe zD1v?)^!D4XP7yI{XH|ZC(2P@>;v7gX6weJ;J5D?&D=7;eJ%pb2IX3$(ZwuEl`o_sP>|3v&OLTJ@xnTe%)9L}J;u<{Vo zoOmHwP9`KD%5C63ay3O>Sa~<7Db<{wg z%2o|_isa7%ZN4==VtDxMClEC)N3Tz7V(RxTqHpQqFA+QU)gq8TbNXbI>94*nQ#ZI) zCxlk5(FO<1Xl;2N_7_}$O_Mx)45j}X=XHX{@V?{b7c}qPwjA-AG4!fX$d>wB(gsei z-NVGKL`(C2 zQw1-~V~Rk`&j?qeWg61{n>W^+o@=xBg9oyIF6c$(ZruPvf1(rbE{{ShzAwa4WfJ-p z^DR>!b2G_kp~QQENy?#*Q+;XozMBz5A$MNH;$}y<&@Rumc2vErhCPG;{9r} zlICkCvO=Oa>3)ZK`OU@Po#x}LZ)aqn_ikvQUDt}!F|Lb+PX*LkwLDqHHIcEH1yuRy zTC0h{8oy(;iTkFp7&>nmHz6eg=N7Z;d-fuOBY*QNe7Od+-Ze2o+{%6jUwtZe# z5{@9ID>W-;85t=a!NJE>JXR zLb>FliR@!&I%95CEZUl}Omi%g6mlyq+;JTn?N1lxG3OlP9jHUy2$&>PDw<2)+hfVr^zfubfY@UBlkyXqa_fm_R1b z2GF##qZ*2z8i=TZwG+YO1eXtzf-6ml+;t@2Y!ft4m+@dpll&RJ3bT z_>(+-^^vvj**udsxMN_epVmof)W5KoVo z7$_KsHHH~?-$NC2-Fr=Ufp1TcbPJ_-Nme3$l43V?U!?=9WRD2xV?PkIszC2FCQT_h zT#-BuwDZ?(+)#E_pF=P45xnOj_cdO;4Lb8X$WM*|Z_DL2aZ*mN<>26f}Bw^&PgPYcylGPWwg?`PfZe8cgn z0ncnI(%%6|3XY;S)Ui+?BLRUs)L;LIyKgYdX5+|_KOpt!x1f(uX%^kmjKj6UKLNq)leEdMGF>uH z7YwYNU0m^0m`V|2Y*JU<`$OQC)t1%fk&6Vv7(GxrJsQrf% zVh4h3KFU7ZJa{+Z?V)n{+A;}u*}nCh&+@Yc_ve5s6BVZA8~+6^ zcP@SfO{_Z;$29Rj@6$K~MgQ?Ttj^iCmH(WToztPtwDnkfM8?a+$QCVtZGOv_7YTxk zCNf6u-hq}6ixj_I{9}KeP)XcvclL2-?~uD!biaJD0dtd}6^_`zmY!_nC`MIKWJSv> zxQ0X$2Y7E+N94SmGz_s;6_Zw57ZK&|eTRL+;{q|lk#RE1NwjRDSYFuw^9!;|aAh*` z=L=w1$mUCw%SODLCK{KHoumRzGYuc2Yu?1#*hKuYj+WNsWZb+zDDLAAiwYY&uo^#C zq1haBuQ#E*L2y!FYc!wm$Ec@H%UmyG3xzo)m(dln#S59S-)j&d&G&2>#vzq+!uJaY z_q-w#<#WXGgzWK)^x+lLRS=NluC}BnuN+r`A#0#5-!;lCH}c=v>w8}^HWmxp8<>U+ z{p{*$6hM2CD~242v0X~@8ULsb(i@d(W=NDMddfYHE8Dh(e??2p}eS+;x6gkyR=B~9h{(Qz#jr9_B zg?FDGSirB<(I>_h)V0ERpho8BF;ENF-qS@(K^5F&z8Elw53#y$xp7XgBg9J=DIJ+e zu>F%Uc~B@}INuL+kPeMJntIWoIo>_J-;TL6bI^4XA!j4TeRxfb`ZUyqB#A53675Iz z{&OKmb8+4FM)}c!26hS>1niSHvN+Wj)ji|;(5|i(!Wcch17@(8&lhXJ6}TL*OK`-G zx}isvQ1IKz0Th?yESgUTKWXNr4qwCb^Ee?qKfCUxAP@2y6SEjWX!J%iOr=k|5GJMi z1l4ndGE_@c@@UM{^U=?SYoWqAw=!;E0z0rKzCCEnYGVrI*UE6)z*~^NwBF!yW5Z>8 z$$EbAlV?~SRxOfQ^tHH%guz;I1hcSua%QkU%`7^rqE>Q&MUeMl8it(o?)u*=MmABb zzH1gJ*oMhMA!(B3)^i%8((P#PFfd#$RHorT^do==xueBO#{sykWI-&orsC<9l2 zM^)L2yJ6$(a06g$80J69%&YU!b=1GJu|-%-95db#AAK$6eU-IY8+X!66YGg}xS{(=V1~wXrvMF86F0~oQe>CyII=OQ42zC9f4rxE zOTRg6Fwb-3*xOT-t$*Pkl^{9~4S0W8T~Gx%bgtgV$!+17w1sZWEUjk@FQJRIh(qzK z3VF@gX#$GAcDe=QKiCx#gVUSVfk%E7kbQBtF8CS>g3Qy!;M|c(@u`|_byMDE-b%zD zF}rML?azCy^hZ34`~2_)YZIp(5?AHEdC;z&|7PS1lpb64W4?PP{RX9{*l#$NEbt^p zdZyr4a$)iEuwUl^C8EA7G_zrH;h(f2Wv7OULJK3|n_BgSgtpi(jvO-L-#2k<} z70;gHBVu|Ml`$MXN~3Y&rY9AH?vkPu?G6(07&&GnrqOehz33SN(uDH;+vVx3R``Zr zLXUd@p}x#{qUs*F}be@wm{WdezS*5%!|PKR!Mj z=)0zGFOBX^Z+pZ8E*eX(-M_^aC~c{`k|f#5Io5tLjXuhWg&c?IB3*?9$veWZ{fwi= z@VI(YuB~I7ZDp92`3IBN`1d!}qLU`hcXS4Nxdgw3FInr{EyL< z_e-+3FAbRIH0{N8pWxer^bg+EOq|UmUc1N-HQyGL@i1^BYcU{Y9n2c#n^g5l2ZN(l z8>Rm=VkEVQM!6MtIC(Gm0!} zGMQK#Rz#7<1}3C?Js0JeC@n8^$yjmu8r4CeA+TvX6F~4e8+S8Mx~_Ppg|+(tunq`=BYV|en-HgF3a?T42R8ABMC*fZ%A@wPtxNBCfRi{L zr?JjHE*bnxRLPrS;)&mS+cpaij;zF0)jxXYWOv@~ra1pFWA0e7?i1Z)%^DWb1mrX| z_pUkV>gqbE?b`C^o-Y9!t#xa&9+f{LzCL#z{c{EG0xb5t&rhtaLdYlxyCDc@T1M)C$NNF zS|tl_mQFVqmb{r@o{WEjj_#~-WZe!(i4dv|CTN}5QH+EU7e9$_CoXV(WaIv+2Sz1X zTJvEMB-@2}=_YBh_!a2RkWW}k^Rfd!1rxJchaV?CCE*}2wAgZ3f-Hu}WFI%#@ zw1C}b+ns|-iqDtVA*Vr3ZeO?&=3!Rmr$`|G0lH2q*vprb*#ur9dAzVG(mtG9|9>D8 zBH;FDzie_B<|dz`>X<)JxHT`Qz6sQOt~%fUisTe^I80KAjWxxuJ)KV?sLezO2^Qs2 zldIn)#7z^ttn3Bj`WZ$Sad{vN!%^tUKX#!^V){L!I1{qP5v^GMWkqUcQ=EekesTd) z6tG$y7S$aJX-Nx1)s8jNF$zoQefS!~9P%XcLhScdy|fGMI*iDgs87zMY{WQr3#(fx zVmtDy&u{V8oITa}G=$eN9rp0@q3O0<5M!`k#k@;fqhkoE0j$+)$V!NLXdbMB*rhq; zBHsZ9J(UjCz5B-9aYLluC$TUEoY7jrJ$nqo0#_(&=H4~-r`5Hdx>BZDwho}&-OSIB0(p_jsWpU+Wb=oSu(B~5yA$te(j4;w`UiHWn_Dbyb&om!BysM1Kyn)j?EzHr7 z0pyi$S=f8IcC_%^aEdN-@&Lsg?9%`>LbYnakiB)e(=G;B}#=I=A0@DnNpXgJltfE4y4yC;l4X5Or5NLhqxe@ zEJ?n6Ua&J&Ss0T~Z2?m?hCDs13x*X`RHSA z2nx4zRQp!UbZ_Axx;LRzkNu=s_kw^1)*6}mCwfNGx-n$i_pbS3FK~{Gefy?4EWp?W z4*tEPugMNuFW5fsEt_^H1VD^IKYQ@YmHWB~`8_Je31%dR!<5|AX>de-%6_~{%McuG zUM`uQdGywzl5s3$Ox_zl$i(Nl#ba4Q{cl+W%2&IPSF=y-v|6w5EI){cPT7NLf{}Db zqIh`sEfOh*cx#Mz5}p32E~fW{dfYBp<@CuIGzGcSx)tsd!Mn>krH+#sH-sdrq;^AL z`%UqJwEW1n(}UVwrSSRN;8x4kS2~@aHKzBdj@=0QlzFk-x;&>io7v_SVqF+rPjlSv z8k_!({d`+x|1EN7uN6zV#?8kP>6X6Sxx-(`%;iVQu6vRk+m!E3eoBJ%wW4Y+U<9QQ zo9=QL+Ew7mc?^R*I9~AuXMJ#Dw>-y|MPgkY*di+{7TfVio!cVHK8VnU6CZTH zbkYdvR8_EiU$#aSSd*t*$!0P)^Q|AefHXG6B;Sjq=@%P+3NhbevnoDO+7L0G{?D^Nw4HtS+0KLT&u@FQV?AZJwJ0HIG%KwcD8um{2Wd z2-NE1fWDrulMR=ZUOx(&an+971Gfk=>r)p`YtzLabW?IXHO!61h7-vmzgcuAuQo%r z5nlaqiZFxjHZErsovAVH;^wBRXw_Q(IKaD7Vz23cRE3(>1`5dR_d783CzlTQF6f?* z8)j|g(QH;aKG2ZX+sfy5JJScRDyHDdVP+rhKg>;2-HAU>)r=7Ct%_XkCq?Ghyj1rC zr&nvy@f$q9c_m0AZ-EMB$F;$vE50S!>=Jf~k(qI((Y(N1Tm}hiXgXC|O{-g;t()D( zTQ0c@;yKkHVfXF+?3o!T7Bon>*<(7)N5CdwT8d~emADr;liKx1LsFAwOX%HW*8*Hw zxA$r?^!TCx^(3b2oR;afBA0tXwj&i*%^Sx44Mc>VINVc&s(47-p~Y!{@I>=iY<6MQJmu50s%kMN^j@wOH~q-vc{-PCM+nomT0 z%p|M&sNbzLU@8l^Z3O10k_XtKr4O2w{XcL!K<+#@hv25c+cZ|*bAP+w8m;LQk!i(F zX5v8ahh!bMbP*F`G$I1_ndsdhrIQ)H>W9idLD$lcExKu{M_IF(xADzB1$9m;6zpyl zZjp-v!5SZu5x&+L{wVta56oP8_sWK~8_W!8jbixG!`< zBYo#R1kR~_Fn6UH#!!QMxv&I)6@Vdm<@9s+`BEnczS<~2uEz=jos4vc*Ywt#4tNv! zc0tEXt*dC&%aPEIoV6V=`SL8qt&DM@N4V2>%e{g>6JMV2-2U2dnb}sCwR<3>18dS4 zd+Zm485?P-1>tsHI6A)9dM$iVtee8jo2T@$>9KAh_V=(?ztmN8UWwo9noQV$kxtdi zJEh-!cHUC&pR=NAi(6s2?*9KqKG*2%H3y-%STFW{lb-{x>X>QAvh}I%8^xU+j^NHD zB=2>?+_6UM=Wwr4(iXVIT%3f3N6rV;#PkFlsm%jUPL`?qP-o=P#PjJfN4bI0tsC}W z@81)3%a%8b(N#O@P5H&g;&i%l5gG7TN%>SOdGsa_HG1ZLc2-#@tlPD~XZTb1g$Xxf z+PK&F<2S`1ru}AXwBKj<3A>R~T&nRFW)Li>%eZHMkxx}I_kebZO8<@aktRLX<EDh%7hIn-*8}QO+QBJH@zLJH|`Zsy;XepABW>5pq zB{I6Ej%IlKR0>Ay;1QpquGvRzip`7ctl$+F$1l3au+s;c1GBZRY4lEiT`oG|pTOf* zZ{YR$aj&z#$qts!-}N`e1L|jfP%*>@v!|{#_s7vTqwn(_l8H^SpjQf=JI3#mFE16K zPz&Q8)RA7cTUcn^l3~~}qn5{6nxBq$YWr)vqZ}ULSSWjtw#M?mC$R||J z18QUlt}+{)n4&faMo2G?&Cd=wYk|t=JDL)7NVqNwv*7a?Y~)wcmA9^y3}P+w{W)NZ z=(EG>9rn{`F|S90-1P*Mn)C3L&a~rNqK*5snTK8im&BC3qv4 z{Kb-+7q;sO-6Jo?k~_mbKL{?)n={DF%sp0Ez&?wVanIhdbc20JzX~~&bT`<{WV}u_ zkb?6JwD#LT26kVrhd(^x<5GDL^9d>zJsM;VXj&V`Qqo=t#`J!sgufqxp74i?-SYj^ z4HE#zu2)z~Q1GZhd+9#ry_I?M@>slCguZ!ELeMVfTdv>XPt(IPZ4q-x2*z(=LjO_8 zpV<0Nn9t!B4@jp~8cAgVA*hXZRCWcb;_lH#UX#@9pE#}6UJs$X##+)wt&)~gk7bs> z9ISsD(*4*h;{ZN95z$FCX_4)N>rJv!{5)Y^1)T079p-cib@UVh41w2vtvYrk4t~cIUJ!)edLPIX;*| z;T;^}wB48d%SvV%X85tbxbDY5t9AG^9sDnhUK@$kNB8Y&(^Y#2#LDU}SQ-vIMm2*K zsWCsPcUt1(5PGQx`qe{9C|?^|b$(h9wJiS*@pGLk#)fPb*+AZ(RpE==r{h{}tr|FpTBV%%xp4)vgzv3|~YToS}UC6*W z$j6wr&%a@Qg#*{>>1swx%3-L!b3;NvLfkbt7pj|5H*AjeTDTaqFm|QSVVjZRu7{uU z^bdNJp5_rT+eP0;6e(kCEz2{UT7J`X7{ZhK3UFbw6IYq?f-T9{P6JabOBDEHzmQN$oCqt!fy#$ppLt3mw}P5 z^vIXGz;PS(EL3_~?LN|?%6D^`1{)}dioO<6Unpa?QxWHpG-Rb!0n{B+v){ExII+nz z?`5y@6Nd@OmY<V_JTu@E@tRm4rLc*&8^4cnP^?%G z#=Rr^FE<_`HifS~f}z9tV*pt-9zT@^YKo(uMgfhZU**SqTh$5@1P6Qw5x{8E6!I=B zgyLA=8kYBe$vuZa-3B&;|4Z)4c?5V19HF_*Xg=k*Hp~F)|1fxXsy~94UUItK%d_RR zod>^Nr%~d|)@CJH*)CpRrbb&vB*de_-UMo-7u#p}ef;qCb|TNv2P^LEcjg{KzOc3S zUEWP@-bg*4dv-pZpTxw7;a-YJ#RVWVSv*fv(2hO0l`&Z`_UGeuh7Eo7xmW z){nqF73|`n_x@vIs{5s6;x#wp{LFH4&P5G#f@O-;Pe=Qcdg4Y6@oL!t>KKRyG$p0LwLPHcvyv49JFC&B!f;Hb1v)euTq|MOM|?@T zn&LfgXJkbUBp|ar^&WoFKyM!AhUsM~RfVDt^>4L4zU9$V;$XrgOFN-Z_!3Blg?$4* z&jZyEBjmzMc2mLr&BD;os^fbM2MUN~Otn(FC)Ww)pPACR~?=G5J9C+fwV zq#G$K!zuOAUL`~&Tz)x$vfo{gIw;}23^16WLW*d`9urd`dI1YDRmsu7=4L3pLp!_J z*)+p;w`QPyUzRQy>%^BR)ljCRSVT+S-eBX}yt&;f=-4Ig=LjmerH-=TsJGx0W0>)- z+v~`fC{}TBFn4>&18Zoqxk@X7EY`Pi$lFR5rIG8TQ{c=5AnYp=?49Jd3?zhi?r{$x zD)OTtTkNl)b^E-Gb5zy!x6Z5%tmKxV$-BR(AbVvAPOjyk_`3y<3}m8LAP|tu+eC-= zn{tTw3Hilhf;!mm%egPVA@ExTMdkQwnr1cXdPt`T>8O=~e^Y|`Sf>9KpfRCX;Php- zxP!B_b-7yI+B`EpoT%k3H$Kcijvuf1)9N@eKs?ltbi$HbvlnQ@R)p^w`3=*sWXc)4%HMM2Uu!W-l&^vR|tE9LMQnZRJP+?M9~CKgZD6Nz{+-GFdfQa z3?x)LJ36rSGfn+9y?A{gokk?NP=7LWp(9kH5BT>A#V4SaQ23M% zEAL`aSlLmmJaI_;l?PXokL7RhCgFKj%lX6;@? z6RD-C8|bU(=3+mnx5%a;CQ)qq;69U=`1t#5aNvdRt?e$QTd7E_vXAJaR+vM*>|d?s zTXFBDR+Etq=LXP22Xs*;nZ$N7I;odMY4scM7lg!jL_{C;5NHr3eKxDw1fxyBnRG2Z>~%X!y4`S%(X<{fSu zEI_fUSi_Jzvy~{ftzY(or=}X@-`MXo7D#FSY0>U#l3n$51i+82wdk0*H*lU z+EEUlBCkNp2Y+-Hj25x9o zs-}-UP_$~uTF*}0&6AN{^8Q0D@vQ5);3O>Q@VftI~?PaZr@!O;zr?bZiU z)kB>gX%h{oXFrx2b+|P~4bX2w#SdU^SmR^yP!~OMK9O*)Yw6fW4woS7CX6!I`@DQh zCLYbHC69IK>CMe{pI=zFh1S;n*Owt{3Rj>Qr-i})Du57F;$?U>=W@6Vt zC*0VzPMZLMvSio5>OKK}OV<}PJCX#{d|Yxd4XQk7>$#atIbt_E0Z3FA0q3NKo~L}| zB%=3pgYb;)MzpIxW?h{&{V?L|rbciXEz%j7vCHQcUTw6Uyfro*;66~Ys07SI77l(M zz}L^-byu+t#W>ep}|tzUd3y$ zeNf{z>$d%%pYQ%TQg^*Y(oY;W#tLmOX_~Z1Hs&?~wR2;}A&{**SoaowQ+2)L6?SF4 z60)J};xXa?@WNs4euKgI(g}rpWR&*FtPbceBze2>1kVu@ovlhBQR{;LHfUF!J#eM4 zoBmA~LdblBtfaBj!+y;u@%JHJZCabG1%=(3JacsK_krS+K(72h zK+-xouXe0p>s>EBX>9($=C;cjNMUHp&7})}_;2*l|Es+4mvWJ4k9!`vPf?tdWjlz3 z$5!82n??OA_*5wdIt+8jorAGsNA2Emx>+~oPwmt~cumKQ0MpCG3YwrVm}FbqX!il( z_YIijO`j4@@pmKdC2PZa>t_6DKHQ0c_qeevB{W_$o^`q2~^3U7@ zW%2jaz{RU^w=lHAtVniEZKq3y@l4&Hl?#pW-yoCUI8(u+qrpwcW)~lw)X1+aRI;)V z?dPfToDh{!iUmRjmlf#hH?z-y>J-h0X8kU<03vhVxa0a;(@JZZIiU9c=QWpjS|?5? zx-4pE+``UL#@|_G{OZ+X5oWlz!Jx}Pf*;na^*bDQSB{;KL@*NMn5gxgzxba=-NQ8 zJdAJ@Z@qq$(q+@@R3peE!rvNyU(Lpdvk@9ulQQWeg6L7`k`quB%HNzvo-1Lg6+reQ zT!j0YPK)X`+kcothQA|y7Vd2I-7;i)dO*#%e2}!QdDaOYD*GU{TRK&H{XZKm-Pgyy zc}wi=`V-y<*9Z7D?%uchzFB#a?1RSoLl=WWN5|`Zo0!)7;x{S_IKH#fW|=Wmf#`>$ zMl5icnSj3cN`nCqxD2i`z?ov4%gVTD(K?80mx&52!vsx#&EYbC4(YBbEo^3y)Dilz zZ6zkA6z-XzWWw8Pf|y4k&$Y5s?ib4? ziMfL73wyq~rTei(Q|C_D{N{Vo1$k@+_>A_3p0X$H8$sjDHFkM&H?)NCDl8Br&V&xG zQm_*~85m3q&@ivTMz?ywLU^UthR|5XlL76YjuBWnBjW89*^1Y|)TW3i`Bh>bJF6J# z6yHH0N3)pVOwps5JaGV-*B@-LT=|6)tiQwA+B-UUbn2lN@Ex_>n~O@afRd6zi|>d^ zbEDn1H%F>dx9un8%o0V0%cPCXMWJuy*OY>umL0>uekrikM)xuFm@4v?{pl%~UtO_r zlm{uteHQ!z?XQ&`jB65HRZCd=8VyE)5AB?Iq#5xoA= ziI9c#X#U;Gd^q5IKI8kga_jZIVNLS54Xe@>PgJc~WV1%z!l~sBscDIkBU>`M9Y(vJKWn66$e^QoCS&r zjub&b)aUB_e)s+S?)!QE1uo$FeBR^rHqj25$aY<}^yIJzoiUoB@@L*c<2)mEc>PS6 zlS!5wI<$_6ACc?MpU>4I@=5c4H@EKm?>Tr>czB4R5nd`YU0*j!Va&bMHWXnQpivel zNPxAB-Pe@++g>5F_Mm>V49422zp~B zJWZT%@7{9{Jen#j8+YVT(tHJ^0}`;^*Hgiu8C)PW*ev3<+UCZd@#B9$KRfiZneHns z;HrjarVNZG7U zEYo%Djgc!2oNXqz>u@F7tJ#_6}#olYUKG{rcVVsCCSBc`*ncn{wZ^ zCiU=D3%A=OIs7fZz1nh*mv<6)_m5}GZA6Cze8eIIdtVYs;Fr@8Qlvvt)OKl zL36|7H6Pz8f#Wph(Nys0WGzrV+kx*!95v!8nsewiiWuIc2fuYAU?vI{ylqe(+D8- zP6aU^(?zG%k~c-*)ghOFUXpvd^{(F!-&XQ*v(eFRrA+vpGMsECl-kVf%L)v)9W}Px zIi*pVcd$AyjLzC}0Q3qmiMJ!W4yNqLk#niNGMYwmva|Hx4}2s8Sd-y*Qbt%IfbP`( zwx=Q_DPl=twvf+wE`hI6%#+<@OKtM)B;I#QOJ!h~MwBo>(7$y-;`2YM1^(d+%3GaC5LrR}4;OI90X0h$w|*oYr^EGiqCP?tEDc zzVz*;aVloV4~&w^5wprw>`rQE7Fb3-rh~*5JPC3={m{;w#o6!I-ErP@)TJ+Q470;!_IShGaFFFg z470#3ei17|ny;K*?}Cn+WO2X~2NwU~761)XF}ZONZ{H7qR{sfslV9()fh7bPJv89Y zCxF({X9(?o_oOVOTTN5ko3l@5s{~(y%Y2iPtWr=fu!(zSAoT|km1`E`2WM) ztB1vA%Ae8JFR+)0&%sLDs9B&=Tu>GdVeKHXj?|anN7?v7S>qb&NRwX`a<243Zf;Y; z<-$f6cZK@ZVyy}$+|sE`1!D8YFxopG>s;C(49*3JTju~`C34<0oAzG_*hYUf3dPEa z5`AS1$4WH+vrDQ&Gd$Jn%1+1Jan)4YgW@URbomg0O8e8zo_w&>DJc3dlXewjRi;@Z z&wI~*!Ibn3zmO}nSy(1?QkocDdwZl?jJL+fCaMa4b#9~E@4Dm!X{HC4v> zCdynHlLC`Sb@GqKK!neHI!uQcarc4rEdQ~fW!)rE9ES4#9a z97ks>C9b3%Rt?-bxv65tSX3~7r`R?z-QjUb<*1FLQL7mFE1mw~UUeMh#tGuncb}YdJ(k&Tkxw4Ii-piojSz9I->M0+&YG#}zIlDOByQ2g z=Z-5q3V>1(eJpZLP70cbnw+{a(>`06q^iWUJ)C-4bF19FV64_eve?iVM^BNd$obnz zUL9%Z=5^R(`z~oS!={C{w?XmkOs!((D7#n*9;)eMB{{J^sI$N_t|Qm*yvbj1S9TKo z)rUQh-B7TfJ+!yhMJk{Bg11k0)XN3Yba8HwIwK<)Aya}L z$5Fk#l#?}K4xz96^ga3(>RMEhy5zQQU2L)xe?pX#3USl-F)uTe*`_ieVYDB$mK>6p zYZ$QWi0L(aXi=$^5c7WCE=HaZjCsBW^%aecmULEoRSj{MyL{fTb3MPpzcm@GEk0f# zi3>h|it$}Sv%1Mso%BGxjKKStb~VJAp=uc_i|3G{smrU zx+!AAFT@Uo8h8X?lqci|og5SkHu&y|rR|!8UKgUoJJ|Mky8s^DH`fz!fjT~};gHf3 z3Bq{WU};%3oxR-ADNw=byc-Zm2h(%ecGO&qj`Seb{N zv~hGwn$RitH{kT-Y>CX>byu8!sh-pR*JASf*xiT4T7ZTWdsoIRcy1_A*s&T}acbU- z=k4+CWX9wwbrYzQL>>8Hy+}!mvHXJ*yrmT!W^{BmODX^0ME*_^1d4Et50fVG?)2k2Gz*PV zlpUKg>~YwywlJ(>zx-$3Z)bv7k$e7-Q%>O!ea58A2WU!BG~ zRak0Ut=?m>xLaqH;v}=gXYRbq#~#gqwQh!M7DO4QU=!dfJ1#*0f(`(Pa&l(`)Na8f zyqgo^v!v)gNO~3)LYL#zpuNclc&#=ilzgQwm}_@hjlmyYA8VvIN2GTRq*$lBlQcuU zvcHKncbH5kgG5P|5OB+pvDba>ejIPF3er>Uism27(<68UJhpD6^?=Xaokakg=Sd1| zl_Gze9W?ciEXiJLmrWwm8f^)>o4s2<2D0y#aZQ1S{7D<5FQR?-N`oh#5r2d(8fq-G8A#i#dOYkio_8m{J8&0#xjDP;Mr$WiKC5x={M<;)bZG5b5fV-7l`ABH zX6i+$L5cfA#(OA|Io6NX3io899I|W(B?l56caMK-o^K2bEG$afIU?*o37olicP3lP zx5jkSm-P+xq}y7=tkgB6&W37K-{0L8Ku4IT;9Gl!Gqs(vvbo~y`a-HvaL>}DGgM9G z-_|*Wm`ey=Pxd0JfC_rPYc8LS^^;OwR(n;8>5~|6khd5c7AFG)_5bT_oq9MB?mL0ImoN032m_rK4fQ3~b zvN5syeIszAsnapApFbS9JRJt?ozFWw`huA}DgZ^XWG>00Ru|6ErAtSl~gOZ!a?Au=X)h-Fb98O~&@K0R~0vV9O5j6OV8mye}Xw+irO% zvgE#EPL`$CdeoQ=cNqoCvHxlM-8cGj_%nBd4bd_89A<&toOZ4e&qM!;#$q;0}^eMj=n$0t)qqJR46l|^-}7eX zxR+%|*3XrBVl#8Fp{TUpbIY+6*kHkDt=y}r=PsW^3cpQ-s@3-Z^c5ajM09!&6F8oH zn3hWM5c(Xp=XE$f>~P5^P^=FhPmfUO2M*p!KW=Zgn(5_*y}blDrFKn{L41Xlk!gZ9 zR1#CAMsV09{|zq9^e#zHRhmswry7Jp25{r^gi^8bT<&A|SWkwwEGorxvLr|9Eo`DU zt1H}2&39|yRJZby2S4SO*TX*khkyUHM{`>#F!7Gl3+9pdZ?0EAHj5ar#*q_E3*44q zdf=mk=*k-@znnTVH6j2{tR^1cc$4o91?Umm*YiR|{n-^Vb*JP%@xSTgNw|F3 zeRQcJq;EE^)y6?v+6#?pIxT^ZC^ckR`Ld?+0){Iz9qHXPuX?f< z06Nrm*q=B3b2VYX^zjAMxLVWt*D0WamWBuaRlE4HhX?1{10N?lk7&8*Z#i-BPh`k< z)$meNTzN27G%M*c@oza~bx2p4<)@Q@T`Gk!?%BB)UY!JJc9Qr}sl)-x&UJwab0NxgrYtDdo>U#YR7E64?#-{R} z<}zGyIhxLW^YC=jvQTq+CV%d{3^?M@p8Z$PYizTxE0j*Obc?kd3UA8{wy!Fkt&*R+FOQhJU6G6#$>K@x~GM)hTP z-FN*6EoBy?<;C9LgA}zW%2d1ju&g~jD1Q&}W7a$~u%hdP)ZA)Stdk=2uFNB*I@(VK zw>f&sNgXwwZ|5TEu{t=CNzZq0@iH*CuUI-7`sYf?e!VyjO-i2Z1}z2KhLKj5Rz!NA zFli5sotm3p=eFGu`I+@FJd~Pd7mZ9?N{`uga{q5$rHuoI%oC)wWI~_N7!Cl0=J=qk zgB){XM6gtCl{^!;vcdidCL3=ZWqqFyU!Jd?!F*#~U#d;M?Tf_^WlW|>O)_c#|RXQmH&k7mVrJ1eATA_+YR=v!AH7Lu=H77~`P z20q_iUfEd=$cHgb_mBPBBccVhaP(WS8^30Lh|1xryKnQ1^!7|d^W>bSZ=KYO5AP6U z^y8S6{!2jnbFdT{KL@P4MwP}kdWXNFJvD7LQwEcCVChTZY*3Q^O~*6mm%)Y$wu z$5p_om`@56rkI#B$}!^JA61?Wn4sHKRrnIf%}-M^n{pAZ8~^{dT*Fog{bG={)yfr* zBO&W|K3m*GGPTG19d~EpJF_UZ)@WZ|U(CPIxPFwW+!3D(-`YC+@0F+tmznCpl-t!a ziKFQ+An+%`wWoA4_bP-T`$X zSXX!O$cNa9=||F`YVgMGwSe!jX@k?WG=Z-nF2~HXqUNNF0l#PUmE0^nhkdb-udw_m zZt1`~BzLF+L?QQ$A^)zZsw zql+s4AM;J_lf$XXjGw>iFcS&iT*3r{#Ae3};WdsczBij(LM#H;BJmyUT?ZuFJ!z(2 zi^0)mZ0+?I1~e0ZRihIV;>>G559Fa9MGmju`$lF9TOH!3EsGs5>Az3S z%9C@EcRJ#`=!a=^i*Vfs8DU+61{@8TZd)YRhxm-N_y?}G0Y)nv-MqrjU7@*b7gzIt zm9CdIJ6va$ayUa{*UJJ!xzV7;Z|5z2s{plr)Vs;12jf~Z(;rO5R%@MVpw!gRxifaN z)rhwg0cS| zqJ&h7OlH48iTlpXM~t{1m^W}9OA!q ztXBc>TY#E3M)_)OAq)e0n@a_O$|j*^G6d+70DxyAo0?Aa`PX8u8u3}DfS+Y!Gz>8R zCV4g3Td)xX+M8!;U|;v`Nlj@um^ft-29W+GCWnUnwedK%k2p z+VCx!fg*;!iM)S1RTc+yXmiK<7(TfN#)BD`pYWd7g^k+J0CTg8L8O;u5%c$<7GDvj z^LT#ax4E^dd0O?G>3z?Ra@?rr99CCVOLh2fcj2OzvfEdx$7t$9;3*Z|pK6;6=Olk$ z_)9|mOyJasVR3-K*?;7>UF4Qmgiq-2V~^oy(bqluU<(8KgwR8^L6f%h;+CCFt!3Pr z2d5Trq$hl!7uf4}IfoWvXP^Azs55di#=x)&z53Z8zg;VEbzd-!>r&ED62)r|=l39+ zhpWsq#fqmkU~}I*9MolmsdJ6aWN~4^s4Nu z(@}plYs;FdHKSIPr5m`3m0)(G1%_M0Cp-zPVdRXRjczN9qgX_3U zMb&aS;DrA4C`SX|JQ+BWn4ZM{qO|>G$s@O)O@hFPRUIOW9lnQoqZX2DgTA(@cvqAU ztz>HB!nBUT15A_+y0au$E3z7VUX9_xPpZjXw88;@U;D8H|H!H93!ATIh=^|iMqkgT zc8~%XYsKk-xp=cNrsCV!!s;6@Muy(NLJWU za7$sbL>q@!D2+BZLa>1P>>I49UTP05YQ7CVP)1yqGG$~(qCWnRui~-3tLAp zVb6zDi>GlS_lo6S+6CQYR|l?{LXUdA2k-cCzW-L@%oFEcWxtUqGwfXWYYfqG9hZKN z>K?!z6b&99%nk6HjK*;DbRFfV$Rhk0evIOJ-qKa;&g)AC(WeXN@AqWp1Gmd}!xIw* zvFmn$Yf<%KWif-{gw7+H>eC6l>UxD4eD=1rFAVmd?pmtWbTp>~CKbNByZ&h^ZFfu} z4M=lNjzvg3IwO(pB;vO?75O&Fh z-79~~*+7ZjToLc#eEs0|CqdopPo;5ysm4zb3~QDlaFwz_MJJFB>+LlgVJrz+pOHSNj6UqTQtT&sxI>FjpMk_=OaZf@+` zS=z|Nkw0pwQ+1|2vUs$JYNznbK1F#}%1I@q0`jpsO|cKw-qPm^A9M%S!rEy5xU2t%=Pgw$+&i4-am*KIrPG%H-0YXv zC}fJg+4i7zilNffcwS83vi;J|!?;aEWJxAQyKsW^G6Lbid<-_#rHLPxvuP@8g>ami z>*;QJA?M}XRtjG9-%INo?vlbf4xiLy&@ZHxAd(oj=kd^B{*afvlA$iJhEq*}_$-|$ zXPCRIf_gDmBnP!!*8?9mxpkOm(Pn!$M?4IcoKxPX;lOoU_>e77l*;kx?9M2r!fdA9 zF8142n4qL#0w+{x;82^4Mm=;^8nJ=%E4mv#i>0&~%xW&J`P?Qb3K6jTjL zA1_?lM}?t#LcE?o`uNV(LxG zTH!TyZxSp(I$f zL;C;2UxSG%gfBp9U<)PW%s}I{VKfNNtLs?vz{a~G4$ifIA^hyaIWa_emKRzZI%4?B zq1>YV!-zY*L*=E)NbUy;%E27^WNrSbvTv!^T2dUnJ4OFWA$$J^*b@2s!Zmd@%WB+Q zYp>Xj0MG$TG=klMPTJRL+03LYh6Y>Y)J{~Rn!wvtt-W?s;Ica0bH| zr;mRX8LU1w!n7(P9jw&ci1OE(oYc&EaUpmnE{N`_*sE;Mq21OESuW_i*GKU%XC}zE zzopH~|4f%!EC^Sz!6Xn^Ur3!+Q>Tkiu6gdr6ma`-vmWT%;VNE6rzCD)chuu-YgA50 zq&qtYGCO-vv+lJPMjNuV{A9`L8F2TkT%9z~fP+LgQI??^UUKkK9F6^kpInG%*(MS8 zbt?}eS6I)Ej4ncL`@1tErQHqKCTc_<1yk4Uebpm-=PWXs{%Q$w{R)g51t*UsT~;(R&d{sZppwn?o$^ERnR#{W96*}D%ceT5PVHaNYj?sVA=8dIV1Y?oh*{ccQ zqk->;LD3NVkAi#0#fHz&=rO?4wQMAp$hO-~Bm)PyhbfP_(VO=}*SxhOq+0Xb;-#ze zTtDfiCYMsYF<&^d3z~vnA0wYOTO-A0p$FK+a9*sL@>$W$&>~k$MQ(YK*8TKOCG*_i-fkj;c5dRDe10?ILQNy9 zZ4sjYS7eA+-%oF;!)!sf3r(hHy%3g$Plj!TPwKQ-=X&)twz9@Hj~pr(-EWR`eP=8| zv4Pb0-SWIz%pYk*e4f3%HXq|z!40MnBKrsiR+8m^ktXwy98bK9JB<6clnU5fDPAl9 z^0pH`PRkAGHs58pA^PSF) z&L@=m>GLP@E;0`01s|!ba8`>hb*iQe+NNTvntprY60O@dq2o)CxYBX|QiGi}>)ezM zB(5_13W5*}1o{K^_O(s(sm=n_uBKXfmEt$ew`_~U_7JYPw{Ir-@4;Kv7T?N<^q!nh zvms)g$(G7@3U;WCQ*+{&+6YkvjjRC({C%OL=cq| z?;}%k47J6wJIMW~LaL5ttCrPjbmBaC)cx^tDmGdwV@WylH^a)mDBJINOT*uxTy+(a z1P?9ZMow<(F*lilpQp0k}` zSG5kdE8U9-+pF~RMEa5T`XKlJp_eXQGfa&eMy3ts=57NdCQe0Pwl}m=(Fps}&t>jPwbD zrBiB>1xutj<+1`xSrNuYga-in`KZed?3UL%Nq!a++3s1be-SaJp|1d`8;=#ZH+jba z!6`Q6q-krTY|iaxC5@Nk^4kxYobOAyB?fE}_#EiHUe<(`nCh8qHTBZ@dqzj{n0oKn zeXMWe5u5ag+$Y>taIX3CX8Y-+ELXzzfSaJ<`+Er?_fK!awi^hqV0ZhQnD#b@R1&-i z01i*vz}bx<%#MQED^#ilrlba=^qHQ;SKVZMFTAx%a|alsR=B!hhdV?e-B^BWG0GhF ze!9{WD=Rh>n(4h0Z~g~L!W*DT1AJ5eL-u=VtSR~Kr`qk4;#Y!O9hU1coPA!%Rs)63 zC$bt?rpbqU92RwYEvgRVpMq&eHm$8&eC&kne<+VN1nP^FdvMJFUN-d}aX7~BPwq`)+_oG z*IH=&__CVj-g{REetY&)`q=LjH8~-%KD|uAj2K(bI*+yLhYg*^DD5E|dth`Q~ z3RiAk>&~g*=bid`&S>gFq3*@pFse)=_b=ay^^4v(O!k#m6JvS}8fQw4VH9Mk?tAUK zqMPGHo$;+_hVkIV)Cq%oWLd^}j)UYN66U>mHSBoJZs?xF$2`xTy*V!<(O-4c&SX1}dfQp2ax9+>=e5p%bJ=Q{X zO#hWeU9umaCue;Abc&`kMz0W+qGYY!$Y(zbX@Z$L&N`n#V#Ten6=9J@4x zm*1Wk6Hs#{v5s!Of9t~9-S55;1p<5zvP0@*{Mi4DRcZfB;CeW@flE|>COqBzdJE*C zC2rV-wkSDzuXMdd?QQsv8>>a?lG=YBj!H%PZz@y$OoK6V0CROj%)giM^hRE~<{g|6 z?9BWC9ep9W`b>~mQ;#v>lLy@qx;dn|uSs#NWryuf8>1*V|P6$WEW;?nr zS0Nq0NQLomnnvx8$(h)htB{V<)-xKxZ;iZ$ysB%Hx#VeU*01wi)HFYM^5~oWaz3>M zPnwu;JB@e=kmKVoyeD7-XS6px3K;wOZoRE+Mru*8dv*uz`K7R_;s{|!@Nz=W-oBY9 zI{uU54F9UEM1yX0DG`J9%y%SDwYSYoXUTEz^gA8@+@Ul!ZxH?oy7l8)5p`CD1jTae z^W*%wp|3P`7nUCI`9tb8)1&?t(|8@y{$}rz&)HYdcMI*P;B19+t_C->-@!QE)}RT< zlN&qlMb-9}fYm#hIiN%as=#5Y&4Gw%w7eUGF#r4t1+{HB&`#7>8^qEf#7M{0c} zed?j(ntqw2U_r*}x~t#;hY(wiLH0ebAXk z*v`iKDl}?;b9Q)I& zNJZ`kWEs~te6OfRL=?HRUN_${BEn?Rm77edW1+L`{a+cM`$ps&(N^dZbZ&f!JFjOX z_XC@pFI>}?q#xsWFPI4B-O(L&pjMb}FU7&;1^W7`OC#*7T_Tjp`D)E(U%rDF%pn?c zM$#2fu%-94!2RnT_$nxU1{Yn%__MSw2zvpl$S^9CK7M$8ul~{BH5i`^FBc*XE;J02 zLCD5`Td32kse#^Qp;K(0Ty!8Y;+v?_*e@?*84cyK7Uj^5n5|z0r?maK`8UnnW%v2gL-gkova0ikH`i3qgkU2?L>yH8LLz za}4_(cq89dIy>EWTwy%dt%;;Yov#h6p*@iB@IQh{`;9)`v-oj7XG?EOV=Y*&B+1lb zEpxOH6%p!(!B{R=#c8PkLeAxL=A~8+jU6K+g6=|w=CQiDPM=F}>wvQEQWdDw5@~=R z)X~jk#}RwAfXbeVxzf*sjJn5=)GS%=j>S2-q?ujUyycU+5@$|)@VF4*lIi`TW>D~N zxejQ?Y^;FlZ9HZk4_fGtN^K8FEv-C3lEt~B&OBP#6piKIG1QRsnbOVNyL#XrvGrR@ zU4bpT8mby6j~1=I(X942-B|5!8(99Bq@^~YYOKrPop^6>wyM&4XmSYmMav986B??K zH_GcbY6O3giOKq{JU9yjG6R+-<_XiyA`_D}x+h0NZW>@rI@LwOLpqNnlC-5`8uT!U z&e-KzT1Yn?k4&y;W<=>022QIi-Z1zfYJ-_aF;@t@Cb!?KCsGtJU zZ_z;}+^cmC$qCq+zIFI9C|)qUX1Ctzo5DTlyYtv9Hsl#dYHBdEZhG;J&K4C#OwQHj z{v)=v^x4d)B|9}MtnFN9U0jlF93eH>sUM9j5vg&#KZAAj)PuJdu|;q0dUm%Vvk~K_ zhM}I!K`gRleaN3sKI*<5HO6#fg-VJ(&;#q*Ll(eoM1I5?o;W$f*a7gf=!gO$hzTsB zt|o-7cZLC-!p;?C4ho}PduyRAX6(Qaaid|vTW)1={3&C0ZNcrF&%4fum730|f4Rf~ zb1!X{(*`YhJ%9^0Hj22LwtUCNKrmaWWpjpQeqWfc9 z?}YDl>A4t=d`UUHm-BueHgSCkKb>|KI{E1H^EsH>CBLf?A=t>`&doP4!JCPH{k`3s z8GcT~Ordz?ccWFGa^WwQ@x(9FZ<)&d3d)?N!Yf%4=7{FCF9s( zl-r1pTkR-LWkGHg!3Ov|qf>l|-J$`VX2f4@x^EGH$ravlq!qF^^y0`Z$<(J=mR!A6 z?mjh*M~%h9&EGd}O@h@c`xQr982ZhwFtOR%Q{)Dn5`~{zc846Vjr{RlCE7l$?u_M% zudz;P9W!%VMU)l#adxBaAdhifJ>*Mi!_lOYSN^-h$nD|f&s-VUs!V5SS&gsNfKifa ztTCG@L7toN|Ie+x@xg5}j!xk3B`PH83mGor!Grj7wG^FOlTMR$pL?@3Hgdiu6pO*d0J81wF9H*F(4y!15iYCt zTb|t9a8jL)3%dSPTO*=$r$|O-9_q=pE`Pli26wue_TO^qDyigscbanqDl!m zXB7GyGpJh}0>)QEn-(lO;pc?DuYe?*%!Y2cNh6cndPB@rE2@m(w5(sJv+b4@j8|H`-twh< z`&aUzw87X8W^NdthNf;if+V zCEri?2X*W`Iji(D^cqjA)#8Iv&6-K)14X5J#*e}7kZ%9g(2#E%o89w!zinJ#T_tY4 z4>Z!^x+zV}(3`41XDzu-mUfG{u{!58NG+}B+>c?znQYa8QUWo)xXLCG#S@A)kG+D zy8V?kqJoqP2^+~I@U1%sjf|__9vM?{5BpTVtcxNx&gs2+%u7`jCwH3m!|E7B{~rMR zOA~u@$Iq1SZImrE`fhruH$?N*@~K{Irp0!6gp1~i9gV#2tfsnjGL!rOHcCj6^2X+C z(_#lE2ta+Mg-e}udu@AlzObjwqxc2^!u>Nv=SAdg$@t67N82kUTleD&<@W-5CeJUb zKl_JrDz0xZe={T+CqrX3j8%d4+A<7_gEcl9Hw6$72)!jWh(Ku9l}LO|y8N~g2Us3O zQx|eC7nJGBcpbrBNcAc0m#^QH83^^7Y}tGK@O~4c=tdQkur6BWF5D}E?ZO`~Uaz|m zteh*kJbT5x4E4k1=_dKI(vh@ih`uSkCTK zU_@jdz42u>K&MVB@M3nMoxu@6G6-D`>E$hK!Tf-Gr4nNoOzLp|M~QNMM0O2exfImm|1D9zMJV2xaG=t71`QJBEl=+mKC^e&dfqZti^_TY zCVW&XQl~p7L_SK{Odo%>rq)Z@uo(tU0Me$m^r1Botq(*%7rp{G1S z-Tsmm{EGYb|8!#iM#MnC^!^RQKXN~{ zG|^u)K@FE$P>Mb`y+o>UC8A&L!4X-a%z+!>6}uU4Bh_>1XPW*StC_;$>6>buag+}` zYhMvWjb$a}q~-O^2j4~3Cg~?M*lJ{so(o%>dsEXNsee4(@-1x0TAsALSXIlrpkJx& zRI{V8m8$`xAhtbCssQ|_nU3p`QDnsSYFgy}yF-LNovgaftf);%dc=l`h6hAfmV;_>=eNplrHNzA}QLm}BJlPeXJypxg zHVkXRT!r8ZPhC*k{qX-rck2fnYOQdt@+mAz#;@pZuJ4t?xLP|vP~RvUs+FnUPmcWHTzgFKONpaTB#q37_?I(p{TA*Uz@c?s%N7FM*xU@T8bw7- zGLN0mGTkm62Cx8&+LbyhO{oHg}vwVC4&!P%CcXi8+FGPhMdsw=c zoO^q*Pd!YeDQ0Rs@v@p+gcxeP7`@zCnUv%7Sy`nQ?~{0nzkXt_h_uX|eairueH^x_ znCd!cAAoTixd@Zg>dE1^%l~-RqV>{xhMy?$nESEslpgVm9mCUnZ&N*{@}#T0({b10 z*v-WyiYN4K9KY?3BY*YSJ0dkn8is82CaDdO@U%3CY7H-Ub*Zs=zQ52#nsHv{RARzo z0rkYX)r@hO*+atxQlM+dY6soas6x9$mL>~^V>J?q(0%c5bSpQ!Z(uPU6zJ0yTi0*a z*gi*By(ss?U06|wdfBA6^}0!BPuz{JRB%&RJkc=cXW+Rks45^4YoN8SK@#6{AL z5RP!}^Q~|VY9`Bk`AqEPmQ9+}LQuR6BdE7FBfYq4GmEnXIr>QZ#a^cr{-14&Up}pm z<~%Mv7yrQP{Mz^sz$i|I%fFvaMk5dvkH4Ms%RlwAVSl^5Wc+(e{({rv=*>z8_{W!V zhNZki^vp)p&e=aD>LIgUhI6vF8p%{y=G?Ho-W4hGM;ZCKKH&n|NEb+$5T_6wW>>k% z3j^aSo2VA7L6*YWX?CIR-I*O5rLp#Bo;^8XUq9~@?m1bntsJC%{OtF0IYIcxuVbD1 z=89sUM|7Bi*OOIM6lO=T6~<{Gm5p&YpY-1x=o?~h@UctYb^N!m=0$GX$&XK2e-T@R zI6O$jtXjyQ1&}vzItAGbQTQ;dhmxcqeJ@1#S@ec@75mr3db2jhdQsEUM|mMpF3Aa} z_L4y||8kn+j5Z=mH)uVx_ZgzQELAX4DANhwvp2pj@7USjEwP+^7mXqwDw>68>FFjF zp}Gq11(Q-SCHj2);sE&k&yvOhBUE#+{JPQh8O3LEAHi6~ADB@yo~PU(+XnzFyr4qi zzPzBs{Q0z*Q&QrKsiIUDhDLSaTzP-3OOlODBJgV5zS5av`O|n3UvqS=#y7q*6BtXb zDo>ZGsi?hi0o7p|?i%nSY$RSYtOF#0n*%}T7c^@yYc9ul0MPEU>SG)b;N|W}e_v*ZpR;9sCUQ}?64rAXk<$25VCho`Ytn(M$=ipZ{t64R# zgzRxSvPm$Foe;4j>u4U#y%$FCilwOzNyK{?F@Ps1_2C5U7TSBaJ3~mS*+;B^Khl7! zH&5~cQtpr5Ib2-FWb2LKcDzgWcZ=XwpN(8C2B!)aNxqC(q7z67h{8oVbe(L$tfXL$ z8Q;2b?0(Wy5Bu#ChZeDS25pvf#>**xDcZYc<8M#i659*zHET5n(-_&+2_q(XWBb-d z(FP>6YnTb3Hc63Cz-Hx=6KE-j9KnlsNFdGP#cx}mJR2-^P*mx#7ofaeE_)fBzZ8a z$OPJ*a}2JY)0H10xpVs$c3N z6RVSOle!Oh zGsFBI|3s~j@!B0@CP0&{K1PakkKduw-GjI!XK1-o3m&VN&d)tVb?Md;<9NFy#Vz(Z z_d#FW2M@(%FMtP8*#%B)Z^>!zp zos4*4*ygS@B2&=h9K(w5_uaQOv|)ka_xSXJ5*Lh5-})g_h7-(|nl;X4)Wo`Q(7BEm&*@7;t>aGr!= zIRnvORJ9SJ@o_G_(-Qizf6NaIQYt4ZANBh9LuIm~{yU3s7 z)ub7#7}%U)e|SNK2DWaMaM{c%>w+@ei*@5AXvri{u=RYBeyGu(E8eRU$N=qlq`|2k zkLC^_f%z8wdW=<_?szF5_i)D8QNw*jUYz_Axx0vu`Dr2Iic29$g+SSU>sE(D>npfl zWZr>XI}e}^BM23(NXeWgTSJo8a>OBHktjE6{*$_AT%~<=>y1*rX^q=ME#z=?Tg$<9 z{rLzkFN33|cLggb5ZgU-vx!wgemh#0@(%qqbHx8EH0k*I-g`BW@QYDu63S8JK)zVDaylp~QJ6srep z>5ck$=OTQ<2^Cgpefg6BH=*PGe{&N)SAL$&V{aF1hN$f%%k3H3d}6Fkxv_@a{uH6J zmiy_p(oW&T132HTb3ZkeVhF-)@Kjq?#Q-U2iShr`_2yAY=l|b#re%(!R904Qm5xkP zX^ttbpjlc|ifyCgo;p#v5aJ3bnU%X{)@W|%v{<=oZYiiK?pyAPD=4_&0tm8m(f51* z?)$p0b1u%|U;f~H&Kq8@=ll71)C|mp3OF5De!=<>5VORgG;vf(Av=*Hp|g?t776Bi zK)VJ>s6nT4(3q#%ZHEYAsrp{%=`buZ7aFTGa-VkvFC zhW&M%2SL_8rkL%Bw`aokkQQCiuuqh*C?EFd@87Z%m%iwoKcbcqVL_B>unhcB1|%IP z8C(Fed&b0vNha_>+aJ~SqdRJkyMjl8KmLk#mS{FZ@LOTE(Ah_#lf2vFTzNtRAZUE1 zP_uC3D{Ca2<{qZG*07~T4V#YWkS1@n>zjLo)3m_JNGWA2BLHN%L8?vl;4ZHV6#-Bg zS)eGct}O`w6X_csa+#Wjh1&CJC)cc;XC&C?T@g{{sZ(<7_Y(|bA``%p5Az0;P1JN-zCa^ zoL?B6KaQ5K3TYX++g_wdU6)bzF}|;r5FP&L=-L@qQ~$|ix=yKoJGtkuVMGFs6;zd- z&i}TcV+guvrnN#I7&x+ZBoSU3urALe!t^qlW-AZ(gc)NSt>E^&gwz_)Jn$e@B->u<zL3w(~n39y_TL$uU+XDP)wtCf)mc^XYx$No6lX)nPY(-jY4GI-$#M z0H{~W;5sy(Jw)ttrssAc| zXAvayl>Ij4hV%_M@42WFrlq+Q6E%vrF0GdLh?K=jGBvHi7dr8#eiqCkHdNxcZZ2?<7AMI@86ikv)~#(_X? z?0zRpA1N>31Z*^UFt7Z*+EQYRs%k;{30=LnY8n!TQvPCG{@!~sK1w<9w~}La3L?@L z2ZX%?bk_e9tIg(+{(rIB_=IQd5mVL6m6OxTyYMDocbM#1udR6-Q~D2 z`^~~w588U}_HbDg9Bvukp?H1f#P&(8)Y={wn^Q*?_tenYSC(_=4^=K;=@OaFx$7D=IV3HcnwBg1IKoWxW^KfnOh#Q9?R?fIBx&ZscHZefg;Kt~O8T za2Edfy(enry&yi2H6v{7=Kr94S{lE+5L*vP&ki3hu3a8V9JYyoh$#>P_VRD=_QA47 zW_P5dF=SkQ@sQ$nM9w#hC}Kaa+qxTPP(aYwq!TaTIGmW^ltK0#2S#0DAzGzE?Jej+ z)nOlfiVl-zkv&mY;POuKz3CTYUVa9qApCc%T4v`x|6CO|%nf~E=T*oLrm8d^6QrOI z4)^VbpU3?k8O)=P4nk>{YJyT|zq~fN+SjG?>{IN6hW|5YD}2)-+TwOVqO|6=o`dMP z%A&>{{N{`ZtMcVQmEG4cPJ;rY0xeCCRyEe~v{`j-)J_;StZA7*LlbD+@1*2{bVwS_ zcL|`$iw_&ZvOrjtSYcM1KJ6*m7Du?T*G~|OZgt(kR_*3@#om&s{Tq;%#v-}Uyk}?V1 zP2d2wR$QA_->||;7kqEIrZR@&opaa#Tl_Qn3j+Gu^;oJc(ab=1;VFbASp-7rAwzZ9 z5$_-*GK3KirN4!22!4D`04b}osvz$wu+OdIjC9`=Y2&`AX8}waKPv@|u|EkupoE($ z9bYjhv__R3l#wBoY4*yh@p=ZOICQ4n1+p_wJlij39+rwlrY)fpYxOF zRIakdeA;BVScWPVA$TT5<%!;5S=rJU<>SVTz#-7|2Og56iGCGYX|}*l?=#a46C7Rb zB3@uk=WjJw@uv5KITfkvQ6dt&KD{S-Mtk!IeH9#yaM{I99l~ zA`o;5G2fLolSh(;-h5bJFeMn{CO9>E|EM}w&TZvt_XGSYoqp8cc8sh;w&{({@AY4t z>F~ioVW{uvTtZE>*hS?FaJ_^tEqSW#uy$MQ`HwnJ>bsMk5gX&LP>V!q`7dwyD+%^{ z>fa%c@w_Be^t`EPc~E0V$Nl=%_QT*6sWDaZsA1u`TFO}oqd;GjW>p4PU8>4s7e{WO z&(_`V({PmUzPvHKCr`Xxxaq05I2F#Rqa>@r{mWzIlc}kRk_X^+JTv7nN89( zbgHRvRd)G@`De#XwIk7E`(M$ir9##x)d|aEE2mQi4Q>y|+J2M=uT_-t@-U$MA12ri z1m{RwusVA~WfZYjgnAMrTNNAlr#@yuap(AtI3^YMGA{c)KmqqvbWa6yuI2q+9H!%6 zq~er5!av!+jW-C6^%eOXvcFK^=ce``#tsAd7S0*sCTFzy{oxQjQ7U{ z#*IkOF5}eK{^M@QK25;GmSUp;*(l_G%B6E1A0=C_J$bKpCT#pTi{1dbscVk6z7(lF z1Mo-uDZ9+JtIb$RzLn4A7X?Or^dOVNhBr><7gmlvjp@x+HxZ|YjXgZ~bY}QEZk3z( z%==25ta<~6mDy`FL^H_D+2*E54&|qZbbncIvD-r!@lw)ALXA{)SgWo78$6U)HL1rn z?kEhKqcs<5E6=TC9xgnf?4HrasRI+un{wdX=kN)AFWlaFbbguIk3ca6P@~fAKI8Or zDw6*q@m5JjlW<8u(JNO86UC>*C$^_wM&f?f+m5pFV%3R$^j-ypQje}&>!$(#lCt^= zOLqKcPtRw~S&@~yaS`ZCn|5Pe_F)Xcg&I5RWXX3RITj*Z5T)Zq1*`<+qmDK0lADabO^S(B+=J1)bfd z2^wil&)a?{TC;}g&MWi}Cm^Syo`}Dm4ksi1mq(4Cf7*<6lzZT!y|(u!GO!QWRew>m z{A}439`H^pOZ|{)d`Bxxsu2~m1heGH*kE?i&=g+5h<-JeT5K@p2V)5;HTK-Ko`A-Y$DpLLqkR$d*37QijU1D z8Fr-W-;S{o-er#iw`AsAfOyp8J%^65Da^U-kc1d6aftWwVQ~Lr1oMyz7Vl7=IBeIU zGBi-Q&qeW><+7F64fbDS6m>zh__L@dmS!yEkS{~&^|Ce&fJ`|cp5wQ8Pq%u zj`^;>nvqQCUz1jYU5GI`%x{Y?I{?OtIIMcrwrd*Jt!f469Fu-+4c zxi7=8{x7PkO@zsLE@&6!Yy91VI-Wlb!sJuFATBjk!S<`&(+s{if2H}Y``n!9ysvLI zZ}oa6rN~1Z^{(i{arR%=Ln!C|NihuG1q-ya*JJ$!xc9Rrl8|DU7qs%ujB$X0N_fk6 z#LUOgxv(TfkBwqKohn<#3gb9T^yZ8nTDT(JXK#4v=>|I6{W_l6_8KoPRGxh4f5Itu z(zCI^`F=qAYE8Rsrn&&D?Di-ztL5;=x_X9|MED8fqL49zSPx~uB$$SIp`n_EnqJ-f zqm0_}^+!N+x1$en9^I#Fm_QN^a@h1dk=3k_SMzKn2q-R+Yr9F4~@4%_zM}n zK%({96Yrt_(%@LmB^Kuc{E8!@hO#E^%@KV8+uZz$#=}VWOB;OleeW?>)TK=-hFqvx4a|Dlt(OIaL41I4&MLc7Fy@sD^mL)uV(CX|95o4MDbycu4VOaA=L|Dvz-X*!UCWY{Q(?rRJy}HZo z?H`HUfnR$)3N^Tu-(CGWK>#_m(;M$hGkcIHfV7bwZH;}VFcOQdd^@#cG$z7v1@j*$ z>{ZaJDmF4he#yZy#VS?1-}|M*MtF~^(|Q#z8o+dtrOrOS;buzCavCrZdG2GV6+u|z zc`!IHpRmu}ZOu3p$?W4A4K(I4Df#J+`pn1hsbv9tm+4X1qp!OQxM-{1Xi6gspfsEK z1WEHakG`+{Z94P8fU&e;%1Vz!j({(Q8my1@_Q}(SjcL4i-u6O(ueNeHgWBSjb706R zdUY50k#{RS9{e}ehFyrCGjfSZ^UR%8(P;ZZc-W@3+1mq3gXSonyiQo{vy^tOE-SZX zg9UPLQN&&N>VpRDUFoxzuT25Z-%Hi@jg3lrO-ep7x7g$1&q@)w4HK^IiOE0yQ1)>N zHNi6DzKmn4BewkkK;x-#L7J5*HT+~ai-HO~Vvtt+GITmX!TYvW`GoHS2?R!{muhDkM?@Qixe7&@QwRDtsFJO1F z$;d#)AuU*@MR;;9Hhi_(`KHL92eji~@^dy_9{pL0f-lu@G*IX~b;!+tZLjE?Fs)>D z*k4p{e*;}TeLa%6SSw$r8Kw^2a-342_5CX865(>wq4R#4vnDKxf23Cu@X*`2x{HM1^Bpst z4nxUE3GAE^v#K_QxjVPXO#rOHKU;nI=-}otN{4h5XfWEd$IisyDi4?zA@%3XlK;1& z@Nzk|`;+|ntg=k?^rCQSpRJGBXU3MYTjsWhZ8+X>Zs<`Toot_*v%L#A!X(uABX0DF z{9u8p2ddgE$_woGN?LbW`V+n0HsWRgEK`((O>o;ZUL6YTuodpJArmhRU2Pg zx>Y{R)Q#=q4aK_&+nEVH(5Y6oNpxL%P&TMC7Xtlo)gt~6<1Cn;PSy+6asSUb#>rqm zYoE}M0L%8*B}YF3Is-lQUwEvW9&rUwG1R5Qw{l<1Ij@I%@w?m-xsx{XxnFQZYu1LC z4XiO^2L@h%PB^S8G3CE{Q2w~5&(odwY~UAt{G?`PH4J<^!`>~zKQNQ}6kgJ!Db)n{ zFSX(Gvb~A}EoR<1=`C|Bh^l{jAT8RT;xh?DyXVZh54-B0_iL9$+yO8*H}~vIqMpmV zE%#g2oCz`uXoy|4?cAG8)RmS79yTvNt+OGRB*eUN^$;6ZL{Jv<7*!M<&j&&n8=q8jU}Mz6{fP>MvG5L%}6z(t8V@Eoh{4qWq8VI$dhDRw_=(3-;W=xK|Qr4)C0&P*J;{_-p1h0xj5}WmUn){H0$YERr1Z)Ua4(zalE5g zVf0?AdGG9G&)tr@NoWozQtAXVb4B@GSsaCm;-R1avWOfj*7FSYG4?`6UqtLLGtk&p zfpYjCJG|9cg|c%Dtm0t27C|ap5M40`aYsEZ*{Q#6*lY4DCxU$xbHO#n&x*6}Qi`jT z0=55x40j^5Ghva-PvcdOOC{fbMv|qL1N_ihUuR(Q45R1ovL^qa0*lP*&-R~v0;EDt zDB)NWRSNxzQV%}3dRcSJUd=sF{90SAVEn7r826aUy_tS3grTtp`6}kO9$D7Ib|(m{ zF-c#KGSZX#!d0L6BgpfY9Huc6tvOxRKM8MICCViFG0v6e^(FcMj{ppf@y^*cOp31Z zbAv%r=R@Lar6~x{-}WPwewPXB(VUYb9YU;yHE5Qty|%ntW*bsDQ2mUYJV_kiS&XZ~ z=gi~o2kO)@+GIv`igLlogc$E^S~jA{L*3Hh{Fs&{#MNMM_8G@2kDH?4OHozbQNK!q znk_nE$9DP9%%i@I_1w7|R8gHG0zJ<9{YY?W!QFq$-X%Y{k!<)pV3y}ajf+?HmyIH8;v_9XG7=o2=&bkzZn`Nd4Rqo;Z1r(o8)f%y>`D9c5(kQTf zC{dpOwH?aE`zilX8jQ6r)O>Gt;n-Z6b?&89$KOblhj=$TgLY48V4Mw}G8 z{|Qt&Vza1Y{4cv&kHaOyeLK={ogh`ih;Ev;gSvJv<9_-MmufpCWo8{98&l8VwhsFh zhZ=Y-`pEhKZIPM6uC{3#?P%{lQf#|$|4y;1WGTbvf>V{6X0N3zhq~5Rmh%?r@weBhfk(cHAkiB){t)wG6uPq(qnHcwry2d3wbtvKLz{s-8-w>zhVd&g^r=Bn!J<-HNWVx z`1h5z$;qRcjfTeN4+iolx~x@Inx@jxKcT_ey+RXR&`g|@W+dm2p+dk1Ec<)9cYe-0 zaQ^J1n$xHL#Q+Oe@kVw_PSqAZ+D?)SpCVAB=7Q!S{_P&lB>_zbggrFLQ0P*aaBaN+ zIK6Lm&$ca2-k)RM(m&S)NDeqC^=amKh>IyYzv`aWn_Eng4%`2(#E!M<={nqh`T#&3 zec-D-`p357_M3ER3(^Idt77g_8n$kyOHD3k|lg#<6hV3_U$94_tf!#ggs@!(x9<2&B0z%@F3qzO{PCXcY(t2*a zkd90WJy?bj#Am8GJUA;?31A{AAPwvDVRksaUFDqI zg@}D7$Gb!uXexxKWOPk8Yao5($<%G6&ycQzd3jZcNuF+R2pD*iH@pupaXx)onB`>; z1bp1-@Xcvja)UIi#e1^eB>Ie-$dhDZt1Xuao>7bSKsVu>Fdtjv{%BUjNfZx$(_5v# zR~`yKtN5@o&+NowU}RwPCB@iVAp;f)S!FdRu5y6;HO&<(Rc=itRHk%z8Xt)^hdb+R$=p16%nuO;O-_=m*taqb%Rki zQ`)n8$~?wx{qnSM*1}cX+F?>7;RAN8JMeZ!a%%rrmbC|GHLopwlM8yBbJ&BiJb>-t z)X9S*1?IFltbp0tH$N#^fj}6#;QVn7 z>@iV=IC`f(T#(lx>{<8I;zLtT)vOa}GgvfJR-C;aMbpp|FJWr~YW*b}tft`9xm_>Q z^R@8rfdR7?``V8_ z8L0CZJ=L9U;E%Xn1}45sJY(!}P+v{@*Yq#H{;LQEu?2>G>9F`Nlh1kmIkOp-62_5R z>DnHbjn|S&oIkATds;XK{0q{C%sP6w5p6<|Mp7d=%~6Z(r7T=`iu!74I8bx#`H2@k z#FLL2C>-;e9gzTA8_BOxQ$~;$9?Z*-Zs|veLsy(Ekf{+vA_5vc$V;7$ycI}brIX+@ z!eNr#2DcT>eR=rY?-$_P8<5TADn}6rW$>eqqY zKhmNXjO3o8R*qKQ!f2`m)<1j0e z9L)|%OzNy~v(~do%oc3Q#1131@EdC{V?7&l!*Hw`2w_PwEnZ}8PWS*hn?m`7JAaNq zEoJcE$vLlPDFg70cfl941y9$EMgo7<+nZYcBNx+L!?S``jE4*JoeZ)Cx9Q&}owWkI z+0DXF(8yJ>!X%Z6KF%=n+x*tCP%&jru^fxZ#EB+xGc@*~Uq1fv7c*{K=ijm*Q^ewr z5?^2pfzlOY`)GlJ=S=zSMBW16)MCaR(QT=axRx}0UK>RgA7%|*ev3gxu{2#LT^j1^ z#vJL5`*)kiw_Z7ck3e{}-O`^N)%Io`gUy}X2nX8XFGg0V-twsN^=?nd=54n>fJ?@+ zR}QU}HvNT^DD6r*huZt}Rz|%fclL#AAOYR5&<@>Xh&I7HrdUBk28PI40+{kr6>i{k z&RlSVv;Su4Vw4^g#IsK?wQWBb?;3Kk5TmBqD^$Oh>3s$PZt|n%qQsq#Yh*)i{= z-vxo1f#;(X_f!1h)RGdFQlX23sNE^jy5r^yauhG-ukC1#xlr}OPo+4+Qz_Qdz3XWZ z^J3tscLS!*iP=T?=Q!yxJ!>a6p`x;OPAPUmQvdF-R*wszcXprCtR(j2!Kv4`ip>3< zIPVUc&Kx4P%QGsW8dEfK->5+>`FM|Kee&c799YC~s;!QHgMU?(B9|mxv%R`PeHOM? zxeH84YbL4+Ti5;cgemL8I6nQm{rs=K84a~%OZv5W}zfqv2!~Ju_+%|w=zY-kg_4MGFj)^k>CyL#>@zqBF?Z+;1lvJZO z19v7QpIYtbAsFi0LV~ioLP}XwC$`>_bwffC+dVIUa9sa>COjfl*#!I~N;p$G5hdBd zV$srIw=r^NJaQ&5);nMBIaYaL%x+unnrYZA?65U=0bhIAjW$@zIcc7WXapHOP0c{n!vR)`e2Xh#SAY*sh< zeKggk#Mbyp>)LnA+`2b)pDG7B92o4s@XR9B&n2cE3%Dc$FsiNLFeON99rino(*U0ob89@O#B`45oR4^&Ex9LXE5TL>I1-KU zPFcOWa6!MFXom#B4Bk>k3Sr$Rw>9slbtG#Zab%(1tKe*-UNE^3pJ-g)Gg?|+UHBq; zI`lh@rPH!O-ie=bdR%C^{=nQ$I#^I2)_rW_8-_r>)Nds{orB9p$!(iIR=a5A-fOZg z7^@|OU{Ukhk$djXln|A=lHZEA&+2RD3w(F3VX>ibx_oCXemy`ySa@Xf5>Rzy{y~|@ z>Vail)S_LKZ^QP`wN#&duhgK%NQ)CSb>5$?bv-Sp!9Eu4+sEV)Qkl5X<_9DFKTZLI zkzQqgS|nS0G@YDZ%uqxGm%K^;%kNaf#Ji}am0wc)+k?9G9Rfu*254NYUE3{pJ?=kn zUXAwpA;RjIs(&hG%cn+=gI$$Z!+zPZtHu8<*mj2zbcO#4Fg#8PcC*PGLxya32B9c$ zyr_SDt!c%N!%N5z!J+0u4hySAa}}VeZ)ZIIS$n7r{!80y% zrSY?>kBEKp~Q#wwXduY^d6ls-_2)JkOlRAB3FE*qq(Pu5^!K}%FFj~*z3F{J_(l^;kWak!- z$ch!FLfnnk>7%sbV{Pp|N!U446*A(r^ai+6n7W>*Nnu(}T($-PMVm|CHk+}b%44_e zOM)nUJD8V)fUQEcJ|}0PMZ{FTcaLG|K{GFZLt|XuH#L}=$JyIqoZllVLr#%R6fn|9 zyG+boiw)r1RPJrmi95MO{ta-i*kyl4-Reni5R-p7!0@_<2uKB*O;f|-5>ARJ8N(M>{>-E35<8GE{-R5n_{*xp>Xpt|5Y~myx5ct|%6>>eG$B2f#^(<3RnR ztY{XpzepP%)XRQia(=)##IdJw%2A)Y`U@$ib^H5{@~N{Q+c%Uj%T=3`rS%IL{lcFf zO~e2+^@I|3t!E`(Tw69>DjC6PT*f{3pepS0NBs@=S!R=tPmq?4e=$D|)+&$nHw2kn zp`L-qLC^;N?+6p*-m8j)U8}K{;1~*9QDHnV^Hdb(gK2h zX4uOd-%Y>1?GgmBazOOZqtWC%@ZlKi>V6eT@%OJ3-EyW)O+BtiOCL)p?=Moo$mwd8 z*KW3RLYaM;{_N(w0L_g50YWNQJCG07YNe^SA2%N#JRf|jH+gsV=(l`>FUqFLxli92 zQK3K~GrE7T;XfK$3QKJ_8V&FfDAt>dbLO+#W4nm2wM#?Boy5UnDqC@zp!@VME#Ovm z2M%1HKTTF+wC$NpkkNMFgy+aF#rG+Xf+~TJ&$x@bucg*HfBO?kZSUH{KG0DNIjK+Z zhIx1=6U~q8m-;&f@=i!KD1#;u(YbU}`=6eF!kv*mPinHc`zDtI9wRPB$)7$DOWMcl zOT6n>r9M)RAipF_;cLSH6RNt4C-23a$+}n6!2uMx&!}f%U^Wws)J<*l9{Dmy9Fsjt ztGJRyi7T_9ECv=wRS>N#ymX{avkTA3jDG&MDK4~veFG{bpV_#zxSjnTw)WBX<_rer zsy~>`>e8PDzo0+5i6NM3O5Wz*)mwD)=6sj=a^mft>hSvo!#GXo+65@N6Gnf`if^$Q z2z21KIl(Ss+;~?b4`c$Jq_|F`Xl;(W!(f%9F0H@rqupEGHNh1Vjv& zjlvX(FT4-lfBM%g41t*h6yKRYcI9lp!jmnX^6|wil6NIzAbeRT??3k;DDA1<(VF_# zH>z<=m?!c2q1YmiBnLMUvnux-Xl}bB0}q~%!o%>ZtPC7ndKaei9|+DZ&QNew>Oug0 zu&H+G=sn5nQCfnQ#{UIRuHG1XVIK*47Su~J|K_3ZsIG>!m`of{UH0?8f-~K&mk-9{ vMih$~(OCBDbbYY`r(flMqhO< Date: Tue, 7 Jan 2020 10:04:48 -0800 Subject: [PATCH 307/478] docs: remove unused file --- docs/visual-studio-cmd.png | Bin 63763 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/visual-studio-cmd.png diff --git a/docs/visual-studio-cmd.png b/docs/visual-studio-cmd.png deleted file mode 100644 index aefe732e57e05e6c9735bcd2061b5007d82863a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63763 zcmYhiXc$ zZrki@-Tm_#e9IQykN;h2z0lXUwrr8UKJ&ZRrGy~e z+*!W zk-$9RG*<$T9}Ps@av({@BN20@2-b3OWG$l($qk1EX zI!diap))1Hd>|K9=oX2df+Wq(WMLj8x#P^kglSH)^a&9iq_Ry=CmIPC)H#sF9z9#r z%rxPrO3=o9G+hYB!`!KEg`Im{-#fs)Vc|+RON3jCvdwSX2U+8+ndS}XCX4-78tva! z8pN<36@6Q4Omi?d&Z#;F|2YbKx8r6b(yermtZAbg#5K>|c%W_R#PM^X%XOQAdiZh3109qWW3y_3aA}oKbUs9;sEx z!|)~#p+5}@{tYvuSbkVYHO;&UJ=@~J)mV76Jl384Ufb3{+HVtTb%h=^4|hIy=fc;y ztqa-<;g%7lLk29)jmaBRQ12Y?l6&Yyi-KMtLM}>y#l6MmTsmYI6w+Xv+6G(9#Kfwp z4kQTC^r-=LG!SjQ9ZNTZOO ze-yduRm-w9n@)0(yVLFQO-4gG<)pCWBh6A_dGbs{KU9~~vrEYlf_waTUMi?DA@uUVyWaNtcFP$^z1f_MS+feUjFv3=OBltg(XoY>7(xanjija|h%S zG4-b;%N0TQHlrJ>D6=h-8xl<@@)=tH$6tAE8)rn}*3I*aDG@tQlyshp47BdG=DIr5 zswTbqwg;CRrfX^jLKvTK_WLyCx4!#AE;tq{+yk1M4Rr*HAAM9d?cPGoqK2Tk7KzD8vR$yy07eS7o9qr zUfE&Un0R&lDIWhC;x1YspRTe2^QD}B=2|B*My~7(T;jcVv$NC#3t+|htskl{FY#-t z6eHVbpNs^e>&biA9WD=be1x{1+YYte3?LGvFCeYN8PQ4;xUPO(f2uu+JGv~p8}km| zu%cS)(y(VFEZO^O_geu|9Bu|v1{6gN1N*6Mr*pVv+<*OaKP~G{I^nu~#=y$n}_31B9^w$!w4?KW2ADpA_%$JSIUzz5l!hmN{TSYw?1k$IOFyQQA z&gXF51$*7ts#67#x;ba+hw!+LC0S#injpvPtch=F?C_zRlox;Xo&n%HOB7uG~2XNOVTIY5BuaP7ph! zIow}aPFgu(6X7K{d5eFQYBe`moE=C=!I_Y0#ZkQ0_l4DNRXRr8Pp7>}yc+>}F~8x8 z>v;QsG%hysDm0zk#6z#T4VYUl-{Sq^8|6r8V>xkHi?+s@PjbIogs3b$Ynh;pHC01j zUjk9f(Cm@&%-}zvBC%0L=|f5lu*=@TLVy5N&~8%fHIvtyWB5k?0UHLcy|$sTWF6gG z^)~`KTCMXS^>9+`CC!n-2v6|=h$x&8c0rffK6js(z~ze$7IgwSx(o|VLtx+$T6t5S zX6KAHIbYWK>G$TgfeiV}QPV}WF|2WmUP7wu*}UxwdXUb>J)c|0=57UD$jD!P4C~kh zSW{-Ox8;3Q?SqQ)kAaeg?pNYMF(M7M5cfH}Lm2}rYU17SDa+s1bMGK<)@&S@EHJO2 z6?li5SwhnG}>g(ryk^)@^+qVYT$BEAf1LTxNU zx=<8bTs_(3fxZ|2S(B;p)Uq~lMTT2pL*Tx-(M&mQMl!`a-LS35 zlYAN}`C_Zto2U4cSMn`s^`%)SBP`&$9$y^A*Z9(pm)T-Mhd}EMAQUIfl`R{<=6y3e zF>1sJs{tuKf+iBu-6>ErHRx7hg@!WD%H=7o3Ya_KS0h&sv6;OLQc`SLg%;#BO67J; zWTx#m0LtU%mF!YUB9;CkRIdS8M(!zn28ppDPxqOIva$ihIcJw$502wLN-fmpB{OWZ zBaZi@lMge;bUqZaW-7e(_CIS3B9P<@OBqrcTR|h@mGgKOXJPaaW|YW^L$KFbh_&i4 z_sF%c1*ChTIBWCTk)6>-Kh6zYo*T`wfi)m!4wVvRr*akkU(fxasqJzmUG;S!aQ(er@{i?V zLb8|wVdG+bF2SP0wRMw5=#Cpv6vedf*=Bj-oGZJHxYoJSDI>cJGQbn}l3#~@SPA{Z zQLpu*o2B_khbir&qD1~94TN4h1zA1CoWN!XTv>wL8bA2~J>;hmcB<)W*<=<(!O=Gb zNY|3sYtbRSo~3Mg4SU>DzCJPMf!$zsZ7^Fp3-YPTa`A)YyKlk*@)W~`i~O;yEcNWH zB1uHUxfD*;8^Xh`#EbXA=wxmpP$Yd^N_s^Zo<=G>vi zVSehL@gcGUJ{NRsfS8{Kga0<}C8+^a@wX#a?_@(&pg)+Z{WsZz&N)z{2+}FF7$DLE zc-u^URkhfqTx8~`b$FmY3o-<^MAz3}pLUPnDr#5cwfUn+8yqLbd}@AVvDPj5=WJM` z3^Q-F@o)Ypr)P+L>ify}0ue|h0usdcx%iC+O!IoVnY=uw-z3j8u&&I}16K2|6X8d~ zobexSuh4alLDQ2OEcr;wGD#BI6`vKS9(+jQtNRIiSERAAm++!wAwKFIy*??U!Q{FX zWA0qFc6Wy~hym=$D4zvS(I7Rg>L|q-`-+(CLqTP-@42e%oQ4DsN#d`V5v$w;A!yE- z&TwA}2Qz2d=t;}*`cL%8l~vAhh1?oFb+6(WHAk&p!p6ns?YT;f3beiVT`y$gpMC`7 zE(6#iIG3UmqkjErR`|^zV&EMfA!#YHWmH9+n6(7fo3r&9`8m?7{~eX2td$K`eu87F zN0xV@=_=4|T8oI672z3{*}Wc;dhBHHE&|ok!A!>u=8Y6@3F;4|t|YCXTG~}oGZnYy zMQhs&P6vq_cj&IDcyX8GTufq&UAKoUJ4|t%&SI&QYwVa?1k~iB1K#+8((<8GXswOe z)U{~J{(kevdqQ>wzXL|iximoyxXYG~D(Ny)#ZkpY5n)pYmcQpzPJS?z)TJ#YbAZXgi?Vaj#G!caBf8^z zpg-XThqlj;l+fpRBy;$IT3^&jT-NWnO52QMfi>216iaLTkZT)ho!EsE4g3Fi!F{}) zW2%z&LLGKhwZxj-iA)gY<2z8_z0KMg_q+6sf86qa&ogX&pTu=c=1N9xmda*!C!t*E z`FIFOnlpddlUhBQ5bKT72ToV|DP^`|lT`le^4qiyHGHs^Fcut@cia0o$Qx!PT_-}dg*xTu) zWrLPh@|m5qH6(rwNyPlnQ@+pW#B#IM<1d(g$Ljcy)XPTCb$Id5NK_34?ngOGIa7w~ zvh#030-&)&Wq_QM?#-{u=mcpdVa$E@D41dS0J(cFb7C$4NC2K_bxQpAQ9;MdFIdXa zX7^8~W+y83AFnp*MzjvbAc)K*<&>Us>hPK_qV?nQ=1+C>iDa~98-5?AaNc8^;nd^Bd&g=4NyReWl}9kKJdfRB=_p8+R+H6Q@n z$6~Ye&`$z8_YGalOp1Ma?WL3=_}ZYP?k#XvQp_KpSiWIt5p&i4*z$KAeV zUw1_qp&1viyy|;ad1KmA5;VX58?CAV+9g8fchA8)fC&P`IB_KFG-dl6>n| zrY}LHO!p<3pehUQ=ashhr31kj`A~~}%cw$fQZWJedCVoZ3XB^KP5zk^0#Dt76buIM z7~}_9$Ic~gg~)d4o#VxMa#U+p) z3))RrzAr`4(US$sRk&;4!++#GIEfuP=t*Ft&@CDt8;n^D(qs&CQY#j`M>`)FbOe(d zmKUWDdnnI+IU#(j+0Yy3hRpUu#Vx|*M*4sd8OONSMr7f)@t_Si*Qdoux_1Llvah`= zX@k(R0Ghm4@M*jB49v)#Ntj~=>_GwmZo|7L3fHEH6jb~bh{ud3sqdrdY4>wDdkf=b zKI8KwvsTsa+nqC=k8gfo%<5O3m;xoQHWM8&Gr611_su5hyY6N)B_TQ{Pl*svX?HY& zprWH>FPVHLSJR4-mdx8P+qLRFQm=F|^zCxk9jbyKx&wzX{tkBC{q9R}^psn-;HyTl zH?kZSVCzsDJd@c6tr71O`}%|=%I8`)BKC8tzGQfg6@Q$u4;n21v9r@Gk4UuadHTCU zOQr4B3uX%t1;6x}zsar2hCbWSDv&6#T(*E2ndj(5<#);TVZCa!s!pB(w#KPA5Zb+6 z@Zs||1w#~VFTrQEV0OKt)pqA}swo)$$-!&jE(s`xy}a_*vZ-7fQojzyp!=SR8n&p+ z|LpX1!3MQ#Gs!|)1H$sNBEK;1Y)UG|X?*goHp$!k)EfO;v}rtv0d{zeA2!f5^oDm( zh8LtMvg-4ebwvgKOHEeoEKyjV+2>TW^?Lc-qeC&d-fdz7pDIl>#~VpLI}lr`b7kEG zTt_EmHC%DQafJB`gkG(lfBU0xmZLT)k|Vhp!uuFu;ixeq-dwY>(D);B4X}Plwd9T< zkQ5_t`M24t=ROhN)OE}MH==GZZha&TCmB4mzjtEikzVx3WAyC9#K3L*unQhNTC<`s z$SBB}CK`I>R0tb-Q93Xx7{GVx=PV5-`e(rsh1HSo3r@K9=!eGGSG{-WHLtijo@fN7 zihs;nrSY&$TCZTQ@m5DY2CR`kzLsJV7MiYJ*ocw=PjfRy>S7K3Yy+U6mM^Az^Y8O3 zrnr8kEvT&H+@_SQ%$!9<;n2j-X=}@spi%Z|O{>PB-0PwG5#B+Kl_8 z^O4II9y}xi8S>S_PLCq2`sI%tj@C0r?x&du54gE23;CeZI6ru7rYWA3AbcKXQeb0P znLiBR-?S-xwxsrqIM9D!K^^5T`=Cc%w9*N$UnuIup3tGq_2IhYWIG5qMW@H2+iXq) zgn}txHElduzdASfSR=5svb5{4ZlQ@RpMO6xM+ePc(uxWMKT!P~&_ZXoz}(jP|7;r| zH@{}I2+EztALKCaI|s%6hK#zSnW{_fdsLKfp^))f)lYrKC$gx#47D_Q>E>y;+9ubh z(cID!7;3+{8=9uWCBD@z@STDI?K&&9uo&)}O2~>6%-+%9;t6XoUE$rWEw$1TDm?(_d$uLjdV-CHN6Sy0dxJ>K z!53nG3U=KxG}`x5w8teqOna*DB!6}vQ1EUp)hY8S7ApKQ8~@uEe&0_??^lNeJp7^~ z4t1gCc~#K6N9ghdv~60hekd)N8=ZGQewmkLoS8z24U1|Yx9z>2qEl%YauB#*57TRp z>3yZ(b>ltkwmXY0tdvpv^JHJVPkcDt>t`mmAO#Hs8Xud2tDUIccFtJq#IAQOQ-g&D zwsG|}%o1qKC7WR_*?a_j1DpKY=)q*kpq5WI_ZpJ8KFW4$TbwL$0FUX4x63xAnIhP4 zs6NN{S8}dtf7>DGu67rdw?}b4hroqceNvMpszWi`@LYc_QB6H?GNFCyisg4{JsEH% z2<~?8L(o~X@D%89EO|tmS}gDk7*16%q3YZJ5bdB_te%H9^5%m5&uWL;o+LSCrWW}J zDIMyOiLvwqYoFEA*V{EuC3et!-5mBA?uH1(Yz+Y?HA|3RnPzx zjNtx6M!7aIUfIF%XY%!Co;Z~WW=aFJK!F|^kWMj&=0|HP=u0Vx3CM}?w&BC+p*!uM zdYMNZ>)s5}j@|@>MX?3n{KiNc*$K8LKz#J~Gauz|9XmfXdS$)x|BHL{2_febVC}lC z`}yNN=Lf)zlYnXx)s0!VRk5vU8#{7o;s2h1z{eWwuim46~)HUb@DBMv#61xbR{ zJq_M5)|$9AIlW=RHz+{%b(St4)Y7+lRjvpg5%e5z_3tzqHKxVnObYsM6S|^e%iRi_ z-ky6K5`E9z5KP+F#VZ=Um~28h{4L9E3hNNkCiBac0>{eGSGi$l3*%fdJ+v{(E->fK zaXo6)KF9`D^c$)5yR#+YWK8EC;>dQ7dZ21=rOKfDhQS>Z>Kw;gqJl_KlNwi&KQ+fp zt#W7S09{(c7pBEZ@5mR^3Px6>+mc|fenn0UCRwJ zMWCiR31-4XY}x(A0CFFBtMrI3Iz;m7@dM}SEL%gzIV_Ua4@@jHncw%(VFhgs@9D|1 zTGuf9vG*@TaQ8P(W_TWzsrShz3+mzAPe4ds89jsq)Q|L=K||218vb!Y@+0G~oHBX$ z6*IC|hq(hDwwUn|9lL8YArmG3YWDV`%GN{o0_%EJ!ln#4drO<&;G?){3VNLu1>ZUR zzme8`kpXUogh(nNR5=q>vj0b^0<|T*oW|qB&zw48aIl1X*l-m6sLK29n_{?2S_-efR)^$zT`%A2`TR4V=HnS}13PI{;a=zmr2X;s`FB8en9tu`6A`pH zAF5&U_Xp2E0tj(mVjsXz2b)7K2ifrKASM@2x_?c`sd7D^W9@tZm&gq}H0y(E>oNKK zi!})M+)t1E%yqrbV4Ur{5&O_nQk#r9g zW(ivVCK&J-JL7&bi58%+o6#>Y`yA3=;677TpcS8#eiTu2)JoU7Hk7+hn_}KOcKR_d zU;8bjw69GFXpvk96h%0s6`5t`ymS*S>6EDliGH#30Te7n1>;^NMql*5`qje6wXPw~ z*Syvl8W5woTBPK4PA31b@7IaH54j--T72$T;Ast zX9IsTiauFDk9${BqCWydvzq}$-fBFMJq-Fo)(PdD9Qx&8RDFDrF30D!crH=O?v zUp{+1z<#Uom)@S4Y^ZQRoG<@%E)lxrX^P~&i$8aL1x+yCgE^D7&>wBW48 zgV>E!SpBZkNJ@=%Z$K98PC#y9L~V)aq-~la;Voe7&korSJKW-4W(TPPGxFJ|s-F@@ z9Vzw-k}Q~V`VhkXo&!I9jLM$u*oKRylKk&pU9V*D-tQPuw*_IxjwXD25FA=^E z?qPrTo8PW}2o>#;emg5|`PH`eu_rZgV2FE9I^K=x>KR}?b8#kx6(KjW|?m^x#GgxdWtB|{#ks8O>E79+-s>i z!Y}+;tYgLd1ffR#D|lJLRW3QjIlQLQ>#U;TF}#+=dGOrq-QGx&d^ALddHTLZ_nYqu z>i-TO{9X(9Ze=_lU5bGbL0y~Vj&#Qg!3soIS&dEdUmzv|?}?ZzmL2?NqPAt5cv+FP z=9r#k$t*=olZkM8*B!(_8-S0@1E)u>5k~Q(2n1~yKM@F@-AjbmI*V+;g+hbnhj{UjXaVs?-Kf|2$$;x9sJ{=nsRsFMQ0lw|s%G33|&k6c+r| zG7wzWN)cOg4{WB|ohK}2>8zAVww4XP6Q{_(@%|tqean%AzsB@0&)5aTDrtGA*C*WR zsmnWkEiY*IUPH$lr-eENu}GTf)QRA<|wX( z$i}-u*E-qghRJ^c%YcL^P@B{-nlxcDCK1kS(M(vsQWYj5|gIPz97Q>0%5q4^PM02^)S9Qqfakq2=*`{Eirx5X7TTmcjvn% zoFx8Ke7*3xru$OeNHKa{YKwhpP}aSfuVgsu0Qevks%eE2cfMaUP9%!r!I-ga z#7K{gH?-s#Knvkt23Hymnkx~-t4Bao}*6e8NosgnwF(+yZDu-^*s>3bh=PRk=VGVeA9T3Bc2xCzI*{7lWE_1yeGyGRk~7 zR5gm7-?LcTbxkw_(fG%|@qS$DF92f9=bmkTD^}^)Nsja+%dByOtcdJ_&n{~w3-qxn z9HR5y+PAdH&=saAw{R%~6Dz1(1CDM3ta#L~&%7X>1odzhj9soNaDiP-r{#?0w%xpF z6IU$6IQc&uCmYwFIt1S+wIeq}?}M$P$`wv27|M^n;WV}cFCIz`86+5nVQ*CVz)g~S z8rfQ_%m>NGoA#rw=DevNlbQ!foFB*GX|4rjt@mWd9806+Ih7#gLbHQS zLqY77bN5*!b?fw3`Q@^mx+i9KSvo^it87TO<$`-3>9zLy_#{Co0j>$(oZu1A^JL-* z`g=`oFmD^E7t30U-Hb0)1Xp?3zkc8Q{?Qc|(efbj%TxVUU1-%VL9r~)<1`yH5&3|9q@;Paw9;jjb#M1%aeaH#!PoU z!^g#=V=2W(M|NY$(f`wSj%QA;SI4Z>X7oSp8B0ijcTtXC0bM-l@m*n&AS?C*jO9mF z%I*?ilRY~wu@!?PPnkI>2-_dB=+vWC^KYxS!kRnr!(sa$M^enwkC>fh9tcQWzP&z` z^i;5CWnQW9OTM*s4CGpR8$LJvVhw9grgDz!pjR$5n z38HH=WKulDAX81aLSfwb+9@oybbGskVos`0-NBoU+xq!Xxw;PkguI#S#a^hXzh@%h z{Kp|21~f|(A`IAC7uhwpN%13V{02qD!;#*wlCC^Wjj`daT~ms;ew`7?)pGO%8p%yy)|QAD zJZqdp%Q@}t^kvl7jbrN_Pf+UCY~V6Z1vi`6P_EaGbGnp4j_k>eW(=CgVJ|LyU~{6! zoQYMItx8Iofwp+)LnXa%f6wHc#~LHqg=jCkdbiSbGJHLKszrFfgFp>>E@Me4jxi2h z(<>jsp$a~NP{9l8U;i}yN9xBja~eMuO6d7xkyRz5oy&39no*Jb&WYsQ^#KG)G*Sta zJPg?uG}pgYVVig0^$>b|_;Q!~6P0$?4{hLjMFBtPZf%ha{f1SQD@n2|;_MxX-#>c<1bfCv4^=kBH@ z0t2RpcT+zz8BkVceP-|HFqRf#2Oywf4U({`-91YDkvIEyRxQ(g9ft2BGWT0fa6s|o z=R^>C6xCd4sVrLT?>R4lfPf3)DJg7t~~l?q|v;&<`-e>9@mnZ2=s zQPGsFH4cT--^a;$sbS6aHl zE~1qpd>135sf8pdlPy6p>yv<_FMw#kbMbfjHlqTkiiqI^VZhCA@D2 zZ9?fJ@B(q)g4)E6r7rvFztc1)q+$CVPxk`rp8g+}iTJ#D-4-~4*ufxMOb*Uo1Di0u z$9}f*Sx!`tuda)ejgDVBd`Y7siAIqUf=S5;(JNZq;sI0-}K5lzU7apPpn z+Ek^of3ml>bJ76L(FO4pn3ZJA!!7QKr#~Ydc-EL0h9wo=YXpX)kzBSB^yAQZN`yu)B zMO^G}nE&|Sd?MVoox6Xf)%{;j6JmR!U2UGsz?^+*{qIe#G}Fg>g*Yghf7z6(>`D0B z`DS4E)WOU6{Uy~4Zkxc@^%^8H%}knNHs(aU+9P#iZSroi@RTMqAOzRKX&gp$@}+OU*+ zZV>fNG^rXN%npKd3IY&cJSwzhRI&;|QW`MxRbR2CM0YV>WEIq~zO34aCT4l8;MTLy zDoR}*L+Id~tUi`UX#c+2l4DuY<97Ad;vfOJxccqK!=004eZaNFY+Dj@aycF-ez%uN zYAut7>V?a!*DjajuRs+!UG?ta6Gtq}J{9Qa0dGFB8mX#L`sV==0HR_Sq$#|D_#O_%WZV2K?NkLicUTS;B< zU8u1QOJ=Z=>&og{H&cG0uPABE0p=vf0l^MlFZ@wtW?ah*Oq#OVvMs5xu+8&MzF)#` z3I794Yk|i&?z2gMG%|P@0l=2Z-A;^>dn}WZT?8a8ve{605hT9opKB`Zlr&?4{>_4i z_+|yVg9p1;=Ci_*`?ksN!irEen~I>o%d|C|cHP(GR@h(qNsW2KDl$YtKGy&aYS!97 zMcx&pZv(hoeOYB!AlFDB^JfL<#z3*Wmge%vB;{U0&x`$tV1&lTsPG?`dR^li1 zEh#M0uQX|HQ{ng`r{T(^NT_+PRzRSYj4>|aEQV}DE$(Js^+<^*9D)rLdquh3Q!tl4 zJe0mX>ZAD}|HP&HILX~F!|R~~U4M#`CX!Q^lc$&Q$|dIVa{T&B5I1SwiWIl@a85~_ zOry(0L`~JdM1J!8H4JR6pH7-v_QRo8CJ7!Z{fI6}*CcdJyhb9wAgb`220(Vi+M7r{ zl}z=MJ1xIbXDi{G?kn}3K?VK%7wI&-QQ)ws{UPrpHN&1_#qu zeG*-{i?+5YBjvT&oR*Zq>-?9a=s1%_blDswg{G`s1dR5!jj&yq#;Ac&8TX~O$DgUN zCT3TLLe#o_E}%_uZ>!yakc!&E>(^sOssv~zd!wpfS&mwb*t)Gk?fi^=z4B#Qi*jZg zkvYNlS#RAqC;^L3gQnhfPM}e<7@nR=xO@(iFunZMKY5ykp)K!*h>=8~o7AVbC~WHG zdn=OcyU!fRJaO$`Gj`zO~^ReE#cglEpet)u?x~j zkqQ6VBxtAyUTu{*T&#N-w1Y%DX_Fted>bY+&U%A!ybRTdxdWrQTMvYu6P$G)HJa(B zW@SBtl*q!lzI1EnG86d}M%T?a<25X&#__&uvhdyHFMvt8^s;h%X+?qg2$4#ce<39H zFDtO@r1g_f(gH;A6tU9Fa*`N#%`p?l;B5Zi%osFaayf>Gz^jze=p9{(G1rpSZyEc? z1S!d)dQIi9pASh2&vK+v_dK?@r9liLqcN_R`E%CSe@}rxdZl}sl z^QqhCcQ)jSKb`yl)?wW?n*14`wAc2$i6sPw{2x(<%>QZyVu!cMDnKN@*k+w(Py$>6 zyBFZCf<6H{N)xI?+b@vDoU-sHyQ~Jq&b>V;;ACeHI)OU_=cVr<{0 z-vB=C!OEZheo!?AB(OHB90TfzQqf97fMT_Ym<3H<@0@@k`hqM;QJX`?0|B4_c*90- zhVgyYpIEgMPR!NewGuk5Q1hs1Jw;U3{f!@;fT5fXQ1Ftg`aLWHYyyArDkYK14Q%s!cN; z^@gbyp_E#C?-J3uEs0er2^ocw`-b}Lg+*5ZjLHmfCRcDvO=P0y+M6yqJ*of4nPMA? z(0m{!SmaXk*f8BzB^kP5u{YrSK`Ld`@xGO!=S2KmDQXp|0%vaA99!~t!7a(wlF&am z#}Ix2>-8c1VIM#{wnp!ao<7lPPpel}Vh-jGdZqs#wTI`82xh`n6`k#3r=*-SK-+Yv zNiP)}AlBxU9!}${*1#R5MSl9y9cMjmK-u$>cp2o_SM!G5|;y08pp z%$8n}&QtpD?fSs41e5@kXFH(FLu`Rhi^{x|=n*vvbGr#sPcUPJkJ&l=x3+ECjFsav zP@6?)r~Qb{9UaEEm3c}iYuo9u#ci1(+&rM`QQhl+Jwt_b6L51k3we=0lM?P9LKEeW z^f;_rK9&d~2Ihipr`?NGtB2PHq0_oq?foD9m#$UG$=-`aFtXkDwud!Wt#bhuYvW*9 zgH>}rX7`aph6TIMXJlUb5f$uSH=mIhomA6HeplPyVc6`@kg7rch1@%AQek$N>Rdsc^$#u8rkF#^+D0q!!%frpQ(G>(6CkWrse+5*KPhj zE_?D2BaS#xIKbabyoYlP)U%gL{yC{7H{$Rx4KbehzSSA8;4G>4>85c+=aPhZ4<5HY z*-K{mQt<}Y-5Pzb=b>{J<0QDAr+LfwMH%lyN+wHwWDo`(N?V2b@`KjkT3_(0q}Q%j zAf6yQMAoGRzddh4#c@+3iu^#PuYEj>P6v2q5e)X^M=u1QNgCEG><13(klUW78a8{& z#4gC5q85vKUp+LX%H6WC1iimsQI3oYO@{xxR(&z4*>~qrixfL*F-P>gg|Pv?Lv7pc z_XEBy@eMw);l}r^=I|4op2mIfb7UG4P+;TJR@yq`S1P=p12OT}+)M4F>P2~3LKgqg zN7f9XdE0FChkph;23b)uV#oTZFW(nFGxI~fcVmyM*Lh3ga5<}!-PyK~w&E;!K6V%8 zNE)p$CaCM!%4$l;3n=9WAoXQip^%Zot-d0Fn>tcEkjJY|FGe0@icj9-GMjNlFwn0oU6#P~@xZS+GeRT$hzQ`?Z%3-Xxe#omV9tw3E z{g%I*a|^GCOacUiznYL~%GpW*Z+$ z00mR2JhiZnVGWz%H2%nt;X;jv=y_TLtVs*y?j`o@K&BVGCb0RoF?htKI|g9D#MY5V zCFj0Z;k+Q?%^?$o^p0SE{nE3#?{LpvbvT9Juwq>eOdd};g4=SUR@*VA-Yx}c?oR8W zZ7$iCiD09&V3C@t!2+B7o{*wW(YK>ZS0~=YCG<@1#X2EO!o0FNN+4D1c zXnO;zdI%O$pvkMMp39dyWz=6+Fp2@14e_1tXO1){-w^ISWIEnegjz6DH@JF;QeGGr zU^@BuHuhZ88NQFMq=<=&>Ht@Ti>laKf0e{do?191KOrD|?Eg(;#gK@9(!%Q25a|x194!cjkUUB+- zf{25M^>#-s1EB7a~nJ%PGwiepQ1S0B(i$fAA z+(LIhTOI!4Wvyn}ZXgY3kvMhe7+4hHspOO^Gk>uT@dw*@w*2AZ%0k-}A#=!#26^Fz zhKVuWaZ6SwgBBC-{&g4Qtj9xpLP9fRu>5g9@qli~R}SL`4S7WGbE(l4X3i{?6&^Qt z=0Y-;QxUx%ToM`G}GYJLjyIB5A@(T{6$V z_wJqK*D<@8>lm*UE~doScwTFfVHHUv#*>u7EaCH~#rrG%w#)=NPbr zq8^D>hF<{U~njI_QnsA{6o4U!jKNzzw-PSHE7^n9`& zc{h9SvXw>M4N0$TwI|t7OET@Rm1M4tB(W} zmXHZB+t5xeD);JH?(?}_BZWLW6Cq9T*6$W!CHN*!0D+PBVsX$a^7~D1Ks)nfZrnv9 zOl{dCPi=hjH=Go4F z6qg-w-`)E4c(#_P%}T?H+$?ILcRFgJC_iSYJfW}+j^2^Kwo7y7FSE@4u3+&{Ei_U7 zNm3v1uAFbADmDL!~88Kx!_vQ8x#&`kOmhmQCfdjxt&3ajMNTy(+zT zmk|`iO?q#ilrM>lId8(^=CrvtE|lgdgJoc^E(mg zPjC|Yw(pU_ODbPFZ+6xeA9>bd#J!B^8iF<9;Z7Qg68m_vk^1aOl6RAL{d`7INC0)F2swN$4|vw{2(*bH&fH`iL20*U ze*-wun>d-J&YhD8>W)1P^|v%AXACP!J(z{=RF6f}=ie81$o!bafQm`4tK4EluRTQX z_#Q`5SipA<7DnS?MCF zmktT5yn*^d2%{_Paw$(RyyAjKZki{{``qZw82!PiuYXy&Yq3mH_fUVyOc8_CmQe#t z1fpH;-rGRe^=KmASzDoRXcO~F;q#;vTZG$H)t)UQ&bP20Tx3cuDK6k_p-q~#s9fP4 z|Ea*8+F_5${dho?iQK72zMX~0oo=z< zK2k7+W;2gYEn-V|%|+Pa=VCrBXMm6!?)U9;rDOEuPUG%S!@iVgoD&5iJERIXPOD@o zLHy;l=a42@jA2;98nxG`mbrn&tUaDAz{~?yYC9GdCZVb{HhXf-6fx;O=f`eCt!+cF zO5yLts+{gocfoS+OS;<;aq_rRHS$>KGDhpIvwd~*f~U=0?>4xb2pBpM?$o25k6NI- zPmsT(wNk1EG#SPp7y6Sg*2QiUA@z$VDu?lsd4H~jrkxc52C$Cys29F)v~eCOEb3p$ zQ#(L(bJTLYe?qlc@W|dwL}Y-J8z%z3P}Wq_D!PL%JO0aJyqUav7B%-&RqS4gQHaJD zb5*}C*q@dcM-rU(K|gfiG4f*YN^Se53-`HSP}_e2s!bLXSLMIrAtdWR%8kER5QnK248Y)Z8fD0uFGR<95*n+ zi)s;_k?W_uCb#ecZ*Q+4h6rlR)J)@+2urM0IeS9U6Zm-Q!*En&khW9@{fH!noOx~x}pSWWv-MsYe>A^ zYg_Bb#emUv&B)R|NBBv-`GkPKRWMG-9aO_r@iw|Sy=D_GghPT5F;4ykO1fEsuT*o_18xpoFL(fj-p~S`n zR9KK=g5b;&Ry-|#mBHWZyr*HKyVu5aeXMsm2HEewjFW$>j1fWQ70~gRpAcQ^lL(09 zHl&%Mj4aptt(R6V8((Y$gObLbZs8pY>z!_n+;_%ZZ41qY{XfdyJ)Y@4j{C19bqSS< zoR+JyuB&pm#2hE7TwTd@b)Xz3isZ0H%wddlr3jVE7#mkbW!w9eKJy~+KyjecFAC0 z{75BeJQ<=J_B?W`Y^&3m-Gd~`yOoYCeNUMy$I61`9g^L~0X}(-qtE?5KDO{##YW@U z+~{ZWMf!LYyW3}n_vBGi7F09v7b#DbjF~_%rET4?gyk8OT=wTF#75{++yc0z^ryr@ zagnBb(k!-=I0O=ZGg?nm!*W{yZfre&Uc+oQ;}{Mnwdq6>Tq>sR!42AqqkxB;}uG} zkN8sr<&?`#ko&%)n~ZvHDMMO^@K{<$$9qPt*j0xl&gzEsERHU6VVpQ|fOu#)MllNAgUNC0N3A#kpAWEW4R2XNhy=Z!mWSAX zoR45=WN{FR1#fiRQWch$1c9kqMQGBuNaea*Q16qp9!zAW968GGje&~x(juN0_K64%j#0g!bnS~r)Pqcv~O%bzap=%UV;M3!6v{iu<7o zuUng1NdsV>g?Edr#DTS+hv3VHaPmr!v~nS3uX_w1H_;?UY$pY*IqC!~O`^t{&)np% ztXw%^XOZvL?Dv8tXaV0Q4tI2FrM?*(6vcU~;kD6LvH1(&(jQ_vMt&Br?UqF=IcPuO zBTA5UVPsQh-$uo6j{kD*eV|6^n9g@|iaTBM_?$^BG0@?s^}tp?q!Zb2x7h>7t0TT5t;EyR}h*V4zF;PzP*&(Rf1uJT;l1}0>E z@JZmH5&~Bz)=-Ku24OVpJ{lvW%ZC=Gj_)PHq?hy&uJf_-2ME!Fh3xRo;SysV@KMj| z`M}oIm12i@))Ut2E-uBFKEz6?UyKYzFAKaz9b_qrJ6{xF#S|f=OC1(0c93T(SW|I3 zy^Mcf<6nKH`Q-Bbw3)1%t)Uw71@c;LBYD|f z{`n=xuX%mCy6fD$qUme&XOeB#wP5bwod1T*@_GO-Uhog`Sbgi2ix~EmbPKoi`uc^P zKx}|)fk_}Oy!1v5^rTYPOGQmDDlP98Qj>&%7!d;}Pu+36n}TwzyVcZe^;1}QzRR$s z5h{KG7rzLlX%Z6zCAF$e<9e57{31>OV@N5Y2JN~Pg!4LRzb*e^SkaI5qgTK%8yHs} z8%}>zJl3?evA+3-8e)(grWb!Evi-L!iv#gv1EFzymNMltnPIGT%G47-a4|~j8TeN9 z*d@VIW>R6o_k~y9*1oX+#uENW;~P&fjDT5K5&_+I1uaWG;@UE+=sU4?e`u*APdDy8baJDHRb}J3M(MYX z&e{9OTGX%6&!{Hhc86kr{lq2BoQkInyOwV=6Y~Y%=ennZYEG*EHqkww_Aff)0Q{ML zu2j7I1o3a3)%$_ye?6kK6t6&QGi4aW22+qxaog$It`OEwRInCG-|%t$IrFEKZKC|!++iL_-)LN zOB)|fMQjv&+oTIqEBnGN0;`AG8{>UWCks*queHEN^>YV{NE?nEs1A z%y{g%I?@#=W+4tBzrhHw96Q!t60BuI)O>mFY;t;zXUo&$icWd_lE%0bnF{R6oBy4&G0e&ogNIa0);?M7Iq_-n_u2lTV-vVVQ% z{{fSl+?Gr&W(Q%Vf-f6k7|^@#b9)p!0x|?~%0v`5acDrVU5^O;8N1}y7Dy0$*KIe% z>oRLITAHK;1xF#CV$XiL5s&hr1s^_s#nZv>QOYTh&4j0>K{nF1@n;^BhM*@Bj>wSxq9mTDeNPh@91s7^P7diV{jk~_-R=x9hGH~MaRmm zVd~e@VeN(^9fdksCON`8Ivm4K`8?8=DTf;x6z>8`pGqI2Zm^u}rdQ{ihSM@}QI9Rl zZ%%W-2yOun(xtEKx0>R0mg9|mg;l~(uZi@5NUHdhFvr16!^IT38@MK0^mw`KO6Bf% z#Rhr;r~$G1I{OOA2Mms@?40&|itwYEAkt|X=ITYw$h;@<;Zry};9_~hb_zFIC@%eq zMNmM@`%Zf1FXIJ=G&?K%xaV)4t{h1obKZ~z{o-;;n;pGIPg+`?tz8V`^HB3WwyQ2w zwI5CVp0Ru~!WVuWMdM+9ZwdqA_L=GddHtBPNlV}2XqvHgjX|M8FIxC|;?dsNt>zj5 zN5^QUQ*djIe(b0@SZb*2Vjy(Vg1X>MLyM6Q`VlSc^Dc07L`#OokPhsIgBqm6JOW3Q zB_S4X@}@^UjDHf%#763!c@#AWJ7u4FaFqpt)ykF{3a#=6g#SVk7g8LpOzNke={84f zM#6c?WR#ul$H@QoIl&GMvvi%~3G&m8eB&1JVjkbv%F>`st>E+<_lovHT9Q*tF|y1m z-P{H5gq)^huh!;Sg!wJI;veOZC6gbT_LyVXrNV9!g~ofF>{R3W{SlKP5^3;8wVAeR zeFUEf_5P^=#ba}5nU65sH7&np?%UYZH$Pb<+BdoTEJAS*o20uzP5FM#hEEKWjjJ1R zA~M}bcnomo|BMMr<>NtbSDxcH!cUxm;n{t2dEv$Kx} zzD~2H)w0{>13QD78Rz&1<0^xxsX;iiyLP}*ui5nZpD!B2r*Sn_SG>KzCR54Q_=hOw zld?3EJmG#{_cRlQo|8W^YoKZLGCd+XJSpaz9uxP;()w{=UQGPW3Aw=uh1H3NpVF`I zDjqm*>pA1w@5z^JXJ2f;offF9c)kaS!`!be8hYks0cr0Gf5CZ&<0-PfEk_0(U-ft5 zl{my%9mAC6gbssq`DJ<2r7htz-H@*R95)wXFR+o&{WZG!MYeip85Vfa%9?u7KBM9< zk)d}raWxBSzG@GGifboXjW2jZXVz%=fyk-i;wch7sL^mTr|CKa*iLTN8M-oY8!o+0 z4?rL85xG%=Jc3IPW9)-Ids>uQ;QB}qQ-Yp(t!RE5waLkfHqdDnV=LDbe|lUd?|7Va zci^n`kVdCg)&r(ad#<_v$H&cuSmDQHOq^2Uy@C6^|4x3uI)qa3-Ld}fC*J8g@%k*k z+RVmOmD(Li#qDqqtIw)aA1kv$wvD=J@T14HI}R1v2=3}I@>*MZVC_1pokYE%*_fsQ znbTp+i||=%4fc@8Nql6GE#B1mg2di)4u+}L=nTYF2766LwDIXp3tM~-%Q|M2 ze&&tbW+z)`4!{x#EqxW?G&EKvx}V?S75p`edt7==W3UQr2)Y4ZI}#u>Y{A{T8r-}e z=`w!2w0ZAH^lhx(P-FL@BN*iDEWyrv)$OQf)ZuJb-{An+2M8=sm0O?XQVK!wmy6;T zoTwkZu1A0?jT>IKwmLu>c{+{k(!*eFn5VV*mei}(f%5l-qIog<+Oqz&iBrhyJwbd6 zqY5DIBbpT>|LLEQ9mmfb;#h&rKiondns?~4n|SZRVW~7d;7hiHlgj*<+qhhsAlbnX zJ#vaki5{rylL7H^>lvBZWbBXRN>_Go{s+K!GLNUfg7UjnxW&!38rJaN#i zH(WH8V*MpcI{k?tdkn;e@nfuhBEFkT8QlnFA(c!sUtUe#<5Zy zQ@_-%?U`&w;8wS|wWjL+-fQLk^{OvBfSWjt1NbH|F~%B7ub|f>?U21g0p}ZH{ILRG zL0NE`L-}z%owQ7qWfC0?fYO`a_YJ4L0TU+t52J=>7-!s70FnWK`QF>cH5=SG9{ z3KI%>!(ao&jrwcT-^>Fx!JtTit+bG(d8Df@lAmOD9V|&4deu}X3VKL_``<&G)+;%9 zz74FNkv$q^WFM<|8hgwru98OThgqAhmAwIYPGXGIu|VEA6^ti@GN>w@Uk*hOieG9( zkcX&XNV*A@Q$l7=SG7^!UEyN6PcNRgv=euP3ecN<-%SwXaHjG56Ux8VS@A8?sXB(buZL2*;XX2w5j937Vp>^l<%kyrDY0%V*UVWI z73)*;zpwqPIiYh77FNxOHH9@SU#ZAGrv4&!LYSSheCk$YZYXKixVpv zT(w7c)$pNm<=BZoBi72LNsV<*qn#h;^&f-bQ2(wNZr#)`7B96gn2G6@D;E(-$C7lp z7)lx~AnQ+ES5^-1$uWI~{p~)MD&KcU0{8^fBSupwp$2-P(fuA5#)NO{^C*CEqo9k@ z3YvRF_;jR}H-!V z7PH+q)q}8=n*GWR2A#|Gz3EBte6(k$PFd2PLrTkiO!@J`l%W&@_aBJ9he+KU>S9h2 zrKngncik4wk8<@BL2sKX#U+w~yI-v1UkD1S%tM;kXh*)Nswmq>Q^_Qrs z$m|0}kYB7Wy{dE5Kb(<)TRh)cXL0_WE3X6}r_`U7h4|NsyNMtD`L`_r@y&d0tth;2 zh+_@0F0g+eWtXx!Rm=StD<)xHAb_9#{Z>e&Z0OJlvp~(zVXuQ9-2Ns%Q47d03)Q$5 z{OxQ=yNG2xP?s07a5JI7j>SCbUtL5_J_v*2kX^5>{*dDJ=BH^MzTDTD*~ThGVO~)C zRY;>CStPlV(%~S@sZ0l6HHd#&&~~;dE3p|`1PoKl@~LZO?uYTaahsusK>@DZ9Kmu* zI2fgqno07C;Ya_xjF<+8jClO5%sz8ysPoA)y5(H4k4~Q>afoxRKx}7bDE%(eKhEDg z$Tp~PU~k^O`XVG<_|9M`6LqT(;75k+&c zzrGxaO+=JeHoZ6yHe=O;efQ|vSzq@f=kM$x%|#~~g`A+9&5%U(_}Ha7*N4R7+b-{1 z|DF(K?bD?e>RE~1lg#h@S|F}!KL|`4v`+m^49<+uyapm8TlRs5FPTxyUsAJdTiz8@ z>nJxCEnaiY-de!Ip=M)V_pmX4KjL31%G!q*Z~4kT0bBF~!f`;S#xNSUifiSd4ixHi zp8tL^_yPOL73Ywy*Z6=?|0pDH8WBIV4DQ@lT-+SpFxO zOYPNf!3qZ1=n9VQuY?n(k7g)-=b*S=dc)2#r=`%pRJTN!eX!_o%<3J~e6RVJmzNcF z_V%HnfYDMVN?V<1$XO?KfKN?F3EkGNHX>tx(S3(UM*e(aH4|bEp^CQgdveh0QPkuH zOPSzV05?E8^`B+&&@81try=q?{c+iuh+U}{5yFA4RtYvHgvL)63F9R}mYdkE&Q(sx zd{RVaM~#D($WkFoJF)5azmA_$G3%7SAk@hQB{_9JKL)7BKX zS6w>&^H6Pz8YZGwtnQsMGvBqjl=6w#fRxDb04!Zlx$Ya~7QD>Tr*aDuPFD8gsuKYq z$muOqUxRAa;WTGBK1WfW)fH`JLY|-y9~vFH<2B^ z?hY+BshW)E#{u`^h@NKOiSIm$S0m;H5BAu5t$WPE-KWx)llv;&!|=+I#3c}+H2m5W zQWklffMoL`6H}A@X#HXMFxZ!4z|7pNvnRUwK@emS7sv#iV2@ha`k7Aa!wUnZl+aBZe zD1v?)^!D4XP7yI{XH|ZC(2P@>;v7gX6weJ;J5D?&D=7;eJ%pb2IX3$(ZwuEl`o_sP>|3v&OLTJ@xnTe%)9L}J;u<{Vo zoOmHwP9`KD%5C63ay3O>Sa~<7Db<{wg z%2o|_isa7%ZN4==VtDxMClEC)N3Tz7V(RxTqHpQqFA+QU)gq8TbNXbI>94*nQ#ZI) zCxlk5(FO<1Xl;2N_7_}$O_Mx)45j}X=XHX{@V?{b7c}qPwjA-AG4!fX$d>wB(gsei z-NVGKL`(C2 zQw1-~V~Rk`&j?qeWg61{n>W^+o@=xBg9oyIF6c$(ZruPvf1(rbE{{ShzAwa4WfJ-p z^DR>!b2G_kp~QQENy?#*Q+;XozMBz5A$MNH;$}y<&@Rumc2vErhCPG;{9r} zlICkCvO=Oa>3)ZK`OU@Po#x}LZ)aqn_ikvQUDt}!F|Lb+PX*LkwLDqHHIcEH1yuRy zTC0h{8oy(;iTkFp7&>nmHz6eg=N7Z;d-fuOBY*QNe7Od+-Ze2o+{%6jUwtZe# z5{@9ID>W-;85t=a!NJE>JXR zLb>FliR@!&I%95CEZUl}Omi%g6mlyq+;JTn?N1lxG3OlP9jHUy2$&>PDw<2)+hfVr^zfubfY@UBlkyXqa_fm_R1b z2GF##qZ*2z8i=TZwG+YO1eXtzf-6ml+;t@2Y!ft4m+@dpll&RJ3bT z_>(+-^^vvj**udsxMN_epVmof)W5KoVo z7$_KsHHH~?-$NC2-Fr=Ufp1TcbPJ_-Nme3$l43V?U!?=9WRD2xV?PkIszC2FCQT_h zT#-BuwDZ?(+)#E_pF=P45xnOj_cdO;4Lb8X$WM*|Z_DL2aZ*mN<>26f}Bw^&PgPYcylGPWwg?`PfZe8cgn z0ncnI(%%6|3XY;S)Ui+?BLRUs)L;LIyKgYdX5+|_KOpt!x1f(uX%^kmjKj6UKLNq)leEdMGF>uH z7YwYNU0m^0m`V|2Y*JU<`$OQC)t1%fk&6Vv7(GxrJsQrf% zVh4h3KFU7ZJa{+Z?V)n{+A;}u*}nCh&+@Yc_ve5s6BVZA8~+6^ zcP@SfO{_Z;$29Rj@6$K~MgQ?Ttj^iCmH(WToztPtwDnkfM8?a+$QCVtZGOv_7YTxk zCNf6u-hq}6ixj_I{9}KeP)XcvclL2-?~uD!biaJD0dtd}6^_`zmY!_nC`MIKWJSv> zxQ0X$2Y7E+N94SmGz_s;6_Zw57ZK&|eTRL+;{q|lk#RE1NwjRDSYFuw^9!;|aAh*` z=L=w1$mUCw%SODLCK{KHoumRzGYuc2Yu?1#*hKuYj+WNsWZb+zDDLAAiwYY&uo^#C zq1haBuQ#E*L2y!FYc!wm$Ec@H%UmyG3xzo)m(dln#S59S-)j&d&G&2>#vzq+!uJaY z_q-w#<#WXGgzWK)^x+lLRS=NluC}BnuN+r`A#0#5-!;lCH}c=v>w8}^HWmxp8<>U+ z{p{*$6hM2CD~242v0X~@8ULsb(i@d(W=NDMddfYHE8Dh(e??2p}eS+;x6gkyR=B~9h{(Qz#jr9_B zg?FDGSirB<(I>_h)V0ERpho8BF;ENF-qS@(K^5F&z8Elw53#y$xp7XgBg9J=DIJ+e zu>F%Uc~B@}INuL+kPeMJntIWoIo>_J-;TL6bI^4XA!j4TeRxfb`ZUyqB#A53675Iz z{&OKmb8+4FM)}c!26hS>1niSHvN+Wj)ji|;(5|i(!Wcch17@(8&lhXJ6}TL*OK`-G zx}isvQ1IKz0Th?yESgUTKWXNr4qwCb^Ee?qKfCUxAP@2y6SEjWX!J%iOr=k|5GJMi z1l4ndGE_@c@@UM{^U=?SYoWqAw=!;E0z0rKzCCEnYGVrI*UE6)z*~^NwBF!yW5Z>8 z$$EbAlV?~SRxOfQ^tHH%guz;I1hcSua%QkU%`7^rqE>Q&MUeMl8it(o?)u*=MmABb zzH1gJ*oMhMA!(B3)^i%8((P#PFfd#$RHorT^do==xueBO#{sykWI-&orsC<9l2 zM^)L2yJ6$(a06g$80J69%&YU!b=1GJu|-%-95db#AAK$6eU-IY8+X!66YGg}xS{(=V1~wXrvMF86F0~oQe>CyII=OQ42zC9f4rxE zOTRg6Fwb-3*xOT-t$*Pkl^{9~4S0W8T~Gx%bgtgV$!+17w1sZWEUjk@FQJRIh(qzK z3VF@gX#$GAcDe=QKiCx#gVUSVfk%E7kbQBtF8CS>g3Qy!;M|c(@u`|_byMDE-b%zD zF}rML?azCy^hZ34`~2_)YZIp(5?AHEdC;z&|7PS1lpb64W4?PP{RX9{*l#$NEbt^p zdZyr4a$)iEuwUl^C8EA7G_zrH;h(f2Wv7OULJK3|n_BgSgtpi(jvO-L-#2k<} z70;gHBVu|Ml`$MXN~3Y&rY9AH?vkPu?G6(07&&GnrqOehz33SN(uDH;+vVx3R``Zr zLXUd@p}x#{qUs*F}be@wm{WdezS*5%!|PKR!Mj z=)0zGFOBX^Z+pZ8E*eX(-M_^aC~c{`k|f#5Io5tLjXuhWg&c?IB3*?9$veWZ{fwi= z@VI(YuB~I7ZDp92`3IBN`1d!}qLU`hcXS4Nxdgw3FInr{EyL< z_e-+3FAbRIH0{N8pWxer^bg+EOq|UmUc1N-HQyGL@i1^BYcU{Y9n2c#n^g5l2ZN(l z8>Rm=VkEVQM!6MtIC(Gm0!} zGMQK#Rz#7<1}3C?Js0JeC@n8^$yjmu8r4CeA+TvX6F~4e8+S8Mx~_Ppg|+(tunq`=BYV|en-HgF3a?T42R8ABMC*fZ%A@wPtxNBCfRi{L zr?JjHE*bnxRLPrS;)&mS+cpaij;zF0)jxXYWOv@~ra1pFWA0e7?i1Z)%^DWb1mrX| z_pUkV>gqbE?b`C^o-Y9!t#xa&9+f{LzCL#z{c{EG0xb5t&rhtaLdYlxyCDc@T1M)C$NNF zS|tl_mQFVqmb{r@o{WEjj_#~-WZe!(i4dv|CTN}5QH+EU7e9$_CoXV(WaIv+2Sz1X zTJvEMB-@2}=_YBh_!a2RkWW}k^Rfd!1rxJchaV?CCE*}2wAgZ3f-Hu}WFI%#@ zw1C}b+ns|-iqDtVA*Vr3ZeO?&=3!Rmr$`|G0lH2q*vprb*#ur9dAzVG(mtG9|9>D8 zBH;FDzie_B<|dz`>X<)JxHT`Qz6sQOt~%fUisTe^I80KAjWxxuJ)KV?sLezO2^Qs2 zldIn)#7z^ttn3Bj`WZ$Sad{vN!%^tUKX#!^V){L!I1{qP5v^GMWkqUcQ=EekesTd) z6tG$y7S$aJX-Nx1)s8jNF$zoQefS!~9P%XcLhScdy|fGMI*iDgs87zMY{WQr3#(fx zVmtDy&u{V8oITa}G=$eN9rp0@q3O0<5M!`k#k@;fqhkoE0j$+)$V!NLXdbMB*rhq; zBHsZ9J(UjCz5B-9aYLluC$TUEoY7jrJ$nqo0#_(&=H4~-r`5Hdx>BZDwho}&-OSIB0(p_jsWpU+Wb=oSu(B~5yA$te(j4;w`UiHWn_Dbyb&om!BysM1Kyn)j?EzHr7 z0pyi$S=f8IcC_%^aEdN-@&Lsg?9%`>LbYnakiB)e(=G;B}#=I=A0@DnNpXgJltfE4y4yC;l4X5Or5NLhqxe@ zEJ?n6Ua&J&Ss0T~Z2?m?hCDs13x*X`RHSA z2nx4zRQp!UbZ_Axx;LRzkNu=s_kw^1)*6}mCwfNGx-n$i_pbS3FK~{Gefy?4EWp?W z4*tEPugMNuFW5fsEt_^H1VD^IKYQ@YmHWB~`8_Je31%dR!<5|AX>de-%6_~{%McuG zUM`uQdGywzl5s3$Ox_zl$i(Nl#ba4Q{cl+W%2&IPSF=y-v|6w5EI){cPT7NLf{}Db zqIh`sEfOh*cx#Mz5}p32E~fW{dfYBp<@CuIGzGcSx)tsd!Mn>krH+#sH-sdrq;^AL z`%UqJwEW1n(}UVwrSSRN;8x4kS2~@aHKzBdj@=0QlzFk-x;&>io7v_SVqF+rPjlSv z8k_!({d`+x|1EN7uN6zV#?8kP>6X6Sxx-(`%;iVQu6vRk+m!E3eoBJ%wW4Y+U<9QQ zo9=QL+Ew7mc?^R*I9~AuXMJ#Dw>-y|MPgkY*di+{7TfVio!cVHK8VnU6CZTH zbkYdvR8_EiU$#aSSd*t*$!0P)^Q|AefHXG6B;Sjq=@%P+3NhbevnoDO+7L0G{?D^Nw4HtS+0KLT&u@FQV?AZJwJ0HIG%KwcD8um{2Wd z2-NE1fWDrulMR=ZUOx(&an+971Gfk=>r)p`YtzLabW?IXHO!61h7-vmzgcuAuQo%r z5nlaqiZFxjHZErsovAVH;^wBRXw_Q(IKaD7Vz23cRE3(>1`5dR_d783CzlTQF6f?* z8)j|g(QH;aKG2ZX+sfy5JJScRDyHDdVP+rhKg>;2-HAU>)r=7Ct%_XkCq?Ghyj1rC zr&nvy@f$q9c_m0AZ-EMB$F;$vE50S!>=Jf~k(qI((Y(N1Tm}hiXgXC|O{-g;t()D( zTQ0c@;yKkHVfXF+?3o!T7Bon>*<(7)N5CdwT8d~emADr;liKx1LsFAwOX%HW*8*Hw zxA$r?^!TCx^(3b2oR;afBA0tXwj&i*%^Sx44Mc>VINVc&s(47-p~Y!{@I>=iY<6MQJmu50s%kMN^j@wOH~q-vc{-PCM+nomT0 z%p|M&sNbzLU@8l^Z3O10k_XtKr4O2w{XcL!K<+#@hv25c+cZ|*bAP+w8m;LQk!i(F zX5v8ahh!bMbP*F`G$I1_ndsdhrIQ)H>W9idLD$lcExKu{M_IF(xADzB1$9m;6zpyl zZjp-v!5SZu5x&+L{wVta56oP8_sWK~8_W!8jbixG!`< zBYo#R1kR~_Fn6UH#!!QMxv&I)6@Vdm<@9s+`BEnczS<~2uEz=jos4vc*Ywt#4tNv! zc0tEXt*dC&%aPEIoV6V=`SL8qt&DM@N4V2>%e{g>6JMV2-2U2dnb}sCwR<3>18dS4 zd+Zm485?P-1>tsHI6A)9dM$iVtee8jo2T@$>9KAh_V=(?ztmN8UWwo9noQV$kxtdi zJEh-!cHUC&pR=NAi(6s2?*9KqKG*2%H3y-%STFW{lb-{x>X>QAvh}I%8^xU+j^NHD zB=2>?+_6UM=Wwr4(iXVIT%3f3N6rV;#PkFlsm%jUPL`?qP-o=P#PjJfN4bI0tsC}W z@81)3%a%8b(N#O@P5H&g;&i%l5gG7TN%>SOdGsa_HG1ZLc2-#@tlPD~XZTb1g$Xxf z+PK&F<2S`1ru}AXwBKj<3A>R~T&nRFW)Li>%eZHMkxx}I_kebZO8<@aktRLX<EDh%7hIn-*8}QO+QBJH@zLJH|`Zsy;XepABW>5pq zB{I6Ej%IlKR0>Ay;1QpquGvRzip`7ctl$+F$1l3au+s;c1GBZRY4lEiT`oG|pTOf* zZ{YR$aj&z#$qts!-}N`e1L|jfP%*>@v!|{#_s7vTqwn(_l8H^SpjQf=JI3#mFE16K zPz&Q8)RA7cTUcn^l3~~}qn5{6nxBq$YWr)vqZ}ULSSWjtw#M?mC$R||J z18QUlt}+{)n4&faMo2G?&Cd=wYk|t=JDL)7NVqNwv*7a?Y~)wcmA9^y3}P+w{W)NZ z=(EG>9rn{`F|S90-1P*Mn)C3L&a~rNqK*5snTK8im&BC3qv4 z{Kb-+7q;sO-6Jo?k~_mbKL{?)n={DF%sp0Ez&?wVanIhdbc20JzX~~&bT`<{WV}u_ zkb?6JwD#LT26kVrhd(^x<5GDL^9d>zJsM;VXj&V`Qqo=t#`J!sgufqxp74i?-SYj^ z4HE#zu2)z~Q1GZhd+9#ry_I?M@>slCguZ!ELeMVfTdv>XPt(IPZ4q-x2*z(=LjO_8 zpV<0Nn9t!B4@jp~8cAgVA*hXZRCWcb;_lH#UX#@9pE#}6UJs$X##+)wt&)~gk7bs> z9ISsD(*4*h;{ZN95z$FCX_4)N>rJv!{5)Y^1)T079p-cib@UVh41w2vtvYrk4t~cIUJ!)edLPIX;*| z;T;^}wB48d%SvV%X85tbxbDY5t9AG^9sDnhUK@$kNB8Y&(^Y#2#LDU}SQ-vIMm2*K zsWCsPcUt1(5PGQx`qe{9C|?^|b$(h9wJiS*@pGLk#)fPb*+AZ(RpE==r{h{}tr|FpTBV%%xp4)vgzv3|~YToS}UC6*W z$j6wr&%a@Qg#*{>>1swx%3-L!b3;NvLfkbt7pj|5H*AjeTDTaqFm|QSVVjZRu7{uU z^bdNJp5_rT+eP0;6e(kCEz2{UT7J`X7{ZhK3UFbw6IYq?f-T9{P6JabOBDEHzmQN$oCqt!fy#$ppLt3mw}P5 z^vIXGz;PS(EL3_~?LN|?%6D^`1{)}dioO<6Unpa?QxWHpG-Rb!0n{B+v){ExII+nz z?`5y@6Nd@OmY<V_JTu@E@tRm4rLc*&8^4cnP^?%G z#=Rr^FE<_`HifS~f}z9tV*pt-9zT@^YKo(uMgfhZU**SqTh$5@1P6Qw5x{8E6!I=B zgyLA=8kYBe$vuZa-3B&;|4Z)4c?5V19HF_*Xg=k*Hp~F)|1fxXsy~94UUItK%d_RR zod>^Nr%~d|)@CJH*)CpRrbb&vB*de_-UMo-7u#p}ef;qCb|TNv2P^LEcjg{KzOc3S zUEWP@-bg*4dv-pZpTxw7;a-YJ#RVWVSv*fv(2hO0l`&Z`_UGeuh7Eo7xmW z){nqF73|`n_x@vIs{5s6;x#wp{LFH4&P5G#f@O-;Pe=Qcdg4Y6@oL!t>KKRyG$p0LwLPHcvyv49JFC&B!f;Hb1v)euTq|MOM|?@T zn&LfgXJkbUBp|ar^&WoFKyM!AhUsM~RfVDt^>4L4zU9$V;$XrgOFN-Z_!3Blg?$4* z&jZyEBjmzMc2mLr&BD;os^fbM2MUN~Otn(FC)Ww)pPACR~?=G5J9C+fwV zq#G$K!zuOAUL`~&Tz)x$vfo{gIw;}23^16WLW*d`9urd`dI1YDRmsu7=4L3pLp!_J z*)+p;w`QPyUzRQy>%^BR)ljCRSVT+S-eBX}yt&;f=-4Ig=LjmerH-=TsJGx0W0>)- z+v~`fC{}TBFn4>&18Zoqxk@X7EY`Pi$lFR5rIG8TQ{c=5AnYp=?49Jd3?zhi?r{$x zD)OTtTkNl)b^E-Gb5zy!x6Z5%tmKxV$-BR(AbVvAPOjyk_`3y<3}m8LAP|tu+eC-= zn{tTw3Hilhf;!mm%egPVA@ExTMdkQwnr1cXdPt`T>8O=~e^Y|`Sf>9KpfRCX;Php- zxP!B_b-7yI+B`EpoT%k3H$Kcijvuf1)9N@eKs?ltbi$HbvlnQ@R)p^w`3=*sWXc)4%HMM2Uu!W-l&^vR|tE9LMQnZRJP+?M9~CKgZD6Nz{+-GFdfQa z3?x)LJ36rSGfn+9y?A{gokk?NP=7LWp(9kH5BT>A#V4SaQ23M% zEAL`aSlLmmJaI_;l?PXokL7RhCgFKj%lX6;@? z6RD-C8|bU(=3+mnx5%a;CQ)qq;69U=`1t#5aNvdRt?e$QTd7E_vXAJaR+vM*>|d?s zTXFBDR+Etq=LXP22Xs*;nZ$N7I;odMY4scM7lg!jL_{C;5NHr3eKxDw1fxyBnRG2Z>~%X!y4`S%(X<{fSu zEI_fUSi_Jzvy~{ftzY(or=}X@-`MXo7D#FSY0>U#l3n$51i+82wdk0*H*lU z+EEUlBCkNp2Y+-Hj25x9o zs-}-UP_$~uTF*}0&6AN{^8Q0D@vQ5);3O>Q@VftI~?PaZr@!O;zr?bZiU z)kB>gX%h{oXFrx2b+|P~4bX2w#SdU^SmR^yP!~OMK9O*)Yw6fW4woS7CX6!I`@DQh zCLYbHC69IK>CMe{pI=zFh1S;n*Owt{3Rj>Qr-i})Du57F;$?U>=W@6Vt zC*0VzPMZLMvSio5>OKK}OV<}PJCX#{d|Yxd4XQk7>$#atIbt_E0Z3FA0q3NKo~L}| zB%=3pgYb;)MzpIxW?h{&{V?L|rbciXEz%j7vCHQcUTw6Uyfro*;66~Ys07SI77l(M zz}L^-byu+t#W>ep}|tzUd3y$ zeNf{z>$d%%pYQ%TQg^*Y(oY;W#tLmOX_~Z1Hs&?~wR2;}A&{**SoaowQ+2)L6?SF4 z60)J};xXa?@WNs4euKgI(g}rpWR&*FtPbceBze2>1kVu@ovlhBQR{;LHfUF!J#eM4 zoBmA~LdblBtfaBj!+y;u@%JHJZCabG1%=(3JacsK_krS+K(72h zK+-xouXe0p>s>EBX>9($=C;cjNMUHp&7})}_;2*l|Es+4mvWJ4k9!`vPf?tdWjlz3 z$5!82n??OA_*5wdIt+8jorAGsNA2Emx>+~oPwmt~cumKQ0MpCG3YwrVm}FbqX!il( z_YIijO`j4@@pmKdC2PZa>t_6DKHQ0c_qeevB{W_$o^`q2~^3U7@ zW%2jaz{RU^w=lHAtVniEZKq3y@l4&Hl?#pW-yoCUI8(u+qrpwcW)~lw)X1+aRI;)V z?dPfToDh{!iUmRjmlf#hH?z-y>J-h0X8kU<03vhVxa0a;(@JZZIiU9c=QWpjS|?5? zx-4pE+``UL#@|_G{OZ+X5oWlz!Jx}Pf*;na^*bDQSB{;KL@*NMn5gxgzxba=-NQ8 zJdAJ@Z@qq$(q+@@R3peE!rvNyU(Lpdvk@9ulQQWeg6L7`k`quB%HNzvo-1Lg6+reQ zT!j0YPK)X`+kcothQA|y7Vd2I-7;i)dO*#%e2}!QdDaOYD*GU{TRK&H{XZKm-Pgyy zc}wi=`V-y<*9Z7D?%uchzFB#a?1RSoLl=WWN5|`Zo0!)7;x{S_IKH#fW|=Wmf#`>$ zMl5icnSj3cN`nCqxD2i`z?ov4%gVTD(K?80mx&52!vsx#&EYbC4(YBbEo^3y)Dilz zZ6zkA6z-XzWWw8Pf|y4k&$Y5s?ib4? ziMfL73wyq~rTei(Q|C_D{N{Vo1$k@+_>A_3p0X$H8$sjDHFkM&H?)NCDl8Br&V&xG zQm_*~85m3q&@ivTMz?ywLU^UthR|5XlL76YjuBWnBjW89*^1Y|)TW3i`Bh>bJF6J# z6yHH0N3)pVOwps5JaGV-*B@-LT=|6)tiQwA+B-UUbn2lN@Ex_>n~O@afRd6zi|>d^ zbEDn1H%F>dx9un8%o0V0%cPCXMWJuy*OY>umL0>uekrikM)xuFm@4v?{pl%~UtO_r zlm{uteHQ!z?XQ&`jB65HRZCd=8VyE)5AB?Iq#5xoA= ziI9c#X#U;Gd^q5IKI8kga_jZIVNLS54Xe@>PgJc~WV1%z!l~sBscDIkBU>`M9Y(vJKWn66$e^QoCS&r zjub&b)aUB_e)s+S?)!QE1uo$FeBR^rHqj25$aY<}^yIJzoiUoB@@L*c<2)mEc>PS6 zlS!5wI<$_6ACc?MpU>4I@=5c4H@EKm?>Tr>czB4R5nd`YU0*j!Va&bMHWXnQpivel zNPxAB-Pe@++g>5F_Mm>V49422zp~B zJWZT%@7{9{Jen#j8+YVT(tHJ^0}`;^*Hgiu8C)PW*ev3<+UCZd@#B9$KRfiZneHns z;HrjarVNZG7U zEYo%Djgc!2oNXqz>u@F7tJ#_6}#olYUKG{rcVVsCCSBc`*ncn{wZ^ zCiU=D3%A=OIs7fZz1nh*mv<6)_m5}GZA6Cze8eIIdtVYs;Fr@8Qlvvt)OKl zL36|7H6Pz8f#Wph(Nys0WGzrV+kx*!95v!8nsewiiWuIc2fuYAU?vI{ylqe(+D8- zP6aU^(?zG%k~c-*)ghOFUXpvd^{(F!-&XQ*v(eFRrA+vpGMsECl-kVf%L)v)9W}Px zIi*pVcd$AyjLzC}0Q3qmiMJ!W4yNqLk#niNGMYwmva|Hx4}2s8Sd-y*Qbt%IfbP`( zwx=Q_DPl=twvf+wE`hI6%#+<@OKtM)B;I#QOJ!h~MwBo>(7$y-;`2YM1^(d+%3GaC5LrR}4;OI90X0h$w|*oYr^EGiqCP?tEDc zzVz*;aVloV4~&w^5wprw>`rQE7Fb3-rh~*5JPC3={m{;w#o6!I-ErP@)TJ+Q470;!_IShGaFFFg z470#3ei17|ny;K*?}Cn+WO2X~2NwU~761)XF}ZONZ{H7qR{sfslV9()fh7bPJv89Y zCxF({X9(?o_oOVOTTN5ko3l@5s{~(y%Y2iPtWr=fu!(zSAoT|km1`E`2WM) ztB1vA%Ae8JFR+)0&%sLDs9B&=Tu>GdVeKHXj?|anN7?v7S>qb&NRwX`a<243Zf;Y; z<-$f6cZK@ZVyy}$+|sE`1!D8YFxopG>s;C(49*3JTju~`C34<0oAzG_*hYUf3dPEa z5`AS1$4WH+vrDQ&Gd$Jn%1+1Jan)4YgW@URbomg0O8e8zo_w&>DJc3dlXewjRi;@Z z&wI~*!Ibn3zmO}nSy(1?QkocDdwZl?jJL+fCaMa4b#9~E@4Dm!X{HC4v> zCdynHlLC`Sb@GqKK!neHI!uQcarc4rEdQ~fW!)rE9ES4#9a z97ks>C9b3%Rt?-bxv65tSX3~7r`R?z-QjUb<*1FLQL7mFE1mw~UUeMh#tGuncb}YdJ(k&Tkxw4Ii-piojSz9I->M0+&YG#}zIlDOByQ2g z=Z-5q3V>1(eJpZLP70cbnw+{a(>`06q^iWUJ)C-4bF19FV64_eve?iVM^BNd$obnz zUL9%Z=5^R(`z~oS!={C{w?XmkOs!((D7#n*9;)eMB{{J^sI$N_t|Qm*yvbj1S9TKo z)rUQh-B7TfJ+!yhMJk{Bg11k0)XN3Yba8HwIwK<)Aya}L z$5Fk#l#?}K4xz96^ga3(>RMEhy5zQQU2L)xe?pX#3USl-F)uTe*`_ieVYDB$mK>6p zYZ$QWi0L(aXi=$^5c7WCE=HaZjCsBW^%aecmULEoRSj{MyL{fTb3MPpzcm@GEk0f# zi3>h|it$}Sv%1Mso%BGxjKKStb~VJAp=uc_i|3G{smrU zx+!AAFT@Uo8h8X?lqci|og5SkHu&y|rR|!8UKgUoJJ|Mky8s^DH`fz!fjT~};gHf3 z3Bq{WU};%3oxR-ADNw=byc-Zm2h(%ecGO&qj`Seb{N zv~hGwn$RitH{kT-Y>CX>byu8!sh-pR*JASf*xiT4T7ZTWdsoIRcy1_A*s&T}acbU- z=k4+CWX9wwbrYzQL>>8Hy+}!mvHXJ*yrmT!W^{BmODX^0ME*_^1d4Et50fVG?)2k2Gz*PV zlpUKg>~YwywlJ(>zx-$3Z)bv7k$e7-Q%>O!ea58A2WU!BG~ zRak0Ut=?m>xLaqH;v}=gXYRbq#~#gqwQh!M7DO4QU=!dfJ1#*0f(`(Pa&l(`)Na8f zyqgo^v!v)gNO~3)LYL#zpuNclc&#=ilzgQwm}_@hjlmyYA8VvIN2GTRq*$lBlQcuU zvcHKncbH5kgG5P|5OB+pvDba>ejIPF3er>Uism27(<68UJhpD6^?=Xaokakg=Sd1| zl_Gze9W?ciEXiJLmrWwm8f^)>o4s2<2D0y#aZQ1S{7D<5FQR?-N`oh#5r2d(8fq-G8A#i#dOYkio_8m{J8&0#xjDP;Mr$WiKC5x={M<;)bZG5b5fV-7l`ABH zX6i+$L5cfA#(OA|Io6NX3io899I|W(B?l56caMK-o^K2bEG$afIU?*o37olicP3lP zx5jkSm-P+xq}y7=tkgB6&W37K-{0L8Ku4IT;9Gl!Gqs(vvbo~y`a-HvaL>}DGgM9G z-_|*Wm`ey=Pxd0JfC_rPYc8LS^^;OwR(n;8>5~|6khd5c7AFG)_5bT_oq9MB?mL0ImoN032m_rK4fQ3~b zvN5syeIszAsnapApFbS9JRJt?ozFWw`huA}DgZ^XWG>00Ru|6ErAtSl~gOZ!a?Au=X)h-Fb98O~&@K0R~0vV9O5j6OV8mye}Xw+irO% zvgE#EPL`$CdeoQ=cNqoCvHxlM-8cGj_%nBd4bd_89A<&toOZ4e&qM!;#$q;0}^eMj=n$0t)qqJR46l|^-}7eX zxR+%|*3XrBVl#8Fp{TUpbIY+6*kHkDt=y}r=PsW^3cpQ-s@3-Z^c5ajM09!&6F8oH zn3hWM5c(Xp=XE$f>~P5^P^=FhPmfUO2M*p!KW=Zgn(5_*y}blDrFKn{L41Xlk!gZ9 zR1#CAMsV09{|zq9^e#zHRhmswry7Jp25{r^gi^8bT<&A|SWkwwEGorxvLr|9Eo`DU zt1H}2&39|yRJZby2S4SO*TX*khkyUHM{`>#F!7Gl3+9pdZ?0EAHj5ar#*q_E3*44q zdf=mk=*k-@znnTVH6j2{tR^1cc$4o91?Umm*YiR|{n-^Vb*JP%@xSTgNw|F3 zeRQcJq;EE^)y6?v+6#?pIxT^ZC^ckR`Ld?+0){Iz9qHXPuX?f< z06Nrm*q=B3b2VYX^zjAMxLVWt*D0WamWBuaRlE4HhX?1{10N?lk7&8*Z#i-BPh`k< z)$meNTzN27G%M*c@oza~bx2p4<)@Q@T`Gk!?%BB)UY!JJc9Qr}sl)-x&UJwab0NxgrYtDdo>U#YR7E64?#-{R} z<}zGyIhxLW^YC=jvQTq+CV%d{3^?M@p8Z$PYizTxE0j*Obc?kd3UA8{wy!Fkt&*R+FOQhJU6G6#$>K@x~GM)hTP z-FN*6EoBy?<;C9LgA}zW%2d1ju&g~jD1Q&}W7a$~u%hdP)ZA)Stdk=2uFNB*I@(VK zw>f&sNgXwwZ|5TEu{t=CNzZq0@iH*CuUI-7`sYf?e!VyjO-i2Z1}z2KhLKj5Rz!NA zFli5sotm3p=eFGu`I+@FJd~Pd7mZ9?N{`uga{q5$rHuoI%oC)wWI~_N7!Cl0=J=qk zgB){XM6gtCl{^!;vcdidCL3=ZWqqFyU!Jd?!F*#~U#d;M?Tf_^WlW|>O)_c#|RXQmH&k7mVrJ1eATA_+YR=v!AH7Lu=H77~`P z20q_iUfEd=$cHgb_mBPBBccVhaP(WS8^30Lh|1xryKnQ1^!7|d^W>bSZ=KYO5AP6U z^y8S6{!2jnbFdT{KL@P4MwP}kdWXNFJvD7LQwEcCVChTZY*3Q^O~*6mm%)Y$wu z$5p_om`@56rkI#B$}!^JA61?Wn4sHKRrnIf%}-M^n{pAZ8~^{dT*Fog{bG={)yfr* zBO&W|K3m*GGPTG19d~EpJF_UZ)@WZ|U(CPIxPFwW+!3D(-`YC+@0F+tmznCpl-t!a ziKFQ+An+%`wWoA4_bP-T`$X zSXX!O$cNa9=||F`YVgMGwSe!jX@k?WG=Z-nF2~HXqUNNF0l#PUmE0^nhkdb-udw_m zZt1`~BzLF+L?QQ$A^)zZsw zql+s4AM;J_lf$XXjGw>iFcS&iT*3r{#Ae3};WdsczBij(LM#H;BJmyUT?ZuFJ!z(2 zi^0)mZ0+?I1~e0ZRihIV;>>G559Fa9MGmju`$lF9TOH!3EsGs5>Az3S z%9C@EcRJ#`=!a=^i*Vfs8DU+61{@8TZd)YRhxm-N_y?}G0Y)nv-MqrjU7@*b7gzIt zm9CdIJ6va$ayUa{*UJJ!xzV7;Z|5z2s{plr)Vs;12jf~Z(;rO5R%@MVpw!gRxifaN z)rhwg0cS| zqJ&h7OlH48iTlpXM~t{1m^W}9OA!q ztXBc>TY#E3M)_)OAq)e0n@a_O$|j*^G6d+70DxyAo0?Aa`PX8u8u3}DfS+Y!Gz>8R zCV4g3Td)xX+M8!;U|;v`Nlj@um^ft-29W+GCWnUnwedK%k2p z+VCx!fg*;!iM)S1RTc+yXmiK<7(TfN#)BD`pYWd7g^k+J0CTg8L8O;u5%c$<7GDvj z^LT#ax4E^dd0O?G>3z?Ra@?rr99CCVOLh2fcj2OzvfEdx$7t$9;3*Z|pK6;6=Olk$ z_)9|mOyJasVR3-K*?;7>UF4Qmgiq-2V~^oy(bqluU<(8KgwR8^L6f%h;+CCFt!3Pr z2d5Trq$hl!7uf4}IfoWvXP^Azs55di#=x)&z53Z8zg;VEbzd-!>r&ED62)r|=l39+ zhpWsq#fqmkU~}I*9MolmsdJ6aWN~4^s4Nu z(@}plYs;FdHKSIPr5m`3m0)(G1%_M0Cp-zPVdRXRjczN9qgX_3U zMb&aS;DrA4C`SX|JQ+BWn4ZM{qO|>G$s@O)O@hFPRUIOW9lnQoqZX2DgTA(@cvqAU ztz>HB!nBUT15A_+y0au$E3z7VUX9_xPpZjXw88;@U;D8H|H!H93!ATIh=^|iMqkgT zc8~%XYsKk-xp=cNrsCV!!s;6@Muy(NLJWU za7$sbL>q@!D2+BZLa>1P>>I49UTP05YQ7CVP)1yqGG$~(qCWnRui~-3tLAp zVb6zDi>GlS_lo6S+6CQYR|l?{LXUdA2k-cCzW-L@%oFEcWxtUqGwfXWYYfqG9hZKN z>K?!z6b&99%nk6HjK*;DbRFfV$Rhk0evIOJ-qKa;&g)AC(WeXN@AqWp1Gmd}!xIw* zvFmn$Yf<%KWif-{gw7+H>eC6l>UxD4eD=1rFAVmd?pmtWbTp>~CKbNByZ&h^ZFfu} z4M=lNjzvg3IwO(pB;vO?75O&Fh z-79~~*+7ZjToLc#eEs0|CqdopPo;5ysm4zb3~QDlaFwz_MJJFB>+LlgVJrz+pOHSNj6UqTQtT&sxI>FjpMk_=OaZf@+` zS=z|Nkw0pwQ+1|2vUs$JYNznbK1F#}%1I@q0`jpsO|cKw-qPm^A9M%S!rEy5xU2t%=Pgw$+&i4-am*KIrPG%H-0YXv zC}fJg+4i7zilNffcwS83vi;J|!?;aEWJxAQyKsW^G6Lbid<-_#rHLPxvuP@8g>ami z>*;QJA?M}XRtjG9-%INo?vlbf4xiLy&@ZHxAd(oj=kd^B{*afvlA$iJhEq*}_$-|$ zXPCRIf_gDmBnP!!*8?9mxpkOm(Pn!$M?4IcoKxPX;lOoU_>e77l*;kx?9M2r!fdA9 zF8142n4qL#0w+{x;82^4Mm=;^8nJ=%E4mv#i>0&~%xW&J`P?Qb3K6jTjL zA1_?lM}?t#LcE?o`uNV(LxG zTH!TyZxSp(I$f zL;C;2UxSG%gfBp9U<)PW%s}I{VKfNNtLs?vz{a~G4$ifIA^hyaIWa_emKRzZI%4?B zq1>YV!-zY*L*=E)NbUy;%E27^WNrSbvTv!^T2dUnJ4OFWA$$J^*b@2s!Zmd@%WB+Q zYp>Xj0MG$TG=klMPTJRL+03LYh6Y>Y)J{~Rn!wvtt-W?s;Ica0bH| zr;mRX8LU1w!n7(P9jw&ci1OE(oYc&EaUpmnE{N`_*sE;Mq21OESuW_i*GKU%XC}zE zzopH~|4f%!EC^Sz!6Xn^Ur3!+Q>Tkiu6gdr6ma`-vmWT%;VNE6rzCD)chuu-YgA50 zq&qtYGCO-vv+lJPMjNuV{A9`L8F2TkT%9z~fP+LgQI??^UUKkK9F6^kpInG%*(MS8 zbt?}eS6I)Ej4ncL`@1tErQHqKCTc_<1yk4Uebpm-=PWXs{%Q$w{R)g51t*UsT~;(R&d{sZppwn?o$^ERnR#{W96*}D%ceT5PVHaNYj?sVA=8dIV1Y?oh*{ccQ zqk->;LD3NVkAi#0#fHz&=rO?4wQMAp$hO-~Bm)PyhbfP_(VO=}*SxhOq+0Xb;-#ze zTtDfiCYMsYF<&^d3z~vnA0wYOTO-A0p$FK+a9*sL@>$W$&>~k$MQ(YK*8TKOCG*_i-fkj;c5dRDe10?ILQNy9 zZ4sjYS7eA+-%oF;!)!sf3r(hHy%3g$Plj!TPwKQ-=X&)twz9@Hj~pr(-EWR`eP=8| zv4Pb0-SWIz%pYk*e4f3%HXq|z!40MnBKrsiR+8m^ktXwy98bK9JB<6clnU5fDPAl9 z^0pH`PRkAGHs58pA^PSF) z&L@=m>GLP@E;0`01s|!ba8`>hb*iQe+NNTvntprY60O@dq2o)CxYBX|QiGi}>)ezM zB(5_13W5*}1o{K^_O(s(sm=n_uBKXfmEt$ew`_~U_7JYPw{Ir-@4;Kv7T?N<^q!nh zvms)g$(G7@3U;WCQ*+{&+6YkvjjRC({C%OL=cq| z?;}%k47J6wJIMW~LaL5ttCrPjbmBaC)cx^tDmGdwV@WylH^a)mDBJINOT*uxTy+(a z1P?9ZMow<(F*lilpQp0k}` zSG5kdE8U9-+pF~RMEa5T`XKlJp_eXQGfa&eMy3ts=57NdCQe0Pwl}m=(Fps}&t>jPwbD zrBiB>1xutj<+1`xSrNuYga-in`KZed?3UL%Nq!a++3s1be-SaJp|1d`8;=#ZH+jba z!6`Q6q-krTY|iaxC5@Nk^4kxYobOAyB?fE}_#EiHUe<(`nCh8qHTBZ@dqzj{n0oKn zeXMWe5u5ag+$Y>taIX3CX8Y-+ELXzzfSaJ<`+Er?_fK!awi^hqV0ZhQnD#b@R1&-i z01i*vz}bx<%#MQED^#ilrlba=^qHQ;SKVZMFTAx%a|alsR=B!hhdV?e-B^BWG0GhF ze!9{WD=Rh>n(4h0Z~g~L!W*DT1AJ5eL-u=VtSR~Kr`qk4;#Y!O9hU1coPA!%Rs)63 zC$bt?rpbqU92RwYEvgRVpMq&eHm$8&eC&kne<+VN1nP^FdvMJFUN-d}aX7~BPwq`)+_oG z*IH=&__CVj-g{REetY&)`q=LjH8~-%KD|uAj2K(bI*+yLhYg*^DD5E|dth`Q~ z3RiAk>&~g*=bid`&S>gFq3*@pFse)=_b=ay^^4v(O!k#m6JvS}8fQw4VH9Mk?tAUK zqMPGHo$;+_hVkIV)Cq%oWLd^}j)UYN66U>mHSBoJZs?xF$2`xTy*V!<(O-4c&SX1}dfQp2ax9+>=e5p%bJ=Q{X zO#hWeU9umaCue;Abc&`kMz0W+qGYY!$Y(zbX@Z$L&N`n#V#Ten6=9J@4x zm*1Wk6Hs#{v5s!Of9t~9-S55;1p<5zvP0@*{Mi4DRcZfB;CeW@flE|>COqBzdJE*C zC2rV-wkSDzuXMdd?QQsv8>>a?lG=YBj!H%PZz@y$OoK6V0CROj%)giM^hRE~<{g|6 z?9BWC9ep9W`b>~mQ;#v>lLy@qx;dn|uSs#NWryuf8>1*V|P6$WEW;?nr zS0Nq0NQLomnnvx8$(h)htB{V<)-xKxZ;iZ$ysB%Hx#VeU*01wi)HFYM^5~oWaz3>M zPnwu;JB@e=kmKVoyeD7-XS6px3K;wOZoRE+Mru*8dv*uz`K7R_;s{|!@Nz=W-oBY9 zI{uU54F9UEM1yX0DG`J9%y%SDwYSYoXUTEz^gA8@+@Ul!ZxH?oy7l8)5p`CD1jTae z^W*%wp|3P`7nUCI`9tb8)1&?t(|8@y{$}rz&)HYdcMI*P;B19+t_C->-@!QE)}RT< zlN&qlMb-9}fYm#hIiN%as=#5Y&4Gw%w7eUGF#r4t1+{HB&`#7>8^qEf#7M{0c} zed?j(ntqw2U_r*}x~t#;hY(wiLH0ebAXk z*v`iKDl}?;b9Q)I& zNJZ`kWEs~te6OfRL=?HRUN_${BEn?Rm77edW1+L`{a+cM`$ps&(N^dZbZ&f!JFjOX z_XC@pFI>}?q#xsWFPI4B-O(L&pjMb}FU7&;1^W7`OC#*7T_Tjp`D)E(U%rDF%pn?c zM$#2fu%-94!2RnT_$nxU1{Yn%__MSw2zvpl$S^9CK7M$8ul~{BH5i`^FBc*XE;J02 zLCD5`Td32kse#^Qp;K(0Ty!8Y;+v?_*e@?*84cyK7Uj^5n5|z0r?maK`8UnnW%v2gL-gkova0ikH`i3qgkU2?L>yH8LLz za}4_(cq89dIy>EWTwy%dt%;;Yov#h6p*@iB@IQh{`;9)`v-oj7XG?EOV=Y*&B+1lb zEpxOH6%p!(!B{R=#c8PkLeAxL=A~8+jU6K+g6=|w=CQiDPM=F}>wvQEQWdDw5@~=R z)X~jk#}RwAfXbeVxzf*sjJn5=)GS%=j>S2-q?ujUyycU+5@$|)@VF4*lIi`TW>D~N zxejQ?Y^;FlZ9HZk4_fGtN^K8FEv-C3lEt~B&OBP#6piKIG1QRsnbOVNyL#XrvGrR@ zU4bpT8mby6j~1=I(X942-B|5!8(99Bq@^~YYOKrPop^6>wyM&4XmSYmMav986B??K zH_GcbY6O3giOKq{JU9yjG6R+-<_XiyA`_D}x+h0NZW>@rI@LwOLpqNnlC-5`8uT!U z&e-KzT1Yn?k4&y;W<=>022QIi-Z1zfYJ-_aF;@t@Cb!?KCsGtJU zZ_z;}+^cmC$qCq+zIFI9C|)qUX1Ctzo5DTlyYtv9Hsl#dYHBdEZhG;J&K4C#OwQHj z{v)=v^x4d)B|9}MtnFN9U0jlF93eH>sUM9j5vg&#KZAAj)PuJdu|;q0dUm%Vvk~K_ zhM}I!K`gRleaN3sKI*<5HO6#fg-VJ(&;#q*Ll(eoM1I5?o;W$f*a7gf=!gO$hzTsB zt|o-7cZLC-!p;?C4ho}PduyRAX6(Qaaid|vTW)1={3&C0ZNcrF&%4fum730|f4Rf~ zb1!X{(*`YhJ%9^0Hj22LwtUCNKrmaWWpjpQeqWfc9 z?}YDl>A4t=d`UUHm-BueHgSCkKb>|KI{E1H^EsH>CBLf?A=t>`&doP4!JCPH{k`3s z8GcT~Ordz?ccWFGa^WwQ@x(9FZ<)&d3d)?N!Yf%4=7{FCF9s( zl-r1pTkR-LWkGHg!3Ov|qf>l|-J$`VX2f4@x^EGH$ravlq!qF^^y0`Z$<(J=mR!A6 z?mjh*M~%h9&EGd}O@h@c`xQr982ZhwFtOR%Q{)Dn5`~{zc846Vjr{RlCE7l$?u_M% zudz;P9W!%VMU)l#adxBaAdhifJ>*Mi!_lOYSN^-h$nD|f&s-VUs!V5SS&gsNfKifa ztTCG@L7toN|Ie+x@xg5}j!xk3B`PH83mGor!Grj7wG^FOlTMR$pL?@3Hgdiu6pO*d0J81wF9H*F(4y!15iYCt zTb|t9a8jL)3%dSPTO*=$r$|O-9_q=pE`Pli26wue_TO^qDyigscbanqDl!m zXB7GyGpJh}0>)QEn-(lO;pc?DuYe?*%!Y2cNh6cndPB@rE2@m(w5(sJv+b4@j8|H`-twh< z`&aUzw87X8W^NdthNf;if+V zCEri?2X*W`Iji(D^cqjA)#8Iv&6-K)14X5J#*e}7kZ%9g(2#E%o89w!zinJ#T_tY4 z4>Z!^x+zV}(3`41XDzu-mUfG{u{!58NG+}B+>c?znQYa8QUWo)xXLCG#S@A)kG+D zy8V?kqJoqP2^+~I@U1%sjf|__9vM?{5BpTVtcxNx&gs2+%u7`jCwH3m!|E7B{~rMR zOA~u@$Iq1SZImrE`fhruH$?N*@~K{Irp0!6gp1~i9gV#2tfsnjGL!rOHcCj6^2X+C z(_#lE2ta+Mg-e}udu@AlzObjwqxc2^!u>Nv=SAdg$@t67N82kUTleD&<@W-5CeJUb zKl_JrDz0xZe={T+CqrX3j8%d4+A<7_gEcl9Hw6$72)!jWh(Ku9l}LO|y8N~g2Us3O zQx|eC7nJGBcpbrBNcAc0m#^QH83^^7Y}tGK@O~4c=tdQkur6BWF5D}E?ZO`~Uaz|m zteh*kJbT5x4E4k1=_dKI(vh@ih`uSkCTK zU_@jdz42u>K&MVB@M3nMoxu@6G6-D`>E$hK!Tf-Gr4nNoOzLp|M~QNMM0O2exfImm|1D9zMJV2xaG=t71`QJBEl=+mKC^e&dfqZti^_TY zCVW&XQl~p7L_SK{Odo%>rq)Z@uo(tU0Me$m^r1Botq(*%7rp{G1S z-Tsmm{EGYb|8!#iM#MnC^!^RQKXN~{ zG|^u)K@FE$P>Mb`y+o>UC8A&L!4X-a%z+!>6}uU4Bh_>1XPW*StC_;$>6>buag+}` zYhMvWjb$a}q~-O^2j4~3Cg~?M*lJ{so(o%>dsEXNsee4(@-1x0TAsALSXIlrpkJx& zRI{V8m8$`xAhtbCssQ|_nU3p`QDnsSYFgy}yF-LNovgaftf);%dc=l`h6hAfmV;_>=eNplrHNzA}QLm}BJlPeXJypxg zHVkXRT!r8ZPhC*k{qX-rck2fnYOQdt@+mAz#;@pZuJ4t?xLP|vP~RvUs+FnUPmcWHTzgFKONpaTB#q37_?I(p{TA*Uz@c?s%N7FM*xU@T8bw7- zGLN0mGTkm62Cx8&+LbyhO{oHg}vwVC4&!P%CcXi8+FGPhMdsw=c zoO^q*Pd!YeDQ0Rs@v@p+gcxeP7`@zCnUv%7Sy`nQ?~{0nzkXt_h_uX|eairueH^x_ znCd!cAAoTixd@Zg>dE1^%l~-RqV>{xhMy?$nESEslpgVm9mCUnZ&N*{@}#T0({b10 z*v-WyiYN4K9KY?3BY*YSJ0dkn8is82CaDdO@U%3CY7H-Ub*Zs=zQ52#nsHv{RARzo z0rkYX)r@hO*+atxQlM+dY6soas6x9$mL>~^V>J?q(0%c5bSpQ!Z(uPU6zJ0yTi0*a z*gi*By(ss?U06|wdfBA6^}0!BPuz{JRB%&RJkc=cXW+Rks45^4YoN8SK@#6{AL z5RP!}^Q~|VY9`Bk`AqEPmQ9+}LQuR6BdE7FBfYq4GmEnXIr>QZ#a^cr{-14&Up}pm z<~%Mv7yrQP{Mz^sz$i|I%fFvaMk5dvkH4Ms%RlwAVSl^5Wc+(e{({rv=*>z8_{W!V zhNZki^vp)p&e=aD>LIgUhI6vF8p%{y=G?Ho-W4hGM;ZCKKH&n|NEb+$5T_6wW>>k% z3j^aSo2VA7L6*YWX?CIR-I*O5rLp#Bo;^8XUq9~@?m1bntsJC%{OtF0IYIcxuVbD1 z=89sUM|7Bi*OOIM6lO=T6~<{Gm5p&YpY-1x=o?~h@UctYb^N!m=0$GX$&XK2e-T@R zI6O$jtXjyQ1&}vzItAGbQTQ;dhmxcqeJ@1#S@ec@75mr3db2jhdQsEUM|mMpF3Aa} z_L4y||8kn+j5Z=mH)uVx_ZgzQELAX4DANhwvp2pj@7USjEwP+^7mXqwDw>68>FFjF zp}Gq11(Q-SCHj2);sE&k&yvOhBUE#+{JPQh8O3LEAHi6~ADB@yo~PU(+XnzFyr4qi zzPzBs{Q0z*Q&QrKsiIUDhDLSaTzP-3OOlODBJgV5zS5av`O|n3UvqS=#y7q*6BtXb zDo>ZGsi?hi0o7p|?i%nSY$RSYtOF#0n*%}T7c^@yYc9ul0MPEU>SG)b;N|W}e_v*ZpR;9sCUQ}?64rAXk<$25VCho`Ytn(M$=ipZ{t64R# zgzRxSvPm$Foe;4j>u4U#y%$FCilwOzNyK{?F@Ps1_2C5U7TSBaJ3~mS*+;B^Khl7! zH&5~cQtpr5Ib2-FWb2LKcDzgWcZ=XwpN(8C2B!)aNxqC(q7z67h{8oVbe(L$tfXL$ z8Q;2b?0(Wy5Bu#ChZeDS25pvf#>**xDcZYc<8M#i659*zHET5n(-_&+2_q(XWBb-d z(FP>6YnTb3Hc63Cz-Hx=6KE-j9KnlsNFdGP#cx}mJR2-^P*mx#7ofaeE_)fBzZ8a z$OPJ*a}2JY)0H10xpVs$c3N z6RVSOle!Oh zGsFBI|3s~j@!B0@CP0&{K1PakkKduw-GjI!XK1-o3m&VN&d)tVb?Md;<9NFy#Vz(Z z_d#FW2M@(%FMtP8*#%B)Z^>!zp zos4*4*ygS@B2&=h9K(w5_uaQOv|)ka_xSXJ5*Lh5-})g_h7-(|nl;X4)Wo`Q(7BEm&*@7;t>aGr!= zIRnvORJ9SJ@o_G_(-Qizf6NaIQYt4ZANBh9LuIm~{yU3s7 z)ub7#7}%U)e|SNK2DWaMaM{c%>w+@ei*@5AXvri{u=RYBeyGu(E8eRU$N=qlq`|2k zkLC^_f%z8wdW=<_?szF5_i)D8QNw*jUYz_Axx0vu`Dr2Iic29$g+SSU>sE(D>npfl zWZr>XI}e}^BM23(NXeWgTSJo8a>OBHktjE6{*$_AT%~<=>y1*rX^q=ME#z=?Tg$<9 z{rLzkFN33|cLggb5ZgU-vx!wgemh#0@(%qqbHx8EH0k*I-g`BW@QYDu63S8JK)zVDaylp~QJ6srep z>5ck$=OTQ<2^Cgpefg6BH=*PGe{&N)SAL$&V{aF1hN$f%%k3H3d}6Fkxv_@a{uH6J zmiy_p(oW&T132HTb3ZkeVhF-)@Kjq?#Q-U2iShr`_2yAY=l|b#re%(!R904Qm5xkP zX^ttbpjlc|ifyCgo;p#v5aJ3bnU%X{)@W|%v{<=oZYiiK?pyAPD=4_&0tm8m(f51* z?)$p0b1u%|U;f~H&Kq8@=ll71)C|mp3OF5De!=<>5VORgG;vf(Av=*Hp|g?t776Bi zK)VJ>s6nT4(3q#%ZHEYAsrp{%=`buZ7aFTGa-VkvFC zhW&M%2SL_8rkL%Bw`aokkQQCiuuqh*C?EFd@87Z%m%iwoKcbcqVL_B>unhcB1|%IP z8C(Fed&b0vNha_>+aJ~SqdRJkyMjl8KmLk#mS{FZ@LOTE(Ah_#lf2vFTzNtRAZUE1 zP_uC3D{Ca2<{qZG*07~T4V#YWkS1@n>zjLo)3m_JNGWA2BLHN%L8?vl;4ZHV6#-Bg zS)eGct}O`w6X_csa+#Wjh1&CJC)cc;XC&C?T@g{{sZ(<7_Y(|bA``%p5Az0;P1JN-zCa^ zoL?B6KaQ5K3TYX++g_wdU6)bzF}|;r5FP&L=-L@qQ~$|ix=yKoJGtkuVMGFs6;zd- z&i}TcV+guvrnN#I7&x+ZBoSU3urALe!t^qlW-AZ(gc)NSt>E^&gwz_)Jn$e@B->u<zL3w(~n39y_TL$uU+XDP)wtCf)mc^XYx$No6lX)nPY(-jY4GI-$#M z0H{~W;5sy(Jw)ttrssAc| zXAvayl>Ij4hV%_M@42WFrlq+Q6E%vrF0GdLh?K=jGBvHi7dr8#eiqCkHdNxcZZ2?<7AMI@86ikv)~#(_X? z?0zRpA1N>31Z*^UFt7Z*+EQYRs%k;{30=LnY8n!TQvPCG{@!~sK1w<9w~}La3L?@L z2ZX%?bk_e9tIg(+{(rIB_=IQd5mVL6m6OxTyYMDocbM#1udR6-Q~D2 z`^~~w588U}_HbDg9Bvukp?H1f#P&(8)Y={wn^Q*?_tenYSC(_=4^=K;=@OaFx$7D=IV3HcnwBg1IKoWxW^KfnOh#Q9?R?fIBx&ZscHZefg;Kt~O8T za2Edfy(enry&yi2H6v{7=Kr94S{lE+5L*vP&ki3hu3a8V9JYyoh$#>P_VRD=_QA47 zW_P5dF=SkQ@sQ$nM9w#hC}Kaa+qxTPP(aYwq!TaTIGmW^ltK0#2S#0DAzGzE?Jej+ z)nOlfiVl-zkv&mY;POuKz3CTYUVa9qApCc%T4v`x|6CO|%nf~E=T*oLrm8d^6QrOI z4)^VbpU3?k8O)=P4nk>{YJyT|zq~fN+SjG?>{IN6hW|5YD}2)-+TwOVqO|6=o`dMP z%A&>{{N{`ZtMcVQmEG4cPJ;rY0xeCCRyEe~v{`j-)J_;StZA7*LlbD+@1*2{bVwS_ zcL|`$iw_&ZvOrjtSYcM1KJ6*m7Du?T*G~|OZgt(kR_*3@#om&s{Tq;%#v-}Uyk}?V1 zP2d2wR$QA_->||;7kqEIrZR@&opaa#Tl_Qn3j+Gu^;oJc(ab=1;VFbASp-7rAwzZ9 z5$_-*GK3KirN4!22!4D`04b}osvz$wu+OdIjC9`=Y2&`AX8}waKPv@|u|EkupoE($ z9bYjhv__R3l#wBoY4*yh@p=ZOICQ4n1+p_wJlij39+rwlrY)fpYxOF zRIakdeA;BVScWPVA$TT5<%!;5S=rJU<>SVTz#-7|2Og56iGCGYX|}*l?=#a46C7Rb zB3@uk=WjJw@uv5KITfkvQ6dt&KD{S-Mtk!IeH9#yaM{I99l~ zA`o;5G2fLolSh(;-h5bJFeMn{CO9>E|EM}w&TZvt_XGSYoqp8cc8sh;w&{({@AY4t z>F~ioVW{uvTtZE>*hS?FaJ_^tEqSW#uy$MQ`HwnJ>bsMk5gX&LP>V!q`7dwyD+%^{ z>fa%c@w_Be^t`EPc~E0V$Nl=%_QT*6sWDaZsA1u`TFO}oqd;GjW>p4PU8>4s7e{WO z&(_`V({PmUzPvHKCr`Xxxaq05I2F#Rqa>@r{mWzIlc}kRk_X^+JTv7nN89( zbgHRvRd)G@`De#XwIk7E`(M$ir9##x)d|aEE2mQi4Q>y|+J2M=uT_-t@-U$MA12ri z1m{RwusVA~WfZYjgnAMrTNNAlr#@yuap(AtI3^YMGA{c)KmqqvbWa6yuI2q+9H!%6 zq~er5!av!+jW-C6^%eOXvcFK^=ce``#tsAd7S0*sCTFzy{oxQjQ7U{ z#*IkOF5}eK{^M@QK25;GmSUp;*(l_G%B6E1A0=C_J$bKpCT#pTi{1dbscVk6z7(lF z1Mo-uDZ9+JtIb$RzLn4A7X?Or^dOVNhBr><7gmlvjp@x+HxZ|YjXgZ~bY}QEZk3z( z%==25ta<~6mDy`FL^H_D+2*E54&|qZbbncIvD-r!@lw)ALXA{)SgWo78$6U)HL1rn z?kEhKqcs<5E6=TC9xgnf?4HrasRI+un{wdX=kN)AFWlaFbbguIk3ca6P@~fAKI8Or zDw6*q@m5JjlW<8u(JNO86UC>*C$^_wM&f?f+m5pFV%3R$^j-ypQje}&>!$(#lCt^= zOLqKcPtRw~S&@~yaS`ZCn|5Pe_F)Xcg&I5RWXX3RITj*Z5T)Zq1*`<+qmDK0lADabO^S(B+=J1)bfd z2^wil&)a?{TC;}g&MWi}Cm^Syo`}Dm4ksi1mq(4Cf7*<6lzZT!y|(u!GO!QWRew>m z{A}439`H^pOZ|{)d`Bxxsu2~m1heGH*kE?i&=g+5h<-JeT5K@p2V)5;HTK-Ko`A-Y$DpLLqkR$d*37QijU1D z8Fr-W-;S{o-er#iw`AsAfOyp8J%^65Da^U-kc1d6aftWwVQ~Lr1oMyz7Vl7=IBeIU zGBi-Q&qeW><+7F64fbDS6m>zh__L@dmS!yEkS{~&^|Ce&fJ`|cp5wQ8Pq%u zj`^;>nvqQCUz1jYU5GI`%x{Y?I{?OtIIMcrwrd*Jt!f469Fu-+4c zxi7=8{x7PkO@zsLE@&6!Yy91VI-Wlb!sJuFATBjk!S<`&(+s{if2H}Y``n!9ysvLI zZ}oa6rN~1Z^{(i{arR%=Ln!C|NihuG1q-ya*JJ$!xc9Rrl8|DU7qs%ujB$X0N_fk6 z#LUOgxv(TfkBwqKohn<#3gb9T^yZ8nTDT(JXK#4v=>|I6{W_l6_8KoPRGxh4f5Itu z(zCI^`F=qAYE8Rsrn&&D?Di-ztL5;=x_X9|MED8fqL49zSPx~uB$$SIp`n_EnqJ-f zqm0_}^+!N+x1$en9^I#Fm_QN^a@h1dk=3k_SMzKn2q-R+Yr9F4~@4%_zM}n zK%({96Yrt_(%@LmB^Kuc{E8!@hO#E^%@KV8+uZz$#=}VWOB;OleeW?>)TK=-hFqvx4a|Dlt(OIaL41I4&MLc7Fy@sD^mL)uV(CX|95o4MDbycu4VOaA=L|Dvz-X*!UCWY{Q(?rRJy}HZo z?H`HUfnR$)3N^Tu-(CGWK>#_m(;M$hGkcIHfV7bwZH;}VFcOQdd^@#cG$z7v1@j*$ z>{ZaJDmF4he#yZy#VS?1-}|M*MtF~^(|Q#z8o+dtrOrOS;buzCavCrZdG2GV6+u|z zc`!IHpRmu}ZOu3p$?W4A4K(I4Df#J+`pn1hsbv9tm+4X1qp!OQxM-{1Xi6gspfsEK z1WEHakG`+{Z94P8fU&e;%1Vz!j({(Q8my1@_Q}(SjcL4i-u6O(ueNeHgWBSjb706R zdUY50k#{RS9{e}ehFyrCGjfSZ^UR%8(P;ZZc-W@3+1mq3gXSonyiQo{vy^tOE-SZX zg9UPLQN&&N>VpRDUFoxzuT25Z-%Hi@jg3lrO-ep7x7g$1&q@)w4HK^IiOE0yQ1)>N zHNi6DzKmn4BewkkK;x-#L7J5*HT+~ai-HO~Vvtt+GITmX!TYvW`GoHS2?R!{muhDkM?@Qixe7&@QwRDtsFJO1F z$;d#)AuU*@MR;;9Hhi_(`KHL92eji~@^dy_9{pL0f-lu@G*IX~b;!+tZLjE?Fs)>D z*k4p{e*;}TeLa%6SSw$r8Kw^2a-342_5CX865(>wq4R#4vnDKxf23Cu@X*`2x{HM1^Bpst z4nxUE3GAE^v#K_QxjVPXO#rOHKU;nI=-}otN{4h5XfWEd$IisyDi4?zA@%3XlK;1& z@Nzk|`;+|ntg=k?^rCQSpRJGBXU3MYTjsWhZ8+X>Zs<`Toot_*v%L#A!X(uABX0DF z{9u8p2ddgE$_woGN?LbW`V+n0HsWRgEK`((O>o;ZUL6YTuodpJArmhRU2Pg zx>Y{R)Q#=q4aK_&+nEVH(5Y6oNpxL%P&TMC7Xtlo)gt~6<1Cn;PSy+6asSUb#>rqm zYoE}M0L%8*B}YF3Is-lQUwEvW9&rUwG1R5Qw{l<1Ij@I%@w?m-xsx{XxnFQZYu1LC z4XiO^2L@h%PB^S8G3CE{Q2w~5&(odwY~UAt{G?`PH4J<^!`>~zKQNQ}6kgJ!Db)n{ zFSX(Gvb~A}EoR<1=`C|Bh^l{jAT8RT;xh?DyXVZh54-B0_iL9$+yO8*H}~vIqMpmV zE%#g2oCz`uXoy|4?cAG8)RmS79yTvNt+OGRB*eUN^$;6ZL{Jv<7*!M<&j&&n8=q8jU}Mz6{fP>MvG5L%}6z(t8V@Eoh{4qWq8VI$dhDRw_=(3-;W=xK|Qr4)C0&P*J;{_-p1h0xj5}WmUn){H0$YERr1Z)Ua4(zalE5g zVf0?AdGG9G&)tr@NoWozQtAXVb4B@GSsaCm;-R1avWOfj*7FSYG4?`6UqtLLGtk&p zfpYjCJG|9cg|c%Dtm0t27C|ap5M40`aYsEZ*{Q#6*lY4DCxU$xbHO#n&x*6}Qi`jT z0=55x40j^5Ghva-PvcdOOC{fbMv|qL1N_ihUuR(Q45R1ovL^qa0*lP*&-R~v0;EDt zDB)NWRSNxzQV%}3dRcSJUd=sF{90SAVEn7r826aUy_tS3grTtp`6}kO9$D7Ib|(m{ zF-c#KGSZX#!d0L6BgpfY9Huc6tvOxRKM8MICCViFG0v6e^(FcMj{ppf@y^*cOp31Z zbAv%r=R@Lar6~x{-}WPwewPXB(VUYb9YU;yHE5Qty|%ntW*bsDQ2mUYJV_kiS&XZ~ z=gi~o2kO)@+GIv`igLlogc$E^S~jA{L*3Hh{Fs&{#MNMM_8G@2kDH?4OHozbQNK!q znk_nE$9DP9%%i@I_1w7|R8gHG0zJ<9{YY?W!QFq$-X%Y{k!<)pV3y}ajf+?HmyIH8;v_9XG7=o2=&bkzZn`Nd4Rqo;Z1r(o8)f%y>`D9c5(kQTf zC{dpOwH?aE`zilX8jQ6r)O>Gt;n-Z6b?&89$KOblhj=$TgLY48V4Mw}G8 z{|Qt&Vza1Y{4cv&kHaOyeLK={ogh`ih;Ev;gSvJv<9_-MmufpCWo8{98&l8VwhsFh zhZ=Y-`pEhKZIPM6uC{3#?P%{lQf#|$|4y;1WGTbvf>V{6X0N3zhq~5Rmh%?r@weBhfk(cHAkiB){t)wG6uPq(qnHcwry2d3wbtvKLz{s-8-w>zhVd&g^r=Bn!J<-HNWVx z`1h5z$;qRcjfTeN4+iolx~x@Inx@jxKcT_ey+RXR&`g|@W+dm2p+dk1Ec<)9cYe-0 zaQ^J1n$xHL#Q+Oe@kVw_PSqAZ+D?)SpCVAB=7Q!S{_P&lB>_zbggrFLQ0P*aaBaN+ zIK6Lm&$ca2-k)RM(m&S)NDeqC^=amKh>IyYzv`aWn_Eng4%`2(#E!M<={nqh`T#&3 zec-D-`p357_M3ER3(^Idt77g_8n$kyOHD3k|lg#<6hV3_U$94_tf!#ggs@!(x9<2&B0z%@F3qzO{PCXcY(t2*a zkd90WJy?bj#Am8GJUA;?31A{AAPwvDVRksaUFDqI zg@}D7$Gb!uXexxKWOPk8Yao5($<%G6&ycQzd3jZcNuF+R2pD*iH@pupaXx)onB`>; z1bp1-@Xcvja)UIi#e1^eB>Ie-$dhDZt1Xuao>7bSKsVu>Fdtjv{%BUjNfZx$(_5v# zR~`yKtN5@o&+NowU}RwPCB@iVAp;f)S!FdRu5y6;HO&<(Rc=itRHk%z8Xt)^hdb+R$=p16%nuO;O-_=m*taqb%Rki zQ`)n8$~?wx{qnSM*1}cX+F?>7;RAN8JMeZ!a%%rrmbC|GHLopwlM8yBbJ&BiJb>-t z)X9S*1?IFltbp0tH$N#^fj}6#;QVn7 z>@iV=IC`f(T#(lx>{<8I;zLtT)vOa}GgvfJR-C;aMbpp|FJWr~YW*b}tft`9xm_>Q z^R@8rfdR7?``V8_ z8L0CZJ=L9U;E%Xn1}45sJY(!}P+v{@*Yq#H{;LQEu?2>G>9F`Nlh1kmIkOp-62_5R z>DnHbjn|S&oIkATds;XK{0q{C%sP6w5p6<|Mp7d=%~6Z(r7T=`iu!74I8bx#`H2@k z#FLL2C>-;e9gzTA8_BOxQ$~;$9?Z*-Zs|veLsy(Ekf{+vA_5vc$V;7$ycI}brIX+@ z!eNr#2DcT>eR=rY?-$_P8<5TADn}6rW$>eqqY zKhmNXjO3o8R*qKQ!f2`m)<1j0e z9L)|%OzNy~v(~do%oc3Q#1131@EdC{V?7&l!*Hw`2w_PwEnZ}8PWS*hn?m`7JAaNq zEoJcE$vLlPDFg70cfl941y9$EMgo7<+nZYcBNx+L!?S``jE4*JoeZ)Cx9Q&}owWkI z+0DXF(8yJ>!X%Z6KF%=n+x*tCP%&jru^fxZ#EB+xGc@*~Uq1fv7c*{K=ijm*Q^ewr z5?^2pfzlOY`)GlJ=S=zSMBW16)MCaR(QT=axRx}0UK>RgA7%|*ev3gxu{2#LT^j1^ z#vJL5`*)kiw_Z7ck3e{}-O`^N)%Io`gUy}X2nX8XFGg0V-twsN^=?nd=54n>fJ?@+ zR}QU}HvNT^DD6r*huZt}Rz|%fclL#AAOYR5&<@>Xh&I7HrdUBk28PI40+{kr6>i{k z&RlSVv;Su4Vw4^g#IsK?wQWBb?;3Kk5TmBqD^$Oh>3s$PZt|n%qQsq#Yh*)i{= z-vxo1f#;(X_f!1h)RGdFQlX23sNE^jy5r^yauhG-ukC1#xlr}OPo+4+Qz_Qdz3XWZ z^J3tscLS!*iP=T?=Q!yxJ!>a6p`x;OPAPUmQvdF-R*wszcXprCtR(j2!Kv4`ip>3< zIPVUc&Kx4P%QGsW8dEfK->5+>`FM|Kee&c799YC~s;!QHgMU?(B9|mxv%R`PeHOM? zxeH84YbL4+Ti5;cgemL8I6nQm{rs=K84a~%OZv5W}zfqv2!~Ju_+%|w=zY-kg_4MGFj)^k>CyL#>@zqBF?Z+;1lvJZO z19v7QpIYtbAsFi0LV~ioLP}XwC$`>_bwffC+dVIUa9sa>COjfl*#!I~N;p$G5hdBd zV$srIw=r^NJaQ&5);nMBIaYaL%x+unnrYZA?65U=0bhIAjW$@zIcc7WXapHOP0c{n!vR)`e2Xh#SAY*sh< zeKggk#Mbyp>)LnA+`2b)pDG7B92o4s@XR9B&n2cE3%Dc$FsiNLFeON99rino(*U0ob89@O#B`45oR4^&Ex9LXE5TL>I1-KU zPFcOWa6!MFXom#B4Bk>k3Sr$Rw>9slbtG#Zab%(1tKe*-UNE^3pJ-g)Gg?|+UHBq; zI`lh@rPH!O-ie=bdR%C^{=nQ$I#^I2)_rW_8-_r>)Nds{orB9p$!(iIR=a5A-fOZg z7^@|OU{Ukhk$djXln|A=lHZEA&+2RD3w(F3VX>ibx_oCXemy`ySa@Xf5>Rzy{y~|@ z>Vail)S_LKZ^QP`wN#&duhgK%NQ)CSb>5$?bv-Sp!9Eu4+sEV)Qkl5X<_9DFKTZLI zkzQqgS|nS0G@YDZ%uqxGm%K^;%kNaf#Ji}am0wc)+k?9G9Rfu*254NYUE3{pJ?=kn zUXAwpA;RjIs(&hG%cn+=gI$$Z!+zPZtHu8<*mj2zbcO#4Fg#8PcC*PGLxya32B9c$ zyr_SDt!c%N!%N5z!J+0u4hySAa}}VeZ)ZIIS$n7r{!80y% zrSY?>kBEKp~Q#wwXduY^d6ls-_2)JkOlRAB3FE*qq(Pu5^!K}%FFj~*z3F{J_(l^;kWak!- z$ch!FLfnnk>7%sbV{Pp|N!U446*A(r^ai+6n7W>*Nnu(}T($-PMVm|CHk+}b%44_e zOM)nUJD8V)fUQEcJ|}0PMZ{FTcaLG|K{GFZLt|XuH#L}=$JyIqoZllVLr#%R6fn|9 zyG+boiw)r1RPJrmi95MO{ta-i*kyl4-Reni5R-p7!0@_<2uKB*O;f|-5>ARJ8N(M>{>-E35<8GE{-R5n_{*xp>Xpt|5Y~myx5ct|%6>>eG$B2f#^(<3RnR ztY{XpzepP%)XRQia(=)##IdJw%2A)Y`U@$ib^H5{@~N{Q+c%Uj%T=3`rS%IL{lcFf zO~e2+^@I|3t!E`(Tw69>DjC6PT*f{3pepS0NBs@=S!R=tPmq?4e=$D|)+&$nHw2kn zp`L-qLC^;N?+6p*-m8j)U8}K{;1~*9QDHnV^Hdb(gK2h zX4uOd-%Y>1?GgmBazOOZqtWC%@ZlKi>V6eT@%OJ3-EyW)O+BtiOCL)p?=Moo$mwd8 z*KW3RLYaM;{_N(w0L_g50YWNQJCG07YNe^SA2%N#JRf|jH+gsV=(l`>FUqFLxli92 zQK3K~GrE7T;XfK$3QKJ_8V&FfDAt>dbLO+#W4nm2wM#?Boy5UnDqC@zp!@VME#Ovm z2M%1HKTTF+wC$NpkkNMFgy+aF#rG+Xf+~TJ&$x@bucg*HfBO?kZSUH{KG0DNIjK+Z zhIx1=6U~q8m-;&f@=i!KD1#;u(YbU}`=6eF!kv*mPinHc`zDtI9wRPB$)7$DOWMcl zOT6n>r9M)RAipF_;cLSH6RNt4C-23a$+}n6!2uMx&!}f%U^Wws)J<*l9{Dmy9Fsjt ztGJRyi7T_9ECv=wRS>N#ymX{avkTA3jDG&MDK4~veFG{bpV_#zxSjnTw)WBX<_rer zsy~>`>e8PDzo0+5i6NM3O5Wz*)mwD)=6sj=a^mft>hSvo!#GXo+65@N6Gnf`if^$Q z2z21KIl(Ss+;~?b4`c$Jq_|F`Xl;(W!(f%9F0H@rqupEGHNh1Vjv& zjlvX(FT4-lfBM%g41t*h6yKRYcI9lp!jmnX^6|wil6NIzAbeRT??3k;DDA1<(VF_# zH>z<=m?!c2q1YmiBnLMUvnux-Xl}bB0}q~%!o%>ZtPC7ndKaei9|+DZ&QNew>Oug0 zu&H+G=sn5nQCfnQ#{UIRuHG1XVIK*47Su~J|K_3ZsIG>!m`of{UH0?8f-~K&mk-9{ vMih$~(OCBDbbYY`r(flMqhO< Date: Tue, 7 Jan 2020 14:22:54 -0500 Subject: [PATCH 308/478] [Basic] Disable body of broken `Located::dump()` method Newer clangs are more reliable about emitting templated methods that are marked "used". --- lib/Basic/Located.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Basic/Located.cpp b/lib/Basic/Located.cpp index 2167d5abde334..0c33f1a14e1e0 100644 --- a/lib/Basic/Located.cpp +++ b/lib/Basic/Located.cpp @@ -20,5 +20,9 @@ void Located::dump() const { template void Located::dump(raw_ostream &os) const { - os << Loc << " " << Item; + // FIXME: The following does not compile on newer clangs because operator<< + // does not exist for SourceLoc. More so, the operator does not exist because + // one needs a SourceManager reference and buffer ID to convert any given + // SourceLoc into line and column information. + //os << Loc << " " << Item; } From be3f949a43670429ef5caae4c4d32b02007070d8 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 7 Jan 2020 12:06:52 -0800 Subject: [PATCH 309/478] [Diagnostics] Add support for new "ternary branch" element to contextual mismatch --- lib/Sema/CSDiag.cpp | 32 -------------------------------- lib/Sema/CSDiagnostics.cpp | 8 ++++++++ 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 760e1405ab17b..3e00e7450b6db 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -248,7 +248,6 @@ class FailureDiagnosis :public ASTVisitor{ bool visitSubscriptExpr(SubscriptExpr *SE); bool visitApplyExpr(ApplyExpr *AE); bool visitCoerceExpr(CoerceExpr *CE); - bool visitIfExpr(IfExpr *IE); bool visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E); }; } // end anonymous namespace @@ -1890,37 +1889,6 @@ bool FailureDiagnosis::visitCoerceExpr(CoerceExpr *CE) { return false; } -bool FailureDiagnosis::visitIfExpr(IfExpr *IE) { - auto typeCheckClauseExpr = [&](Expr *clause, Type contextType = Type(), - ContextualTypePurpose convertPurpose = - CTP_Unused) -> Expr * { - // Provide proper contextual type when type conversion is specified. - return typeCheckChildIndependently(clause, contextType, convertPurpose, - TCCOptions(), nullptr, false); - }; - // Check all of the subexpressions independently. - auto condExpr = typeCheckClauseExpr(IE->getCondExpr()); - if (!condExpr) return true; - auto trueExpr = typeCheckClauseExpr(IE->getThenExpr(), CS.getContextualType(), - CS.getContextualTypePurpose()); - if (!trueExpr) return true; - auto falseExpr = typeCheckClauseExpr( - IE->getElseExpr(), CS.getContextualType(), CS.getContextualTypePurpose()); - if (!falseExpr) return true; - - // If the true/false values already match, it must be a contextual problem. - if (CS.getType(trueExpr)->isEqual(CS.getType(falseExpr))) - return false; - - // Otherwise, the true/false result types must not be matching. - diagnose(IE->getColonLoc(), diag::if_expr_cases_mismatch, - CS.getType(trueExpr), CS.getType(falseExpr)) - .highlight(trueExpr->getSourceRange()) - .highlight(falseExpr->getSourceRange()); - return true; -} - - bool FailureDiagnosis:: visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E) { // Don't walk the children for this node, it leads to multiple diagnostics diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 54b6dad9064ce..e388c9bca280f 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1906,6 +1906,14 @@ bool ContextualFailure::diagnoseAsError() { break; } + case ConstraintLocator::TernaryBranch: { + auto *ifExpr = cast(getRawAnchor()); + fromType = getType(ifExpr->getThenExpr()); + toType = getType(ifExpr->getElseExpr()); + diagnostic = diag::if_expr_cases_mismatch; + break; + } + case ConstraintLocator::ContextualType: { if (diagnoseConversionToBool()) return true; From 3d1739fa8670cc467baa690e46538881e8fae85b Mon Sep 17 00:00:00 2001 From: David Zarzycki Date: Sun, 5 Jan 2020 13:14:03 -0500 Subject: [PATCH 310/478] NFC: Fix -Wdeprecated-copy warnings --- include/swift/Reflection/Records.h | 2 ++ lib/IDE/Formatting.cpp | 4 ++++ lib/SILOptimizer/IPO/CapturePromotion.cpp | 2 ++ lib/Sema/InstrumenterSupport.h | 3 ++- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/swift/Reflection/Records.h b/include/swift/Reflection/Records.h index 431877b309420..c43ade467c6b0 100644 --- a/include/swift/Reflection/Records.h +++ b/include/swift/Reflection/Records.h @@ -266,6 +266,8 @@ struct AssociatedTypeRecordIterator { return *this; } + AssociatedTypeRecordIterator(const AssociatedTypeRecordIterator &Other) + : Cur(Other.Cur), End(Other.End) {} AssociatedTypeRecordIterator operator=(const AssociatedTypeRecordIterator &Other) { return { Other.Cur, Other.End }; diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index 89a27d52979ec..714116fce3ee1 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -564,6 +564,10 @@ class FormatWalker : public SourceEntityWalker { public: SourceLocIterator(TokenIt It) :It(It) {} SourceLocIterator(const SourceLocIterator& mit) : It(mit.It) {} + const SourceLocIterator &operator=(const SourceLocIterator &mit) { + It = mit.It; + return *this; + } SourceLocIterator& operator++() {++It; return *this;} SourceLocIterator operator++(int) { SourceLocIterator tmp(*this); diff --git a/lib/SILOptimizer/IPO/CapturePromotion.cpp b/lib/SILOptimizer/IPO/CapturePromotion.cpp index 8290ac6817cd5..2bb212748025c 100644 --- a/lib/SILOptimizer/IPO/CapturePromotion.cpp +++ b/lib/SILOptimizer/IPO/CapturePromotion.cpp @@ -160,6 +160,8 @@ class ReachingBlockSet { return !(*this == RHS); } + ReachingBlockSet(const ReachingBlockSet &RHS) + : Bits(RHS.Bits), NumBitWords(RHS.NumBitWords) {} const ReachingBlockSet &operator=(const ReachingBlockSet &RHS) { assert(NumBitWords == RHS.NumBitWords && "mismatched sets"); for (size_t i = 0, e = NumBitWords; i != e; ++i) diff --git a/lib/Sema/InstrumenterSupport.h b/lib/Sema/InstrumenterSupport.h index 28c4645862f19..1197a950eefc5 100644 --- a/lib/Sema/InstrumenterSupport.h +++ b/lib/Sema/InstrumenterSupport.h @@ -28,7 +28,8 @@ template class Added { public: Added() {} - Added(E NewContents) { Contents = NewContents; } + Added(E NewContents) : Contents(NewContents) {} + Added(const Added &rhs) : Contents(rhs.Contents) {} const Added &operator=(const Added &rhs) { Contents = rhs.Contents; return *this; From 9a62701e95a6e056dd2c5e5430a0cfd6199f8515 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 7 Jan 2020 13:50:09 -0800 Subject: [PATCH 311/478] [ConstraintSystem] Use new branch element and fix mismatch between ternary branches --- lib/Sema/CSGen.cpp | 18 ++++---- lib/Sema/CSSimplify.cpp | 53 +++++++++++++++++++++- test/Constraints/diagnostics.swift | 3 +- test/Constraints/one_way_constraints.swift | 8 ++-- 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index f51ec576cc4f0..a20818d3a68a7 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2616,18 +2616,18 @@ namespace { CS.addConstraint( ConstraintKind::Conversion, CS.getType(expr->getCondExpr()), boolDecl->getDeclaredType(), - CS.getConstraintLocator(expr, LocatorPathElt::Condition())); + CS.getConstraintLocator(expr, ConstraintLocator::Condition)); // The branches must be convertible to a common type. - return CS.addJoinConstraint(CS.getConstraintLocator(expr), - { - { CS.getType(expr->getThenExpr()), - CS.getConstraintLocator(expr->getThenExpr()) }, - { CS.getType(expr->getElseExpr()), - CS.getConstraintLocator(expr->getElseExpr()) } - }); + return CS.addJoinConstraint( + CS.getConstraintLocator(expr), + {{CS.getType(expr->getThenExpr()), + CS.getConstraintLocator(expr, LocatorPathElt::TernaryBranch(true))}, + {CS.getType(expr->getElseExpr()), + CS.getConstraintLocator(expr, + LocatorPathElt::TernaryBranch(false))}}); } - + virtual Type visitImplicitConversionExpr(ImplicitConversionExpr *expr) { llvm_unreachable("Already type-checked"); } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index ec545ffd027c1..5d36a4f8b4ed6 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2804,6 +2804,13 @@ bool ConstraintSystem::repairFailures( }); }; + auto markAnyTypeVarsAsPotentialHoles = [&](Type type) { + type.visit([&](Type subType) { + if (auto *typeVar = subType->getAs()) + recordPotentialHole(typeVar); + }); + }; + if (path.empty()) { if (!anchor) return false; @@ -3663,6 +3670,33 @@ bool ConstraintSystem::repairFailures( break; } + case ConstraintLocator::TernaryBranch: { + markAnyTypeVarsAsPotentialHoles(lhs); + markAnyTypeVarsAsPotentialHoles(rhs); + + // If `if` expression has a contextual type, let's consider it a source of + // truth and produce a contextual mismatch instead of per-branch failure, + // because it's a better pointer than potential then-to-else type mismatch. + if (auto contextualType = getContextualType(anchor)) { + if (contextualType->isEqual(rhs)) { + auto *loc = + getConstraintLocator(anchor, LocatorPathElt::ContextualType()); + if (hasFixFor(loc, FixKind::ContextualMismatch)) + return true; + + conversionsOrFixes.push_back( + ContextualMismatch::create(*this, lhs, rhs, loc)); + break; + } + } + + // If there is no contextual type, this is most likely a contextual type + // mismatch between then/else branches of ternary operator. + conversionsOrFixes.push_back(ContextualMismatch::create( + *this, lhs, rhs, getConstraintLocator(locator))); + break; + } + default: break; } @@ -8626,7 +8660,24 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( } case FixKind::ContextualMismatch: { - if (recordFix(fix)) + auto impact = 1; + + auto locator = fix->getLocator(); + if (auto branchElt = + locator->getLastElementAs()) { + // If this is `else` branch of a ternary operator, let's + // increase its impact to eliminate the chance of ambiguity. + // + // Branches are connected through two `subtype` constraints + // to a common type variable with represents their join, which + // means that result would attempt a type from each side if + // one is available and that would result in two fixes - one for + // each mismatched branch. + if (branchElt->forElse()) + impact = 10; + } + + if (recordFix(fix, impact)) return SolutionKind::Error; if (auto *fnType1 = type1->getAs()) { diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 6b5af5f049e4c..00e86417b19ef 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -289,7 +289,8 @@ func r18800223(_ i : Int) { var buttonTextColor: String? - _ = (buttonTextColor != nil) ? 42 : {$0}; // expected-error {{unable to infer closure type in the current context}} + _ = (buttonTextColor != nil) ? 42 : {$0}; // expected-error {{result values in '? :' expression have mismatching types 'Int' and '(_) -> _'}} + // expected-error@-1 {{unable to infer closure return type; add explicit type to disambiguate}} } // Bogus "'_' can only appear in a pattern or on the left side of an assignment" is back diff --git a/test/Constraints/one_way_constraints.swift b/test/Constraints/one_way_constraints.swift index a56c143246f40..d830367d35d78 100644 --- a/test/Constraints/one_way_constraints.swift +++ b/test/Constraints/one_way_constraints.swift @@ -10,11 +10,11 @@ func testTernaryOneWay(b: Bool) { let _: Float = b ? 3.14159 : 17 // Errors due to one-way inference. - let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{cannot convert value of type 'Double' to specified type 'Float'}} + let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{result values in '? :' expression have mismatching types 'Double' and 'Int'}} : 17 - let _: Float = b ? 3.14159 - : Builtin.one_way(17) // expected-error{{cannot convert value of type 'Int' to specified type 'Float'}} - let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{cannot convert value of type 'Double' to specified type 'Float'}} + let _: Float = b ? 3.14159 // expected-error {{cannot convert value of type 'Int' to specified type 'Float'}} + : Builtin.one_way(17) + let _: Float = b ? Builtin.one_way(3.14159) // expected-error {{cannot convert value of type 'Int' to specified type 'Float'}} : Builtin.one_way(17) // Okay: default still works. From f85dfbb95af4839c645f9e38bb5ccc4307499754 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 7 Jan 2020 13:50:53 -0800 Subject: [PATCH 312/478] [TypeChecker.rst] NFC: Mark that if/ternary diagnostics have been ported --- docs/TypeChecker.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/TypeChecker.rst b/docs/TypeChecker.rst index a8498a9007ef4..ac856e2481354 100644 --- a/docs/TypeChecker.rst +++ b/docs/TypeChecker.rst @@ -985,9 +985,6 @@ The things in the queue yet to be ported are: Most of the associated diagnostics have been ported and fixes are located in ``ConstraintSystem::simplifyMemberConstraint``. -- Diagnostics related to ``if`` statement - "conditional" type mismatch - and, in case of ternary operator, type mismatches between branches. - - Problems related to calls and operator applications e.g. - Missing explicit ``Self.`` and ``self.`` From 43712bb8601845a8a5ed1718939acb937b0cb106 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 7 Jan 2020 10:56:36 -0800 Subject: [PATCH 313/478] [Diagnostics] Diagnose general ambiguity failures in diagnoseAmbiguityWithFixes. --- lib/Sema/ConstraintSystem.cpp | 25 ++++++++++++++++++++++--- test/Constraints/members.swift | 7 +++++-- test/Constraints/operator.swift | 1 - test/Constraints/overload.swift | 7 +++---- test/Parse/super.swift | 7 ++++--- test/Parse/type_expr.swift | 2 +- test/decl/subscript/subscripting.swift | 12 ++++++------ 7 files changed, 41 insertions(+), 20 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 2977ffa3737e6..8e3133bc333df 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2872,9 +2872,28 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( return true; } - case AmbiguityKind::General: - // TODO: Handle general ambiguity here. - return false; + case AmbiguityKind::General: { + emitGeneralAmbiguityFailure(); + + // Notes for operators are diagnosed through emitGeneralAmbiguityFailure + if (name.isOperator()) + return true; + + llvm::SmallSet candidateTypes; + for (const auto &viable: viableSolutions) { + auto overload = viable->getOverloadChoice(commonCalleeLocator); + auto *decl = overload.choice.getDecl(); + auto type = viable->simplifyType(overload.openedType); + if (decl->getLoc().isInvalid()) { + if (candidateTypes.insert(type->getCanonicalType()).second) + DE.diagnose(commonAnchor->getLoc(), diag::found_candidate_type, type); + } else { + DE.diagnose(decl->getLoc(), diag::found_candidate); + } + } + + return true; + } } auto *fix = viableSolutions.front()->Fixes.front(); diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index 479179e50591d..7a39dd010c393 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -616,8 +616,11 @@ func rdar50679161() { func rdar_50467583_and_50909555() { // rdar://problem/50467583 - let _: Set = [Int][] - // expected-error@-1 {{instance member 'subscript' cannot be used on type '[Int]'}} + let _: Set = [Int][] // expected-error {{no exact matches in call to subscript}} + // expected-note@-1 {{found candidate with type '(Int) -> Int'}} + // expected-note@-2 {{found candidate with type '(Range) -> ArraySlice'}} + // expected-note@-3 {{found candidate with type '((UnboundedRange_) -> ()) -> ArraySlice'}} + // expected-note@-4 {{found candidate with type '(Range.Index>) -> Slice<[Int]>' (aka '(Range) -> Slice>')}} // rdar://problem/50909555 struct S { diff --git a/test/Constraints/operator.swift b/test/Constraints/operator.swift index 14cdeb624d2e2..3b85bda0b8792 100644 --- a/test/Constraints/operator.swift +++ b/test/Constraints/operator.swift @@ -218,7 +218,6 @@ func rdar46459603() { _ = arr.values == [e] // expected-error@-1 {{binary operator '==' cannot be applied to operands of type 'Dictionary.Values' and '[E]'}} - // expected-note@-2 {{expected an argument list of type '(Self, Self)'}} _ = [arr.values] == [[e]] // expected-error@-1 {{value of protocol type 'Any' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols}} // expected-note@-2 {{requirement from conditional conformance of '[Any]' to 'Equatable'}} diff --git a/test/Constraints/overload.swift b/test/Constraints/overload.swift index da7dae970f8f5..5838918cba3be 100644 --- a/test/Constraints/overload.swift +++ b/test/Constraints/overload.swift @@ -127,12 +127,11 @@ func test20886179(_ handlers: [(Int) -> Void], buttonIndex: Int) { // The problem here is that the call has a contextual result type incompatible // with *all* overload set candidates. This is not an ambiguity. -func overloaded_identity(_ a : Int) -> Int {} -func overloaded_identity(_ b : Float) -> Float {} +func overloaded_identity(_ a : Int) -> Int {} // expected-note {{found this candidate}} +func overloaded_identity(_ b : Float) -> Float {} // expected-note {{found this candidate}} func test_contextual_result_1() { - return overloaded_identity() // expected-error {{cannot invoke 'overloaded_identity' with no arguments}} - // expected-note @-1 {{overloads for 'overloaded_identity' exist with these partially matching parameter lists: (Float), (Int)}} + return overloaded_identity() // expected-error {{no exact matches in call to global function 'overloaded_identity'}} } func test_contextual_result_2() { diff --git a/test/Parse/super.swift b/test/Parse/super.swift index 52ffa73cf7f52..c4317a4bf18b8 100644 --- a/test/Parse/super.swift +++ b/test/Parse/super.swift @@ -4,8 +4,8 @@ class B { var foo: Int func bar() {} - init() {} - init(x: Int) {} + init() {} // expected-note {{found this candidate}} + init(x: Int) {} // expected-note {{found this candidate}} subscript(x: Int) -> Int { get {} @@ -38,7 +38,8 @@ class D : B { super.foo.bar // expected-error {{value of type 'Int' has no member 'bar'}} super.bar // expected-error {{expression resolves to an unused function}} super.bar() - super.init // expected-error{{'super.init' cannot be called outside of an initializer}} + // FIXME: should also say "'super.init' cannot be referenced outside of an initializer" + super.init // expected-error{{no exact matches in call to initializer}} super.init() // expected-error{{'super.init' cannot be called outside of an initializer}} super.init(0) // expected-error{{'super.init' cannot be called outside of an initializer}} // expected-error {{missing argument label 'x:' in call}} super[0] // expected-error {{expression resolves to an unused subscript}} diff --git a/test/Parse/type_expr.swift b/test/Parse/type_expr.swift index 87c9cd955023a..4384e823852fb 100644 --- a/test/Parse/type_expr.swift +++ b/test/Parse/type_expr.swift @@ -265,7 +265,7 @@ protocol P2 {} protocol P3 {} func compositionType() { _ = P1 & P2 // expected-error {{expected member name or constructor call after type name}} expected-note{{use '.self'}} {{7-7=(}} {{14-14=).self}} - _ = P1 & P2.self // expected-error {{binary operator '&' cannot be applied to operands of type 'P1.Protocol' and 'P2.Protocol'}} expected-note {{overloads}} + _ = P1 & P2.self // expected-error {{binary operator '&' cannot be applied to operands of type 'P1.Protocol' and 'P2.Protocol'}} _ = (P1 & P2).self // Ok. _ = (P1 & (P2)).self // FIXME: OK? while `typealias P = P1 & (P2)` is rejected. _ = (P1 & (P2, P3)).self // expected-error {{non-protocol, non-class type '(P2, P3)' cannot be used within a protocol-constrained type}} diff --git a/test/decl/subscript/subscripting.swift b/test/decl/subscript/subscripting.swift index 8c236d323ba69..4061785c917ad 100644 --- a/test/decl/subscript/subscripting.swift +++ b/test/decl/subscript/subscripting.swift @@ -349,12 +349,14 @@ func testUnresolvedMemberSubscriptFixit(_ s0: GenSubscriptFixitTest) { struct SubscriptTest1 { subscript(keyword:String) -> Bool { return true } - // expected-note@-1 3 {{found this candidate}} expected-note@-1 {{found candidate with type 'Bool'}} + // expected-note@-1 5 {{found this candidate}} expected-note@-1 {{found candidate with type 'Bool'}} subscript(keyword:String) -> String? {return nil } - // expected-note@-1 3 {{found this candidate}} expected-note@-1 {{found candidate with type 'String?'}} + // expected-note@-1 5 {{found this candidate}} expected-note@-1 {{found candidate with type 'String?'}} subscript(arg: SubClass) -> Bool { return true } // expected-note {{declared here}} + // expected-note@-1 2 {{found this candidate}} subscript(arg: Protocol) -> Bool { return true } // expected-note 2 {{declared here}} + // expected-note@-1 2 {{found this candidate}} subscript(arg: (foo: Bool, bar: (Int, baz: SubClass)), arg2: String) -> Bool { return true } // expected-note@-1 3 {{declared here}} @@ -379,11 +381,9 @@ func testSubscript1(_ s1 : SubscriptTest1) { _ = s1.subscript(ClassConformingToRefinedProtocol()) // expected-error@-1 {{value of type 'SubscriptTest1' has no property or method named 'subscript'; did you mean to use the subscript operator?}} {{9-10=}} {{10-19=}} {{19-20=[}} {{54-55=]}} _ = s1.subscript(true) - // expected-error@-1 {{cannot invoke 'subscript' with an argument list of type '(Bool)'}} - // expected-note@-2 {{overloads for 'subscript' exist with these partially matching parameter lists: (Protocol), (String), (SubClass)}} + // expected-error@-1 {{no exact matches in call to subscript}} _ = s1.subscript(SuperClass()) - // expected-error@-1 {{cannot invoke 'subscript' with an argument list of type '(SuperClass)'}} - // expected-note@-2 {{overloads for 'subscript' exist with these partially matching parameter lists: (Protocol), (String), (SubClass)}} + // expected-error@-1 {{no exact matches in call to subscript}} _ = s1.subscript("hello") // expected-error@-1 {{value of type 'SubscriptTest1' has no property or method named 'subscript'; did you mean to use the subscript operator?}} _ = s1.subscript("hello" From afc6ccdeb5a5eb5ef681e1cb89cd85a09f2d8bf8 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 7 Jan 2020 12:04:44 -0800 Subject: [PATCH 314/478] Re-land parsing and printing for Clang function types. This reverts commit e805fe486e0eb13fac64c2825abf00300a1b59b1, which reverted the change earlier. The problem was caused due to a simultaneous change to some code by the PR with parsing and printing for Clang function types (#28737) and the PR which introduced Located (#28643). This commit also includes a small change to make sure the intersecting region is fixed: the change is limited to using the fields of Located in the `tryParseClangType` lambda. --- include/swift/AST/ASTPrinter.h | 3 + include/swift/AST/ClangModuleLoader.h | 11 +++ include/swift/AST/DiagnosticsSema.def | 7 ++ include/swift/AST/PrintOptions.h | 21 ++++- include/swift/AST/Types.h | 16 ++++ include/swift/ClangImporter/ClangImporter.h | 6 ++ .../swift/Frontend/ModuleInterfaceSupport.h | 4 + include/swift/Option/FrontendOptions.td | 5 ++ lib/AST/ASTContext.cpp | 5 ++ lib/AST/ASTPrinter.cpp | 76 ++++++++++++++++--- lib/AST/Type.cpp | 6 ++ lib/ClangImporter/ClangImporter.cpp | 23 ++++++ lib/Frontend/CompilerInvocation.cpp | 2 + lib/Frontend/ModuleInterfaceSupport.cpp | 2 +- lib/IDE/CodeCompletionResultBuilder.h | 3 +- lib/IDE/IDETypeChecking.cpp | 3 +- lib/Sema/TypeCheckType.cpp | 41 +++++++++- test/ClangImporter/clang-function-types.swift | 12 +++ .../clang-importer-sdk/usr/include/ctypes.h | 10 +++ test/ModuleInterface/full-convention.swift | 32 ++++++++ test/Parse/c_function_pointers.swift | 4 +- test/attr/attr_convention.swift | 4 + 22 files changed, 274 insertions(+), 22 deletions(-) create mode 100644 test/ClangImporter/clang-function-types.swift create mode 100644 test/ModuleInterface/full-convention.swift diff --git a/include/swift/AST/ASTPrinter.h b/include/swift/AST/ASTPrinter.h index a9e9b89a99005..98d665b1fd728 100644 --- a/include/swift/AST/ASTPrinter.h +++ b/include/swift/AST/ASTPrinter.h @@ -14,6 +14,7 @@ #define SWIFT_AST_ASTPRINTER_H #include "swift/Basic/LLVM.h" +#include "swift/Basic/QuotedString.h" #include "swift/Basic/UUID.h" #include "swift/AST/Identifier.h" #include "llvm/ADT/StringRef.h" @@ -185,6 +186,8 @@ class ASTPrinter { return *this; } + ASTPrinter &operator<<(QuotedString s); + ASTPrinter &operator<<(unsigned long long N); ASTPrinter &operator<<(UUID UU); diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index a3d53be0584a7..0e3d109226fd0 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -21,6 +21,7 @@ class CompilerInstance; class Preprocessor; class Sema; class TargetInfo; +class Type; } // namespace clang namespace swift { @@ -100,6 +101,16 @@ class ClangModuleLoader : public ModuleLoader { lookupRelatedEntity(StringRef clangName, ClangTypeKind kind, StringRef relatedEntityKind, llvm::function_ref receiver) = 0; + + /// Try to parse the string as a Clang function type. + /// + /// Returns null if there was a parsing failure. + virtual const clang::Type *parseClangFunctionType(StringRef type, + SourceLoc loc) const = 0; + + /// Print the Clang type. + virtual void printClangType(const clang::Type *type, + llvm::raw_ostream &os) const = 0; }; } // namespace swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 990e072795f72..af4de2c836114 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3924,6 +3924,13 @@ ERROR(unsupported_convention,none, "convention '%0' not supported", (StringRef)) ERROR(unreferenced_generic_parameter,none, "generic parameter '%0' is not used in function signature", (StringRef)) +ERROR(unexpected_ctype_for_non_c_convention,none, + "convention '%0' does not support the 'cType' argument label, did you " + "mean @convention(c, cType: \"%1\") or @convention(block, cType: \"%1\") " + "instead?", (StringRef, StringRef)) +ERROR(unable_to_parse_c_function_type,none, + "unable to parse '%0'; it should be a C function pointer type or a " + "block pointer type", (StringRef)) // Opaque types ERROR(unsupported_opaque_type,none, diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 329136a29b222..6a142e02da510 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -306,8 +306,21 @@ struct PrintOptions { /// List of decls that should be printed even if they are implicit and \c SkipImplicit is set to true. std::vector TreatAsExplicitDeclList; + enum class FunctionRepresentationMode : uint8_t { + /// Print the entire convention, including an arguments. + /// For example, this will print a cType argument label if applicable. + Full, + /// Print only the name of the convention, skipping extra argument labels. + NameOnly, + /// Skip printing the @convention(..) altogether. + None + }; + /// Whether to print function @convention attribute on function types. - bool PrintFunctionRepresentationAttrs = true; + // FIXME: [clang-function-type-serialization] Once we start serializing Clang + // types, we should also start printing the full type in the swiftinterface. + FunctionRepresentationMode PrintFunctionRepresentationAttrs = + FunctionRepresentationMode::NameOnly; /// Whether to print storage representation attributes on types, e.g. /// '@sil_weak', '@sil_unmanaged'. @@ -502,7 +515,8 @@ struct PrintOptions { /// consistent and well-formed. /// /// \see swift::emitSwiftInterface - static PrintOptions printSwiftInterfaceFile(bool preferTypeRepr); + static PrintOptions printSwiftInterfaceFile(bool preferTypeRepr, + bool printFullConvention); /// Retrieve the set of options suitable for "Generated Interfaces", which /// are a prettified representation of the public API of a module, to be @@ -585,7 +599,8 @@ struct PrintOptions { PO.SkipUnderscoredKeywords = true; PO.EnumRawValues = EnumRawValueMode::Print; PO.PrintImplicitAttrs = false; - PO.PrintFunctionRepresentationAttrs = false; + PO.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; PO.PrintDocumentationComments = false; PO.ExcludeAttrList.push_back(DAK_Available); PO.SkipPrivateStdlibDecls = true; diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index c03a523d0ae40..bf066910de272 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -59,6 +59,7 @@ class AssociatedTypeDecl; class ASTContext; enum BufferPointerTypeKind : unsigned; class ClassDecl; +class ClangModuleLoader; class DependentMemberType; class GenericTypeParamDecl; class GenericTypeParamType; @@ -2950,6 +2951,11 @@ class AnyFunctionType : public TypeBase { bool empty() const { return !ClangFunctionType; } Uncommon(const clang::Type *type) : ClangFunctionType(type) {} + + public: + /// Use the ClangModuleLoader to print the Clang type as a string. + void printClangFunctionType(ClangModuleLoader *cml, + llvm::raw_ostream &os); }; private: @@ -3894,6 +3900,11 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, bool empty() const { return !ClangFunctionType; } Uncommon(const clang::FunctionType *type) : ClangFunctionType(type) {} + + public: + /// Analog of AnyFunctionType::ExtInfo::Uncommon::printClangFunctionType. + void printClangFunctionType(ClangModuleLoader *cml, + llvm::raw_ostream &os) const; }; Uncommon Other; @@ -3947,6 +3958,11 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, return getSILFunctionLanguage(getRepresentation()); } + /// Return the underlying Uncommon value if it is not the default value. + Optional getUncommonInfo() const { + return Other.empty() ? Optional() : Other; + } + bool hasSelfParam() const { switch (getRepresentation()) { case Representation::Thick: diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 9cace98692d33..9ac0d73ef7b9b 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -40,6 +40,7 @@ namespace clang { class NamedDecl; class Sema; class TargetInfo; + class Type; class VisibleDeclConsumer; class DeclarationName; } @@ -416,6 +417,11 @@ class ClangImporter final : public ClangModuleLoader { /// with -import-objc-header option. getPCHFilename(const ClangImporterOptions &ImporterOptions, StringRef SwiftPCHHash, bool &isExplicit); + + const clang::Type *parseClangFunctionType(StringRef type, + SourceLoc loc) const override; + void printClangType(const clang::Type *type, + llvm::raw_ostream &os) const override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/include/swift/Frontend/ModuleInterfaceSupport.h b/include/swift/Frontend/ModuleInterfaceSupport.h index 7a3e54ee8bc64..4289150c14554 100644 --- a/include/swift/Frontend/ModuleInterfaceSupport.h +++ b/include/swift/Frontend/ModuleInterfaceSupport.h @@ -31,6 +31,10 @@ struct ModuleInterfaceOptions { /// interface, or should we fully-qualify them? bool PreserveTypesAsWritten = false; + /// Should we emit the cType when printing @convention(c) or no? + /// FIXME: [clang-function-type-serialization] This check should go away. + bool PrintFullConvention = false; + /// Copy of all the command-line flags passed at .swiftinterface /// generation time, re-applied to CompilerInvocation when reading /// back .swiftinterface and reconstructing .swiftmodule. diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 711dbd7dcf0a4..a19bd5f383b96 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -602,6 +602,11 @@ def module_interface_preserve_types_as_written : HelpText<"When emitting a module interface, preserve types as they were " "written in the source">; +def experimental_print_full_convention : + Flag<["-"], "experimental-print-full-convention">, + HelpText<"When emitting a module interface, emit additional @convention " + "arguments, regardless of whether they were written in the source">; + def prebuilt_module_cache_path : Separate<["-"], "prebuilt-module-cache-path">, HelpText<"Directory of prebuilt modules for loading module interfaces">; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index a9bda93b6538f..060f2e60a2cab 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3156,6 +3156,11 @@ ArrayRef GenericFunctionType::getRequirements() const { return Signature->getRequirements(); } +void SILFunctionType::ExtInfo::Uncommon::printClangFunctionType( + ClangModuleLoader *cml, llvm::raw_ostream &os) const { + cml->printClangType(ClangFunctionType, os); +} + void SILFunctionType::Profile( llvm::FoldingSetNodeID &id, GenericSignature genericParams, diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 94461dc2473db..99a0ca8c982a8 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTMangler.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/Attr.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/Comment.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" @@ -98,7 +99,8 @@ static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) { return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic(); } -PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr) { +PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr, + bool printFullConvention) { PrintOptions result; result.PrintLongAttrsOnSeparateLines = true; result.TypeDefinitions = true; @@ -115,6 +117,9 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr) { result.OpaqueReturnTypePrinting = OpaqueReturnTypePrintingMode::StableReference; result.PreferTypeRepr = preferTypeRepr; + if (printFullConvention) + result.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::Full; // We should print __consuming, __owned, etc for the module interface file. result.SkipUnderscoredKeywords = false; @@ -321,6 +326,14 @@ void ASTPrinter::callPrintDeclPre(const Decl *D, printDeclPre(D, Bracket); } +ASTPrinter &ASTPrinter::operator<<(QuotedString s) { + llvm::SmallString<32> Str; + llvm::raw_svector_ostream OS(Str); + OS << s; + printTextImpl(OS.str()); + return *this; +} + ASTPrinter &ASTPrinter::operator<<(unsigned long long N) { llvm::SmallString<32> Str; llvm::raw_svector_ostream OS(Str); @@ -3478,6 +3491,15 @@ void Pattern::print(llvm::raw_ostream &OS, const PrintOptions &Options) const { // Type Printing //===----------------------------------------------------------------------===// +template +void printCType(ASTContext &Ctx, ASTPrinter &Printer, ExtInfo &info) { + auto *cml = Ctx.getClangModuleLoader(); + SmallString<64> buf; + llvm::raw_svector_ostream os(buf); + info.getUncommonInfo().getValue().printClangFunctionType(cml, os); + Printer << ", cType: " << QuotedString(os.str()); +} + namespace { class TypePrinter : public TypeVisitor { using super = TypeVisitor; @@ -3840,7 +3862,7 @@ class TypePrinter : public TypeVisitor { visit(staticSelfT); } - void printFunctionExtInfo(AnyFunctionType::ExtInfo info) { + void printFunctionExtInfo(ASTContext &Ctx, AnyFunctionType::ExtInfo info) { if (Options.SkipAttributes) return; @@ -3853,9 +3875,18 @@ class TypePrinter : public TypeVisitor { } } - if (Options.PrintFunctionRepresentationAttrs && - !Options.excludeAttrKind(TAK_convention) && - info.getSILRepresentation() != SILFunctionType::Representation::Thick) { + SmallString<64> buf; + switch (Options.PrintFunctionRepresentationAttrs) { + case PrintOptions::FunctionRepresentationMode::None: + return; + case PrintOptions::FunctionRepresentationMode::Full: + case PrintOptions::FunctionRepresentationMode::NameOnly: + if (Options.excludeAttrKind(TAK_convention) || + info.getSILRepresentation() == SILFunctionType::Representation::Thick) + return; + + bool printNameOnly = Options.PrintFunctionRepresentationAttrs == + PrintOptions::FunctionRepresentationMode::NameOnly; Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); Printer << "("; @@ -3871,6 +3902,11 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; + // FIXME: [clang-function-type-serialization] Once we start serializing + // Clang function types, we should be able to remove the second check. + if (printNameOnly || !info.getUncommonInfo().hasValue()) + break; + printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::Method: Printer << "method"; @@ -3891,7 +3927,8 @@ class TypePrinter : public TypeVisitor { } } - void printFunctionExtInfo(SILFunctionType::ExtInfo info, + void printFunctionExtInfo(ASTContext &Ctx, + SILFunctionType::ExtInfo info, ProtocolConformanceRef witnessMethodConformance) { if (Options.SkipAttributes) return; @@ -3905,9 +3942,19 @@ class TypePrinter : public TypeVisitor { } } - if (Options.PrintFunctionRepresentationAttrs && - !Options.excludeAttrKind(TAK_convention) && - info.getRepresentation() != SILFunctionType::Representation::Thick) { + + SmallString<64> buf; + switch (Options.PrintFunctionRepresentationAttrs) { + case PrintOptions::FunctionRepresentationMode::None: + break; + case PrintOptions::FunctionRepresentationMode::NameOnly: + case PrintOptions::FunctionRepresentationMode::Full: + if (Options.excludeAttrKind(TAK_convention) || + info.getRepresentation() == SILFunctionType::Representation::Thick) + break; + + bool printNameOnly = Options.PrintFunctionRepresentationAttrs == + PrintOptions::FunctionRepresentationMode::NameOnly; Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); Printer << "("; @@ -3922,6 +3969,11 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; + // FIXME: [clang-function-type-serialization] Once we start serializing + // Clang function types, we should be able to remove the second check. + if (printNameOnly || !info.getUncommonInfo().hasValue()) + break; + printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::Method: Printer << "method"; @@ -3991,7 +4043,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getExtInfo()); + printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); // If we're stripping argument labels from types, do it when printing. visitAnyFunctionTypeParams(T->getParams(), /*printLabels*/false); @@ -4028,7 +4080,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getExtInfo()); + printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); printGenericSignature(T->getGenericSignature(), PrintAST::PrintParams | PrintAST::PrintRequirements); @@ -4081,7 +4133,7 @@ class TypePrinter : public TypeVisitor { void visitSILFunctionType(SILFunctionType *T) { printSILCoroutineKind(T->getCoroutineKind()); - printFunctionExtInfo(T->getExtInfo(), + printFunctionExtInfo(T->getASTContext(), T->getExtInfo(), T->getWitnessMethodConformanceOrInvalid()); printCalleeConvention(T->getCalleeConvention()); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index d63b809756ac4..31f898b14addc 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -17,6 +17,7 @@ #include "swift/AST/Types.h" #include "ForeignRepresentationInfo.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/ReferenceCounting.h" #include "swift/AST/TypeCheckRequests.h" @@ -3239,6 +3240,11 @@ Type ProtocolCompositionType::get(const ASTContext &C, return build(C, CanTypes, HasExplicitAnyObject); } +void AnyFunctionType::ExtInfo::Uncommon::printClangFunctionType( + ClangModuleLoader *cml, llvm::raw_ostream &os) { + cml->printClangType(ClangFunctionType, os); +} + void AnyFunctionType::ExtInfo::assertIsFunctionType(const clang::Type *type) { #ifndef NDEBUG diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 8a8b5d1781619..5363d6c94ac2a 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3275,6 +3275,29 @@ void ClangImporter::verifyAllModules() { #endif } +const clang::Type * +ClangImporter::parseClangFunctionType(StringRef typeStr, + SourceLoc loc) const { + auto &sema = Impl.getClangSema(); + StringRef filename = Impl.SwiftContext.SourceMgr.getDisplayNameForLoc(loc); + // TODO: Obtain a clang::SourceLocation from the swift::SourceLoc we have + auto parsedType = sema.ParseTypeFromStringCallback(typeStr, filename, {}); + if (!parsedType.isUsable()) + return nullptr; + clang::QualType resultType = clang::Sema::GetTypeFromParser(parsedType.get()); + auto *typePtr = resultType.getTypePtrOrNull(); + if (typePtr && (typePtr->isFunctionPointerType() + || typePtr->isBlockPointerType())) + return typePtr; + return nullptr; +} + +void ClangImporter::printClangType(const clang::Type *type, + llvm::raw_ostream &os) const { + auto policy = clang::PrintingPolicy(getClangASTContext().getLangOpts()); + clang::QualType(type, 0).print(os, policy); +} + //===----------------------------------------------------------------------===// // ClangModule Implementation //===----------------------------------------------------------------------===// diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 355aeff500586..2eeee7f96f54d 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -227,6 +227,8 @@ static void ParseModuleInterfaceArgs(ModuleInterfaceOptions &Opts, Opts.PreserveTypesAsWritten |= Args.hasArg(OPT_module_interface_preserve_types_as_written); + Opts.PrintFullConvention |= + Args.hasArg(OPT_experimental_print_full_convention); } /// Save a copy of any flags marked as ModuleInterfaceOption, if running diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index b1b6b73673822..3369917df62b9 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -440,7 +440,7 @@ bool swift::emitSwiftInterface(raw_ostream &out, printImports(out, M); const PrintOptions printOptions = PrintOptions::printSwiftInterfaceFile( - Opts.PreserveTypesAsWritten); + Opts.PreserveTypesAsWritten, Opts.PrintFullConvention); InheritedProtocolCollector::PerTypeMap inheritedProtocolMap; SmallVector topLevelDecls; diff --git a/lib/IDE/CodeCompletionResultBuilder.h b/lib/IDE/CodeCompletionResultBuilder.h index a56e8eee1dada..cc4793d9eb1ba 100644 --- a/lib/IDE/CodeCompletionResultBuilder.h +++ b/lib/IDE/CodeCompletionResultBuilder.h @@ -400,7 +400,8 @@ class CodeCompletionResultBuilder { if (auto AFT = Ty->getAs()) { // If this is a closure type, add ChunkKind::CallParameterClosureType. PrintOptions PO; - PO.PrintFunctionRepresentationAttrs = false; + PO.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; PO.SkipAttributes = true; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; diff --git a/lib/IDE/IDETypeChecking.cpp b/lib/IDE/IDETypeChecking.cpp index 0b719b59c85e8..6d4050743df8f 100644 --- a/lib/IDE/IDETypeChecking.cpp +++ b/lib/IDE/IDETypeChecking.cpp @@ -85,7 +85,8 @@ PrintOptions PrintOptions::printDocInterface() { PrintOptions::ArgAndParamPrintingMode::BothAlways; result.PrintDocumentationComments = false; result.PrintRegularClangComments = false; - result.PrintFunctionRepresentationAttrs = false; + result.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; return result; } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 7f3563f4f6b0a..e5c0cb67970d0 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1799,6 +1799,8 @@ namespace { AnyFunctionType::Representation representation = AnyFunctionType::Representation::Swift, bool noescape = false, + const clang::Type *parsedClangFunctionType + = nullptr, DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable); bool @@ -2166,7 +2168,29 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Function attributes require a syntactic function type. auto *fnRepr = dyn_cast(repr); + auto tryParseClangType = [this](TypeAttributes::Convention &conv, + bool hasConventionCOrBlock) + -> const clang::Type * { + if (conv.ClangType.Item.empty()) + return nullptr; + if (!hasConventionCOrBlock) { + diagnose(conv.ClangType.Loc, + diag::unexpected_ctype_for_non_c_convention, + conv.Name, conv.ClangType.Item); + return nullptr; + } + + const clang::Type *type = Context.getClangModuleLoader() + ->parseClangFunctionType(conv.ClangType.Item, + conv.ClangType.Loc); + if (!type) + diagnose(conv.ClangType.Loc, diag::unable_to_parse_c_function_type, + conv.ClangType.Item); + return type; + }; + if (fnRepr && hasFunctionAttr) { + const clang::Type *parsedClangFunctionType = nullptr; if (options & TypeResolutionFlags::SILType) { SILFunctionType::Representation rep; TypeRepr *witnessMethodProtocol = nullptr; @@ -2214,6 +2238,11 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, rep = SILFunctionType::Representation::Thin; } else { rep = *parsedRep; + bool isCOrBlock = + rep == SILFunctionTypeRepresentation::CFunctionPointer + || rep == SILFunctionTypeRepresentation::Block; + parsedClangFunctionType = + tryParseClangType(attrs.ConventionArguments.getValue(), isCOrBlock); } if (rep == SILFunctionType::Representation::WitnessMethod) { @@ -2264,6 +2293,11 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, rep = FunctionType::Representation::Swift; } else { rep = *parsedRep; + + bool isCOrBlock = rep == FunctionTypeRepresentation::CFunctionPointer + || rep == FunctionTypeRepresentation::Block; + parsedClangFunctionType = + tryParseClangType(attrs.ConventionArguments.getValue(), isCOrBlock); } } @@ -2280,6 +2314,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, } ty = resolveASTFunctionType(fnRepr, options, rep, /*noescape=*/false, + parsedClangFunctionType, diffKind); if (!ty || ty->hasError()) return ty; @@ -2593,6 +2628,7 @@ Type TypeResolver::resolveOpaqueReturnType(TypeRepr *repr, Type TypeResolver::resolveASTFunctionType( FunctionTypeRepr *repr, TypeResolutionOptions parentOptions, AnyFunctionType::Representation representation, bool noescape, + const clang::Type *parsedClangFunctionType, DifferentiabilityKind diffKind) { TypeResolutionOptions options = None; @@ -2631,8 +2667,9 @@ Type TypeResolver::resolveASTFunctionType( noescape, repr->throws(), diffKind, /*clangFunctionType*/nullptr); - const clang::Type *clangFnType = nullptr; - if (representation == AnyFunctionType::Representation::CFunctionPointer) + const clang::Type *clangFnType = parsedClangFunctionType; + if (representation == AnyFunctionType::Representation::CFunctionPointer + && !clangFnType) clangFnType = Context.getClangFunctionType( params, outputTy, incompleteExtInfo, AnyFunctionType::Representation::CFunctionPointer); diff --git a/test/ClangImporter/clang-function-types.swift b/test/ClangImporter/clang-function-types.swift new file mode 100644 index 0000000000000..52d7dae1dd75a --- /dev/null +++ b/test/ClangImporter/clang-function-types.swift @@ -0,0 +1,12 @@ +// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -sdk %clang-importer-sdk -enable-library-evolution %s -experimental-print-full-convention | %FileCheck %s + +import ctypes + +// CHECK: f1: (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)? +public let f1 = getFunctionPointer_() + +// CHECK: f2: (@convention(c, cType: "int (*(*)(int (*)(int)))(int)") ((@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?) -> (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?)? +public let f2 = getHigherOrderFunctionPointer() + +// CHECK: f3: () -> (@convention(c, cType: "Dummy *(*)(Dummy *)") (Swift.UnsafeMutablePointer?) -> Swift.UnsafeMutablePointer?)? +public let f3 = getFunctionPointer3 diff --git a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h index c055b48ebff88..4d4a7cf018575 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h @@ -205,6 +205,8 @@ typedef int (*fptr)(int); fptr getFunctionPointer(void); void useFunctionPointer(fptr); +int (*getFunctionPointer_(void))(int); + struct FunctionPointerWrapper { fptr a; fptr b; @@ -214,6 +216,14 @@ typedef void (*fptr2)(int, long, void *); fptr2 getFunctionPointer2(void); void useFunctionPointer2(fptr2); +int (*(*getHigherOrderFunctionPointer(void))(int (*)(int)))(int); + +typedef struct Dummy { + int x; +} Dummy; + +Dummy * (*getFunctionPointer3(void))(Dummy *); + //===--- // Unions //===--- diff --git a/test/ModuleInterface/full-convention.swift b/test/ModuleInterface/full-convention.swift new file mode 100644 index 0000000000000..950ad95402ba6 --- /dev/null +++ b/test/ModuleInterface/full-convention.swift @@ -0,0 +1,32 @@ +// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -enable-library-evolution %s -experimental-print-full-convention | %FileCheck %s + +public func f( + // CHECK: g: @convention(c, cType: "void (*)(void)") + g: @convention(c) () -> (), + + // CHECK: h0: @convention(c, cType: "int (*)(long long)") + h0: @convention(c) (Int64) -> Int32, + // CHECK: h1: @convention(c, cType: "int (*)(long long)") + h1: @convention(c, cType: "int (*)(long long)") (Int64) -> Int32, + + // CHECK: i0: @convention(c, cType: "int *(*)(long long, int)") + i0: @convention(c) (Int64, Int32) -> Optional>, + // CHECK: i1: @convention(c, cType: "int *(*)(long long, int)") + i1: @convention(c, cType: "int *(*)(long long, int)") (Int64, Int32) -> Optional>, + + // CHECK: p0: @convention(c, cType: "void (*)(void (*)(int))") + // CHECK: @convention(c, cType: "void (*)(int)") + p0: @convention(c) (@convention(c) (Int32) -> Void) -> Void, + + // CHECK: p1: @convention(c, cType: "void (*)(void (*)(int))") + // CHECK: @convention(c, cType: "void (*)(int)") + p1: @convention(c, cType: "void (*)(void (*)(int))") (@convention(c) (Int32) -> Void) -> Void, + + // CHECK: p2: @convention(c, cType: "void (*)(void (*)(int))") + // CHECK: @convention(c, cType: "void (*)(int)") + p2: @convention(c) (@convention(c, cType: "void (*)(int)") (Int32) -> Void) -> Void, + + // CHECK: p3: @convention(c, cType: "void (*)(void (*)(int))") + // CHECK: @convention(c, cType: "void (*)(int)") + p3: @convention(c, cType: "void (*)(void (*)(int))") (@convention(c, cType: "void (*)(int)") (Int32) -> Void) -> Void +) {} diff --git a/test/Parse/c_function_pointers.swift b/test/Parse/c_function_pointers.swift index 3d51e63098b5f..188634808dfac 100644 --- a/test/Parse/c_function_pointers.swift +++ b/test/Parse/c_function_pointers.swift @@ -50,8 +50,8 @@ let f: @convention(c) (Int) -> Int = genericFunc // expected-error{{cannot be fo func ct1() -> () { print("") } -let ct1ref0 : @convention(c, cType: "void *(void)") () -> () = ct1 -let ct1ref1 : @convention(c, cType: "void *(void)") = ct1 // expected-error{{expected type}} +let ct1ref0 : @convention(c, cType: "void (*)(void)") () -> () = ct1 +let ct1ref1 : @convention(c, cType: "void (*)(void)") = ct1 // expected-error{{expected type}} let ct1ref2 : @convention(c, ) () -> () = ct1 // expected-error{{expected 'cType' label in 'convention' attribute}} let ct1ref3 : @convention(c, cType) () -> () = ct1 // expected-error{{expected ':' after 'cType' for 'convention' attribute}} let ct1ref4 : @convention(c, cType: ) () -> () = ct1 // expected-error{{expected string literal containing clang type for 'cType' in 'convention' attribute}} diff --git a/test/attr/attr_convention.swift b/test/attr/attr_convention.swift index 7ee849bdb3079..83048435363fc 100644 --- a/test/attr/attr_convention.swift +++ b/test/attr/attr_convention.swift @@ -2,8 +2,12 @@ let f1: (Int) -> Int = { $0 } let f2: @convention(swift) (Int) -> Int = { $0 } +let f2a: @convention(swift, cType: "int *(int)") (Int32) -> Int32 = { $0 } // expected-error{{convention 'swift' does not support the 'cType' argument label, did you mean @convention(c, cType: "int *(int)") or @convention(block, cType: "int *(int)") instead?}} let f3: @convention(block) (Int) -> Int = { $0 } let f4: @convention(c) (Int) -> Int = { $0 } +let f4a: @convention(c, cType: "int (int)") (Int32) -> Int32 = { $0 } // expected-error{{unable to parse 'int (int)'; it should be a C function pointer type or a block pointer type}} +let f4b: @convention(c, cType: "void *") (Int32) -> Int32 = { $0 } // expected-error{{unable to parse 'void *'; it should be a C function pointer type or a block pointer type}} +let f4c: @convention(c, cType: "int (*)(int)") (Int32) -> Int32 = { $0 } let f5: @convention(INTERCAL) (Int) -> Int = { $0 } // expected-error{{convention 'INTERCAL' not supported}} From f071cf133addb6c5391fce65e6233b195e81d12e Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Tue, 7 Jan 2020 16:05:21 -0800 Subject: [PATCH 315/478] Revert "[basic] Add a simple vector backed 2 stage multi map." --- include/swift/Basic/FrozenMultiMap.h | 187 ------------------------- unittests/Basic/CMakeLists.txt | 1 - unittests/Basic/FrozenMultiMapTest.cpp | 187 ------------------------- 3 files changed, 375 deletions(-) delete mode 100644 include/swift/Basic/FrozenMultiMap.h delete mode 100644 unittests/Basic/FrozenMultiMapTest.cpp diff --git a/include/swift/Basic/FrozenMultiMap.h b/include/swift/Basic/FrozenMultiMap.h deleted file mode 100644 index c4e497f7f5d6c..0000000000000 --- a/include/swift/Basic/FrozenMultiMap.h +++ /dev/null @@ -1,187 +0,0 @@ -//===--- FrozenMultiMap.h ----------------------------------*- 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 -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// -/// A 2 stage multi-map. Initially the multimap is mutable and can only be -/// initialized. Once complete, the map is frozen and can be only used for map -/// operations. It is guaranteed that all values are still in insertion order. -/// -/// DISCUSSION: These restrictions flow from the internal implementation of the -/// multi-map being a pair of keys, values. We form the map property by -/// performing a stable_sort of the (key, value) in the process of freezing the -/// map. -/// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_BASIC_FROZENMULTIMAP_H -#define SWIFT_BASIC_FROZENMULTIMAP_H - -#include "swift/Basic/LLVM.h" -#include "swift/Basic/STLExtras.h" -#include "llvm/ADT/SmallVector.h" -#include - -namespace swift { - -template >> -class FrozenMultiMap { - VectorStorage storage; - bool frozen = false; - -private: - struct PairToSecondElt; - -public: - using PairToSecondEltRange = - TransformRange>, PairToSecondElt>; - - FrozenMultiMap() = default; - - void insert(const Key &key, const Value &value) { - assert(!isFrozen() && "Can not insert new keys once map is frozen"); - storage.emplace_back(key, value); - } - - Optional find(const Key &key) const { - assert(isFrozen() && - "Can not perform a find operation until the map is frozen"); - // Since our array is sorted, we need to first find the first pair with our - // inst as the first element. - auto start = std::lower_bound( - storage.begin(), storage.end(), std::make_pair(key, Value()), - [&](const std::pair &p1, const std::pair &p2) { - return p1.first < p2.first; - }); - if (start == storage.end() || start->first != key) { - return None; - } - - // Ok, we found our first element. Now scan forward until we find a pair - // whose instruction is not our own instruction. - auto end = find_if_not( - start, storage.end(), - [&](const std::pair &pair) { return pair.first == key; }); - unsigned count = std::distance(start, end); - ArrayRef> slice(&*start, count); - return PairToSecondEltRange(slice, PairToSecondElt()); - } - - bool isFrozen() const { return frozen; } - - /// Set this map into its frozen state when we - void setFrozen() { - std::stable_sort(storage.begin(), storage.end(), - [&](const std::pair &lhs, - const std::pair &rhs) { - // Only compare the first entry so that we preserve - // insertion order. - return lhs.first < rhs.first; - }); - frozen = true; - } - - unsigned size() const { return storage.size(); } - bool empty() const { return storage.empty(); } - - struct iterator : std::iterator>> { - using base_iterator = typename decltype(storage)::iterator; - - FrozenMultiMap ↦ - base_iterator baseIter; - Optional> currentValue; - - iterator(FrozenMultiMap &map, base_iterator iter) - : map(map), baseIter(iter), currentValue() { - // If we are end, just return. - if (iter == map.storage.end()) { - return; - } - - // Otherwise, prime our first range. - updateCurrentValue(); - } - - void updateCurrentValue() { - base_iterator end = map.storage.end(); - auto rangeEnd = std::find_if_not(std::next(baseIter), end, - [&](const std::pair &elt) { - return elt.first == baseIter->first; - }); - unsigned count = std::distance(baseIter, rangeEnd); - ArrayRef> slice(&*baseIter, count); - currentValue = {baseIter->first, - PairToSecondEltRange(slice, PairToSecondElt())}; - } - - iterator &operator++() { - baseIter = std::find_if_not(std::next(baseIter), map.storage.end(), - [&](const std::pair &elt) { - return elt.first == baseIter->first; - }); - updateCurrentValue(); - return *this; - } - - iterator operator++(int) { - auto tmp = *this; - baseIter = std::find_if_not(std::next(baseIter), map.storage.end(), - [&](const std::pair &elt) { - return elt.first == baseIter->first; - }); - updateCurrentValue(); - return tmp; - } - - std::pair operator*() const { - return *currentValue; - } - - bool operator==(const iterator &RHS) const { - return baseIter == RHS.baseIter; - } - - bool operator!=(const iterator &RHS) const { - return baseIter != RHS.baseIter; - } - }; - - /// Return a range of (key, ArrayRef) pairs. The keys are guaranteed to - /// be in key sorted order and the ArrayRef are in insertion order. - llvm::iterator_range getRange() const { - assert(isFrozen() && - "Can not create range until data structure is frozen?!"); - auto *self = const_cast(this); - iterator iter1 = iterator(*self, self->storage.begin()); - iterator iter2 = iterator(*self, self->storage.end()); - return llvm::make_range(iter1, iter2); - } -}; - -template -struct FrozenMultiMap::PairToSecondElt { - PairToSecondElt() {} - - Value operator()(const std::pair &pair) const { - return pair.second; - } -}; - -template -using SmallFrozenMultiMap = - FrozenMultiMap, SmallSize>>; - -} // namespace swift - -#endif diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt index b26199c3897ce..488dc579d239c 100644 --- a/unittests/Basic/CMakeLists.txt +++ b/unittests/Basic/CMakeLists.txt @@ -15,7 +15,6 @@ add_swift_unittest(SwiftBasicTests EncodedSequenceTest.cpp ExponentialGrowthAppendingBinaryByteStreamTests.cpp FileSystemTest.cpp - FrozenMultiMapTest.cpp ImmutablePointerSetTest.cpp JSONSerialization.cpp OptionSetTest.cpp diff --git a/unittests/Basic/FrozenMultiMapTest.cpp b/unittests/Basic/FrozenMultiMapTest.cpp deleted file mode 100644 index 40207ef59c3b0..0000000000000 --- a/unittests/Basic/FrozenMultiMapTest.cpp +++ /dev/null @@ -1,187 +0,0 @@ -//===--- FrozenMultiMapTest.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 -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "swift-frozen-multi-map-test" - -#include "swift/Basic/FrozenMultiMap.h" -#include "swift/Basic/LLVM.h" -#include "swift/Basic/Lazy.h" -#include "swift/Basic/NullablePtr.h" -#include "swift/Basic/Range.h" -#include "swift/Basic/STLExtras.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "gtest/gtest.h" -#include -#include -#include - -using namespace swift; - -namespace { - -class Canary { - static unsigned currentID; - unsigned id; - -public: - static void resetIDs() { currentID = 0; } - Canary(unsigned id) : id(id) {} - Canary() { - id = currentID; - ++currentID; - } - - unsigned getID() const { return id; } - bool operator<(const Canary &other) const { return id < other.id; } - - bool operator==(const Canary &other) const { return id == other.id; } - - bool operator!=(const Canary &other) const { return !(*this == other); } -}; - -unsigned Canary::currentID = 0; - -} // namespace - -TEST(FrozenMultiMapCustomTest, SimpleFind) { - Canary::resetIDs(); - FrozenMultiMap map; - - auto key1 = Canary(); - auto key2 = Canary(); - map.insert(key1, Canary()); - map.insert(key1, Canary()); - map.insert(key1, Canary()); - map.insert(key2, Canary()); - map.insert(key2, Canary()); - - map.setFrozen(); - - EXPECT_EQ(map.size(), 5u); - { - auto r = map.find(key1); - EXPECT_TRUE(r.hasValue()); - EXPECT_EQ(r->size(), 3u); - EXPECT_EQ((*r)[0].getID(), 2u); - EXPECT_EQ((*r)[1].getID(), 3u); - EXPECT_EQ((*r)[2].getID(), 4u); - } - - { - auto r = map.find(key2); - EXPECT_TRUE(r.hasValue()); - EXPECT_EQ(r->size(), 2u); - EXPECT_EQ((*r)[0].getID(), 5u); - EXPECT_EQ((*r)[1].getID(), 6u); - } -} - -TEST(FrozenMultiMapCustomTest, SimpleIter) { - Canary::resetIDs(); - FrozenMultiMap map; - - auto key1 = Canary(); - auto key2 = Canary(); - map.insert(key1, Canary()); - map.insert(key1, Canary()); - map.insert(key1, Canary()); - map.insert(key2, Canary()); - map.insert(key2, Canary()); - - map.setFrozen(); - - EXPECT_EQ(map.size(), 5u); - - auto range = map.getRange(); - - EXPECT_EQ(std::distance(range.begin(), range.end()), 2); - - auto iter = range.begin(); - { - auto p = *iter; - EXPECT_EQ(p.first.getID(), key1.getID()); - EXPECT_EQ(p.second.size(), 3u); - EXPECT_EQ(p.second[0].getID(), 2u); - EXPECT_EQ(p.second[1].getID(), 3u); - EXPECT_EQ(p.second[2].getID(), 4u); - } - - ++iter; - { - auto p = *iter; - EXPECT_EQ(p.first.getID(), key2.getID()); - EXPECT_EQ(p.second.size(), 2u); - EXPECT_EQ(p.second[0].getID(), 5u); - EXPECT_EQ(p.second[1].getID(), 6u); - } -} - -TEST(FrozenMultiMapCustomTest, RandomAgainstStdMultiMap) { - Canary::resetIDs(); - FrozenMultiMap map; - std::multimap stdMultiMap; - - auto seed = - std::chrono::high_resolution_clock::now().time_since_epoch().count(); - std::mt19937 mt_rand(seed); - - std::vector keyIdList; - for (unsigned i = 0; i < 1024; ++i) { - unsigned keyID = mt_rand() % 20; - keyIdList.push_back(keyID); - for (unsigned valueID = (mt_rand()) % 15; valueID < 15; ++valueID) { - map.insert(keyID, valueID); - stdMultiMap.emplace(keyID, valueID); - } - } - - map.setFrozen(); - - // Then for each key. - for (unsigned i : keyIdList) { - // Make sure that we have the same elements in the same order for each key. - auto range = *map.find(i); - auto stdRange = stdMultiMap.equal_range(i); - EXPECT_EQ(std::distance(range.begin(), range.end()), - std::distance(stdRange.first, stdRange.second)); - auto modernStdRange = llvm::make_range(stdRange.first, stdRange.second); - for (auto p : llvm::zip(range, modernStdRange)) { - unsigned lhs = std::get<0>(p); - unsigned rhs = std::get<1>(p).second; - EXPECT_EQ(lhs, rhs); - } - } - - // Then check that when we iterate over both ranges, we get the same order. - { - auto range = map.getRange(); - auto rangeIter = range.begin(); - auto stdRangeIter = stdMultiMap.begin(); - - while (rangeIter != range.end()) { - auto rangeElt = *rangeIter; - - for (unsigned i : indices(rangeElt.second)) { - EXPECT_EQ(rangeElt.first, stdRangeIter->first); - EXPECT_EQ(rangeElt.second[i], stdRangeIter->second); - ++stdRangeIter; - } - - ++rangeIter; - } - } -} From 42c73334f09f8ee6a9b68465eedd414d5ec16603 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 6 Jan 2020 14:38:43 -0800 Subject: [PATCH 316/478] [CSGen] Diagnose subscripting a nil literal in CSGen. --- lib/Sema/CSDiag.cpp | 6 ------ lib/Sema/CSGen.cpp | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index f3c5a89b30468..eea090aa402ee 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -1418,12 +1418,6 @@ bool FailureDiagnosis::diagnoseSubscriptErrors(SubscriptExpr *SE, if (!baseExpr) return true; auto baseType = CS.getType(baseExpr); - if (isa(baseExpr)) { - diagnose(baseExpr->getLoc(), diag::cannot_subscript_nil_literal) - .highlight(baseExpr->getSourceRange()); - return true; - } - std::function)> callback = [&](ArrayRef candidates) -> bool { CalleeCandidateInfo calleeInfo(Type(), candidates, SE->hasTrailingClosure(), diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index f51ec576cc4f0..c57605fbf3fdd 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1785,6 +1785,7 @@ namespace { } Type visitSubscriptExpr(SubscriptExpr *expr) { + auto &ctx = CS.getASTContext(); ValueDecl *decl = nullptr; if (expr->hasDecl()) { decl = expr->getDecl().getDecl(); @@ -1792,6 +1793,12 @@ namespace { return Type(); } + if (auto nilLiteral = dyn_cast(expr->getBase())) { + ctx.Diags.diagnose(nilLiteral->getLoc(), + diag::cannot_subscript_nil_literal); + return nullptr; + } + return addSubscriptConstraints(expr, CS.getType(expr->getBase()), expr->getIndex(), decl, expr->getArgumentLabels(), From 834eee6f4e9b5d94f9ea0f020a098fcf7ee9ffdb Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 6 Jan 2020 15:22:45 -0800 Subject: [PATCH 317/478] [Diagnostics] Implement MissingArgumentsFailure::diagnoseAsNote in order to diagnose ambiguities due to missing arguments. --- lib/Sema/CSDiagnostics.cpp | 15 +++++++++++++++ lib/Sema/CSDiagnostics.h | 2 ++ test/decl/subscript/subscripting.swift | 5 +++-- test/type/types.swift | 8 ++++++-- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 54b6dad9064ce..a00e9b1fc4a12 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3809,6 +3809,21 @@ bool MissingArgumentsFailure::diagnoseAsError() { return true; } +bool MissingArgumentsFailure::diagnoseAsNote() { + auto *locator = getLocator(); + if (auto overload = getChoiceFor(locator)) { + auto *fn = resolveType(overload->openedType)->getAs(); + auto loc = overload->choice.getDecl()->getLoc(); + if (loc.isInvalid()) + loc = getAnchor()->getLoc(); + emitDiagnostic(loc, diag::candidate_partial_match, + fn->getParamListAsString(fn->getParams())); + return true; + } + + return false; +} + bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { auto &ctx = getASTContext(); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index dfa22d0122f3d..5628d42ae2c75 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1210,6 +1210,8 @@ class MissingArgumentsFailure final : public FailureDiagnostic { bool diagnoseAsError() override; + bool diagnoseAsNote() override; + bool diagnoseSingleMissingArgument() const; private: diff --git a/test/decl/subscript/subscripting.swift b/test/decl/subscript/subscripting.swift index 4061785c917ad..c7b92dc292d9c 100644 --- a/test/decl/subscript/subscripting.swift +++ b/test/decl/subscript/subscripting.swift @@ -398,12 +398,13 @@ func testSubscript1(_ s1 : SubscriptTest1) { struct SubscriptTest2 { subscript(a : String, b : Int) -> Int { return 0 } // expected-note {{candidate expects value of type 'Int' for parameter #2}} // expected-note@-1 {{declared here}} + // expected-note@-2 {{candidate has partially matching parameter list (String, Int)}} subscript(a : String, b : String) -> Int { return 0 } // expected-note {{candidate expects value of type 'String' for parameter #2}} + // expected-note@-1 {{candidate has partially matching parameter list (String, String)}} } func testSubscript1(_ s2 : SubscriptTest2) { - _ = s2["foo"] // expected-error {{cannot subscript a value of type 'SubscriptTest2' with an argument of type 'String'}} - // expected-note @-1 {{overloads for 'subscript' exist with these partially matching parameter lists: (String, Int), (String, String)}} + _ = s2["foo"] // expected-error {{no exact matches in call to subscript}} let a = s2["foo", 1.0] // expected-error {{no exact matches in call to subscript}} diff --git a/test/type/types.swift b/test/type/types.swift index 47b4b8f5ab689..6ee2cb8dd5922 100644 --- a/test/type/types.swift +++ b/test/type/types.swift @@ -19,8 +19,12 @@ var d3 : () -> Float = { 4 } var d4 : () -> Int = { d2 } // expected-error{{function produces expected type 'Int'; did you mean to call it with '()'?}} {{26-26=()}} var e0 : [Int] -e0[] // expected-error {{cannot subscript a value of type '[Int]' with an argument of type '()'}} - // expected-note @-1 {{overloads for 'subscript' exist with these partially matching parameter lists: ((UnboundedRange_) -> ()), (Int), (R), (Range), (Range)}} +e0[] // expected-error {{no exact matches in call to subscript}} +// expected-note@-1 {{candidate has partially matching parameter list (Int)}} +// expected-note@-2 {{candidate has partially matching parameter list (Range)}} +// expected-note@-3 {{candidate has partially matching parameter list ((UnboundedRange_) -> ())}} +// expected-note@-4 {{candidate has partially matching parameter list (Range.Index>)}} +// expected-note@-5 {{candidate has partially matching parameter list ((UnboundedRange_) -> ())}} var f0 : [Float] var f1 : [(Int,Int)] From e26794a11cd61bca984fd19ab6695eb544c044fb Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 7 Jan 2020 16:35:00 -0800 Subject: [PATCH 318/478] [CSDiag] Remove diagnoseSubscriptErrors from CSDiag --- lib/Sema/CSDiag.cpp | 154 -------------------------------------------- 1 file changed, 154 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index eea090aa402ee..7cf1175fdfcd6 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -234,8 +234,6 @@ class FailureDiagnosis :public ASTVisitor{ Optional)>> callback = None, bool includeInaccessibleMembers = true); - bool diagnoseSubscriptErrors(SubscriptExpr *SE, bool performingSet); - bool visitExpr(Expr *E); bool visitIdentityExpr(IdentityExpr *E); bool visitTryExpr(TryExpr *E); @@ -245,7 +243,6 @@ class FailureDiagnosis :public ASTVisitor{ bool visitDictionaryExpr(DictionaryExpr *E); bool visitObjectLiteralExpr(ObjectLiteralExpr *E); - bool visitSubscriptExpr(SubscriptExpr *SE); bool visitApplyExpr(ApplyExpr *AE); bool visitCoerceExpr(CoerceExpr *CE); bool visitIfExpr(IfExpr *IE); @@ -1412,157 +1409,6 @@ bool FailureDiagnosis::diagnoseParameterErrors(CalleeCandidateInfo &CCI, return false; } -bool FailureDiagnosis::diagnoseSubscriptErrors(SubscriptExpr *SE, - bool inAssignmentDestination) { - auto baseExpr = typeCheckChildIndependently(SE->getBase()); - if (!baseExpr) return true; - auto baseType = CS.getType(baseExpr); - - std::function)> callback = - [&](ArrayRef candidates) -> bool { - CalleeCandidateInfo calleeInfo(Type(), candidates, SE->hasTrailingClosure(), - CS, /*selfAlreadyApplied*/ false); - - // We're about to typecheck the index list, which needs to be processed with - // self already applied. - for (unsigned i = 0, e = calleeInfo.size(); i != e; ++i) - calleeInfo.candidates[i].skipCurriedSelf = true; - - auto indexExpr = - typeCheckArgumentChildIndependently(SE->getIndex(), Type(), calleeInfo); - if (!indexExpr) - return true; - - // Back to analyzing the candidate list with self applied. - for (unsigned i = 0, e = calleeInfo.size(); i != e; ++i) - calleeInfo.candidates[i].skipCurriedSelf = false; - - ArrayRef argLabels = SE->getArgumentLabels(); - if (diagnoseParameterErrors(calleeInfo, SE, indexExpr, argLabels)) - return true; - - auto indexType = CS.getType(indexExpr); - - auto decomposedBaseType = decomposeArgType(baseType, {Identifier()}); - auto decomposedIndexType = decomposeArgType(indexType, argLabels); - calleeInfo.filterList( - [&](OverloadCandidate cand) -> CalleeCandidateInfo::ClosenessResultTy { - // Classify how close this match is. Non-subscript decls don't match. - auto subscriptDecl = dyn_cast_or_null(cand.getDecl()); - if (!subscriptDecl || - (inAssignmentDestination && !subscriptDecl->supportsMutation())) - return {CC_GeneralMismatch, {}}; - - // Check whether the self type matches. - auto selfConstraint = CC_ExactMatch; - if (calleeInfo.evaluateCloseness(cand, decomposedBaseType).first != - CC_ExactMatch) - selfConstraint = CC_SelfMismatch; - - // Set a flag to look past the self argument to the indices. - cand.skipCurriedSelf = true; - - // Explode out multi-index subscripts to find the best match. - auto indexResult = - calleeInfo.evaluateCloseness(cand, decomposedIndexType); - if (selfConstraint > indexResult.first) - return {selfConstraint, {}}; - return indexResult; - }); - - // If the closest matches all mismatch on self, we either have something - // that cannot be subscripted, or an ambiguity. - if (calleeInfo.closeness == CC_SelfMismatch) { - diagnose(SE->getLoc(), diag::cannot_subscript_base, baseType) - .highlight(SE->getBase()->getSourceRange()); - // FIXME: Should suggest overload set, but we're not ready for that until - // it points to candidates and identifies the self type in the diagnostic. - // calleeInfo.suggestPotentialOverloads(SE->getLoc()); - return true; - } - - // Any other failures relate to the index list. - for (unsigned i = 0, e = calleeInfo.size(); i != e; ++i) - calleeInfo.candidates[i].skipCurriedSelf = true; - - // TODO: Is there any reason to check for CC_NonLValueInOut here? - - if (calleeInfo.closeness == CC_ExactMatch) { - auto message = diag::ambiguous_subscript; - - // If there is an exact match on the argument with - // a single candidate, let's type-check subscript - // as a whole to figure out if there is any structural - // problem after all. - if (calleeInfo.size() == 1) { - Expr *expr = SE; - ConcreteDeclRef decl = nullptr; - message = diag::cannot_subscript_with_index; - - if (TypeChecker::getTypeOfExpressionWithoutApplying(expr, CS.DC, decl)) - return false; - - // If we are down to a single candidate but with an unresolved - // index type, we can substitute in the base type to get a simpler - // and more concrete expected type for this subscript decl, in order - // to diagnose a better error. - if (baseType && indexType->hasUnresolvedType()) { - auto cand = calleeInfo.candidates[0]; - auto candType = baseType->getTypeOfMember(CS.DC->getParentModule(), - cand.getDecl(), nullptr); - if (auto *candFunc = candType->getAs()) { - auto paramsType = FunctionType::composeInput(CS.getASTContext(), - candFunc->getParams(), - false); - if (!typeCheckChildIndependently( - indexExpr, paramsType, CTP_CallArgument, TCC_ForceRecheck)) - return true; - } - } - } - - diagnose(SE->getLoc(), message, baseType, indexType) - .highlight(indexExpr->getSourceRange()) - .highlight(baseExpr->getSourceRange()); - - // FIXME: suggestPotentialOverloads should do this. - // calleeInfo.suggestPotentialOverloads(SE->getLoc()); - for (auto candidate : calleeInfo.candidates) - if (auto decl = candidate.getDecl()) - diagnose(decl, diag::found_candidate); - else - diagnose(candidate.getExpr()->getLoc(), diag::found_candidate); - - return true; - } - - if (diagnoseParameterErrors(calleeInfo, SE, indexExpr, argLabels)) - return true; - - // Diagnose some simple and common errors. - if (calleeInfo.diagnoseSimpleErrors(SE)) - return true; - - diagnose(SE->getLoc(), diag::cannot_subscript_with_index, baseType, - indexType); - - calleeInfo.suggestPotentialOverloads(SE->getLoc()); - return true; - }; - - auto locator = - CS.getConstraintLocator(SE, ConstraintLocator::SubscriptMember); - - return diagnoseMemberFailures(SE, baseExpr, ConstraintKind::ValueMember, - DeclNameRef::createSubscript(), - FunctionRefKind::DoubleApply, locator, - callback); -} - -bool FailureDiagnosis::visitSubscriptExpr(SubscriptExpr *SE) { - return diagnoseSubscriptErrors(SE, /* inAssignmentDestination = */ false); -} - // Check if there is a structural problem in the function expression // by performing type checking with the option to allow unresolved // type variables. If that is going to produce a function type with From c9c51beda373a6f23800f841495de65b9c906683 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Tue, 7 Jan 2020 19:24:52 -0800 Subject: [PATCH 319/478] [AutoDiff] Attribute gardening. (#29050) Upstream changes from `tensorflow` branch: - https://github.com/apple/swift/pull/28932: deprecate `@differentiable(jvp:vjp)` arguments. - https://github.com/apple/swift/pull/29038: gardening. Additional gardening included. --- include/swift/AST/Attr.h | 28 +-- include/swift/AST/DiagnosticsParse.def | 13 +- include/swift/Parse/Parser.h | 11 +- lib/AST/Attr.cpp | 55 ++--- lib/Parse/ParseDecl.cpp | 100 +++++---- lib/Sema/TypeCheckAttr.cpp | 192 +++++++++--------- lib/Sema/TypeChecker.h | 23 ++- .../Parse/differentiable_attr_parse.swift | 67 ++++-- 8 files changed, 275 insertions(+), 214 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index e4d624c0bd42e..04f34ae1271f5 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1650,7 +1650,7 @@ class DifferentiableAttr final /// Whether this function is linear (optional). bool Linear; - /// The number of parsed parameters specified in 'wrt:'. + /// The number of parsed differentiability parameters specified in 'wrt:'. unsigned NumParsedParameters = 0; /// The JVP function. Optional JVP; @@ -1662,7 +1662,7 @@ class DifferentiableAttr final /// The VJP function (optional), resolved by the type checker if VJP name is /// specified. FuncDecl *VJPFunction = nullptr; - /// The differentiation parameters' indices, resolved by the type checker. + /// The differentiability parameter indices, resolved by the type checker. IndexSubset *ParameterIndices = nullptr; /// The trailing where clause (optional). TrailingWhereClause *WhereClause = nullptr; @@ -1720,7 +1720,7 @@ class DifferentiableAttr final ParameterIndices = parameterIndices; } - /// The parsed differentiation parameters, i.e. the list of parameters + /// The parsed differentiability parameters, i.e. the list of parameters /// specified in 'wrt:'. ArrayRef getParsedParameters() const { return {getTrailingObjects(), NumParsedParameters}; @@ -1771,15 +1771,15 @@ class DifferentiableAttr final /// /// The `@derivative(of:)` attribute also has an optional `wrt:` clause /// specifying the parameters that are differentiated "with respect to", i.e. -/// the differentiation parameters. The differentiation parameters must conform -/// to the `Differentiable` protocol. +/// the differentiability parameters. The differentiability parameters must +/// conform to the `Differentiable` protocol. /// -/// If the `wrt:` clause is unspecified, the differentiation parameters are +/// If the `wrt:` clause is unspecified, the differentiability parameters are /// inferred to be all parameters that conform to `Differentiable`. /// /// `@derivative(of:)` attribute type-checking verifies that the type of the /// derivative function declaration is consistent with the type of the -/// referenced original declaration and the differentiation parameters. +/// referenced original declaration and the differentiability parameters. /// /// Examples: /// @derivative(of: sin(_:)) @@ -1798,9 +1798,9 @@ class DerivativeAttr final DeclNameRefWithLoc OriginalFunctionName; /// The original function declaration, resolved by the type checker. AbstractFunctionDecl *OriginalFunction = nullptr; - /// The number of parsed parameters specified in 'wrt:'. + /// The number of parsed differentiability parameters specified in 'wrt:'. unsigned NumParsedParameters = 0; - /// The differentiation parameters' indices, resolved by the type checker. + /// The differentiability parameter indices, resolved by the type checker. IndexSubset *ParameterIndices = nullptr; /// The derivative function kind (JVP or VJP), resolved by the type checker. Optional Kind = None; @@ -1843,7 +1843,7 @@ class DerivativeAttr final } void setDerivativeKind(AutoDiffDerivativeFunctionKind kind) { Kind = kind; } - /// The parsed differentiation parameters, i.e. the list of parameters + /// The parsed differentiability parameters, i.e. the list of parameters /// specified in 'wrt:'. ArrayRef getParsedParameters() const { return {getTrailingObjects(), NumParsedParameters}; @@ -1872,7 +1872,7 @@ class DerivativeAttr final /// computed property declaration. /// /// The `@transpose(of:)` attribute also has a `wrt:` clause specifying the -/// parameters that are transposed "with respect to", i.e. the transposed +/// parameters that are transposed "with respect to", i.e. the linearity /// parameters. /// /// Examples: @@ -1892,9 +1892,9 @@ class TransposeAttr final DeclNameRefWithLoc OriginalFunctionName; /// The original function declaration, resolved by the type checker. AbstractFunctionDecl *OriginalFunction = nullptr; - /// The number of parsed parameters specified in 'wrt:'. + /// The number of parsed linearity parameters specified in 'wrt:'. unsigned NumParsedParameters = 0; - /// The transposed parameters' indices, resolved by the type checker. + /// The linearity parameter indices, resolved by the type checker. IndexSubset *ParameterIndices = nullptr; explicit TransposeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, @@ -1927,7 +1927,7 @@ class TransposeAttr final OriginalFunction = decl; } - /// The parsed transposed parameters, i.e. the list of parameters specified in + /// The parsed linearity parameters, i.e. the list of parameters specified in /// 'wrt:'. ArrayRef getParsedParameters() const { return {getTrailingObjects(), NumParsedParameters}; diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index e4888fe373852..fabdc9682b4f0 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1542,21 +1542,24 @@ ERROR(attr_implements_expected_member_name,PointsToFirstBadToken, "expected a member name as second parameter in '_implements' attribute", ()) // differentiable +// TODO(TF-1001): Remove diagnostic when deprecated `jvp:`, `vjp:` are removed. ERROR(attr_differentiable_expected_function_name,PointsToFirstBadToken, "expected a %0 function name", (StringRef)) ERROR(attr_differentiable_expected_parameter_list,PointsToFirstBadToken, "expected a list of parameters to differentiate with respect to", ()) +// TODO(TF-1001): Remove diagnostic when deprecated `jvp:`, `vjp:` are removed. ERROR(attr_differentiable_use_wrt_not_withrespectto,none, "use 'wrt:' to specify parameters to differentiate with respect to", ()) -ERROR(attr_differentiable_missing_label,PointsToFirstBadToken, - "missing label '%0:' in '@differentiable' attribute", (StringRef)) ERROR(attr_differentiable_expected_label,none, "expected either 'wrt:' or a function specifier label, e.g. 'jvp:', " "or 'vjp:'", ()) -ERROR(differentiable_attribute_expected_rparen,none, - "expected ')' in '@differentiable' attribute", ()) -ERROR(unexpected_argument_differentiable,none, +ERROR(attr_differentiable_unexpected_argument,none, "unexpected argument '%0' in '@differentiable' attribute", (StringRef)) +// TODO(TF-1001): Remove diagnostic when deprecated `jvp:`, `vjp:` are removed. +WARNING(attr_differentiable_jvp_vjp_deprecated_warning,none, + "'jvp:' and 'vjp:' arguments in '@differentiable' attribute are " + "deprecated; use '@derivative' attribute for derivative registration " + "instead", ()) // differentiation `wrt` parameters clause ERROR(expected_colon_after_label,PointsToFirstBadToken, diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 21f3014298573..93dfc1cfe3f6c 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1002,12 +1002,13 @@ class Parser { Optional &vjpSpec, TrailingWhereClause *&whereClause); - /// Parse a differentiation parameters clause, i.e. the 'wrt:' clause in - /// `@differentiable` and `@derivative` attributes. + /// Parse a differentiability parameters clause, i.e. the 'wrt:' clause in + /// `@differentiable`, `@derivative`, and `@transpose` attributes. + /// /// If `allowNamedParameters` is false, allow only index parameters and - /// 'self'. - bool parseDifferentiationParametersClause( - SmallVectorImpl ¶ms, StringRef attrName, + /// 'self'. Used for `@transpose` attributes. + bool parseDifferentiabilityParametersClause( + SmallVectorImpl ¶meters, StringRef attrName, bool allowNamedParameters = true); /// Parse the @derivative attribute. diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index bb6d7c47d8079..46d80f1c8c8d2 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -366,39 +366,46 @@ static void printShortFormAvailable(ArrayRef Attrs, Printer.printNewline(); } -/// Printing style for a differentiation parameter in a `wrt:` differentiation -/// parameters clause. Used for printing `@differentiable`, `@derivative`, and -/// `@transpose` attributes. -enum class DifferentiationParameterPrintingStyle { - /// Print parameter by name. +/// The kind of a parameter in a `wrt:` differentiation parameters clause: +/// either a differentiability parameter or a linearity parameter. Used for +/// printing `@differentiable`, `@derivative`, and `@transpose` attributes. +enum class DifferentiationParameterKind { + /// A differentiability parameter, printed by name. /// Used for `@differentiable` and `@derivative` attribute. - Name, - /// Print parameter by index. + Differentiability, + /// A linearity parameter, printed by index. /// Used for `@transpose` attribute. - Index + Linearity }; /// Returns the differentiation parameters clause string for the given function, -/// parameter indices, parsed parameters, . Use the parameter indices if -/// specified; otherwise, use the parsed parameters. +/// parameter indices, parsed parameters, and differentiation parameter kind. +/// Use the parameter indices if specified; otherwise, use the parsed +/// parameters. static std::string getDifferentiationParametersClauseString( - const AbstractFunctionDecl *function, IndexSubset *paramIndices, + const AbstractFunctionDecl *function, IndexSubset *parameterIndices, ArrayRef parsedParams, - DifferentiationParameterPrintingStyle style) { + DifferentiationParameterKind parameterKind) { assert(function); bool isInstanceMethod = function->isInstanceMember(); + bool isStaticMethod = function->isStatic(); std::string result; llvm::raw_string_ostream printer(result); // Use the parameter indices, if specified. - if (paramIndices) { - auto parameters = paramIndices->getBitVector(); + if (parameterIndices) { + auto parameters = parameterIndices->getBitVector(); auto parameterCount = parameters.count(); printer << "wrt: "; if (parameterCount > 1) printer << '('; // Check if differentiating wrt `self`. If so, manually print it first. - if (isInstanceMethod && parameters.test(parameters.size() - 1)) { + bool isWrtSelf = + (isInstanceMethod || + (isStaticMethod && + parameterKind == DifferentiationParameterKind::Linearity)) && + parameters.test(parameters.size() - 1); + if (isWrtSelf) { parameters.reset(parameters.size() - 1); printer << "self"; if (parameters.any()) @@ -406,11 +413,13 @@ static std::string getDifferentiationParametersClauseString( } // Print remaining differentiation parameters. interleave(parameters.set_bits(), [&](unsigned index) { - switch (style) { - case DifferentiationParameterPrintingStyle::Name: + switch (parameterKind) { + // Print differentiability parameters by name. + case DifferentiationParameterKind::Differentiability: printer << function->getParameters()->get(index)->getName().str(); break; - case DifferentiationParameterPrintingStyle::Index: + // Print linearity parameters by index. + case DifferentiationParameterKind::Linearity: printer << index; break; } @@ -487,7 +496,7 @@ static void printDifferentiableAttrArguments( if (!omitWrtClause) { auto diffParamsString = getDifferentiationParametersClauseString( original, attr->getParameterIndices(), attr->getParsedParameters(), - DifferentiationParameterPrintingStyle::Name); + DifferentiationParameterKind::Differentiability); // Check whether differentiation parameter clause is empty. // Handles edge case where resolved parameter indices are unset and // parsed parameters are empty. This case should never trigger for @@ -927,7 +936,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, auto *derivative = cast(D); auto diffParamsString = getDifferentiationParametersClauseString( derivative, attr->getParameterIndices(), attr->getParsedParameters(), - DifferentiationParameterPrintingStyle::Name); + DifferentiationParameterKind::Differentiability); if (!diffParamsString.empty()) Printer << ", " << diffParamsString; Printer << ')'; @@ -942,7 +951,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, auto *transpose = cast(D); auto transParamsString = getDifferentiationParametersClauseString( transpose, attr->getParameterIndices(), attr->getParsedParameters(), - DifferentiationParameterPrintingStyle::Index); + DifferentiationParameterKind::Linearity); if (!transParamsString.empty()) Printer << ", " << transParamsString; Printer << ')'; @@ -1510,11 +1519,11 @@ GenericEnvironment *DifferentiableAttr::getDerivativeGenericEnvironment( void DifferentiableAttr::print(llvm::raw_ostream &OS, const Decl *D, bool omitWrtClause, - bool omitAssociatedFunctions) const { + bool omitDerivativeFunctions) const { StreamPrinter P(OS); P << "@" << getAttrName(); printDifferentiableAttrArguments(this, P, PrintOptions(), D, omitWrtClause, - omitAssociatedFunctions); + omitDerivativeFunctions); } DerivativeAttr::DerivativeAttr(bool implicit, SourceLoc atLoc, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 5c85c9c749551..599c7c6bab428 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -792,7 +792,7 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) { /// /// \verbatim /// differentiable-attribute-arguments: -/// '(' (differentiation-params-clause ',')? +/// '(' (differentiability-params-clause ',')? /// (differentiable-attr-func-specifier ',')? /// differentiable-attr-func-specifier? /// where-clause? @@ -805,7 +805,7 @@ Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) { StringRef AttrName = "differentiable"; SourceLoc lParenLoc = loc, rParenLoc = loc; bool linear = false; - SmallVector params; + SmallVector parameters; Optional jvpSpec; Optional vjpSpec; TrailingWhereClause *whereClause = nullptr; @@ -813,8 +813,8 @@ Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) { // Parse '('. if (consumeIf(tok::l_paren, lParenLoc)) { // Parse @differentiable attribute arguments. - if (parseDifferentiableAttributeArguments(linear, params, jvpSpec, vjpSpec, - whereClause)) + if (parseDifferentiableAttributeArguments(linear, parameters, jvpSpec, + vjpSpec, whereClause)) return makeParserError(); // Parse ')'. if (!consumeIf(tok::r_paren, rParenLoc)) { @@ -824,10 +824,9 @@ Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) { } } - return ParserResult( - DifferentiableAttr::create(Context, /*implicit*/ false, atLoc, - SourceRange(loc, rParenLoc), linear, - params, jvpSpec, vjpSpec, whereClause)); + return ParserResult(DifferentiableAttr::create( + Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), linear, + parameters, jvpSpec, vjpSpec, whereClause)); } // Attribute parsing error helper. @@ -847,29 +846,29 @@ static bool errorAndSkipUntilConsumeRightParen(Parser &P, StringRef attrName, return true; }; -/// Parse a differentiation parameters 'wrt:' clause, returning true on error. +/// Parse a differentiability parameters 'wrt:' clause, returning true on error. /// If `allowNamedParameters` is false, allow only index parameters and 'self'. /// /// \verbatim -/// differentiation-params-clause: -/// 'wrt' ':' (differentiation-param | differentiation-params) -/// differentiation-params: -/// '(' differentiation-param (',' differentiation-param)* ')' -/// differentiation-param: +/// differentiability-params-clause: +/// 'wrt' ':' (differentiability-param | differentiability-params) +/// differentiability-params: +/// '(' differentiability-param (',' differentiability-param)* ')' +/// differentiability-param: /// 'self' | identifier | [0-9]+ /// \endverbatim -bool Parser::parseDifferentiationParametersClause( - SmallVectorImpl ¶ms, StringRef attrName, +bool Parser::parseDifferentiabilityParametersClause( + SmallVectorImpl ¶meters, StringRef attrName, bool allowNamedParameters) { SyntaxParsingContext DiffParamsClauseContext( - SyntaxContext, SyntaxKind::DifferentiationParamsClause); + SyntaxContext, SyntaxKind::DifferentiationParamsClause); consumeToken(tok::identifier); if (!consumeIf(tok::colon)) { diagnose(Tok, diag::expected_colon_after_label, "wrt"); return errorAndSkipUntilConsumeRightParen(*this, attrName); } - // Function that parses a parameter into `params`. Returns true if error + // Function that parses a parameter into `parameters`. Returns true if error // occurred. auto parseParam = [&](bool parseTrailingComma = true) -> bool { SyntaxParsingContext DiffParamContext( @@ -886,8 +885,8 @@ bool Parser::parseDifferentiationParametersClause( if (parseIdentifier(paramName, paramLoc, diag::diff_params_clause_expected_parameter)) return true; - params.push_back(ParsedAutoDiffParameter::getNamedParameter( - paramLoc, paramName)); + parameters.push_back( + ParsedAutoDiffParameter::getNamedParameter(paramLoc, paramName)); break; } case tok::integer_literal: { @@ -896,13 +895,13 @@ bool Parser::parseDifferentiationParametersClause( paramNum, paramLoc, diag::diff_params_clause_expected_parameter)) return true; - params.push_back(ParsedAutoDiffParameter::getOrderedParameter( - paramLoc, paramNum)); + parameters.push_back( + ParsedAutoDiffParameter::getOrderedParameter(paramLoc, paramNum)); break; } case tok::kw_self: { paramLoc = consumeToken(tok::kw_self); - params.push_back(ParsedAutoDiffParameter::getSelfParameter(paramLoc)); + parameters.push_back(ParsedAutoDiffParameter::getSelfParameter(paramLoc)); break; } default: @@ -940,10 +939,9 @@ bool Parser::parseDifferentiationParametersClause( } bool Parser::parseDifferentiableAttributeArguments( - bool &linear, SmallVectorImpl ¶ms, + bool &linear, SmallVectorImpl ¶meters, Optional &jvpSpec, - Optional &vjpSpec, - TrailingWhereClause *&whereClause) { + Optional &vjpSpec, TrailingWhereClause *&whereClause) { StringRef AttrName = "differentiable"; // Parse trailing comma, if it exists, and check for errors. @@ -968,7 +966,7 @@ bool Parser::parseDifferentiableAttributeArguments( SyntaxParsingContext ContentContext( SyntaxContext, SyntaxKind::DifferentiableAttributeArguments); - // Parse optional differentiation parameters. + // Parse optional differentiability parameters. // Parse 'linear' label (optional). linear = false; if (isIdentifier(Tok, "linear")) { @@ -989,9 +987,9 @@ bool Parser::parseDifferentiableAttributeArguments( .fixItReplace(withRespectToRange, "wrt:"); return errorAndSkipUntilConsumeRightParen(*this, AttrName); } - // Parse differentiation parameters' clause. + // Parse the optional 'wrt' differentiability parameters clause. if (isIdentifier(Tok, "wrt")) { - if (parseDifferentiationParametersClause(params, AttrName)) + if (parseDifferentiabilityParametersClause(parameters, AttrName)) return true; // If no trailing comma or 'where' clause, terminate parsing arguments. if (Tok.isNot(tok::comma, tok::kw_where)) @@ -1005,8 +1003,8 @@ bool Parser::parseDifferentiableAttributeArguments( auto parseFuncSpec = [&](StringRef label, DeclNameRefWithLoc &result, bool &terminateParsingArgs) -> bool { // Parse label. - if (parseSpecificIdentifier(label, - diag::attr_differentiable_missing_label, label) || + if (parseSpecificIdentifier(label, diag::attr_missing_label, label, + AttrName) || parseToken(tok::colon, diag::expected_colon_after_label, label)) return true; // Parse the name of the function. @@ -1016,6 +1014,13 @@ bool Parser::parseDifferentiableAttributeArguments( { label }); result.Name = parseDeclNameRef(result.Loc, funcDiag, DeclNameFlag::AllowZeroArgCompoundNames | DeclNameFlag::AllowOperators); + // Emit warning for deprecated `jvp:` and `vjp:` arguments. + // TODO(TF-1001): Remove deprecated `jvp:` and `vjp:` arguments. + if (result.Loc.isValid()) { + diagnose(result.Loc.getStartLoc(), + diag::attr_differentiable_jvp_vjp_deprecated_warning) + .highlight(result.Loc.getSourceRange()); + } // If no trailing comma or 'where' clause, terminate parsing arguments. if (Tok.isNot(tok::comma, tok::kw_where)) terminateParsingArgs = true; @@ -1133,7 +1138,7 @@ static bool parseQualifiedDeclName(Parser &P, Diag<> nameParseError, /// /// \verbatim /// derivative-attribute-arguments: -/// '(' 'of' ':' qualified-decl-name (',' differentiation-params-clause)? +/// '(' 'of' ':' qualified-decl-name (',' differentiability-params-clause)? /// ')' /// \endverbatim ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, @@ -1142,7 +1147,7 @@ ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, SourceLoc lParenLoc = loc, rParenLoc = loc; TypeRepr *baseType = nullptr; DeclNameRefWithLoc original; - SmallVector params; + SmallVector parameters; // Parse trailing comma, if it exists, and check for errors. auto consumeIfTrailingComma = [&]() -> bool { @@ -1182,9 +1187,9 @@ ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, } if (consumeIfTrailingComma()) return makeParserError(); - // Parse the optional 'wrt' differentiation parameters clause. + // Parse the optional 'wrt' differentiability parameters clause. if (isIdentifier(Tok, "wrt") && - parseDifferentiationParametersClause(params, AttrName)) + parseDifferentiabilityParametersClause(parameters, AttrName)) return makeParserError(); } // Parse ')'. @@ -1195,14 +1200,20 @@ ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, } return ParserResult(DerivativeAttr::create( Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), baseType, - original, params)); + original, parameters)); } /// Parse a `@transpose(of:)` attribute, returning true on error. /// /// \verbatim /// transpose-attribute-arguments: -/// '(' 'of' ':' qualified-decl-name (',' transposed-params-clause)? ')' +/// '(' 'of' ':' qualified-decl-name (',' linearity-params-clause)? ')' +/// linearity-params-clause: +/// 'wrt' ':' (linearity-param | linearity-params) +/// linearity-params: +/// '(' linearity-param (',' linearity-param)* ')' +/// linearity-param: +/// 'self' | [0-9]+ /// \endverbatim ParserResult Parser::parseTransposeAttribute(SourceLoc atLoc, SourceLoc loc) { @@ -1210,7 +1221,7 @@ ParserResult Parser::parseTransposeAttribute(SourceLoc atLoc, SourceLoc lParenLoc = loc, rParenLoc = loc; TypeRepr *baseType = nullptr; DeclNameRefWithLoc original; - SmallVector params; + SmallVector parameters; // Parse trailing comma, if it exists, and check for errors. auto consumeIfTrailingComma = [&]() -> bool { @@ -1251,10 +1262,10 @@ ParserResult Parser::parseTransposeAttribute(SourceLoc atLoc, } if (consumeIfTrailingComma()) return makeParserError(); - // Parse the optional 'wrt' transposed parameters clause. + // Parse the optional 'wrt' linearity parameters clause. if (Tok.is(tok::identifier) && Tok.getText() == "wrt" && - parseDifferentiationParametersClause(params, AttrName, - /*allowNamedParameters*/ false)) + parseDifferentiabilityParametersClause(parameters, AttrName, + /*allowNamedParameters*/ false)) return makeParserError(); } // Parse ')'. @@ -1265,7 +1276,7 @@ ParserResult Parser::parseTransposeAttribute(SourceLoc atLoc, } return ParserResult(TransposeAttr::create( Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), baseType, - original, params)); + original, parameters)); } void Parser::parseObjCSelector(SmallVector &Names, @@ -2612,7 +2623,8 @@ static bool parseDifferentiableAttributeArgument(Parser &P, if (P.Tok.is(tok::l_paren)) { backtrack.cancelBacktrack(); if (emitDiagnostics) - P.diagnose(P.Tok, diag::differentiable_attribute_expected_rparen); + P.diagnose(P.Tok, diag::attr_expected_rparen, "@differentiable", + /*DeclModifier*/ false); return true; } return false; @@ -2628,7 +2640,7 @@ static bool parseDifferentiableAttributeArgument(Parser &P, if (argument.getText() != "linear") { if (emitDiagnostics) - P.diagnose(argument, diag::unexpected_argument_differentiable, + P.diagnose(argument, diag::attr_differentiable_unexpected_argument, argument.getText()); return true; } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 03756c2a8bef1..9a2b2db26aa25 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -272,7 +272,7 @@ void AttributeChecker::visitTransparentAttr(TransparentAttr *attr) { if (!(isa(D) && D->isImplicit())) diagnoseAndRemoveAttr(attr, diag::transparent_in_classes_not_supported); } - + if (auto *VD = dyn_cast(D)) { // Stored properties and variables can't be transparent. if (VD->hasStorage()) @@ -339,7 +339,7 @@ void AttributeChecker::visitMutationAttr(DeclAttribute *attr) { } } } - + // Verify that we don't have a static function. if (FD->isStatic()) diagnoseAndRemoveAttr(attr, diag::static_functions_not_mutating); @@ -560,7 +560,7 @@ isAcceptableOutletType(Type type, bool &isArray, ASTContext &ctx) { if (type->isExistentialType()) return diag::iboutlet_nonobjc_protocol; - + // No other types are permitted. return diag::iboutlet_nonobject_type; } @@ -1430,7 +1430,7 @@ void AttributeChecker::visitSwiftNativeObjCRuntimeBaseAttr( attr->setInvalid(); return; } - + if (theClass->hasSuperclass()) { diagnose(attr->getLocation(), diag::swift_native_objc_runtime_base_not_on_root_class); @@ -1582,7 +1582,7 @@ void AttributeChecker::visitNSCopyingAttr(NSCopyingAttr *attr) { // Check the type. It must be an [unchecked]optional, weak, a normal // class, AnyObject, or classbound protocol. // It must conform to the NSCopying protocol. - + } void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, @@ -1594,7 +1594,7 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, UIApplicationMainClass, NSApplicationMainClass, }; - + unsigned applicationMainKind; if (isa(attr)) applicationMainKind = UIApplicationMainClass; @@ -1602,9 +1602,9 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, applicationMainKind = NSApplicationMainClass; else llvm_unreachable("not an ApplicationMain attr"); - + auto *CD = dyn_cast(D); - + // The applicant not being a class should have been diagnosed by the early // checker. if (!CD) return; @@ -1617,7 +1617,7 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, attr->setInvalid(); return; } - + // @XXApplicationMain classes must conform to the XXApplicationDelegate // protocol. auto *SF = cast(CD->getModuleScopeContext()); @@ -1646,7 +1646,7 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, if (attr->isInvalid()) return; - + // Register the class as the main class in the module. If there are multiples // they will be diagnosed. if (SF->registerMainClass(CD, attr->getLocation())) @@ -1969,7 +1969,7 @@ void AttributeChecker::visitFixedLayoutAttr(FixedLayoutAttr *attr) { if (isa(D)) { diagnose(attr->getLocation(), diag::fixed_layout_struct) .fixItReplace(attr->getRange(), "@frozen"); - } + } auto *VD = cast(D); @@ -2469,10 +2469,10 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { attr->setInvalid(); return; } - + return; } - + // If the nominal type is a function builder type, verify that D is a // function, storage with an explicit getter, or parameter of function type. if (nominal->getAttrs().hasAttribute()) { @@ -2942,7 +2942,7 @@ static bool conformsToDifferentiable(Type type, DeclContext *DC) { return !tanType.isNull() && !tanType->hasError(); }; -IndexSubset *TypeChecker::inferDifferentiationParameters( +IndexSubset *TypeChecker::inferDifferentiabilityParameters( AbstractFunctionDecl *AFD, GenericEnvironment *derivativeGenEnv) { auto &ctx = AFD->getASTContext(); auto *functionType = AFD->getInterfaceType()->castTo(); @@ -2981,7 +2981,7 @@ IndexSubset *TypeChecker::inferDifferentiationParameters( for (auto ¶m : functionType->getParams()) allParamTypes.push_back(param.getPlainType()); - // Set differentiation parameters. + // Set differentiability parameters. for (unsigned i : range(parameterBits.size())) if (isDifferentiableParam(i)) parameterBits.set(i); @@ -2989,14 +2989,15 @@ IndexSubset *TypeChecker::inferDifferentiationParameters( return IndexSubset::get(ctx, parameterBits); } -// Computes `IndexSubset` from the given parsed differentiation parameters -// (possibly empty) for the given function and derivative generic environment, -// then verifies that the parameter indices are valid. +// Computes the differentiability parameter indices from the given parsed +// differentiability parameters for the given original or derivative +// `AbstractFunctionDecl` and derivative generic environment. On error, emits +// diagnostics and returns `nullptr`. // - If parsed parameters are empty, infer parameter indices. // - Otherwise, build parameter indices from parsed parameters. // The attribute name/location are used in diagnostics. -static IndexSubset *computeDifferentiationParameters( - ArrayRef parsedWrtParams, +static IndexSubset *computeDifferentiabilityParameters( + ArrayRef parsedDiffParams, AbstractFunctionDecl *function, GenericEnvironment *derivativeGenEnv, StringRef attrName, SourceLoc attrLoc) { auto &ctx = function->getASTContext(); @@ -3036,13 +3037,14 @@ static IndexSubset *computeDifferentiationParameters( } } - // If parsed differentiation parameters are empty, infer parameter indices + // If parsed differentiability parameters are empty, infer parameter indices // from the function type. - if (parsedWrtParams.empty()) - return TypeChecker::inferDifferentiationParameters(function, - derivativeGenEnv); + if (parsedDiffParams.empty()) + return TypeChecker::inferDifferentiabilityParameters(function, + derivativeGenEnv); - // Otherwise, build parameter indices from parsed differentiation parameters. + // Otherwise, build parameter indices from parsed differentiability + // parameters. auto numUncurriedParams = functionType->getNumParams(); if (auto *resultFnType = functionType->getResult()->getAs()) { @@ -3050,17 +3052,17 @@ static IndexSubset *computeDifferentiationParameters( } llvm::SmallBitVector parameterBits(numUncurriedParams); int lastIndex = -1; - for (unsigned i : indices(parsedWrtParams)) { - auto paramLoc = parsedWrtParams[i].getLoc(); - switch (parsedWrtParams[i].getKind()) { + for (unsigned i : indices(parsedDiffParams)) { + auto paramLoc = parsedDiffParams[i].getLoc(); + switch (parsedDiffParams[i].getKind()) { case ParsedAutoDiffParameter::Kind::Named: { auto nameIter = llvm::find_if(params.getArray(), [&](ParamDecl *param) { - return param->getName() == parsedWrtParams[i].getName(); + return param->getName() == parsedDiffParams[i].getName(); }); // Parameter name must exist. if (nameIter == params.end()) { diags.diagnose(paramLoc, diag::diff_params_clause_param_name_unknown, - parsedWrtParams[i].getName()); + parsedDiffParams[i].getName()); return nullptr; } // Parameter names must be specified in the original order. @@ -3090,7 +3092,7 @@ static IndexSubset *computeDifferentiationParameters( break; } case ParsedAutoDiffParameter::Kind::Ordered: { - auto index = parsedWrtParams[i].getIndex(); + auto index = parsedDiffParams[i].getIndex(); if (index >= numParams) { diags.diagnose(paramLoc, diag::diff_params_clause_param_index_out_of_range); @@ -3111,50 +3113,53 @@ static IndexSubset *computeDifferentiationParameters( return IndexSubset::get(ctx, parameterBits); } -// Checks if the given `IndexSubset` instance is valid for the given function +// Checks if the given differentiability parameter indices are valid for the +// given original or derivative `AbstractFunctionDecl` and original function // type in the given derivative generic environment and module context. Returns // true on error. -// The parsed differentiation parameters and attribute location are used in +// +// The parsed differentiability parameters and attribute location are used in // diagnostics. -static bool checkDifferentiationParameters( - AbstractFunctionDecl *AFD, IndexSubset *indices, +static bool checkDifferentiabilityParameters( + AbstractFunctionDecl *AFD, IndexSubset *diffParamIndices, AnyFunctionType *functionType, GenericEnvironment *derivativeGenEnv, - ModuleDecl *module, ArrayRef parsedWrtParams, + ModuleDecl *module, ArrayRef parsedDiffParams, SourceLoc attrLoc) { auto &ctx = AFD->getASTContext(); auto &diags = ctx.Diags; - // Diagnose empty parameter indices. This occurs when no `wrt:` clause is - // declared and no differentiation parameters can be inferred. - if (indices->isEmpty()) { + // Diagnose empty differentiability indices. No differentiability parameters + // were resolved or inferred. + if (diffParamIndices->isEmpty()) { diags.diagnose(attrLoc, diag::diff_params_clause_no_inferred_parameters); return true; } - // Check that differentiation parameters have allowed types. - SmallVector wrtParamTypes; - autodiff::getSubsetParameterTypes(indices, functionType, wrtParamTypes); - for (unsigned i : range(wrtParamTypes.size())) { + // Check that differentiability parameters have allowed types. + SmallVector diffParamTypes; + autodiff::getSubsetParameterTypes(diffParamIndices, functionType, + diffParamTypes); + for (unsigned i : range(diffParamTypes.size())) { SourceLoc loc = - parsedWrtParams.empty() ? attrLoc : parsedWrtParams[i].getLoc(); - auto wrtParamType = wrtParamTypes[i]; + parsedDiffParams.empty() ? attrLoc : parsedDiffParams[i].getLoc(); + auto diffParamType = diffParamTypes[i]; // `inout` parameters are not yet supported. - if (wrtParamType->is()) { + if (diffParamType->is()) { diags.diagnose(loc, diag::diff_params_clause_cannot_diff_wrt_inout_parameter, - wrtParamType); + diffParamType); return true; } - if (!wrtParamType->hasTypeParameter()) - wrtParamType = wrtParamType->mapTypeOutOfContext(); + if (!diffParamType->hasTypeParameter()) + diffParamType = diffParamType->mapTypeOutOfContext(); if (derivativeGenEnv) - wrtParamType = derivativeGenEnv->mapTypeIntoContext(wrtParamType); + diffParamType = derivativeGenEnv->mapTypeIntoContext(diffParamType); else - wrtParamType = AFD->mapTypeIntoContext(wrtParamType); + diffParamType = AFD->mapTypeIntoContext(diffParamType); // Parameter must conform to `Differentiable`. - if (!conformsToDifferentiable(wrtParamType, AFD)) { + if (!conformsToDifferentiable(diffParamType, AFD)) { diags.diagnose(loc, diag::diff_params_clause_param_not_differentiable, - wrtParamType); + diffParamType); return true; } } @@ -3330,26 +3335,20 @@ static bool checkFunctionSignature( return checkFunctionSignature(requiredResultFnTy, candidateResultTy); }; -// Returns an `AnyFunctionType` with the same `ExtInfo` as `fnType`, but with -// the given parameters, result type, and generic signature. If the given -// generic signature is `null`, use the generic signature of `fnType`. +// Returns an `AnyFunctionType` from the given parameters, result type, and +// generic signature. static AnyFunctionType * -makeFunctionType(AnyFunctionType *fnType, - ArrayRef parameters, Type resultType, +makeFunctionType(ArrayRef parameters, Type resultType, GenericSignature genericSignature) { - if (!genericSignature) - if (auto *genericFunctionType = fnType->getAs()) - genericSignature = genericFunctionType->getGenericSignature(); if (genericSignature) - return GenericFunctionType::get(genericSignature, parameters, resultType, - fnType->getExtInfo()); - return FunctionType::get(parameters, resultType, fnType->getExtInfo()); + return GenericFunctionType::get(genericSignature, parameters, resultType); + return FunctionType::get(parameters, resultType); } // Computes the original function type corresponding to the given derivative -// function type. -AnyFunctionType * -getAutoDiffOriginalFunctionType(AnyFunctionType *derivativeFnTy) { +// function type. Used for `@derivative` attribute type-checking. +static AnyFunctionType * +getDerivativeOriginalFunctionType(AnyFunctionType *derivativeFnTy) { // Unwrap curry levels. At most, two parameter lists are necessary, for // curried method types with a `(Self)` parameter list. SmallVector curryLevels; @@ -3367,7 +3366,7 @@ getAutoDiffOriginalFunctionType(AnyFunctionType *derivativeFnTy) { "Expected derivative result to be a two-element tuple"); auto originalResult = derivativeResult->getElement(0).getType(); auto *originalType = makeFunctionType( - curryLevels.back(), curryLevels.back()->getParams(), originalResult, + curryLevels.back()->getParams(), originalResult, curryLevels.size() == 1 ? derivativeFnTy->getOptGenericSignature() : nullptr); @@ -3378,7 +3377,7 @@ getAutoDiffOriginalFunctionType(AnyFunctionType *derivativeFnTy) { unsigned i = pair.index(); AnyFunctionType *curryLevel = pair.value(); originalType = - makeFunctionType(curryLevel, curryLevel->getParams(), originalType, + makeFunctionType(curryLevel->getParams(), originalType, i == curryLevelsWithoutLast.size() - 1 ? derivativeFnTy->getOptGenericSignature() : nullptr); @@ -3466,7 +3465,7 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, // Compute expected original function type and look up original function. auto *originalFnType = - getAutoDiffOriginalFunctionType(derivativeInterfaceType); + getDerivativeOriginalFunctionType(derivativeInterfaceType); // Returns true if the generic parameters in `source` satisfy the generic // requirements in `target`. @@ -3585,39 +3584,40 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, } attr->setOriginalFunction(originalAFD); - // Get the resolved `wrt:` parameter indices. - auto *resolvedWrtParamIndices = attr->getParameterIndices(); + // Get the resolved differentiability parameter indices. + auto *resolvedDiffParamIndices = attr->getParameterIndices(); - // Get the parsed `wrt:` parameter indices, which have not yet been resolved. - // Parsed `wrt:` paraeter indices are defined only for parsed attributes. - auto parsedWrtParams = attr->getParsedParameters(); + // Get the parsed differentiability parameter indices, which have not yet been + // resolved. Parsed differentiability parameter indices are defined only for + // parsed attributes. + auto parsedDiffParams = attr->getParsedParameters(); - // If parameter indices are not resolved, compute them. - if (!resolvedWrtParamIndices) - resolvedWrtParamIndices = computeDifferentiationParameters( - parsedWrtParams, derivative, derivative->getGenericEnvironment(), + // If differentiability parameter indices are not resolved, compute them. + if (!resolvedDiffParamIndices) + resolvedDiffParamIndices = computeDifferentiabilityParameters( + parsedDiffParams, derivative, derivative->getGenericEnvironment(), attr->getAttrName(), attr->getLocation()); - if (!resolvedWrtParamIndices) + if (!resolvedDiffParamIndices) return true; - // Check if the `wrt:` parameter indices are valid. - if (checkDifferentiationParameters( - originalAFD, resolvedWrtParamIndices, originalFnType, + // Check if the differentiability parameter indices are valid. + if (checkDifferentiabilityParameters( + originalAFD, resolvedDiffParamIndices, originalFnType, derivative->getGenericEnvironment(), derivative->getModuleContext(), - parsedWrtParams, attr->getLocation())) + parsedDiffParams, attr->getLocation())) return true; - // Set the resolved `wrt:` parameter indices in the attribute. - attr->setParameterIndices(resolvedWrtParamIndices); + // Set the resolved differentiability parameter indices in the attribute. + attr->setParameterIndices(resolvedDiffParamIndices); - // Gather `wrt:` parameters. - SmallVector wrtParamTypes; - autodiff::getSubsetParameterTypes(resolvedWrtParamIndices, originalFnType, - wrtParamTypes); + // Gather differentiability parameters. + SmallVector diffParamTypes; + autodiff::getSubsetParameterTypes(resolvedDiffParamIndices, originalFnType, + diffParamTypes); - // Get the `TangentVector` associated types of the `wrt:` parameters. - auto wrtParamTanTypes = - map>(wrtParamTypes, [&](Type paramType) { + // Get the differentiability parameters' `TangentVector` associated types. + auto diffParamTanTypes = + map>(diffParamTypes, [&](Type paramType) { if (paramType->hasTypeParameter()) paramType = derivative->mapTypeIntoContext(paramType); auto conf = TypeChecker::conformsToProtocol(paramType, diffableProto, @@ -3650,14 +3650,14 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, Type expectedFuncEltType; if (kind == AutoDiffDerivativeFunctionKind::JVP) { auto diffParams = map>( - wrtParamTanTypes, [&](TupleTypeElt elt) { + diffParamTanTypes, [&](TupleTypeElt elt) { return AnyFunctionType::Param(elt.getType()); }); expectedFuncEltType = FunctionType::get(diffParams, resultTanType); } else { expectedFuncEltType = FunctionType::get({AnyFunctionType::Param(resultTanType)}, - TupleType::get(wrtParamTanTypes, Ctx)); + TupleType::get(diffParamTanTypes, Ctx)); } expectedFuncEltType = expectedFuncEltType->mapTypeOutOfContext(); @@ -3695,7 +3695,7 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, // Reject duplicate `@derivative` attributes. auto &derivativeAttrs = Ctx.DerivativeAttrs[std::make_tuple( - originalAFD, resolvedWrtParamIndices, kind)]; + originalAFD, resolvedDiffParamIndices, kind)]; derivativeAttrs.insert(attr); if (derivativeAttrs.size() > 1) { diags.diagnose(attr->getLocation(), diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 13a5aac4f5ba2..f509f31abee51 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1580,18 +1580,19 @@ class TypeChecker final { static DeclTypeCheckingSemantics getDeclTypeCheckingSemantics(ValueDecl *decl); - /// Creates an `IndexSubset` for the given function type, representing - /// all inferred differentiation parameters. Used by `@differentiable` and - /// `@derivative` attribute type-checking. - /// - /// The differentiation parameters are inferred to be: - /// - All parameters of the function type that conform to `Differentiable`. - /// - If the function type's result is a function type (i.e. it is a curried - /// method type), then also all parameters of the function result type that - /// conform to `Differentiable`. + /// Infers the differentiability parameter indices for the given + /// original or derivative `AbstractFunctionDecl`. + /// + /// The differentiability parameters are inferred to be: + /// - All parameters of the function that conform to `Differentiable`. + /// - If the function result type is a function type (i.e. the function has + /// a curried method type), then also all parameters of the function result + /// type that conform to `Differentiable`. + /// + /// Used by `@differentiable` and `@derivative` attribute type-checking. static IndexSubset * - inferDifferentiationParameters(AbstractFunctionDecl *AFD, - GenericEnvironment *derivativeGenEnv); + inferDifferentiabilityParameters(AbstractFunctionDecl *AFD, + GenericEnvironment *derivativeGenEnv); public: /// Require that the library intrinsics for working with Optional diff --git a/test/AutoDiff/Parse/differentiable_attr_parse.swift b/test/AutoDiff/Parse/differentiable_attr_parse.swift index 354d385ba3604..eb94ff59728ab 100644 --- a/test/AutoDiff/Parse/differentiable_attr_parse.swift +++ b/test/AutoDiff/Parse/differentiable_attr_parse.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -parse -verify %s +// TODO(TF-1021): Remove "deprecated 'jvp:' and 'vjp:' argument" warnings. + /// Good struct Foo { @@ -7,21 +9,31 @@ struct Foo { var x: Float } +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} @differentiable(vjp: foo(_:_:)) // okay func bar(_ x: Float, _: Float) -> Float { return 1 + x } +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: foo(_:_:)) // okay +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} @differentiable(vjp: foo(_:_:) where T : FloatingPoint) // okay func bar(_ x: T, _: T) -> T { return 1 + x } +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: foo(_:_:)) // okay +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} @differentiable(wrt: (self, x, y), vjp: foo(_:_:)) // okay func bar(_ x: Float, _ y: Float) -> Float { return 1 + x } +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: foo(_:_:)) // okay +// expected-warning @+1 2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} @differentiable(wrt: (self, x, y), jvp: bar, vjp: foo(_:_:)) // okay func bar(_ x: Float, _ y: Float) -> Float { return 1 + x @@ -55,6 +67,7 @@ func playWellWithOtherAttrs(_ x: Float, _: Float) -> Float { } @_transparent +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} @differentiable(wrt: (self), vjp: _vjpSquareRoot) // okay public func squareRoot() -> Self { var lhs = self @@ -99,82 +112,104 @@ func two(x: Float, y: Float) -> Float { /// Bad -@differentiable(3) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(3) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(foo(_:_:)) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(foo(_:_:)) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(vjp: foo(_:_:), 3) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(vjp: foo(_:_:), 3) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(wrt: (x), foo(_:_:)) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: (x), foo(_:_:)) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(wrt: x, y) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: x, y) func bar(_ x: Float, _ y: Float) -> Float { return 1 + x } -@differentiable(wrt: 0, 1) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: 0, 1) func two(x: Float, y: Float) -> Float { return x + y } -@differentiable(wrt: 0, y) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: 0, y) func two(x: Float, y: Float) -> Float { return x + y } -@differentiable(wrt: 0,) // expected-error {{unexpected ',' separator}} +// expected-error @+1 {{unexpected ',' separator}} +@differentiable(wrt: 0,) func two(x: Float, y: Float) -> Float { return x + y } -@differentiable(vjp: foo(_:_:) // expected-error {{expected ')' in 'differentiable' attribute}} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{expected ')' in 'differentiable' attribute}} +@differentiable(vjp: foo(_:_:) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(vjp: foo(_:_:) where T) // expected-error {{expected ':' or '==' to indicate a conformance or same-type requirement}} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{expected ':' or '==' to indicate a conformance or same-type requirement}} +@differentiable(vjp: foo(_:_:) where T) func bar(_ x: T, _: T) -> T { return 1 + x } -@differentiable(,) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(,) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(vjp: foo(_:_:),) // expected-error {{unexpected ',' separator}} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{unexpected ',' separator}} +@differentiable(vjp: foo(_:_:),) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(vjp: foo(_:_:), where T) // expected-error {{unexpected ',' separator}} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{unexpected ',' separator}} +@differentiable(vjp: foo(_:_:), where T) func bar(_ x: T, _: T) -> T { return 1 + x } -@differentiable(wrt: x, linear) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: x, linear) func slope4(_ x: Float) -> Float { return 4 * x } -@differentiable(wrt: x, linear, vjp: const5) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: x, linear, vjp: const5) func slope5(_ x: Float) -> Float { return 5 * x } -@differentiable(wrt: x, vjp: const6, linear) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: x, vjp: const6, linear) func slope5(_ x: Float) -> Float { return 6 * x } From 18f52dcbb00571836031ca25872c0fc1a9e584c2 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Tue, 7 Jan 2020 13:03:00 -0300 Subject: [PATCH 320/478] [CSDiag] Remove visitCoerceExpr from CSDiag --- lib/Sema/CSDiag.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 246de41067235..12863e7e790a1 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -247,7 +247,6 @@ class FailureDiagnosis :public ASTVisitor{ bool visitSubscriptExpr(SubscriptExpr *SE); bool visitApplyExpr(ApplyExpr *AE); - bool visitCoerceExpr(CoerceExpr *CE); bool visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E); }; } // end anonymous namespace @@ -1878,6 +1877,7 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { return true; } +<<<<<<< HEAD bool FailureDiagnosis::visitCoerceExpr(CoerceExpr *CE) { // Coerce the input to whatever type is specified by the CoerceExpr. auto expr = typeCheckChildIndependently(CE->getSubExpr(), From 06201a64beb888e95aafbd68ed843c417aba0c5e Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Tue, 7 Jan 2020 13:17:39 -0300 Subject: [PATCH 321/478] [Sema] Diagnose wrong type coercion involving metatypes --- lib/Sema/CSDiagnostics.cpp | 18 ++++++++++++++++-- lib/Sema/CSSimplify.cpp | 26 +++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index e388c9bca280f..398b6f074864e 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1905,6 +1905,12 @@ bool ContextualFailure::diagnoseAsError() { diagnostic = diag::cannot_convert_condition_value; break; } + + case ConstraintLocator::InstanceType: { + if (diagnoseCoercionToUnrelatedType()) + return true; + break; + } case ConstraintLocator::TernaryBranch: { auto *ifExpr = cast(getRawAnchor()); @@ -2235,11 +2241,12 @@ bool ContextualFailure::diagnoseCoercionToUnrelatedType() const { auto *anchor = getAnchor(); if (auto *coerceExpr = dyn_cast(anchor)) { - auto fromType = getFromType(); + auto fromType = getType(coerceExpr->getSubExpr()); auto toType = getType(coerceExpr->getCastTypeLoc()); + auto diagnostic = getDiagnosticFor(CTP_CoerceOperand, - /*forProtocol=*/toType->isAnyExistentialType()); + /*forProtocol=*/toType->isExistentialType()); auto diag = emitDiagnostic(anchor->getLoc(), *diagnostic, fromType, toType); @@ -4772,6 +4779,13 @@ bool MissingContextualConformanceFailure::diagnoseAsError() { break; } + case ConstraintLocator::InstanceType: { + // If the missing conformance involves a coercion of metatypes + if (diagnoseCoercionToUnrelatedType()) + return true; + break; + } + default: break; } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 5d36a4f8b4ed6..f77838e71bf05 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2203,6 +2203,15 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, if (!(anchor && isa(anchor))) return getTypeMatchFailure(locator); } + + auto *anchor = locator.getAnchor(); + if (isa(anchor)) { + auto *fix = ContextualMismatch::create( + *this, type1, type2, getConstraintLocator(locator)); + if (recordFix(fix)) + return getTypeMatchFailure(locator); + break; + } auto *fix = MissingConformance::forContextual( *this, type1, proto, getConstraintLocator(locator)); @@ -4059,9 +4068,20 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, subKind = ConstraintKind::Bind; } - return matchTypes( - instanceType1, instanceType2, subKind, subflags, - locator.withPathElement(ConstraintLocator::InstanceType)); + auto result = + matchTypes(instanceType1, instanceType2, subKind, subflags, + locator.withPathElement(ConstraintLocator::InstanceType)); + if (shouldAttemptFixes() && result.isFailure()) { + auto *anchor = locator.getAnchor(); + if (anchor && isa(anchor)) { + auto *fix = + ContextualMismatch::create(*this, instanceType1, instanceType2, + getConstraintLocator(locator)); + conversionsOrFixes.push_back(fix); + break; + } + } + return result; } case TypeKind::Function: { From 0e1a03dc612edab8d94880f16c019b9436ee0c4d Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Tue, 7 Jan 2020 16:37:09 -0300 Subject: [PATCH 322/478] [CSSimplify] Handle optional wrong type conversion --- lib/Sema/CSSimplify.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index f77838e71bf05..cb4e3ae92c2da 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2842,8 +2842,10 @@ bool ConstraintSystem::repairFailures( // If it has a deep equality restriction, defer the diagnostic to // GenericMismatch. - if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality)) - return false; + if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality)) { + if (!lhs->getOptionalObjectType() && !rhs->getOptionalObjectType()) + return false; + } auto *fix = ContextualMismatch::create(*this, lhs, rhs, getConstraintLocator(locator)); From fc49af2949e0669fdfaa3ed5742feb77c2e12e99 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Tue, 7 Jan 2020 18:52:03 -0300 Subject: [PATCH 323/478] [CSSimplify] Handle coercion to wrong type for function result locator --- lib/Sema/CSSimplify.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index cb4e3ae92c2da..1f49277b64161 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2842,8 +2842,8 @@ bool ConstraintSystem::repairFailures( // If it has a deep equality restriction, defer the diagnostic to // GenericMismatch. - if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality)) { - if (!lhs->getOptionalObjectType() && !rhs->getOptionalObjectType()) + if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) && + !lhs->getOptionalObjectType() && !rhs->getOptionalObjectType()) { return false; } @@ -3503,6 +3503,13 @@ bool ConstraintSystem::repairFailures( break; } } + // Handle function result coerce expression wrong type conversion. + if (isa(anchor)) { + auto *fix = + ContextualMismatch::create(*this, lhs, rhs, loc); + conversionsOrFixes.push_back(fix); + break; + } LLVM_FALLTHROUGH; } From 4d982f34309d045cc6020441a12dcfc602fa3f10 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Tue, 7 Jan 2020 21:50:51 -0300 Subject: [PATCH 324/478] [CSSimplify] Do not record fix to coerce with existential restriction --- lib/Sema/CSSimplify.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 1f49277b64161..01d2adacc417b 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2200,7 +2200,8 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, } } else { // There are no elements in the path auto *anchor = locator.getAnchor(); - if (!(anchor && isa(anchor))) + if (!(anchor && + (isa(anchor) || isa(anchor)))) return getTypeMatchFailure(locator); } @@ -2839,13 +2840,17 @@ bool ConstraintSystem::repairFailures( conversionsOrFixes.push_back(coerceToCheckCastFix); return true; } - + // If it has a deep equality restriction, defer the diagnostic to // GenericMismatch. if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) && - !lhs->getOptionalObjectType() && !rhs->getOptionalObjectType()) { - return false; + !hasConversionOrRestriction( + ConversionRestrictionKind::OptionalToOptional)) { + return false; } + + if (hasConversionOrRestriction(ConversionRestrictionKind::Existential)) + return false; auto *fix = ContextualMismatch::create(*this, lhs, rhs, getConstraintLocator(locator)); From 0238a29e70011fa97b62504fa2d2577774de7331 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Tue, 7 Jan 2020 23:42:09 -0300 Subject: [PATCH 325/478] [tests] Fixing tests --- test/ClangImporter/objc_parse.swift | 4 ++-- test/Generics/function_defs.swift | 2 +- test/decl/protocol/protocols.swift | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/ClangImporter/objc_parse.swift b/test/ClangImporter/objc_parse.swift index 58817ab06c170..01087fb0038e8 100644 --- a/test/ClangImporter/objc_parse.swift +++ b/test/ClangImporter/objc_parse.swift @@ -550,8 +550,8 @@ func testProtocolQualified(_ obj: CopyableNSObject, cell: CopyableSomeCell, _ = cell as NSCopying _ = cell as SomeCell - _ = plainObj as CopyableNSObject // expected-error {{'NSObject' is not convertible to 'CopyableNSObject' (aka 'NSCopying & NSObjectProtocol'); did you mean to use 'as!' to force downcast?}} {{16-18=as!}} - _ = plainCell as CopyableSomeCell // expected-error {{'SomeCell' is not convertible to 'CopyableSomeCell' (aka 'SomeCell & NSCopying'); did you mean to use 'as!' to force downcast?}} + _ = plainObj as CopyableNSObject // expected-error {{value of type 'NSObject' does not conform to 'CopyableNSObject' (aka 'NSCopying & NSObjectProtocol') in coercion}} + _ = plainCell as CopyableSomeCell // expected-error {{value of type 'SomeCell' does not conform to 'CopyableSomeCell' (aka 'SomeCell & NSCopying') in coercion}} } extension Printing { diff --git a/test/Generics/function_defs.swift b/test/Generics/function_defs.swift index e60cef430ce6f..9aa0ab436477c 100644 --- a/test/Generics/function_defs.swift +++ b/test/Generics/function_defs.swift @@ -53,7 +53,7 @@ func otherExistential(_ t1: T) { otherEqComp2 = t1 // expected-error{{value of type 'T' does not conform to 'OtherEqualComparable' in assignment}} _ = otherEqComp2 - _ = t1 as EqualComparable & OtherEqualComparable // expected-error{{'T' is not convertible to 'EqualComparable & OtherEqualComparable'; did you mean to use 'as!' to force downcast?}} {{10-12=as!}} expected-error{{protocol 'OtherEqualComparable' can only be used as a generic constraint}} expected-error{{protocol 'EqualComparable' can only be used as a generic constraint}} + _ = t1 as EqualComparable & OtherEqualComparable // expected-error{{value of type 'T' does not conform to 'EqualComparable & OtherEqualComparable' in coercion}} expected-error{{protocol 'OtherEqualComparable' can only be used as a generic constraint}} expected-error{{protocol 'EqualComparable' can only be used as a generic constraint}} } protocol Runcible { diff --git a/test/decl/protocol/protocols.swift b/test/decl/protocol/protocols.swift index 0bdede947e547..f0b6bf4977bc6 100644 --- a/test/decl/protocol/protocols.swift +++ b/test/decl/protocol/protocols.swift @@ -118,7 +118,7 @@ struct Circle { func testCircular(_ circle: Circle) { // FIXME: It would be nice if this failure were suppressed because the protocols // have circular definitions. - _ = circle as CircleStart // expected-error{{'Circle' is not convertible to 'CircleStart'; did you mean to use 'as!' to force downcast?}} {{14-16=as!}} + _ = circle as CircleStart // expected-error{{value of type 'Circle' does not conform to 'CircleStart' in coercion}} } // @@ -482,7 +482,7 @@ func f(_ x : T) { class C2 {} func g(_ x : T) { - x as P2 // expected-error{{'T' is not convertible to 'P2'; did you mean to use 'as!' to force downcast?}} {{5-7=as!}} + x as P2 // expected-error{{value of type 'T' does not conform to 'P2' in coercion}} } class C3 : P1 {} // expected-error{{type 'C3' does not conform to protocol 'P1'}} From 4443966f3d272751ce76ff7407c631eee6337a26 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 8 Jan 2020 00:26:58 -0300 Subject: [PATCH 326/478] [CSDiagnostics] Removing duplicated diagnose for InstanceType --- lib/Sema/CSDiag.cpp | 12 ------------ lib/Sema/CSDiagnostics.cpp | 7 ------- 2 files changed, 19 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 12863e7e790a1..553cc8be593cf 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -1877,18 +1877,6 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { return true; } -<<<<<<< HEAD -bool FailureDiagnosis::visitCoerceExpr(CoerceExpr *CE) { - // Coerce the input to whatever type is specified by the CoerceExpr. - auto expr = typeCheckChildIndependently(CE->getSubExpr(), - CS.getType(CE->getCastTypeLoc()), - CTP_CoerceOperand); - if (!expr) - return true; - - return false; -} - bool FailureDiagnosis:: visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E) { // Don't walk the children for this node, it leads to multiple diagnostics diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 398b6f074864e..734b6eea990f9 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -4779,13 +4779,6 @@ bool MissingContextualConformanceFailure::diagnoseAsError() { break; } - case ConstraintLocator::InstanceType: { - // If the missing conformance involves a coercion of metatypes - if (diagnoseCoercionToUnrelatedType()) - return true; - break; - } - default: break; } From 7e7c79907693e6b892d1e34f80b14e1f918c2870 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Wed, 8 Jan 2020 08:16:55 -0800 Subject: [PATCH 327/478] [Frontend] Don't perform additional parsing for -verify Given `-verify` is used for testing, ideally it shouldn't exhibit different behaviour to an equivalent invocation without it. --- lib/FrontendTool/FrontendTool.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 676a601f35fdb..9ef1a539aa56e 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1263,8 +1263,7 @@ static bool performCompile(CompilerInstance &Instance, if (FrontendOptions::shouldActionOnlyParse(Action)) { bool ParseDelayedDeclListsOnEnd = - Action == FrontendOptions::ActionType::DumpParse || - Invocation.getDiagnosticOptions().VerifyMode != DiagnosticOptions::NoVerify; + Action == FrontendOptions::ActionType::DumpParse; Instance.performParseOnly(/*EvaluateConditionals*/ Action == FrontendOptions::ActionType::EmitImportedModules, ParseDelayedDeclListsOnEnd); From 6b87dce84592361f9d70458c4bdca2c7c6a258ce Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Wed, 8 Jan 2020 08:42:31 -0800 Subject: [PATCH 328/478] Remove DelayedDeclLists from PersistentParserState Rather than parsing all delayed bodies for `-dump-parse` once we finish parsing, tell the parser not to delay any bodies. This then allows us to remove `DelayedDeclLists` from PersistentParserState. --- include/swift/Frontend/Frontend.h | 2 +- include/swift/Parse/PersistentParserState.h | 6 ------ lib/Frontend/Frontend.cpp | 10 +++------- lib/FrontendTool/FrontendTool.cpp | 7 ++++--- lib/Parse/ParseDecl.cpp | 2 -- lib/Parse/PersistentParserState.cpp | 9 --------- 6 files changed, 8 insertions(+), 28 deletions(-) diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 3c0777805fd56..3b7847178ada2 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -609,7 +609,7 @@ class CompilerInstance { /// Parses the input file but does no type-checking or module imports. /// Note that this only supports parsing an invocation with a single file. void performParseOnly(bool EvaluateConditionals = false, - bool ParseDelayedBodyOnEnd = false); + bool CanDelayBodies = true); /// Parses and performs name binding on all input files. /// diff --git a/include/swift/Parse/PersistentParserState.h b/include/swift/Parse/PersistentParserState.h index 5b01eb94f50bc..9e0678c5e3971 100644 --- a/include/swift/Parse/PersistentParserState.h +++ b/include/swift/Parse/PersistentParserState.h @@ -78,8 +78,6 @@ class PersistentParserState { std::unique_ptr CodeCompletionDelayedDeclStat; - std::vector DelayedDeclLists; - /// The local context for all top-level code. TopLevelContext TopLevelCode; @@ -112,10 +110,6 @@ class PersistentParserState { return std::move(CodeCompletionDelayedDeclStat); } - void delayDeclList(IterableDeclContext *D); - - void parseAllDelayedDeclLists(); - TopLevelContext &getTopLevelContext() { return TopLevelCode; } diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index cb272f431d53e..50957b6112fef 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -1071,7 +1071,7 @@ SourceFile *CompilerInstance::createSourceFileForMainModule( } void CompilerInstance::performParseOnly(bool EvaluateConditionals, - bool ParseDelayedBodyOnEnd) { + bool CanDelayBodies) { const InputFileKind Kind = Invocation.getInputKind(); ModuleDecl *const MainModule = getMainModule(); Context->LoadedModules[MainModule->getName()] = MainModule; @@ -1093,12 +1093,8 @@ void CompilerInstance::performParseOnly(bool EvaluateConditionals, } PersistentState = llvm::make_unique(); - - SWIFT_DEFER { - if (ParseDelayedBodyOnEnd) - PersistentState->parseAllDelayedDeclLists(); - }; PersistentState->PerformConditionEvaluation = EvaluateConditionals; + // Parse all the library files. for (auto BufferID : InputSourceCodeBufferIDs) { if (BufferID == MainBufferID) @@ -1111,7 +1107,7 @@ void CompilerInstance::performParseOnly(bool EvaluateConditionals, BufferID); parseIntoSourceFileFull(*NextInput, BufferID, PersistentState.get(), - /*DelayBodyParsing=*/!IsPrimary); + /*DelayBodyParsing=*/!IsPrimary && CanDelayBodies); } // Now parse the main file. diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 9ef1a539aa56e..5ae5755ed69e1 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1262,11 +1262,12 @@ static bool performCompile(CompilerInstance &Instance, return compileLLVMIR(Invocation, Instance, Stats); if (FrontendOptions::shouldActionOnlyParse(Action)) { - bool ParseDelayedDeclListsOnEnd = - Action == FrontendOptions::ActionType::DumpParse; + // Disable delayed parsing of type and function bodies when we've been + // asked to dump the resulting AST. + bool CanDelayBodies = Action != FrontendOptions::ActionType::DumpParse; Instance.performParseOnly(/*EvaluateConditionals*/ Action == FrontendOptions::ActionType::EmitImportedModules, - ParseDelayedDeclListsOnEnd); + CanDelayBodies); } else if (Action == FrontendOptions::ActionType::ResolveImports) { Instance.performParseAndResolveImportsOnly(); } else { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 599c7c6bab428..471f0f13876ea 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -4441,8 +4441,6 @@ bool Parser::delayParsingDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, RBLoc = Tok.getLoc(); error = true; } - - State->delayDeclList(IDC); return error; } diff --git a/lib/Parse/PersistentParserState.cpp b/lib/Parse/PersistentParserState.cpp index 4c758ad055421..4e4d15e4dd072 100644 --- a/lib/Parse/PersistentParserState.cpp +++ b/lib/Parse/PersistentParserState.cpp @@ -50,12 +50,3 @@ void PersistentParserState::restoreCodeCompletionDelayedDeclState( ScopeInfo.saveCurrentScope(), other.StartOffset, other.EndOffset, other.PrevOffset)); } - -void PersistentParserState::delayDeclList(IterableDeclContext *D) { - DelayedDeclLists.push_back(D); -} - -void PersistentParserState::parseAllDelayedDeclLists() { - for (auto IDC : DelayedDeclLists) - IDC->loadAllMembers(); -} From 024e346f77f93423c85d9e20cb1428f99bb7bdea Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Wed, 8 Jan 2020 10:18:57 -0800 Subject: [PATCH 329/478] [Frontend] Enable delayed body parsing for a non-primary main This better matches the behavior of `parseAndTypeCheckMainFileUpTo`. --- lib/Frontend/Frontend.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 50957b6112fef..a54f2852b7deb 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -1095,19 +1095,25 @@ void CompilerInstance::performParseOnly(bool EvaluateConditionals, PersistentState = llvm::make_unique(); PersistentState->PerformConditionEvaluation = EvaluateConditionals; + auto shouldDelayBodies = [&](unsigned bufferID) -> bool { + if (!CanDelayBodies) + return false; + + // Don't delay bodies in whole module mode or for primary files. + return !(isWholeModuleCompilation() || isPrimaryInput(bufferID)); + }; + // Parse all the library files. for (auto BufferID : InputSourceCodeBufferIDs) { if (BufferID == MainBufferID) continue; - auto IsPrimary = isWholeModuleCompilation() || isPrimaryInput(BufferID); - SourceFile *NextInput = createSourceFileForMainModule( SourceFileKind::Library, SourceFile::ImplicitModuleImportKind::None, BufferID); parseIntoSourceFileFull(*NextInput, BufferID, PersistentState.get(), - /*DelayBodyParsing=*/!IsPrimary && CanDelayBodies); + shouldDelayBodies(BufferID)); } // Now parse the main file. @@ -1115,10 +1121,10 @@ void CompilerInstance::performParseOnly(bool EvaluateConditionals, SourceFile &MainFile = MainModule->getMainSourceFile(Invocation.getSourceFileKind()); MainFile.SyntaxParsingCache = Invocation.getMainFileSyntaxParsingCache(); + assert(MainBufferID == MainFile.getBufferID()); - parseIntoSourceFileFull(MainFile, MainFile.getBufferID().getValue(), - PersistentState.get(), - /*DelayBodyParsing=*/false); + parseIntoSourceFileFull(MainFile, MainBufferID, PersistentState.get(), + shouldDelayBodies(MainBufferID)); } assert(Context->LoadedModules.size() == 1 && From 86cc73d27809d4f793dc54258aeebc8811eeee29 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Wed, 8 Jan 2020 11:31:30 -0800 Subject: [PATCH 330/478] [Frontend] NFC: Remove an outdated comment `performParseOnly` has been able to handle multiple input files for a while now. --- include/swift/Frontend/Frontend.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 3b7847178ada2..a77186a52cba8 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -607,14 +607,13 @@ class CompilerInstance { void performSema(); /// Parses the input file but does no type-checking or module imports. - /// Note that this only supports parsing an invocation with a single file. void performParseOnly(bool EvaluateConditionals = false, bool CanDelayBodies = true); /// Parses and performs name binding on all input files. /// - /// Like a parse-only invocation, a single file is required. Unlike a - /// parse-only invocation, module imports will be processed. + /// This is similar to a parse-only invocation, but module imports will also + /// be processed. void performParseAndResolveImportsOnly(); /// Performs mandatory, diagnostic, and optimization passes over the SIL. From 9b9760eb4c3c3be2820b4a428d5b4bfcd06ad7e5 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Jan 2020 15:17:08 -0800 Subject: [PATCH 331/478] [silgen] Change silgen destructor to access ref_element_addr through begin_access [deinit]. --- lib/SILGen/SILGenDestructor.cpp | 4 ++++ test/SILGen/ivar_destroyer.swift | 4 +++- test/SILGen/lifetime.swift | 36 +++++++++++++++++++++----------- test/SILGen/objc_dealloc.swift | 4 +++- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 3041fb58404ee..357bf94625f1f 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -186,7 +186,11 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, SILValue addr = B.createRefElementAddr(cleanupLoc, selfValue.getValue(), vd, ti.getLoweredType().getAddressType()); + addr = B.createBeginAccess( + cleanupLoc, addr, SILAccessKind::Deinit, SILAccessEnforcement::Static, + false /*noNestedConflict*/, false /*fromBuiltin*/); B.createDestroyAddr(cleanupLoc, addr); + B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); } } } diff --git a/test/SILGen/ivar_destroyer.swift b/test/SILGen/ivar_destroyer.swift index ed49bda2c33f8..8e0846e2dcbc8 100644 --- a/test/SILGen/ivar_destroyer.swift +++ b/test/SILGen/ivar_destroyer.swift @@ -29,7 +29,9 @@ class DerivedClassWithNonTrivialProperties : RootClassWithoutProperties { // CHECK: bb0(%0 : @guaranteed $DerivedClassWithNonTrivialProperties): // CHECK-NEXT: debug_value %0 // CHECK-NEXT: [[Z_ADDR:%.*]] = ref_element_addr %0 -// CHECK-NEXT: destroy_addr [[Z_ADDR]] +// CHECK-NEXT: [[Z_ADDR_DEINIT_ACCESS:%.*]] = begin_access [deinit] [static] [[Z_ADDR]] +// CHECK-NEXT: destroy_addr [[Z_ADDR_DEINIT_ACCESS]] +// CHECK-NEXT: end_access [[Z_ADDR_DEINIT_ACCESS]] // CHECK-NEXT: [[RESULT:%.*]] = tuple () // CHECK-NEXT: return [[RESULT]] diff --git a/test/SILGen/lifetime.swift b/test/SILGen/lifetime.swift index d0e433bb92247..e797724c3620f 100644 --- a/test/SILGen/lifetime.swift +++ b/test/SILGen/lifetime.swift @@ -263,7 +263,7 @@ struct Daleth { } class He { - + // -- default allocator: // CHECK-LABEL: sil hidden [exact_self_class] [ossa] @$s8lifetime2HeC{{[_0-9a-zA-Z]*}}fC : $@convention(method) (@thick He.Type) -> @owned He { // CHECK: bb0({{%.*}} : $@thick He.Type): @@ -292,7 +292,7 @@ struct Waw { var b:Val // -- loadable value initializer with tuple destructuring: - // CHECK-LABEL: sil hidden [ossa] @$s8lifetime3WawV{{[_0-9a-zA-Z]*}}fC : $@convention(method) (@owned Ref, Val, Val, @thin Waw.Type) -> @owned Waw + // CHECK-LABEL: sil hidden [ossa] @$s8lifetime3WawV{{[_0-9a-zA-Z]*}}fC : $@convention(method) (@owned Ref, Val, Val, @thin Waw.Type) -> @owned Waw // CHECK: bb0([[A0:%.*]] : @owned $Ref, [[A1:%.*]] : $Val, [[B:%.*]] : $Val, {{%.*}} : $@thin Waw.Type): // CHECK-NEXT: [[A:%.*]] = tuple ([[A0]] : {{.*}}, [[A1]] : {{.*}}) // CHECK-NEXT: [[RET:%.*]] = struct $Waw ([[A]] : {{.*}}, [[B]] : {{.*}}) @@ -513,7 +513,7 @@ class Foo { x = chi.intify() } - + // CHECK-LABEL: sil hidden [ossa] @$s8lifetime3FooCfd : $@convention(method) (@guaranteed Foo) -> @owned Builtin.NativeObject deinit { @@ -526,13 +526,19 @@ class Foo { // CHECK-NOT: ref_element_addr [[THIS]] : {{.*}}, #Foo.x // -- destroy_value y // CHECK: [[YADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #Foo.y - // CHECK: destroy_addr [[YADDR]] + // CHECK: [[YADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[YADDR]] + // CHECK: destroy_addr [[YADDR_ACCESS]] + // CHECK: end_access [[YADDR_ACCESS]] // -- destroy_value z // CHECK: [[ZADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #Foo.z - // CHECK: destroy_addr [[ZADDR]] + // CHECK: [[ZADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[ZADDR]] + // CHECK: destroy_addr [[ZADDR_ACCESS]] + // CHECK: end_access [[ZADDR_ACCESS]] // -- destroy_value w // CHECK: [[WADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #Foo.w - // CHECK: destroy_addr [[WADDR]] + // CHECK: [[WADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[WADDR]] + // CHECK: destroy_addr [[WADDR_ACCESS]] + // CHECK: end_access [[WADDR_ACCESS]] // -- return back this // CHECK: [[PTR:%.*]] = unchecked_ref_cast [[THIS]] : $Foo to $Builtin.NativeObject // CHECK: [[PTR_OWNED:%.*]] = unchecked_ownership_conversion [[PTR]] : $Builtin.NativeObject, @guaranteed to @owned @@ -567,7 +573,7 @@ class FooSubclass : Foo { // CHECK: [[BORROWED_PTR:%.*]] = begin_borrow [[PTR]] // CHECK: end_borrow [[BORROWED_PTR]] // CHECK: return [[PTR]] - + deinit { bar() @@ -586,18 +592,22 @@ class ImplicitDtor { // CHECK-NOT: ref_element_addr [[THIS]] : {{.*}}, #ImplicitDtor.x // -- destroy_value y // CHECK: [[YADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #ImplicitDtor.y - // CHECK: destroy_addr [[YADDR]] + // CHECK: [[YADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[YADDR]] + // CHECK: destroy_addr [[YADDR_ACCESS]] + // CHECK: end_access [[YADDR_ACCESS]] // -- destroy_value w // CHECK: [[WADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #ImplicitDtor.w - // CHECK: destroy_addr [[WADDR]] + // CHECK: [[WADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[WADDR]] + // CHECK: destroy_addr [[WADDR_ACCESS]] + // CHECK: end_access [[WADDR_ACCESS]] // CHECK: return } class ImplicitDtorDerived : ImplicitDtor { var z:T - init(z : T) { - super.init() + init(z : T) { + super.init() self.z = z } @@ -611,7 +621,9 @@ class ImplicitDtorDerived : ImplicitDtor { // CHECK: [[BORROWED_PTR:%.*]] = begin_borrow [[PTR]] // CHECK: [[CAST_BORROWED_PTR:%.*]] = unchecked_ref_cast [[BORROWED_PTR]] : $Builtin.NativeObject to $ImplicitDtorDerived // CHECK: [[ZADDR:%[0-9]+]] = ref_element_addr [[CAST_BORROWED_PTR]] : {{.*}}, #ImplicitDtorDerived.z - // CHECK: destroy_addr [[ZADDR]] + // CHECK: [[ZADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[ZADDR]] + // CHECK: destroy_addr [[ZADDR_ACCESS]] + // CHECK: end_access [[ZADDR_ACCESS]] // CHECK: end_borrow [[BORROWED_PTR]] // -- epilog // CHECK-NOT: unchecked_ref_cast diff --git a/test/SILGen/objc_dealloc.swift b/test/SILGen/objc_dealloc.swift index d9a601e1d5ce5..8c45c5dc68756 100644 --- a/test/SILGen/objc_dealloc.swift +++ b/test/SILGen/objc_dealloc.swift @@ -82,7 +82,9 @@ class SwiftGizmo : Gizmo { // CHECK-NEXT: debug_value [[SELF]] : $SwiftGizmo, let, name "self" // CHECK-NEXT: [[SELF_BORROW:%.*]] = begin_borrow [[SELF]] // CHECK-NEXT: [[X:%[0-9]+]] = ref_element_addr [[SELF_BORROW]] : $SwiftGizmo, #SwiftGizmo.x - // CHECK-NEXT: destroy_addr [[X]] : $*X + // CHECK-NEXT: [[X_ACCESS:%.*]] = begin_access [deinit] [static] [[X]] + // CHECK-NEXT: destroy_addr [[X_ACCESS]] + // CHECK-NEXT: end_access [[X_ACCESS]] // CHECK-NEXT: end_borrow [[SELF_BORROW]] // CHECK-NEXT: [[RESULT:%[0-9]+]] = tuple () // CHECK-NEXT: return [[RESULT]] : $() From 7e056671a3af688296abc402e2a6fc9e13f9a0b4 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 8 Jan 2020 12:51:19 -0800 Subject: [PATCH 332/478] [CSGen] Abstract checking for a nil literal in visitSubscriptExpr into a general isValidBaseOfMemberRef function. --- lib/Sema/CSGen.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index c57605fbf3fdd..ceff6a18616b3 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -930,6 +930,16 @@ namespace { = { nullptr, nullptr }; unsigned currentEditorPlaceholderVariable = 0; + /// Returns false and emits the specified diagnostic if the member reference + /// base is a nil literal. Returns true otherwise. + bool isValidBaseOfMemberRef(Expr *base, Diag<> diagnostic) { + if (auto nilLiteral = dyn_cast(base)) { + CS.getASTContext().Diags.diagnose(nilLiteral->getLoc(), diagnostic); + return false; + } + return true; + } + /// Add constraints for a reference to a named member of the given /// base type, and return the type of such a reference. Type addMemberRefConstraints(Expr *expr, Expr *base, DeclNameRef name, @@ -1785,7 +1795,6 @@ namespace { } Type visitSubscriptExpr(SubscriptExpr *expr) { - auto &ctx = CS.getASTContext(); ValueDecl *decl = nullptr; if (expr->hasDecl()) { decl = expr->getDecl().getDecl(); @@ -1793,13 +1802,11 @@ namespace { return Type(); } - if (auto nilLiteral = dyn_cast(expr->getBase())) { - ctx.Diags.diagnose(nilLiteral->getLoc(), - diag::cannot_subscript_nil_literal); + auto *base = expr->getBase(); + if (!isValidBaseOfMemberRef(base, diag::cannot_subscript_nil_literal)) return nullptr; - } - return addSubscriptConstraints(expr, CS.getType(expr->getBase()), + return addSubscriptConstraints(expr, CS.getType(base), expr->getIndex(), decl, expr->getArgumentLabels(), expr->hasTrailingClosure()); From 7e829d74376cc6a9af451e3d3b4e86fdebf510f6 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 2 Jan 2020 16:55:06 +0000 Subject: [PATCH 333/478] NFC: Clean up an unnecessary switch --- lib/AST/Type.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 31f898b14addc..00486319c22b1 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -848,19 +848,14 @@ ParameterListInfo::ParameterListInfo( return; } - switch (params.size()) { - case 0: + if (params.empty()) return; - default: - // Arguments and parameters are not guaranteed to always line-up - // perfectly, e.g. failure diagnostics tries to match argument type - // to different "candidate" parameters. - if (params.size() != paramList->size()) - return; - - break; - } + // Arguments and parameters are not guaranteed to always line-up + // perfectly, e.g. failure diagnostics tries to match argument type + // to different "candidate" parameters. + if (params.size() != paramList->size()) + return; // Note which parameters have default arguments and/or function builders. for (auto i : range(0, params.size())) { From c6b6e72aacb19200e150ddc98006d6ecd9331184 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 2 Jan 2020 17:02:31 +0000 Subject: [PATCH 334/478] [CS] Tighten up assertions for getCalleeDeclAndArgs Assert that we have a correct locator and that we recorded argument information for it, rather than returning no information. In addition, always return the callee locator, even if there is no callee. --- lib/Sema/CSSimplify.cpp | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 01d2adacc417b..85cd23ebccf9e 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -770,45 +770,25 @@ static std::tuple, bool, ConstraintLocator *> getCalleeDeclAndArgs(ConstraintSystem &cs, ConstraintLocatorBuilder callBuilder) { - auto formUnknownCallee = - []() -> std::tuple, bool, - ConstraintLocator *> { - return std::make_tuple(/*decl*/ nullptr, /*hasAppliedSelf*/ false, - /*argLabels*/ ArrayRef(), - /*hasTrailingClosure*/ false, - /*calleeLocator*/ nullptr); - }; - auto *callLocator = cs.getConstraintLocator(callBuilder); - auto *callExpr = callLocator->getAnchor(); - - // Break down the call. - if (!callExpr) - return formUnknownCallee(); - - // Our remaining path can only be 'ApplyArgument'. - auto path = callLocator->getPath(); - if (!path.empty() && !path.back().is()) - return formUnknownCallee(); + assert(callLocator->isLastElement()); - // Dig out the callee information. + // Dig out the argument information. auto argInfo = cs.getArgumentInfo(callLocator); - if (!argInfo) - return formUnknownCallee(); + assert(argInfo); auto argLabels = argInfo->Labels; auto hasTrailingClosure = argInfo->HasTrailingClosure; - auto calleeLocator = cs.getCalleeLocator(callLocator); // Find the overload choice corresponding to the callee locator. + auto *calleeLocator = cs.getCalleeLocator(callLocator); auto selectedOverload = cs.findSelectedOverloadFor(calleeLocator); // If we didn't find any matching overloads, we're done. Just return the // argument info. if (!selectedOverload) return std::make_tuple(/*decl*/ nullptr, /*hasAppliedSelf*/ false, - argLabels, hasTrailingClosure, - /*calleeLocator*/ nullptr); + argLabels, hasTrailingClosure, calleeLocator); // Return the found declaration, assuming there is one. auto choice = selectedOverload->choice; From 1a8477aea147dfad14dda7c43bbd65c5ee62eae9 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 2 Jan 2020 17:04:48 +0000 Subject: [PATCH 335/478] [CS] NFC: Inline getCalleeDeclAndArgs --- lib/Sema/CSSimplify.cpp | 65 +++++++++++++---------------------------- 1 file changed, 20 insertions(+), 45 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 85cd23ebccf9e..2c67febd7ec37 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -764,38 +764,6 @@ matchCallArguments(SmallVectorImpl &args, return listener.relabelArguments(actualArgNames); } -/// Find the callee declaration and uncurry level for a given call -/// locator. -static std::tuple, bool, - ConstraintLocator *> -getCalleeDeclAndArgs(ConstraintSystem &cs, - ConstraintLocatorBuilder callBuilder) { - auto *callLocator = cs.getConstraintLocator(callBuilder); - assert(callLocator->isLastElement()); - - // Dig out the argument information. - auto argInfo = cs.getArgumentInfo(callLocator); - assert(argInfo); - - auto argLabels = argInfo->Labels; - auto hasTrailingClosure = argInfo->HasTrailingClosure; - - // Find the overload choice corresponding to the callee locator. - auto *calleeLocator = cs.getCalleeLocator(callLocator); - auto selectedOverload = cs.findSelectedOverloadFor(calleeLocator); - - // If we didn't find any matching overloads, we're done. Just return the - // argument info. - if (!selectedOverload) - return std::make_tuple(/*decl*/ nullptr, /*hasAppliedSelf*/ false, - argLabels, hasTrailingClosure, calleeLocator); - - // Return the found declaration, assuming there is one. - auto choice = selectedOverload->choice; - return std::make_tuple(choice.getDeclOrNull(), hasAppliedSelf(cs, choice), - argLabels, hasTrailingClosure, calleeLocator); -} - class ArgumentFailureTracker : public MatchCallArgumentListener { ConstraintSystem &CS; SmallVectorImpl &Arguments; @@ -982,22 +950,29 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( ArrayRef args, ArrayRef params, ConstraintKind subKind, ConstraintLocatorBuilder locator) { - // Extract the parameters. - ValueDecl *callee; - bool hasAppliedSelf; - ArrayRef argLabels; - bool hasTrailingClosure = false; - ConstraintLocator *calleeLocator; - std::tie(callee, hasAppliedSelf, argLabels, hasTrailingClosure, - calleeLocator) = - getCalleeDeclAndArgs(cs, locator); - - ParameterListInfo paramInfo(params, callee, hasAppliedSelf); + auto *loc = cs.getConstraintLocator(locator); + assert(loc->isLastElement()); + + ValueDecl *callee = nullptr; + bool appliedSelf = false; + + // Resolve the callee for the application. + auto *calleeLocator = cs.getCalleeLocator(loc); + if (auto overload = cs.findSelectedOverloadFor(calleeLocator)) { + callee = overload->choice.getDeclOrNull(); + appliedSelf = hasAppliedSelf(cs, overload->choice); + } + + ParameterListInfo paramInfo(params, callee, appliedSelf); + + // Dig out the argument information. + auto argInfo = cs.getArgumentInfo(loc); + assert(argInfo); // Apply labels to arguments. SmallVector argsWithLabels; argsWithLabels.append(args.begin(), args.end()); - AnyFunctionType::relabelParams(argsWithLabels, argLabels); + AnyFunctionType::relabelParams(argsWithLabels, argInfo->Labels); // Special case when a single tuple argument if used // instead of N distinct arguments e.g.: @@ -1039,7 +1014,7 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( ArgumentFailureTracker listener(cs, argsWithLabels, params, parameterBindings, locator); if (constraints::matchCallArguments( - argsWithLabels, params, paramInfo, hasTrailingClosure, + argsWithLabels, params, paramInfo, argInfo->HasTrailingClosure, cs.shouldAttemptFixes(), listener, parameterBindings)) return cs.getTypeMatchFailure(locator); From b4cf1afd19c3510c17ed561073baf4584f7d9024 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Jan 2020 15:19:55 -0800 Subject: [PATCH 336/478] [di] When emitting element addresses to destroy fields, use begin_access [deinit]. --- .../Mandatory/DIMemoryUseCollector.cpp | 19 ++++---- .../Mandatory/DIMemoryUseCollector.h | 6 ++- .../Mandatory/DefiniteInitialization.cpp | 48 ++++++++++++++----- ...nite_init_failable_initializers_objc.swift | 4 +- ...ite_init_markuninitialized_derivedself.sil | 4 +- ...finite_init_markuninitialized_rootself.sil | 4 +- .../di-conditional-destroy-scope.swift | 5 +- 7 files changed, 62 insertions(+), 28 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 125820a51d063..76dc72b8b7c98 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -212,11 +212,11 @@ SILType DIMemoryObjectInfo::getElementType(unsigned EltNo) const { Module, MemorySILType, EltNo, isNonDelegatingInit()); } -/// computeTupleElementAddress - Given a tuple element number (in the flattened -/// sense) return a pointer to a leaf element of the specified number. -SILValue DIMemoryObjectInfo::emitElementAddress( +/// Given a tuple element number (in the flattened sense) return a pointer to a +/// leaf element of the specified number, so we can insert destroys for it. +SILValue DIMemoryObjectInfo::emitElementAddressForDestroy( unsigned EltNo, SILLocation Loc, SILBuilder &B, - llvm::SmallVectorImpl> &EndBorrowList) const { + SmallVectorImpl> &EndScopeList) const { SILValue Ptr = getUninitializedValue(); bool IsSelf = isNonDelegatingInit(); auto &Module = MemoryInst->getModule(); @@ -260,7 +260,7 @@ SILValue DIMemoryObjectInfo::emitElementAddress( if (isa(NTD) && Ptr->getType().isAddress()) { SILValue Original = Ptr; SILValue Borrowed = Ptr = B.createLoadBorrow(Loc, Ptr); - EndBorrowList.emplace_back(Borrowed, Original); + EndScopeList.emplace_back(Borrowed, EndScopeKind::Borrow); } } auto expansionContext = TypeExpansionContext(B.getFunction()); @@ -277,12 +277,13 @@ SILValue DIMemoryObjectInfo::emitElementAddress( if (Ptr.getOwnershipKind() != ValueOwnershipKind::Guaranteed) { Original = Ptr; Borrowed = Ptr = B.createBeginBorrow(Loc, Ptr); + EndScopeList.emplace_back(Borrowed, EndScopeKind::Borrow); } Ptr = B.createRefElementAddr(Loc, Ptr, VD); - if (Original) { - assert(Borrowed); - EndBorrowList.emplace_back(Borrowed, Original); - } + Ptr = B.createBeginAccess( + Loc, Ptr, SILAccessKind::Deinit, SILAccessEnforcement::Static, + false /*noNestedConflict*/, false /*fromBuiltin*/); + EndScopeList.emplace_back(Ptr, EndScopeKind::Access); } PointeeType = FieldType; diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index ae97da306a2a3..66a878cc5717f 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -212,11 +212,13 @@ class DIMemoryObjectInfo { return MemoryInst->isDelegatingSelfAllocated(); } + enum class EndScopeKind { Borrow, Access }; + /// Given an element number (in the flattened sense) return a pointer to a /// leaf element of the specified number. - SILValue emitElementAddress( + SILValue emitElementAddressForDestroy( unsigned TupleEltNo, SILLocation Loc, SILBuilder &B, - SmallVectorImpl> &EndBorrowList) const; + SmallVectorImpl> &EndScopeList) const; /// Return the swift type of the specified element. SILType getElementType(unsigned EltNo) const; diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 3496285babc51..1574b966d0e12 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -2303,14 +2303,26 @@ SILValue LifetimeChecker::handleConditionalInitAssign() { B.setInsertionPoint(TrueBB->begin()); SILValue EltPtr; { - llvm::SmallVector, 4> EndBorrowList; - EltPtr = TheMemory.emitElementAddress(Elt, Loc, B, EndBorrowList); + using EndScopeKind = DIMemoryObjectInfo::EndScopeKind; + SmallVector, 4> EndScopeList; + EltPtr = + TheMemory.emitElementAddressForDestroy(Elt, Loc, B, EndScopeList); if (auto *DA = B.emitDestroyAddrAndFold(Loc, EltPtr)) Destroys.push_back(DA); - while (!EndBorrowList.empty()) { - SILValue Borrowed, Original; - std::tie(Borrowed, Original) = EndBorrowList.pop_back_val(); - B.createEndBorrow(Loc, Borrowed, Original); + while (!EndScopeList.empty()) { + SILValue value; + EndScopeKind kind; + std::tie(value, kind) = EndScopeList.pop_back_val(); + + switch (kind) { + case EndScopeKind::Borrow: + B.createEndBorrow(Loc, value); + continue; + case EndScopeKind::Access: + B.createEndAccess(Loc, value, false /*can abort*/); + continue; + } + llvm_unreachable("Covered switch isn't covered!"); } } B.setInsertionPoint(ContBB->begin()); @@ -2364,15 +2376,27 @@ handleConditionalDestroys(SILValue ControlVariableAddr) { // Utilities. auto destroyMemoryElement = [&](SILLocation Loc, unsigned Elt) { - llvm::SmallVector, 4> EndBorrowList; + using EndScopeKind = DIMemoryObjectInfo::EndScopeKind; + SmallVector, 4> EndScopeList; SILValue EltPtr = - TheMemory.emitElementAddress(Elt, Loc, B, EndBorrowList); + TheMemory.emitElementAddressForDestroy(Elt, Loc, B, EndScopeList); if (auto *DA = B.emitDestroyAddrAndFold(Loc, EltPtr)) Destroys.push_back(DA); - while (!EndBorrowList.empty()) { - SILValue Borrowed, Original; - std::tie(Borrowed, Original) = EndBorrowList.pop_back_val(); - B.createEndBorrow(Loc, Borrowed, Original); + + while (!EndScopeList.empty()) { + SILValue value; + EndScopeKind kind; + std::tie(value, kind) = EndScopeList.pop_back_val(); + + switch (kind) { + case EndScopeKind::Borrow: + B.createEndBorrow(Loc, value); + continue; + case EndScopeKind::Access: + B.createEndAccess(Loc, value, false /*can abort*/); + continue; + } + llvm_unreachable("Covered switch isn't covered!"); } }; diff --git a/test/SILOptimizer/definite_init_failable_initializers_objc.swift b/test/SILOptimizer/definite_init_failable_initializers_objc.swift index f6b40025e5cb9..f1be59b403c2d 100644 --- a/test/SILOptimizer/definite_init_failable_initializers_objc.swift +++ b/test/SILOptimizer/definite_init_failable_initializers_objc.swift @@ -49,7 +49,9 @@ class Cat : FakeNSObject { // CHECK: bb1: // CHECK-NEXT: [[FIELD_ADDR:%.*]] = ref_element_addr [[ARG2]] : $Cat, #Cat.x - // CHECK-NEXT: destroy_addr [[FIELD_ADDR]] : $*LifetimeTracked + // CHECK-NEXT: [[FIELD_ADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[FIELD_ADDR]] + // CHECK-NEXT: destroy_addr [[FIELD_ADDR_ACCESS]] : $*LifetimeTracked + // CHECK-NEXT: end_access [[FIELD_ADDR_ACCESS]] // CHECK-NEXT: strong_release [[ARG2]] // CHECK-NEXT: [[RELOAD_FROM_BOX:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thick Cat.Type diff --git a/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil b/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil index 7b7db31d6dc24..8ab4fc33ec85b 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil @@ -195,7 +195,9 @@ bb0(%0 : @owned $DerivedClassWithNontrivialStoredProperties): // Now we destroy the stored value. // CHECK: [[SELF:%[0-9]+]] = load_borrow [[SELFBOX]] // CHECK: [[SELF_FIELD:%.*]] = ref_element_addr [[SELF]] -// CHECK: destroy_addr [[SELF_FIELD]] +// CHECK: [[SELF_FIELD_ACCESS:%.*]] = begin_access [deinit] [static] [[SELF_FIELD]] +// CHECK: destroy_addr [[SELF_FIELD_ACCESS]] +// CHECK: end_access [[SELF_FIELD_ACCESS]] // CHECK: end_borrow [[SELF]] // // And then perform dealloc_partial_ref. diff --git a/test/SILOptimizer/definite_init_markuninitialized_rootself.sil b/test/SILOptimizer/definite_init_markuninitialized_rootself.sil index d9346af18cefd..ffc00fc2f1dfe 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_rootself.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_rootself.sil @@ -109,7 +109,9 @@ bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): // CHECK: end_borrow [[BORROWED_SELF]] // CHECK: [[BORROWED_SELF:%.*]] = begin_borrow [[SELF]] // CHECK: [[SELF_FIELD:%.*]] = ref_element_addr [[BORROWED_SELF]] -// CHECK: destroy_addr [[SELF_FIELD]] +// CHECK: [[SELF_FIELD_ACCESS:%.*]] = begin_access [deinit] [static] [[SELF_FIELD]] +// CHECK: destroy_addr [[SELF_FIELD_ACCESS]] +// CHECK: end_access [[SELF_FIELD_ACCESS]] // CHECK: end_borrow [[BORROWED_SELF]] // CHECK: [[METATYPE:%[0-9]+]] = metatype $@thick RootClassWithNontrivialStoredProperties.Type // CHECK: dealloc_partial_ref [[SELF]] : $RootClassWithNontrivialStoredProperties, [[METATYPE]] : $@thick RootClassWithNontrivialStoredProperties.Type diff --git a/test/SILOptimizer/di-conditional-destroy-scope.swift b/test/SILOptimizer/di-conditional-destroy-scope.swift index 366c0d39399da..e87d8a7da9ed0 100644 --- a/test/SILOptimizer/di-conditional-destroy-scope.swift +++ b/test/SILOptimizer/di-conditional-destroy-scope.swift @@ -6,8 +6,9 @@ // REQUIRES: objc_interop -// CHECK: [[ADR:%.*]] = ref_element_addr %{{.*}} : $RecursibleDirectoryContentsGenerator, #RecursibleDirectoryContentsGenerator.fileSystem, loc {{.*}}:38:5, scope 2 -// CHECK: destroy_addr [[ADR]] : $*FileSystem, loc {{.*}}:38:5, scope 2 +// CHECK: [[ADR:%.*]] = ref_element_addr %{{.*}} : $RecursibleDirectoryContentsGenerator, #RecursibleDirectoryContentsGenerator.fileSystem, loc {{.*}}:39:5, scope 2 +// CHECK: [[ADR_ACCESS:%.*]] = begin_access [deinit] [static] [[ADR]] +// CHECK: destroy_addr [[ADR_ACCESS]] : $*FileSystem, loc {{.*}}:39:5, scope 2 import Foundation From d48cdd9cad4f1d1a0aea5225aca933e0e361032a Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 8 Jan 2020 15:21:22 -0800 Subject: [PATCH 337/478] [benchmark-dtrace] Set SWIFT_DETERMINISTIC_HASHING=1 before calling subjobs. This prevents a bunch of instability in the retain, release numbers. I am still getting some of it, but this helps a lot. --- benchmark/scripts/Benchmark_DTrace.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/benchmark/scripts/Benchmark_DTrace.in b/benchmark/scripts/Benchmark_DTrace.in index dc50118bbbcc6..5818ac218f0a2 100644 --- a/benchmark/scripts/Benchmark_DTrace.in +++ b/benchmark/scripts/Benchmark_DTrace.in @@ -86,11 +86,13 @@ class DTraceBenchmarkDriver(perf_test_driver.BenchmarkDriver): sys.stdout.flush() def get_results_with_iters(iters): + e = os.environ + e['SWIFT_DETERMINISTIC_HASHING'] = '1' p = subprocess.Popen([ 'sudo', 'dtrace', '-s', DTRACE_PATH, '-c', '%s %s %s' % (data['path'], data['test_name'], '--num-iters=%d' % iters) - ], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w')) + ], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w'), env=e) results = [x for x in p.communicate()[0].split("\n") if len(x) > 0] return [ x.split(',')[1] for x in From c7c2e6e17bc4874893e660d4e6a9388b8000884b Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 8 Jan 2020 15:22:27 -0800 Subject: [PATCH 338/478] [benchmark-dtrace] Fix the amount of samples taken along side the number of iters. Otherwise, the output is not stable. --- benchmark/scripts/Benchmark_DTrace.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/benchmark/scripts/Benchmark_DTrace.in b/benchmark/scripts/Benchmark_DTrace.in index 5818ac218f0a2..0f450793d4f2f 100644 --- a/benchmark/scripts/Benchmark_DTrace.in +++ b/benchmark/scripts/Benchmark_DTrace.in @@ -90,8 +90,9 @@ class DTraceBenchmarkDriver(perf_test_driver.BenchmarkDriver): e['SWIFT_DETERMINISTIC_HASHING'] = '1' p = subprocess.Popen([ 'sudo', 'dtrace', '-s', DTRACE_PATH, - '-c', '%s %s %s' % (data['path'], data['test_name'], - '--num-iters=%d' % iters) + '-c', '%s %s %s %s' % (data['path'], data['test_name'], + '--num-iters=%d' % iters, + '--num-samples=2') ], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w'), env=e) results = [x for x in p.communicate()[0].split("\n") if len(x) > 0] return [ From 6fff30c1221b109c363d21092ce63bef330a0aeb Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 8 Jan 2020 15:24:24 -0800 Subject: [PATCH 339/478] [benchmark-dtrace] Enabling multiprocessing option to speed up gathering data. --- benchmark/scripts/Benchmark_DTrace.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/scripts/Benchmark_DTrace.in b/benchmark/scripts/Benchmark_DTrace.in index 0f450793d4f2f..9b823008136dd 100644 --- a/benchmark/scripts/Benchmark_DTrace.in +++ b/benchmark/scripts/Benchmark_DTrace.in @@ -70,7 +70,7 @@ class DTraceBenchmarkDriver(perf_test_driver.BenchmarkDriver): def __init__(self, binary, xfail_list, csv_output): perf_test_driver.BenchmarkDriver.__init__( self, binary, xfail_list, - enable_parallel=False, + enable_parallel=True, opt_levels=['O']) self.csv_output = csv_output From ac24491dae498dac72589107ce0bd211472a1053 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 8 Jan 2020 16:19:44 -0800 Subject: [PATCH 340/478] [Constraint system] Generalize function builder application APIs. Teach the constraint system to handle matching a function builder to a function as well as a closure. --- include/swift/AST/AnyFunctionRef.h | 11 ++++ include/swift/AST/TypeCheckRequests.h | 5 +- include/swift/AST/TypeCheckerTypeIDZone.def | 2 +- lib/AST/Decl.cpp | 7 +++ lib/Sema/BuilderTransform.cpp | 57 +++++++++++---------- lib/Sema/CSSimplify.cpp | 8 +-- lib/Sema/ConstraintSystem.h | 6 +-- 7 files changed, 59 insertions(+), 37 deletions(-) diff --git a/include/swift/AST/AnyFunctionRef.h b/include/swift/AST/AnyFunctionRef.h index 73f99f0f775cc..bc57d7ae7ba8b 100644 --- a/include/swift/AST/AnyFunctionRef.h +++ b/include/swift/AST/AnyFunctionRef.h @@ -216,6 +216,15 @@ class AnyFunctionRef { return lhs.TheFunction != rhs.TheFunction; } + friend llvm::hash_code hash_value(AnyFunctionRef fn) { + using llvm::hash_value; + return hash_value(fn.TheFunction.getOpaqueValue()); + } + + friend SourceLoc extractNearestSourceLoc(AnyFunctionRef fn) { + return fn.getLoc(); + } + private: ArrayRef getYieldResultsImpl(SmallVectorImpl &buffer, @@ -243,6 +252,8 @@ class AnyFunctionRef { #pragma warning(pop) #endif +void simple_display(llvm::raw_ostream &out, AnyFunctionRef fn); + } // namespace swift namespace llvm { diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 05391c166363e..2eb562c497958 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -16,6 +16,7 @@ #ifndef SWIFT_TYPE_CHECK_REQUESTS_H #define SWIFT_TYPE_CHECK_REQUESTS_H +#include "swift/AST/AnyFunctionRef.h" #include "swift/AST/ASTTypeIDs.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Type.h" @@ -1760,7 +1761,7 @@ enum class FunctionBuilderClosurePreCheck : uint8_t { class PreCheckFunctionBuilderRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -1770,7 +1771,7 @@ class PreCheckFunctionBuilderRequest // Evaluation. llvm::Expected - evaluate(Evaluator &evaluator, ClosureExpr *closure) const; + evaluate(Evaluator &evaluator, AnyFunctionRef fn) const; public: // Separate caching. diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 4df7737c807ee..a5e4df61a61cb 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -196,7 +196,7 @@ SWIFT_REQUEST(TypeChecker, HasUserDefinedDesignatedInitRequest, SWIFT_REQUEST(TypeChecker, HasMemberwiseInitRequest, bool(StructDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, PreCheckFunctionBuilderRequest, - FunctionBuilderClosurePreCheck(ClosureExpr *), + FunctionBuilderClosurePreCheck(AnyFunctionRef), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest, bool(NominalTypeDecl *, ImplicitMemberAction), Uncached, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a6b9f36b26e88..55ba01f4027f1 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7907,3 +7907,10 @@ void ParseAbstractFunctionBodyRequest::cacheResult(BraceStmt *value) const { } } + +void swift::simple_display(llvm::raw_ostream &out, AnyFunctionRef fn) { + if (auto func = fn.getAbstractFunctionDecl()) + simple_display(out, func); + else + out << "closure"; +} diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 9f655c89cfa26..fdeec6c1c041b 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -510,21 +510,23 @@ TypeChecker::applyFunctionBuilderBodyTransform(FuncDecl *FD, body->getRBraceLoc()); } -ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( - ClosureExpr *closure, Type builderType, ConstraintLocator *calleeLocator, - ConstraintLocatorBuilder locator) { +ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder( + AnyFunctionRef fn, Type builderType, Type bodyResultType, + ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator) { auto builder = builderType->getAnyNominal(); assert(builder && "Bad function builder type"); assert(builder->getAttrs().hasAttribute()); // FIXME: Right now, single-expression closures suppress the function // builder translation. - if (closure->hasSingleExpressionBody()) - return getTypeMatchSuccess(); + if (auto closure = fn.getAbstractClosureExpr()) { + if (closure->hasSingleExpressionBody()) + return getTypeMatchSuccess(); + } // Pre-check the closure body: pre-check any expressions in it and look // for return statements. - auto request = PreCheckFunctionBuilderRequest{closure}; + auto request = PreCheckFunctionBuilderRequest{fn}; switch (evaluateOrDefault(getASTContext().evaluator, request, FunctionBuilderClosurePreCheck::Error)) { case FunctionBuilderClosurePreCheck::Okay: @@ -547,7 +549,7 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( // Check whether we can apply this specific function builder. BuilderClosureVisitor visitor(getASTContext(), this, /*wantExpr=*/false, builderType); - (void)visitor.visit(closure->getBody()); + (void)visitor.visit(fn.getBody()); // If we saw a control-flow statement or declaration that the builder // cannot handle, we don't have a well-formed function builder application. @@ -585,17 +587,18 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( BuilderClosureVisitor visitor(getASTContext(), this, /*wantExpr=*/true, builderType); - Expr *singleExpr = visitor.visit(closure->getBody()); + Expr *singleExpr = visitor.visit(fn.getBody()); // We've already pre-checked all the original expressions, but do the // pre-check to the generated expression just to set up any preconditions // that CSGen might have. // // TODO: just build the AST the way we want it in the first place. - if (ConstraintSystem::preCheckExpression(singleExpr, closure)) + auto dc = fn.getAsDeclContext(); + if (ConstraintSystem::preCheckExpression(singleExpr, dc)) return getTypeMatchFailure(locator); - singleExpr = generateConstraints(singleExpr, closure); + singleExpr = generateConstraints(singleExpr, dc); if (!singleExpr) return getTypeMatchFailure(locator); @@ -607,18 +610,15 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( functionBuilderTransformed.begin(), functionBuilderTransformed.end(), [&](const std::pair &elt) { - return elt.first == closure; + return elt.first == fn; }) == functionBuilderTransformed.end() && "already transformed this closure along this path!?!"); functionBuilderTransformed.push_back( - std::make_pair(closure, + std::make_pair(fn, AppliedBuilderTransform{builderType, singleExpr})); - // Bind the result type of the closure to the type of the transformed - // expression. - Type closureType = getType(closure); - auto fnType = closureType->castTo(); - addConstraint(ConstraintKind::Equal, fnType->getResult(), transformedType, + // Bind the body result type to the type of the transformed expression. + addConstraint(ConstraintKind::Equal, bodyResultType, transformedType, locator); return getTypeMatchSuccess(); } @@ -626,22 +626,21 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( namespace { /// Pre-check all the expressions in the closure body. -class PreCheckFunctionBuilderClosure : public ASTWalker { - ClosureExpr *Closure; +class PreCheckFunctionBuilderApplication : public ASTWalker { + AnyFunctionRef Fn; bool HasReturnStmt = false; bool HasError = false; public: - PreCheckFunctionBuilderClosure(ClosureExpr *closure) - : Closure(closure) {} + PreCheckFunctionBuilderApplication(AnyFunctionRef fn) : Fn(fn) {} FunctionBuilderClosurePreCheck run() { - Stmt *oldBody = Closure->getBody(); + Stmt *oldBody = Fn.getBody(); Stmt *newBody = oldBody->walk(*this); // If the walk was aborted, it was because we had a problem of some kind. assert((newBody == nullptr) == (HasError || HasReturnStmt) && - "unexpected short-circuit while walking closure body"); + "unexpected short-circuit while walking body"); if (!newBody) { if (HasError) return FunctionBuilderClosurePreCheck::Error; @@ -658,7 +657,7 @@ class PreCheckFunctionBuilderClosure : public ASTWalker { // Pre-check the expression. If this fails, abort the walk immediately. // Otherwise, replace the expression with the result of pre-checking. // In either case, don't recurse into the expression. - if (ConstraintSystem::preCheckExpression(E, /*DC*/ Closure)) { + if (ConstraintSystem::preCheckExpression(E, /*DC*/ Fn.getAsDeclContext())) { HasError = true; return std::make_pair(false, nullptr); } @@ -682,10 +681,12 @@ class PreCheckFunctionBuilderClosure : public ASTWalker { llvm::Expected PreCheckFunctionBuilderRequest::evaluate(Evaluator &eval, - ClosureExpr *closure) const { + AnyFunctionRef fn) const { // Single-expression closures should already have been pre-checked. - if (closure->hasSingleExpressionBody()) - return FunctionBuilderClosurePreCheck::Okay; + if (auto closure = fn.getAbstractClosureExpr()) { + if (closure->hasSingleExpressionBody()) + return FunctionBuilderClosurePreCheck::Okay; + } - return PreCheckFunctionBuilderClosure(closure).run(); + return PreCheckFunctionBuilderApplication(fn).run(); } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 233af658c7670..2cfe967574bfd 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1145,9 +1145,11 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( = paramInfo.getFunctionBuilderType(paramIdx)) { Expr *arg = getArgumentExpr(locator.getAnchor(), argIdx); if (auto closure = dyn_cast_or_null(arg)) { - auto result = - cs.applyFunctionBuilder(closure, functionBuilderType, - calleeLocator, loc); + auto closureType = cs.getType(closure); + auto result = cs.matchFunctionBuilder( + closure, functionBuilderType, + closureType->castTo()->getResult(), + calleeLocator, loc); if (result.isFailure()) return result; } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 67ff1cb53f523..3d376d096a83e 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -3477,9 +3477,9 @@ class ConstraintSystem { void simplifyDisjunctionChoice(Constraint *choice); /// Apply the given function builder to the closure expression. - TypeMatchResult applyFunctionBuilder(ClosureExpr *closure, Type builderType, - ConstraintLocator *calleeLocator, - ConstraintLocatorBuilder locator); + TypeMatchResult matchFunctionBuilder( + AnyFunctionRef fn, Type builderType, Type bodyResultType, + ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator); private: /// The kind of bindings that are permitted. From 4c1e2c6ce196e7fc65db8f506ff27d11afda8684 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 8 Jan 2020 17:17:26 -0800 Subject: [PATCH 341/478] [Diagnostics] Add nullptr check for anchor while fixing function result failures --- lib/Sema/CSSimplify.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 01d2adacc417b..1ec063d7e4273 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3509,7 +3509,7 @@ bool ConstraintSystem::repairFailures( } } // Handle function result coerce expression wrong type conversion. - if (isa(anchor)) { + if (anchor && isa(anchor)) { auto *fix = ContextualMismatch::create(*this, lhs, rhs, loc); conversionsOrFixes.push_back(fix); From e388c4a3810d7d22f3e37b99c249f0ea080a70d1 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 8 Jan 2020 16:45:39 -0800 Subject: [PATCH 342/478] [CodeCompletion] Get associatedtype constraints from ProtocolDecl::getRequirementSignature() instead of AssociatedTypeDecl::getInherited() when checking if the return type should be suggested as "opaque result type" in override completion. AssociatedTypeDecl::getInherited() is not serialized. So if the protocol is declared in a module, it was never suggested as 'some' result. rdar://problem/57245073 --- lib/AST/ASTPrinter.cpp | 4 +- lib/IDE/CodeCompletion.cpp | 58 +++++++++++++++++++-------- test/IDE/complete_crossmodule.swift | 22 ++++++++++ test/IDE/complete_opaque_result.swift | 25 +++++++++++- 4 files changed, 88 insertions(+), 21 deletions(-) create mode 100644 test/IDE/complete_crossmodule.swift diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 99a0ca8c982a8..72b183fe88f1e 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3039,10 +3039,10 @@ void PrintAST::visitSubscriptDecl(SubscriptDecl *decl) { Printer << " -> "; TypeLoc elementTy = decl->getElementTypeLoc(); - Printer.printDeclResultTypePre(decl, elementTy); - Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType); if (!elementTy.getTypeRepr()) elementTy = TypeLoc::withoutLoc(decl->getElementInterfaceType()); + Printer.printDeclResultTypePre(decl, elementTy); + Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType); // HACK: When printing result types for subscripts with opaque result types, // always print them using the `some` keyword instead of printing diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 7d4d97af2075a..324b50d7555eb 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -4159,8 +4159,8 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { /// Return type if the result type if \p VD should be represented as opaque /// result type. - TypeLoc getOpaqueResultTypeLoc(const ValueDecl *VD, DeclVisibilityKind Reason, - DynamicLookupInfo dynamicLookupInfo) { + Type getOpaqueResultType(const ValueDecl *VD, DeclVisibilityKind Reason, + DynamicLookupInfo dynamicLookupInfo) { if (Reason != DeclVisibilityKind::MemberOfProtocolImplementedByCurrentNominal) return nullptr; @@ -4179,26 +4179,50 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { else return nullptr; - if (!ResultT->is()) - // The result is not associatedtype. - return nullptr; - - // If associatedtype doesn't have conformance/superclass constraint, we - // can't use opaque type. - auto assocTyD = ResultT->castTo()->getAssocType(); - if (!assocTyD->getInherited().size()) + if (!ResultT->is() || + !ResultT->castTo()->getAssocType()) + // The result is not a valid associatedtype. return nullptr; // Try substitution to see if the associated type is resolved to concrete // type. auto substMap = currTy->getMemberSubstitutionMap( CurrDeclContext->getParentModule(), VD); - ResultT = ResultT.subst(substMap); - if (!ResultT || !ResultT->is()) + if (!ResultT.subst(substMap)->is()) // If resolved print it. return nullptr; - return assocTyD->getInherited()[0]; + // Collect requirements on the associatedtype. + ProtocolDecl *protoD = + ResultT->castTo()->getAssocType()->getProtocol(); + + SmallVector opaqueTypes; + bool hasExplicitAnyObject = false; + for (auto req : protoD->getRequirementSignature()) { + if (!req.getFirstType()->isEqual(ResultT)) + continue; + + switch (req.getKind()) { + case RequirementKind::Conformance: + case RequirementKind::Superclass: + opaqueTypes.push_back(req.getSecondType()); + break; + case RequirementKind::Layout: + hasExplicitAnyObject |= req.getLayoutConstraint()->isClass(); + break; + case RequirementKind::SameType: + return nullptr; + } + } + + if (!hasExplicitAnyObject) { + if (opaqueTypes.empty()) + return nullptr; + if (opaqueTypes.size() == 1) + return opaqueTypes.front(); + } + return ProtocolCompositionType::get( + VD->getASTContext(), opaqueTypes, hasExplicitAnyObject); } void addValueOverride(const ValueDecl *VD, DeclVisibilityKind Reason, @@ -4206,14 +4230,14 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { CodeCompletionResultBuilder &Builder, bool hasDeclIntroducer) { class DeclPrinter : public StreamPrinter { - TypeLoc OpaqueBaseTy; + Type OpaqueBaseTy; public: using StreamPrinter::StreamPrinter; Optional NameOffset; - DeclPrinter(raw_ostream &OS, TypeLoc OpaqueBaseTy) + DeclPrinter(raw_ostream &OS, Type OpaqueBaseTy) : StreamPrinter(OS), OpaqueBaseTy(OpaqueBaseTy) {} void printDeclLoc(const Decl *D) override { @@ -4225,7 +4249,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { void printDeclResultTypePre(ValueDecl *VD, TypeLoc &TL) override { if (!OpaqueBaseTy.isNull()) { OS << "some "; - TL = OpaqueBaseTy; + TL = TypeLoc::withoutLoc(OpaqueBaseTy); } } }; @@ -4235,7 +4259,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { { llvm::raw_svector_ostream OS(DeclStr); DeclPrinter Printer( - OS, getOpaqueResultTypeLoc(VD, Reason, dynamicLookupInfo)); + OS, getOpaqueResultType(VD, Reason, dynamicLookupInfo)); PrintOptions Options; if (auto transformType = CurrDeclContext->getDeclaredTypeInContext()) Options.setBaseType(transformType); diff --git a/test/IDE/complete_crossmodule.swift b/test/IDE/complete_crossmodule.swift new file mode 100644 index 0000000000000..f52c8d238c20b --- /dev/null +++ b/test/IDE/complete_crossmodule.swift @@ -0,0 +1,22 @@ +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %target-swift-frontend -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Test.swift -I %t -code-completion-token=OPAQUE_RESULT | %FileCheck --check-prefix=OPAQUE_RESULT %s + +// BEGIN MyModule.swift + +public protocol HasAssocWithConstraint { + associatedtype AssocWithContraint: HasAssocWithConstraint + var value: AssocWithContraint { get } +} + +// BEGIN Test.swift +import MyModule + +struct MyValue: HasAssocWithConstraint { + var #^OPAQUE_RESULT^# +// OPAQUE_RESULT: Begin completions +// OPAQUE_RESULT-DAG: Decl[InstanceVar]/Super: value: some HasAssocWithConstraint; +// OPAQUE_RESULT: End completions +} diff --git a/test/IDE/complete_opaque_result.swift b/test/IDE/complete_opaque_result.swift index c1e221593d2f8..0adb1f178d32e 100644 --- a/test/IDE/complete_opaque_result.swift +++ b/test/IDE/complete_opaque_result.swift @@ -102,6 +102,18 @@ protocol HasAssocWithConstraintAndDefault { associatedtype AssocWithConstraintAndDefault: MyProtocol = ConcreteMyProtocol func returnAssocWithConstraintAndDefault() -> AssocWithConstraintAndDefault } +protocol HasAssocWithAnyObjectConstraint { + associatedtype AssocWithAnyObjectConstraint: AnyObject & MyProtocol + func returnAssocWithAnyObjectConstraint() -> AssocWithAnyObjectConstraint +} +protocol HasAssocWithConstraintOnProto where Self.AssocWithConstraintOnProto : MyProtocol { + associatedtype AssocWithConstraintOnProto + func returnAssocWithConstraintOnProto() -> AssocWithConstraintOnProto +} +protocol HasAssocWithSameTypeConstraint where Self.AssocWithSameTypeConstraint == MyClass { + associatedtype AssocWithSameTypeConstraint + func returnAssocWithSameTypeConstraint() -> AssocWithSameTypeConstraint +} class TestClass : HasAssocPlain, @@ -109,7 +121,10 @@ class TestClass : HasAssocWithSuperClassConstraint, HasAssocWithCompositionConstraint, HasAssocWithDefault, - HasAssocWithConstraintAndDefault { + HasAssocWithConstraintAndDefault, + HasAssocWithAnyObjectConstraint, + HasAssocWithConstraintOnProto, + HasAssocWithSameTypeConstraint { #^OVERRIDE_TestClass^# // OVERRIDE: Begin completions // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocPlain() -> AssocPlain {|}; @@ -118,6 +133,9 @@ class TestClass : // OVERRIDE-DAG: Decl[Subscript]/Super: subscript(idx: T) -> some MyClass & MyProtocol where T : Comparable {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithDefault() -> MyEnum {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConstraintAndDefault() -> ConcreteMyProtocol {|}; +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithAnyObjectConstraint() -> some MyProtocol & AnyObject {|} +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConstraintOnProto() -> some MyProtocol {|} +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithSameTypeConstraint() -> AssocWithSameTypeConstraint {|} // OVERRIDE: End completions } @@ -127,7 +145,10 @@ struct TestStruct : HasAssocWithSuperClassConstraint, HasAssocWithCompositionConstraint, HasAssocWithDefault, - HasAssocWithConstraintAndDefault { + HasAssocWithConstraintAndDefault, + HasAssocWithAnyObjectConstraint, + HasAssocWithConstraintOnProto, + HasAssocWithSameTypeConstraint { #^OVERRIDE_TestStruct^# } From 1dd6fe5688d8719188c5f0d7ddb84b8dac2db81e Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 8 Jan 2020 17:20:52 -0800 Subject: [PATCH 343/478] [CodeCompletion] Stop suggesting opaque result type for generic function in override completion. As per SE-0244: > Associated type inference can only infer an opaque result type for a > non-generic requirement, because the opaque type is parameterized by > the function's own generic arguments --- lib/IDE/CodeCompletion.cpp | 17 +++++++++++++---- test/IDE/complete_opaque_result.swift | 15 +++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 324b50d7555eb..4f406327944f0 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -4170,14 +4170,23 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { return nullptr; Type ResultT; - if (auto *FD = dyn_cast(VD)) + if (auto *FD = dyn_cast(VD)) { + if (FD->getGenericParams()) { + // Generic function cannot have opaque result type. + return nullptr; + } ResultT = FD->getResultInterfaceType(); - else if (auto *SD = dyn_cast(VD)) + } else if (auto *SD = dyn_cast(VD)) { + if (SD->getGenericParams()) { + // Generic subscript cannot have opaque result type. + return nullptr; + } ResultT = SD->getElementInterfaceType(); - else if (auto *VarD = dyn_cast(VD)) + } else if (auto *VarD = dyn_cast(VD)) { ResultT = VarD->getInterfaceType(); - else + } else { return nullptr; + } if (!ResultT->is() || !ResultT->castTo()->getAssocType()) diff --git a/test/IDE/complete_opaque_result.swift b/test/IDE/complete_opaque_result.swift index 0adb1f178d32e..fd77fcc0574e5 100644 --- a/test/IDE/complete_opaque_result.swift +++ b/test/IDE/complete_opaque_result.swift @@ -92,7 +92,7 @@ protocol HasAssocWithSuperClassConstraint { } protocol HasAssocWithCompositionConstraint { associatedtype AssocWithCompositionConstraint: MyClass & MyProtocol - subscript(idx: T) -> AssocWithCompositionConstraint where T: Comparable { get } + subscript(idx: Int) -> AssocWithCompositionConstraint { get } } protocol HasAssocWithDefault { associatedtype AssocWithDefault = MyEnum @@ -114,6 +114,10 @@ protocol HasAssocWithSameTypeConstraint where Self.AssocWithSameTypeConstraint = associatedtype AssocWithSameTypeConstraint func returnAssocWithSameTypeConstraint() -> AssocWithSameTypeConstraint } +protocol HasAssocWithConformanceConstraintGeneric { + associatedtype AssocWithConformanceConstraintGeneric: MyProtocol + func returnAssocWithConformanceConstraintGeneric(arg: T) -> AssocWithConformanceConstraintGeneric +} class TestClass : HasAssocPlain, @@ -124,18 +128,20 @@ class TestClass : HasAssocWithConstraintAndDefault, HasAssocWithAnyObjectConstraint, HasAssocWithConstraintOnProto, - HasAssocWithSameTypeConstraint { + HasAssocWithSameTypeConstraint, + HasAssocWithConformanceConstraintGeneric { #^OVERRIDE_TestClass^# // OVERRIDE: Begin completions // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocPlain() -> AssocPlain {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConformanceConstraint(fn: (Int) -> Int) -> some MyProtocol {|}; // OVERRIDE-DAG: Decl[InstanceVar]/Super: var valAssocWithSuperClassConstraint: some MyClass; -// OVERRIDE-DAG: Decl[Subscript]/Super: subscript(idx: T) -> some MyClass & MyProtocol where T : Comparable {|}; +// OVERRIDE-DAG: Decl[Subscript]/Super: subscript(idx: Int) -> some MyClass & MyProtocol {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithDefault() -> MyEnum {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConstraintAndDefault() -> ConcreteMyProtocol {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithAnyObjectConstraint() -> some MyProtocol & AnyObject {|} // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConstraintOnProto() -> some MyProtocol {|} // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithSameTypeConstraint() -> AssocWithSameTypeConstraint {|} +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConformanceConstraintGeneric(arg: T) -> AssocWithConformanceConstraintGeneric {|} // OVERRIDE: End completions } @@ -148,7 +154,8 @@ struct TestStruct : HasAssocWithConstraintAndDefault, HasAssocWithAnyObjectConstraint, HasAssocWithConstraintOnProto, - HasAssocWithSameTypeConstraint { + HasAssocWithSameTypeConstraint, + HasAssocWithConformanceConstraintGeneric { #^OVERRIDE_TestStruct^# } From 652f8ee95b581d732d2292f506a3dcdf646cf3e1 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 7 Jan 2020 17:32:43 -0500 Subject: [PATCH 344/478] SIL: VTable thunks should use the 'override generic signature' We recently added some checking to ensure that a method override's generic signature does not have any generic requirements not satisfied by the base method. Loosening requirements in the other direction was allowed, because it means the derived method can be called on potentially more types than the base method. However, if the generic signatures don't match, a thunk must be emitted. While we correctly determined whether a thunk should be emitted, the thunk had the wrong generic signature, and therefore the wrong calling convention, which would cause crashes at runtime. Fixes . --- lib/AST/ASTContext.cpp | 8 ++- lib/SIL/SILFunctionType.cpp | 81 +++++++++++++--------- test/SILGen/vtable_generic_signature.swift | 76 ++++++++++++++++++++ 3 files changed, 128 insertions(+), 37 deletions(-) create mode 100644 test/SILGen/vtable_generic_signature.swift diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 060f2e60a2cab..a98573fa31355 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -4430,11 +4430,13 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, const ValueDecl *derived) { auto baseGenericCtx = base->getAsGenericContext(); auto derivedGenericCtx = derived->getAsGenericContext(); - auto &ctx = base->getASTContext(); if (!baseGenericCtx || !derivedGenericCtx) return nullptr; + if (base == derived) + return derivedGenericCtx->getGenericSignature(); + auto baseClass = base->getDeclContext()->getSelfClassDecl(); if (!baseClass) return nullptr; @@ -4490,7 +4492,7 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, } return CanGenericTypeParamType::get( - gp->getDepth() - baseDepth + derivedDepth, gp->getIndex(), ctx); + gp->getDepth() - baseDepth + derivedDepth, gp->getIndex(), *this); }; auto lookupConformanceFn = @@ -4510,7 +4512,7 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, } auto genericSig = evaluateOrDefault( - ctx.evaluator, + evaluator, AbstractGenericSignatureRequest{ derivedClass->getGenericSignature().getPointer(), std::move(addedGenericParams), diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index de5ec75efcad8..e5190d4abd128 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -2514,64 +2514,77 @@ TypeConverter::getConstantOverrideInfo(TypeExpansionContext context, assert(base.requiresNewVTableEntry() && "base must not be an override"); + // Figure out the generic signature for the class method call. This is the + // signature of the derived class, with requirements transplanted from + // the base method. The derived method is allowed to have fewer + // requirements, in which case the thunk will translate the calling + // convention appropriately before calling the derived method. + bool hasGenericRequirementDifference = false; + + auto derivedSig = derived.getDecl()->getAsGenericContext() + ->getGenericSignature(); + auto genericSig = Context.getOverrideGenericSignature(base.getDecl(), + derived.getDecl()); + if (genericSig) { + hasGenericRequirementDifference = + !genericSig->requirementsNotSatisfiedBy(derivedSig).empty(); + } + auto baseInfo = getConstantInfo(context, base); auto derivedInfo = getConstantInfo(context, derived); - // If the derived method is ABI-compatible with the base method, give the - // vtable thunk the same signature as the derived method. - auto basePattern = AbstractionPattern(baseInfo.LoweredType); - - auto baseInterfaceTy = baseInfo.FormalType; - auto derivedInterfaceTy = derivedInfo.FormalType; - - auto params = derivedInterfaceTy.getParams(); + auto params = derivedInfo.FormalType.getParams(); assert(params.size() == 1); auto selfInterfaceTy = params[0].getPlainType()->getMetatypeInstanceType(); auto overrideInterfaceTy = + cast( selfInterfaceTy->adjustSuperclassMemberDeclType( - base.getDecl(), derived.getDecl(), baseInterfaceTy); - - // Copy generic signature from derived to the override type, to handle - // the case where the base member is not generic (because the base class - // is concrete) but the derived member is generic (because the derived - // class is generic). - if (auto derivedInterfaceFnTy = derivedInterfaceTy->getAs()) { - auto overrideInterfaceFnTy = overrideInterfaceTy->castTo(); - overrideInterfaceTy = - GenericFunctionType::get(derivedInterfaceFnTy->getGenericSignature(), - overrideInterfaceFnTy->getParams(), - overrideInterfaceFnTy->getResult(), - overrideInterfaceFnTy->getExtInfo()); - } + base.getDecl(), derived.getDecl(), baseInfo.FormalType) + ->getCanonicalType()); - // Lower the formal AST type. - auto bridgedTypes = getLoweredFormalTypes(derived, - cast(overrideInterfaceTy->getCanonicalType())); - auto overrideLoweredInterfaceTy = bridgedTypes.Uncurried; + // Build the formal AST function type for the class method call. + auto basePattern = AbstractionPattern(baseInfo.LoweredType); + + if (!hasGenericRequirementDifference && + !checkASTTypeForABIDifferences(derivedInfo.FormalType, + overrideInterfaceTy)) { - if (!checkASTTypeForABIDifferences(derivedInfo.LoweredType, - overrideLoweredInterfaceTy)) { + // The derived method is ABI-compatible with the base method. Let's + // just use the derived method's formal type. basePattern = AbstractionPattern( copyOptionalityFromDerivedToBase( *this, derivedInfo.LoweredType, baseInfo.LoweredType)); - overrideLoweredInterfaceTy = derivedInfo.LoweredType; + overrideInterfaceTy = derivedInfo.FormalType; + } + + if (genericSig && !genericSig->areAllParamsConcrete()) { + overrideInterfaceTy = + cast( + GenericFunctionType::get(genericSig, + overrideInterfaceTy->getParams(), + overrideInterfaceTy->getResult(), + overrideInterfaceTy->getExtInfo()) + ->getCanonicalType()); } - // Build the SILFunctionType for the vtable thunk. + // Build the lowered AST function type for the class method call. + auto bridgedTypes = getLoweredFormalTypes(derived, overrideInterfaceTy); + + // Build the SILFunctionType for the class method call. CanSILFunctionType fnTy = getNativeSILFunctionType( - *this, context, basePattern, overrideLoweredInterfaceTy, base, derived, + *this, context, basePattern, bridgedTypes.Uncurried, base, derived, /*reqt subs*/ None, ProtocolConformanceRef()); // Build the SILConstantInfo and cache it. auto resultBuf = Context.Allocate(sizeof(SILConstantInfo), alignof(SILConstantInfo)); auto result = ::new (resultBuf) SILConstantInfo{ - derivedInterfaceTy, - bridgedTypes.Pattern, - overrideLoweredInterfaceTy, + overrideInterfaceTy, + basePattern, + bridgedTypes.Uncurried, fnTy}; auto inserted = ConstantOverrideTypes.insert({{derived, base}, result}); diff --git a/test/SILGen/vtable_generic_signature.swift b/test/SILGen/vtable_generic_signature.swift new file mode 100644 index 0000000000000..4e08869cf0800 --- /dev/null +++ b/test/SILGen/vtable_generic_signature.swift @@ -0,0 +1,76 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +protocol P {} +protocol Q : P {} + +class ConcreteBase { + func f(_: U) {} +} + +class ConcreteDerivedFromConcreteBase : ConcreteBase { + override func f(_: U) {} +} + +class GenericDerivedFromConcreteBase : ConcreteBase { + override func f(_: U) {} +} + +class GenericBase { + func f(_: U) {} +} + +class ConcreteDerivedFromGenericBase : GenericBase { + override func f(_: U) {} +} + +class GenericDerivedFromGenericBase : GenericBase<(T) -> Int> { + override func f(_: U) {} +} + +// All the vtable thunks should traffic in , because that's +// what the base method declares. + +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseC1fyyxAA1PRzlFAA0dG0CADyyxAA1QRzlFTV : $@convention(method) (@in_guaranteed U, @guaranteed ConcreteDerivedFromConcreteBase) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseC1fyyqd__AA1PRd__lFAA0gH0CADyyxAA1QRzlFTV : $@convention(method) (@in_guaranteed U, @guaranteed GenericDerivedFromConcreteBase) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseC1fyyxAA1PRzlFAA0gH0CADyyqd__AA1QRd__lFTV : $@convention(method) (@in_guaranteed U, @guaranteed ConcreteDerivedFromGenericBase) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature018GenericDerivedFromD4BaseC1fyyqd__AA1PRd__lFAA0dG0CADyyqd__AA1QRd__lFTV : $@convention(method) (@in_guaranteed U, @guaranteed GenericDerivedFromGenericBase) -> () + +// CHECK-LABEL: sil_vtable ConcreteBase { +// CHECK-NEXT: #ConcreteBase.f!1: (ConcreteBase) -> (U) -> () : @$s24vtable_generic_signature12ConcreteBaseC1fyyxAA1QRzlF +// CHECK-NEXT: #ConcreteBase.init!allocator.1: (ConcreteBase.Type) -> () -> ConcreteBase : @$s24vtable_generic_signature12ConcreteBaseCACycfC +// CHECK-NEXT: #ConcreteBase.deinit!deallocator.1: @$s24vtable_generic_signature12ConcreteBaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable ConcreteDerivedFromConcreteBase { +// CHECK-NEXT: #ConcreteBase.f!1: (ConcreteBase) -> (U) -> () : @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseC1fyyxAA1PRzlFAA0dG0CADyyxAA1QRzlFTV [override] +// CHECK-NEXT: #ConcreteBase.init!allocator.1: (ConcreteBase.Type) -> () -> ConcreteBase : @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseCACycfC [override] +// CHECK-NEXT: #ConcreteDerivedFromConcreteBase.f!1: (ConcreteDerivedFromConcreteBase) -> (U) -> () : @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseC1fyyxAA1PRzlF +// CHECK-NEXT: #ConcreteDerivedFromConcreteBase.deinit!deallocator.1: @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable GenericDerivedFromConcreteBase { +// CHECK-NEXT: #ConcreteBase.f!1: (ConcreteBase) -> (U) -> () : @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseC1fyyqd__AA1PRd__lFAA0gH0CADyyxAA1QRzlFTV [override] +// CHECK-NEXT: #ConcreteBase.init!allocator.1: (ConcreteBase.Type) -> () -> ConcreteBase : @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseCACyxGycfC [override] +// CHECK-NEXT: #GenericDerivedFromConcreteBase.f!1: (GenericDerivedFromConcreteBase) -> (U) -> () : @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseC1fyyqd__AA1PRd__lF +// CHECK-NEXT: #GenericDerivedFromConcreteBase.deinit!deallocator.1: @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable GenericBase { +// CHECK-NEXT: #GenericBase.f!1: (GenericBase) -> (U) -> () : @$s24vtable_generic_signature11GenericBaseC1fyyqd__AA1QRd__lF +// CHECK-NEXT: #GenericBase.init!allocator.1: (GenericBase.Type) -> () -> GenericBase : @$s24vtable_generic_signature11GenericBaseCACyxGycfC +// CHECK-NEXT: #GenericBase.deinit!deallocator.1: @$s24vtable_generic_signature11GenericBaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable ConcreteDerivedFromGenericBase { +// CHECK-NEXT: #GenericBase.f!1: (GenericBase) -> (U) -> () : @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseC1fyyxAA1PRzlFAA0gH0CADyyqd__AA1QRd__lFTV [override] +// CHECK-NEXT: #GenericBase.init!allocator.1: (GenericBase.Type) -> () -> GenericBase : @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseCACycfC [override] +// CHECK-NEXT: #ConcreteDerivedFromGenericBase.f!1: (ConcreteDerivedFromGenericBase) -> (U) -> () : @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseC1fyyxAA1PRzlF +// CHECK-NEXT: #ConcreteDerivedFromGenericBase.deinit!deallocator.1: @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable GenericDerivedFromGenericBase { +// CHECK-NEXT: #GenericBase.f!1: (GenericBase) -> (U) -> () : @$s24vtable_generic_signature018GenericDerivedFromD4BaseC1fyyqd__AA1PRd__lFAA0dG0CADyyqd__AA1QRd__lFTV [override] +// CHECK-NEXT: #GenericBase.init!allocator.1: (GenericBase.Type) -> () -> GenericBase : @$s24vtable_generic_signature018GenericDerivedFromD4BaseCACyxGycfC [override] +// CHECK-NEXT: #GenericDerivedFromGenericBase.f!1: (GenericDerivedFromGenericBase) -> (U) -> () : @$s24vtable_generic_signature018GenericDerivedFromD4BaseC1fyyqd__AA1PRd__lF +// CHECK-NEXT: #GenericDerivedFromGenericBase.deinit!deallocator.1: @$s24vtable_generic_signature018GenericDerivedFromD4BaseCfD +// CHECK-NEXT: } From 2d670f40a0ebb1eb1ae406797f05ced768e93cee Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 8 Jan 2020 16:44:27 -0500 Subject: [PATCH 345/478] AST: Update documentation comment in SubstitutionMap.h --- include/swift/AST/SubstitutionMap.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/swift/AST/SubstitutionMap.h b/include/swift/AST/SubstitutionMap.h index 0ae98500eb524..deb897f5053bb 100644 --- a/include/swift/AST/SubstitutionMap.h +++ b/include/swift/AST/SubstitutionMap.h @@ -52,11 +52,10 @@ enum class CombineSubstitutionMaps { /// any entity that can reference type parameters, e.g., types (via /// Type::subst()) and conformances (via ProtocolConformanceRef::subst()). /// -/// SubstitutionMaps are constructed by calling the getSubstitutionMap() method -/// on a GenericSignature or (equivalently) by calling one of the static -/// \c SubstitutionMap::get() methods. However, most substitution maps are +/// SubstitutionMaps are constructed by calling the an overload of the static +/// method \c SubstitutionMap::get(). However, most substitution maps are /// computed using higher-level entry points such as -/// TypeBase::getMemberSubstitutionMap(). +/// TypeBase::getContextSubstitutionMap(). /// /// Substitution maps are ASTContext-allocated and are uniqued on construction, /// so they can be used as fields in AST nodes. From 377d22e7579f292ca9607fa3a897da882f828341 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 8 Jan 2020 16:53:59 -0500 Subject: [PATCH 346/478] SILGen: Use the correct generic environment in vtable thunks Make sure we use the generic environment derived from the generic signature of the vtable thunk itself, which can now be different than the generic signature of the derived method. This allows vtable thunks that re-abstract generic requirements to lower all the way through to IRGen. --- lib/SILGen/SILGenPoly.cpp | 8 +++---- lib/SILGen/SILGenType.cpp | 6 ++++- test/SILGen/vtable_generic_signature.swift | 18 +++++++++++---- .../vtable_thunks_reabstraction_modify.swift | 22 +++++++++---------- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 017415a368032..ed8b7fd0d9fd5 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -3602,10 +3602,10 @@ SILGenFunction::emitVTableThunk(SILDeclRef base, SGM.Types.getConstantInfo(getTypeExpansionContext(), derived).SILFnType; } - SubstitutionMap subs; - if (auto *genericEnv = fd->getGenericEnvironment()) { - F.setGenericEnvironment(genericEnv); - subs = getForwardingSubstitutionMap(); + auto subs = getForwardingSubstitutionMap(); + if (auto genericSig = derivedFTy->getSubstGenericSignature()) { + subs = SubstitutionMap::get(genericSig, subs); + derivedFTy = derivedFTy->substGenericArgs(SGM.M, subs, getTypeExpansionContext()); diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 450c98beca5e5..e0c75ea2a0ab6 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -165,12 +165,16 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, if (auto existingThunk = M.lookUpFunction(name)) return SILVTable::Entry(base, existingThunk, implKind); + GenericEnvironment *genericEnv = nullptr; + if (auto genericSig = overrideInfo.FormalType.getOptGenericSignature()) + genericEnv = genericSig->getGenericEnvironment(); + // Emit the thunk. SILLocation loc(derivedDecl); SILGenFunctionBuilder builder(*this); auto thunk = builder.createFunction( SILLinkage::Private, name, overrideInfo.SILFnType, - cast(derivedDecl)->getGenericEnvironment(), loc, + genericEnv, loc, IsBare, IsNotTransparent, IsNotSerialized, IsNotDynamic, ProfileCounter(), IsThunk); thunk->setDebugScope(new (M) SILDebugScope(loc, thunk)); diff --git a/test/SILGen/vtable_generic_signature.swift b/test/SILGen/vtable_generic_signature.swift index 4e08869cf0800..78674b0cde823 100644 --- a/test/SILGen/vtable_generic_signature.swift +++ b/test/SILGen/vtable_generic_signature.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-emit-silgen %s | %FileCheck %s +// RUN: %target-swift-emit-ir %s protocol P {} protocol Q : P {} @@ -27,13 +28,22 @@ class GenericDerivedFromGenericBase : GenericBase<(T) -> Int> { override func f(_: U) {} } +// Make sure we call these methods with the correct substitution map. +func call(_ t: T, _ u: U) { + ConcreteDerivedFromConcreteBase().f(u) + GenericDerivedFromConcreteBase().f(u) + + ConcreteDerivedFromGenericBase().f(u) + GenericDerivedFromGenericBase().f(u) +} + // All the vtable thunks should traffic in , because that's // what the base method declares. -// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseC1fyyxAA1PRzlFAA0dG0CADyyxAA1QRzlFTV : $@convention(method) (@in_guaranteed U, @guaranteed ConcreteDerivedFromConcreteBase) -> () -// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseC1fyyqd__AA1PRd__lFAA0gH0CADyyxAA1QRzlFTV : $@convention(method) (@in_guaranteed U, @guaranteed GenericDerivedFromConcreteBase) -> () -// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseC1fyyxAA1PRzlFAA0gH0CADyyqd__AA1QRd__lFTV : $@convention(method) (@in_guaranteed U, @guaranteed ConcreteDerivedFromGenericBase) -> () -// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature018GenericDerivedFromD4BaseC1fyyqd__AA1PRd__lFAA0dG0CADyyqd__AA1QRd__lFTV : $@convention(method) (@in_guaranteed U, @guaranteed GenericDerivedFromGenericBase) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseC1fyyxAA1PRzlFAA0dG0CADyyxAA1QRzlFTV : $@convention(method) <τ_0_0 where τ_0_0 : Q> (@in_guaranteed τ_0_0, @guaranteed ConcreteDerivedFromConcreteBase) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseC1fyyqd__AA1PRd__lFAA0gH0CADyyxAA1QRzlFTV : $@convention(method) <τ_0_0><τ_1_0 where τ_1_0 : Q> (@in_guaranteed τ_1_0, @guaranteed GenericDerivedFromConcreteBase<τ_0_0>) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseC1fyyxAA1PRzlFAA0gH0CADyyqd__AA1QRd__lFTV : $@convention(method) <τ_0_0 where τ_0_0 : Q> (@in_guaranteed τ_0_0, @guaranteed ConcreteDerivedFromGenericBase) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature018GenericDerivedFromD4BaseC1fyyqd__AA1PRd__lFAA0dG0CADyyqd__AA1QRd__lFTV : $@convention(method) <τ_0_0><τ_1_0 where τ_1_0 : Q> (@in_guaranteed τ_1_0, @guaranteed GenericDerivedFromGenericBase<τ_0_0>) -> () // CHECK-LABEL: sil_vtable ConcreteBase { // CHECK-NEXT: #ConcreteBase.f!1: (ConcreteBase) -> (U) -> () : @$s24vtable_generic_signature12ConcreteBaseC1fyyxAA1QRzlF diff --git a/test/SILGen/vtable_thunks_reabstraction_modify.swift b/test/SILGen/vtable_thunks_reabstraction_modify.swift index d8c6409482471..7ad5fe25af81a 100644 --- a/test/SILGen/vtable_thunks_reabstraction_modify.swift +++ b/test/SILGen/vtable_thunks_reabstraction_modify.swift @@ -15,21 +15,21 @@ public class DerivedClass : BaseClass { } } -// CHECK-LABEL: sil private [thunk] [ossa] @$s34vtable_thunks_reabstraction_modify12DerivedClassC8callbackyxSicvMAA04BaseF0CADyq_xcvMTV : $@yield_once @convention(method) (@guaranteed DerivedClass) -> @yields @inout @callee_guaranteed (@in_guaranteed Int) -> @out Result { +// CHECK-LABEL: sil private [thunk] [ossa] @$s34vtable_thunks_reabstraction_modify12DerivedClassC8callbackyxSicvMAA04BaseF0CADyq_xcvMTV : $@yield_once @convention(method) <τ_0_0> (@guaranteed DerivedClass<τ_0_0>) -> @yields @inout @callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 { // CHECK: [[DERIVED:%.*]] = function_ref @$s34vtable_thunks_reabstraction_modify12DerivedClassC8callbackyxSicvM : $@yield_once @convention(method) <τ_0_0> (@guaranteed DerivedClass<τ_0_0>) -> @yields @inout @callee_guaranteed (Int) -> @out τ_0_0 -// CHECK: ([[RESULT_BUF:%.*]], [[TOKEN:%.*]]) = begin_apply [[DERIVED]](%0) : $@yield_once @convention(method) <τ_0_0> (@guaranteed DerivedClass<τ_0_0>) -> @yields @inout @callee_guaranteed (Int) -> @out τ_0_0 -// CHECK: [[OUTER_RESULT_BUF:%.*]] = alloc_stack $@callee_guaranteed (@in_guaranteed Int) -> @out Result -// CHECK: [[RESULT:%.*]] = load [take] [[RESULT_BUF]] : $*@callee_guaranteed (Int) -> @out Result +// CHECK: ([[RESULT_BUF:%.*]], [[TOKEN:%.*]]) = begin_apply [[DERIVED]]<τ_0_0>(%0) : $@yield_once @convention(method) <τ_0_0> (@guaranteed DerivedClass<τ_0_0>) -> @yields @inout @callee_guaranteed (Int) -> @out τ_0_0 +// CHECK: [[OUTER_RESULT_BUF:%.*]] = alloc_stack $@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 +// CHECK: [[RESULT:%.*]] = load [take] [[RESULT_BUF]] : $*@callee_guaranteed (Int) -> @out τ_0_0 // CHECK: [[THUNK_FN:%.*]] = function_ref @$sSixIegyr_SixIegnr_lTR : $@convention(thin) <τ_0_0> (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> @out τ_0_0) -> @out τ_0_0 -// CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]([[RESULT]]) : $@convention(thin) <τ_0_0> (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> @out τ_0_0) -> @out τ_0_0 -// CHECK: store [[THUNK]] to [init] [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out Result -// CHECK: yield [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out Result, resume bb1, unwind bb2 +// CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]<τ_0_0>([[RESULT]]) : $@convention(thin) <τ_0_0> (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> @out τ_0_0) -> @out τ_0_0 +// CHECK: store [[THUNK]] to [init] [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 +// CHECK: yield [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0, resume bb1, unwind bb2 // CHECK: bb1: -// CHECK: [[MODIFIED:%.*]] = load [take] [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out Result +// CHECK: [[MODIFIED:%.*]] = load [take] [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 // CHECK: [[THUNK_FN:%.*]] = function_ref @$sSixIegnr_SixIegyr_lTR : $@convention(thin) <τ_0_0> (Int, @guaranteed @callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0) -> @out τ_0_0 -// CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]([[MODIFIED]]) : $@convention(thin) <τ_0_0> (Int, @guaranteed @callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0) -> @out τ_0_0 -// CHECK: store [[THUNK]] to [init] [[RESULT_BUF]] : $*@callee_guaranteed (Int) -> @out Result -// CHECK: dealloc_stack [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out Result +// CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]<τ_0_0>([[MODIFIED]]) : $@convention(thin) <τ_0_0> (Int, @guaranteed @callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0) -> @out τ_0_0 +// CHECK: store [[THUNK]] to [init] [[RESULT_BUF]] : $*@callee_guaranteed (Int) -> @out τ_0_0 +// CHECK: dealloc_stack [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 // CHECK: end_apply [[TOKEN]] // CHECK: return From 7d8b40bbb9f9e16298b990228f66b8164e89e10e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 8 Jan 2020 18:32:58 -0500 Subject: [PATCH 347/478] IRGen: Fix warning about unused capture --- lib/IRGen/IRGenMangler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 5db708ee2d57d..06f85871b7b36 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -91,7 +91,7 @@ IRGenMangler::withSymbolicReferences(IRGenModule &IGM, CanSymbolicReferenceLocally(CanSymbolicReference); AllowSymbolicReferences = true; - CanSymbolicReference = [&IGM](SymbolicReferent s) -> bool { + CanSymbolicReference = [](SymbolicReferent s) -> bool { if (auto type = s.dyn_cast()) { // The short-substitution types in the standard library have compact // manglings already, and the runtime ought to have a lookup table for From 0eb2484a9368762f2b4459d61ee05ecdc295ae03 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 1 Nov 2019 14:25:45 -0400 Subject: [PATCH 348/478] AST: Only build request dependency graph if frontend is run with -build-request-dependency-graph This adds a measurable amount of runtime overhead, but it's only needed for debugging. --- include/swift/AST/Evaluator.h | 10 ++++++++-- include/swift/Basic/LangOptions.h | 5 ++++- include/swift/Option/FrontendOptions.td | 4 +++- lib/AST/ASTContext.cpp | 4 +++- lib/AST/Evaluator.cpp | 16 +++++++++++----- lib/Frontend/CompilerInvocation.cpp | 3 +++ test/decl/class/circular_inheritance.swift | 2 +- unittests/AST/ArithmeticEvaluator.cpp | 8 ++++++-- 8 files changed, 39 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/Evaluator.h b/include/swift/AST/Evaluator.h index 606ebd8164382..7a74a2c600d95 100644 --- a/include/swift/AST/Evaluator.h +++ b/include/swift/AST/Evaluator.h @@ -187,6 +187,9 @@ class Evaluator { /// Whether to dump detailed debug info for cycles. bool debugDumpCycles; + /// Whether we're building a request dependency graph. + bool buildDependencyGraph; + /// Used to report statistics about which requests were evaluated, if /// non-null. UnifiedStatsReporter *stats = nullptr; @@ -237,7 +240,9 @@ class Evaluator { public: /// Construct a new evaluator that can emit cyclic-dependency /// diagnostics through the given diagnostics engine. - Evaluator(DiagnosticEngine &diags, bool debugDumpCycles=false); + Evaluator(DiagnosticEngine &diags, + bool debugDumpCycles, + bool buildDependencyGraph); /// Emit GraphViz output visualizing the request graph. void emitRequestEvaluatorGraphViz(llvm::StringRef graphVizPath); @@ -370,7 +375,8 @@ class Evaluator { getResultUncached(const Request &request) { // Clear out the dependencies on this request; we're going to recompute // them now anyway. - dependencies.find_as(request)->second.clear(); + if (buildDependencyGraph) + dependencies.find_as(request)->second.clear(); PrettyStackTraceRequest prettyStackTrace(request); diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 8d24bd41d831b..0795566e66c58 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -173,7 +173,10 @@ namespace swift { /// Whether to dump debug info for request evaluator cycles. bool DebugDumpCycles = false; - + + /// Whether to build a request dependency graph for debugging. + bool BuildRequestDependencyGraph = false; + /// Enable SIL type lowering bool EnableSubstSILFunctionTypesForFunctionValues = false; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index a19bd5f383b96..3601598937269 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -246,8 +246,10 @@ def debug_forbid_typecheck_prefix : Separate<["-"], "debug-forbid-typecheck-pref def debug_cycles : Flag<["-"], "debug-cycles">, HelpText<"Print out debug dumps when cycles are detected in evaluation">; +def build_request_dependency_graph : Flag<["-"], "build-request-dependency-graph">, + HelpText<"Build request dependency graph">; def output_request_graphviz : Separate<["-"], "output-request-graphviz">, - HelpText<"Emit GraphViz output visualizing the request graph">; + HelpText<"Emit GraphViz output visualizing the request dependency graph">; def debug_time_compilation : Flag<["-"], "debug-time-compilation">, HelpText<"Prints the time taken by each compilation phase">; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 060f2e60a2cab..54b2c9021d3fb 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -533,7 +533,9 @@ ASTContext::ASTContext(LangOptions &langOpts, TypeCheckerOptions &typeckOpts, : LangOpts(langOpts), TypeCheckerOpts(typeckOpts), SearchPathOpts(SearchPathOpts), SourceMgr(SourceMgr), Diags(Diags), - evaluator(Diags, langOpts.DebugDumpCycles), + evaluator(Diags, + langOpts.DebugDumpCycles, + langOpts.BuildRequestDependencyGraph), TheBuiltinModule(createBuiltinModule(*this)), StdlibModuleName(getIdentifier(STDLIB_NAME)), SwiftShimsModuleName(getIdentifier(SWIFT_SHIMS_NAME)), diff --git a/lib/AST/Evaluator.cpp b/lib/AST/Evaluator.cpp index c73ed3e97c357..f229cd50838d0 100644 --- a/lib/AST/Evaluator.cpp +++ b/lib/AST/Evaluator.cpp @@ -62,8 +62,12 @@ void Evaluator::registerRequestFunctions( requestFunctionsByZone.push_back({zoneID, functions}); } -Evaluator::Evaluator(DiagnosticEngine &diags, bool debugDumpCycles) - : diags(diags), debugDumpCycles(debugDumpCycles) { } +Evaluator::Evaluator(DiagnosticEngine &diags, + bool debugDumpCycles, + bool buildDependencyGraph) + : diags(diags), + debugDumpCycles(debugDumpCycles), + buildDependencyGraph(buildDependencyGraph) { } void Evaluator::emitRequestEvaluatorGraphViz(llvm::StringRef graphVizPath) { std::error_code error; @@ -72,9 +76,11 @@ void Evaluator::emitRequestEvaluatorGraphViz(llvm::StringRef graphVizPath) { } bool Evaluator::checkDependency(const AnyRequest &request) { - // If there is an active request, record it's dependency on this request. - if (!activeRequests.empty()) - dependencies[activeRequests.back()].push_back(request); + if (buildDependencyGraph) { + // If there is an active request, record it's dependency on this request. + if (!activeRequests.empty()) + dependencies[activeRequests.back()].push_back(request); + } // Record this as an active request. if (activeRequests.insert(request)) { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 2eeee7f96f54d..dd0af19225e90 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -392,6 +392,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, if (Args.getLastArg(OPT_debug_cycles)) Opts.DebugDumpCycles = true; + if (Args.getLastArg(OPT_build_request_dependency_graph)) + Opts.BuildRequestDependencyGraph = true; + if (const Arg *A = Args.getLastArg(OPT_output_request_graphviz)) { Opts.RequestEvaluatorGraphVizPath = A->getValue(); } diff --git a/test/decl/class/circular_inheritance.swift b/test/decl/class/circular_inheritance.swift index 202e5a9ffe13f..fb53f95a29f27 100644 --- a/test/decl/class/circular_inheritance.swift +++ b/test/decl/class/circular_inheritance.swift @@ -1,7 +1,7 @@ // RUN: rm -rf %t/stats-dir // RUN: mkdir -p %t/stats-dir // RUN: %target-typecheck-verify-swift -// RUN: not %target-swift-frontend -typecheck -debug-cycles %s -output-request-graphviz %t.dot -stats-output-dir %t/stats-dir 2> %t.cycles +// RUN: not %target-swift-frontend -typecheck -debug-cycles %s -build-request-dependency-graph -output-request-graphviz %t.dot -stats-output-dir %t/stats-dir 2> %t.cycles // RUN: %FileCheck %s < %t.cycles // RUN: %FileCheck -check-prefix CHECK-DOT %s < %t.dot diff --git a/unittests/AST/ArithmeticEvaluator.cpp b/unittests/AST/ArithmeticEvaluator.cpp index 7738ba8e55fea..ed85576e07c57 100644 --- a/unittests/AST/ArithmeticEvaluator.cpp +++ b/unittests/AST/ArithmeticEvaluator.cpp @@ -210,7 +210,9 @@ TEST(ArithmeticEvaluator, Simple) { SourceManager sourceMgr; DiagnosticEngine diags(sourceMgr); - Evaluator evaluator(diags); + Evaluator evaluator(diags, + /*debugDumpCycles=*/false, + /*buildDependencyGraph=*/true); evaluator.registerRequestFunctions(Zone::ArithmeticEvaluator, arithmeticRequestFunctions); @@ -333,7 +335,9 @@ TEST(ArithmeticEvaluator, Cycle) { SourceManager sourceMgr; DiagnosticEngine diags(sourceMgr); - Evaluator evaluator(diags); + Evaluator evaluator(diags, + /*debugDumpCycles=*/false, + /*buildDependencyGraph=*/false); evaluator.registerRequestFunctions(Zone::ArithmeticEvaluator, arithmeticRequestFunctions); From 1ed545301953db97607348c5cacfb7e629b1ab38 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 8 Jan 2020 22:17:39 -0500 Subject: [PATCH 349/478] AST: Look for cached request result before checking for a cycle This improves performance in the common case where the result has already been cached, because the cycle check constructs an AnyRequest existential, which is expensive. --- include/swift/AST/Evaluator.h | 71 +++++++++++++++-------------------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/include/swift/AST/Evaluator.h b/include/swift/AST/Evaluator.h index 7a74a2c600d95..4b4e89a8e3c43 100644 --- a/include/swift/AST/Evaluator.h +++ b/include/swift/AST/Evaluator.h @@ -258,26 +258,28 @@ class Evaluator { void registerRequestFunctions(Zone zone, ArrayRef functions); - /// Evaluate the given request and produce its result, - /// consulting/populating the cache as required. - template + /// Retrieve the result produced by evaluating a request that can + /// be cached. + template::type * = nullptr> llvm::Expected operator()(const Request &request) { - // Check for a cycle. - if (checkDependency(getCanonicalRequest(request))) { - return llvm::Error( - llvm::make_unique>(request, *this)); - } + // The request can be cached, but check a predicate to determine + // whether this particular instance is cached. This allows more + // fine-grained control over which instances get cache. + if (request.isCached()) + return getResultCached(request); - // Make sure we remove this from the set of active requests once we're - // done. - SWIFT_DEFER { - assert(activeRequests.back().castTo() == request); - activeRequests.pop_back(); - }; + return getResultUncached(request); + } - // Get the result. - return getResult(request); + /// Retrieve the result produced by evaluating a request that + /// will never be cached. + template::type * = nullptr> + llvm::Expected + operator()(const Request &request) { + return getResultUncached(request); } /// Evaluate a set of requests and return their results as a tuple. @@ -345,34 +347,23 @@ class Evaluator { /// request to the \c activeRequests stack. bool checkDependency(const AnyRequest &request); - /// Retrieve the result produced by evaluating a request that can - /// be cached. - template::type * = nullptr> - llvm::Expected - getResult(const Request &request) { - // The request can be cached, but check a predicate to determine - // whether this particular instance is cached. This allows more - // fine-grained control over which instances get cache. - if (request.isCached()) - return getResultCached(request); - - return getResultUncached(request); - } - - /// Retrieve the result produced by evaluating a request that - /// will never be cached. - template::type * = nullptr> - llvm::Expected - getResult(const Request &request) { - return getResultUncached(request); - } - /// Produce the result of the request without caching. template llvm::Expected getResultUncached(const Request &request) { + // Check for a cycle. + if (checkDependency(getCanonicalRequest(request))) { + return llvm::Error( + llvm::make_unique>(request, *this)); + } + + // Make sure we remove this from the set of active requests once we're + // done. + SWIFT_DEFER { + assert(activeRequests.back().castTo() == request); + activeRequests.pop_back(); + }; + // Clear out the dependencies on this request; we're going to recompute // them now anyway. if (buildDependencyGraph) From 518ab9f22f64bc09273862df48a3b97f1193f254 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 7 Jan 2020 12:23:49 -0800 Subject: [PATCH 350/478] [NFC] Unblock Lazy Member Loading For Import-As-Member Lazy member loading had an antagonistic relationship with the import-as-member facilities. The member tables were stored in a hash map that is keyed by serialized declaration context. While this was good for importing the entire member set of a given extension, it's in the complete wrong order for lazy member loading, which wants the same data keyed by base name. Given that it is annoying to redo the globals-as-member tables to support one use case or the other, coupled with the fact that optimizing for one use-case automatically pessimizes the other, just take a page from rdar://18696086 and store the same information twice in two separate formats each optimized for the task at hand. Preliminary benchmarks indicate that this leads to a 5% reduction in Clang-Imported entities which will drastically speed up most apps that use Dispatch and CoreGraphics. --- lib/ClangImporter/ClangImporter.cpp | 34 ++- lib/ClangImporter/ImportDecl.cpp | 2 +- lib/ClangImporter/SwiftLookupTable.cpp | 311 +++++++++++++++++++------ lib/ClangImporter/SwiftLookupTable.h | 62 ++++- 4 files changed, 320 insertions(+), 89 deletions(-) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 8a8b5d1781619..e2d4c3e34eaf1 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3010,7 +3010,7 @@ void ClangImporter::loadExtensions(NominalTypeDecl *nominal, // FIXME: If we already looked at this for this generation, // skip. - for (auto entry : table.lookupGlobalsAsMembers(effectiveClangContext)) { + for (auto entry : table.allGlobalsAsMembersInContext(effectiveClangContext)) { // If the entry is not visible, skip it. if (!isVisibleClangEntry(clangCtx, entry)) continue; @@ -3763,17 +3763,6 @@ ClangImporter::Implementation::loadNamedMembers( return None; } - // Also bail out if there are any global-as-member mappings for this context; - // we can support some of them lazily but the full set of idioms seems - // prohibitively complex (also they're not stored in by-name lookup, for - // reasons unclear). - if (isa(D) && !checkedGlobalsAsMembers.insert(IDC).second) { - if (forEachLookupTable([&](SwiftLookupTable &table) -> bool { - return (!table.lookupGlobalsAsMembers(effectiveClangContext).empty()); - })) - return None; - } - // There are 3 cases: // // - The decl is from a bridging header, CMO is Some(nullptr) @@ -3823,6 +3812,27 @@ ClangImporter::Implementation::loadNamedMembers( } } + for (auto entry : table->lookupGlobalsAsMembers(SerializedSwiftName(N), + effectiveClangContext)) { + if (!entry.is()) continue; + auto member = entry.get(); + if (!isVisibleClangEntry(clangCtx, member)) continue; + + // Skip Decls from different clang::DeclContexts + if (member->getDeclContext() != CDC) continue; + + SmallVector tmp; + insertMembersAndAlternates(member, tmp); + for (auto *TD : tmp) { + if (auto *V = dyn_cast(TD)) { + // Skip ValueDecls if they import under different names. + if (V->getBaseName() == N) { + Members.push_back(V); + } + } + } + } + if (N == DeclBaseName::createConstructor()) { if (auto *classDecl = dyn_cast(D)) { SmallVector ctors; diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 9df4e527a38a6..8d3601813a1fe 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -8544,7 +8544,7 @@ void ClangImporter::Implementation::loadAllMembersIntoExtension( startedImportingEntity(); // Load the members. - for (auto entry : table->lookupGlobalsAsMembers(effectiveClangContext)) { + for (auto entry : table->allGlobalsAsMembersInContext(effectiveClangContext)) { auto decl = entry.get(); // Only include members in the same submodule as this extension. diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp index 632edc4527a0e..0aa7476df5d55 100644 --- a/lib/ClangImporter/SwiftLookupTable.cpp +++ b/lib/ClangImporter/SwiftLookupTable.cpp @@ -58,6 +58,9 @@ namespace { llvm::OnDiskIterableChainedHashTable; using SerializedGlobalsAsMembersTable = + llvm::OnDiskIterableChainedHashTable; + + using SerializedGlobalsAsMembersIndex = llvm::OnDiskIterableChainedHashTable; } // end anonymous namespace @@ -99,6 +102,7 @@ class SwiftLookupTableReader : public clang::ModuleFileExtensionReader { std::unique_ptr SerializedTable; ArrayRef Categories; std::unique_ptr GlobalsAsMembersTable; + std::unique_ptr GlobalsAsMembersIndex; SwiftLookupTableReader(clang::ModuleFileExtension *extension, clang::ASTReader &reader, @@ -108,11 +112,14 @@ class SwiftLookupTableReader : public clang::ModuleFileExtensionReader { serializedTable, ArrayRef categories, std::unique_ptr - globalsAsMembersTable) + globalsAsMembersTable, + std::unique_ptr + globalsAsMembersIndex) : ModuleFileExtensionReader(extension), Reader(reader), ModuleFile(moduleFile), OnRemove(onRemove), SerializedTable(std::move(serializedTable)), Categories(categories), - GlobalsAsMembersTable(std::move(globalsAsMembersTable)) {} + GlobalsAsMembersTable(std::move(globalsAsMembersTable)), + GlobalsAsMembersIndex(std::move(globalsAsMembersIndex)) {} public: /// Create a new lookup table reader for the given AST reader and stream @@ -148,12 +155,22 @@ class SwiftLookupTableReader : public clang::ModuleFileExtensionReader { /// injected into them. SmallVector getGlobalsAsMembersContexts(); + SmallVector getGlobalsAsMembersBaseNames(); + /// Retrieve the set of global declarations that are going to be /// imported as members into the given context. /// /// \returns true if we found anything, false otherwise. - bool lookupGlobalsAsMembers(SwiftLookupTable::StoredContext context, - SmallVectorImpl &entries); + bool lookupGlobalsAsMembersInContext(SwiftLookupTable::StoredContext context, + SmallVectorImpl &entries); + + /// Retrieve the set of global declarations that are going to be imported as members under the given + /// Swift base name. + /// + /// \returns true if we found anything, false otherwise. + bool lookupGlobalsAsMembers( + SerializedSwiftName baseName, + SmallVectorImpl &entries); }; } // namespace swift @@ -491,63 +508,81 @@ void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry, return; } - // Populate cache from reader if necessary. - findOrCreate(name.getBaseName()); + auto updateTableWithEntry = [this](SingleEntry newEntry, StoredContext context, + TableType::value_type::second_type &entries){ + for (auto &entry : entries) { + if (entry.Context == context) { + // We have entries for this context. + (void)addLocalEntry(newEntry, entry.DeclsOrMacros); + return; + } else { + (void)newEntry; + } + } - auto context = *contextOpt; + // This is a new context for this name. Add it. + auto decl = newEntry.dyn_cast(); + auto macro = newEntry.dyn_cast(); + auto moduleMacro = newEntry.dyn_cast(); + + FullTableEntry entry; + entry.Context = context; + if (decl) + entry.DeclsOrMacros.push_back(encodeEntry(decl)); + else if (macro) + entry.DeclsOrMacros.push_back(encodeEntry(macro)); + else + entry.DeclsOrMacros.push_back(encodeEntry(moduleMacro)); + + entries.push_back(entry); + }; // If this is a global imported as a member, record is as such. + auto context = *contextOpt; if (isGlobalAsMember(newEntry, context)) { - auto &entries = GlobalsAsMembers[context]; + // Populate cache from reader if necessary. + findOrCreate(GlobalsAsMembers, name.getBaseName(), + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookupGlobalsAsMembers(Name, results); + }); + updateTableWithEntry(newEntry, context, + GlobalsAsMembers[name.getBaseName()]); + + // Populate the index as well. + auto &entries = GlobalsAsMembersIndex[context]; (void)addLocalEntry(newEntry, entries); } - // Find the list of entries for this base name. - auto &entries = LookupTable[name.getBaseName()]; - auto decl = newEntry.dyn_cast(); - auto macro = newEntry.dyn_cast(); - auto moduleMacro = newEntry.dyn_cast(); - for (auto &entry : entries) { - if (entry.Context == context) { - // We have entries for this context. - (void)addLocalEntry(newEntry, entry.DeclsOrMacros); - return; - } - } - - // This is a new context for this name. Add it. - FullTableEntry entry; - entry.Context = context; - if (decl) - entry.DeclsOrMacros.push_back(encodeEntry(decl)); - else if (macro) - entry.DeclsOrMacros.push_back(encodeEntry(macro)); - else - entry.DeclsOrMacros.push_back(encodeEntry(moduleMacro)); - entries.push_back(entry); + // Populate cache from reader if necessary. + findOrCreate(LookupTable, name.getBaseName(), + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookup(Name, results); + }); + updateTableWithEntry(newEntry, context, LookupTable[name.getBaseName()]); } -auto SwiftLookupTable::findOrCreate(SerializedSwiftName baseName) - -> llvm::DenseMap>::iterator { +SwiftLookupTable::TableType::iterator +SwiftLookupTable::findOrCreate(TableType &Table, + SerializedSwiftName baseName, + llvm::function_ref create) { // If there is no base name, there is nothing to find. - if (baseName.empty()) return LookupTable.end(); + if (baseName.empty()) return Table.end(); // Find entries for this base name. - auto known = LookupTable.find(baseName); + auto known = Table.find(baseName); // If we found something, we're done. - if (known != LookupTable.end()) return known; + if (known != Table.end()) return known; // If there's no reader, we've found all there is to find. if (!Reader) return known; // Lookup this base name in the module file. SmallVector results; - (void)Reader->lookup(baseName, results); + create(results, *Reader, baseName); // Add an entry to the table so we don't look again. - known = LookupTable.insert({ std::move(baseName), std::move(results) }).first; + known = Table.insert({ std::move(baseName), std::move(results) }).first; return known; } @@ -558,7 +593,10 @@ SwiftLookupTable::lookup(SerializedSwiftName baseName, SmallVector result; // Find the lookup table entry for this base name. - auto known = findOrCreate(baseName); + auto known = findOrCreate(LookupTable, baseName, + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookup(Name, results); + }); if (known == LookupTable.end()) return result; // Walk each of the entries. @@ -578,24 +616,53 @@ SwiftLookupTable::lookup(SerializedSwiftName baseName, } SmallVector -SwiftLookupTable::lookupGlobalsAsMembers(StoredContext context) { +SwiftLookupTable::lookupGlobalsAsMembersImpl(SerializedSwiftName baseName, + llvm::Optional searchContext) { SmallVector result; // Find entries for this base name. - auto known = GlobalsAsMembers.find(context); + auto known = findOrCreate(GlobalsAsMembers, baseName, + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookupGlobalsAsMembers(Name, results); + }); + if (known == GlobalsAsMembers.end()) return result; + + + // Walk each of the entries. + for (auto &entry : known->second) { + // If we're looking in a particular context and it doesn't match the + // entry context, we're done. + if (searchContext && entry.Context != *searchContext) + continue; + + // Map each of the declarations. + for (auto &stored : entry.DeclsOrMacros) + if (auto entry = mapStored(stored)) + result.push_back(entry); + } + + return result; +} + +SmallVector +SwiftLookupTable::allGlobalsAsMembersInContext(StoredContext context) { + SmallVector result; + + // Find entries for this base name. + auto known = GlobalsAsMembersIndex.find(context); // If we didn't find anything... - if (known == GlobalsAsMembers.end()) { + if (known == GlobalsAsMembersIndex.end()) { // If there's no reader, we've found all there is to find. if (!Reader) return result; // Lookup this base name in the module extension file. SmallVector results; - (void)Reader->lookupGlobalsAsMembers(context, results); + (void)Reader->lookupGlobalsAsMembersInContext(context, results); // Add an entry to the table so we don't look again. - known = GlobalsAsMembers.insert({ std::move(context), - std::move(results) }).first; + known = GlobalsAsMembersIndex.insert({ std::move(context), + std::move(results) }).first; } // Map each of the results. @@ -607,14 +674,26 @@ SwiftLookupTable::lookupGlobalsAsMembers(StoredContext context) { } SmallVector -SwiftLookupTable::lookupGlobalsAsMembers(EffectiveClangContext context) { - // Translate context. +SwiftLookupTable::lookupGlobalsAsMembers(SerializedSwiftName baseName, + Optional searchContext) { + // Propagate the null search context. + if (!searchContext) + return lookupGlobalsAsMembersImpl(baseName, None); + + Optional storedContext = translateContext(*searchContext); + if (!storedContext) return { }; + + return lookupGlobalsAsMembersImpl(baseName, *storedContext); +} + +SmallVector +SwiftLookupTable::allGlobalsAsMembersInContext(EffectiveClangContext context) { if (!context) return { }; Optional storedContext = translateContext(context); if (!storedContext) return { }; - return lookupGlobalsAsMembers(*storedContext); + return allGlobalsAsMembersInContext(*storedContext); } SmallVector @@ -622,13 +701,13 @@ SwiftLookupTable::allGlobalsAsMembers() { // If we have a reader, deserialize all of the globals-as-members data. if (Reader) { for (auto context : Reader->getGlobalsAsMembersContexts()) { - (void)lookupGlobalsAsMembers(context); + (void)allGlobalsAsMembersInContext(context); } } // Collect all of the keys and sort them. SmallVector contexts; - for (const auto &globalAsMember : GlobalsAsMembers) { + for (const auto &globalAsMember : GlobalsAsMembersIndex) { contexts.push_back(globalAsMember.first); } llvm::array_pod_sort(contexts.begin(), contexts.end()); @@ -636,12 +715,25 @@ SwiftLookupTable::allGlobalsAsMembers() { // Collect all of the results in order. SmallVector results; for (const auto &context : contexts) { - for (auto &entry : GlobalsAsMembers[context]) + for (auto &entry : GlobalsAsMembersIndex[context]) results.push_back(mapStored(entry)); } return results; } +SmallVector +SwiftLookupTable::allGlobalsAsMembersBaseNames() { + // If we have a reader, enumerate its base names. + if (Reader) return Reader->getGlobalsAsMembersBaseNames(); + + // Otherwise, walk the lookup table. + SmallVector result; + for (const auto &entry : GlobalsAsMembers) { + result.push_back(entry.first); + } + return result; +} + SmallVector SwiftLookupTable::lookup(SerializedSwiftName baseName, EffectiveClangContext searchContext) { @@ -672,7 +764,10 @@ SwiftLookupTable::lookupObjCMembers(SerializedSwiftName baseName) { SmallVector result; // Find the lookup table entry for this base name. - auto known = findOrCreate(baseName); + auto known = findOrCreate(LookupTable, baseName, + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookup(Name, results); + }); if (known == LookupTable.end()) return result; // Walk each of the entries. @@ -774,10 +869,14 @@ void SwiftLookupTable::deserializeAll() { (void)lookup(baseName, None); } + for (auto baseName : Reader->getGlobalsAsMembersBaseNames()) { + (void)lookupGlobalsAsMembersImpl(baseName, None); + } + (void)categories(); for (auto context : Reader->getGlobalsAsMembersContexts()) { - (void)lookupGlobalsAsMembers(context); + (void)allGlobalsAsMembersInContext(context); } } @@ -908,10 +1007,10 @@ void SwiftLookupTable::dump(raw_ostream &os) const { os << "\n"; } - if (!GlobalsAsMembers.empty()) { + if (!GlobalsAsMembersIndex.empty()) { os << "Globals-as-members mapping:\n"; SmallVector contexts; - for (const auto &entry : GlobalsAsMembers) { + for (const auto &entry : GlobalsAsMembersIndex) { contexts.push_back(entry.first); } llvm::array_pod_sort(contexts.begin(), contexts.end()); @@ -920,7 +1019,7 @@ void SwiftLookupTable::dump(raw_ostream &os) const { printStoredContext(context, os); os << ": "; - const auto &entries = GlobalsAsMembers.find(context)->second; + const auto &entries = GlobalsAsMembersIndex.find(context)->second; interleave(entries.begin(), entries.end(), [this, &os](uint64_t entry) { printStoredEntry(this, entry, os); @@ -955,7 +1054,11 @@ namespace { /// Record that contains the mapping from contexts to the list of /// globals that will be injected as members into those contexts. - GLOBALS_AS_MEMBERS_RECORD_ID + GLOBALS_AS_MEMBERS_RECORD_ID, + + /// Record that contains the mapping from contexts to the list of + /// globals that will be injected as members into those contexts. + GLOBALS_AS_MEMBERS_INDEX_RECORD_ID, }; using BaseNameToEntitiesTableRecordLayout @@ -967,6 +1070,9 @@ namespace { using GlobalsAsMembersTableRecordLayout = BCRecordLayout, BCBlob>; + using GlobalsAsMembersIndexRecordLayout + = BCRecordLayout, BCBlob>; + /// Trait used to write the on-disk hash table for the base name -> entities /// mapping. class BaseNameToEntitiesTableWriterInfo { @@ -1212,9 +1318,39 @@ void SwiftLookupTableWriter::writeExtensionContents( // Write the globals-as-members table, if non-empty. if (!table.GlobalsAsMembers.empty()) { + // First, gather the sorted list of base names. + SmallVector baseNames; + for (const auto &entry : table.GlobalsAsMembers) + baseNames.push_back(entry.first); + llvm::array_pod_sort(baseNames.begin(), baseNames.end()); + + // Form the mapping from base names to entities with their context. + { + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator + generator; + BaseNameToEntitiesTableWriterInfo info(table, Writer); + for (auto baseName : baseNames) + generator.insert(baseName, table.GlobalsAsMembers[baseName], info); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::write(blobStream, 0, little); + tableOffset = generator.Emit(blobStream, info); + } + + GlobalsAsMembersTableRecordLayout layout(stream); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); + } + } + + // Write the globals-as-members index, if non-empty. + if (!table.GlobalsAsMembersIndex.empty()) { // Sort the keys. SmallVector contexts; - for (const auto &entry : table.GlobalsAsMembers) { + for (const auto &entry : table.GlobalsAsMembersIndex) { contexts.push_back(entry.first); } llvm::array_pod_sort(contexts.begin(), contexts.end()); @@ -1227,7 +1363,7 @@ void SwiftLookupTableWriter::writeExtensionContents( generator; GlobalsAsMembersTableWriterInfo info(table, Writer); for (auto context : contexts) - generator.insert(context, table.GlobalsAsMembers[context], info); + generator.insert(context, table.GlobalsAsMembersIndex[context], info); llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 @@ -1235,7 +1371,7 @@ void SwiftLookupTableWriter::writeExtensionContents( tableOffset = generator.Emit(blobStream, info); } - GlobalsAsMembersTableRecordLayout layout(stream); + GlobalsAsMembersIndexRecordLayout layout(stream); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } } @@ -1490,6 +1626,7 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, } llvm::BitstreamEntry next = maybeNext.get(); std::unique_ptr serializedTable; + std::unique_ptr globalsAsMembersIndex; std::unique_ptr globalsAsMembersTable; ArrayRef categories; while (next.Kind != llvm::BitstreamEntry::EndBlock) { @@ -1539,6 +1676,22 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, break; } + case GLOBALS_AS_MEMBERS_INDEX_RECORD_ID: { + // Already saw globals as members index. + if (globalsAsMembersIndex) + return nullptr; + + uint32_t tableOffset; + GlobalsAsMembersIndexRecordLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(blobData.data()); + + globalsAsMembersIndex.reset( + SerializedGlobalsAsMembersIndex::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + case CATEGORIES_RECORD_ID: { // Already saw categories; input is malformed. if (!categories.empty()) return nullptr; @@ -1590,7 +1743,8 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, return std::unique_ptr( new SwiftLookupTableReader(extension, reader, moduleFile, onRemove, std::move(serializedTable), categories, - std::move(globalsAsMembersTable))); + std::move(globalsAsMembersTable), + std::move(globalsAsMembersIndex))); } @@ -1617,21 +1771,46 @@ bool SwiftLookupTableReader::lookup( SmallVector SwiftLookupTableReader::getGlobalsAsMembersContexts() { SmallVector results; - if (!GlobalsAsMembersTable) return results; + if (!GlobalsAsMembersIndex) return results; - for (auto key : GlobalsAsMembersTable->keys()) { + for (auto key : GlobalsAsMembersIndex->keys()) { results.push_back(key); } return results; } -bool SwiftLookupTableReader::lookupGlobalsAsMembers( +bool SwiftLookupTableReader::lookupGlobalsAsMembersInContext( SwiftLookupTable::StoredContext context, SmallVectorImpl &entries) { + if (!GlobalsAsMembersIndex) return false; + + // Look for an entry with this context name. + auto known = GlobalsAsMembersIndex->find(context); + if (known == GlobalsAsMembersIndex->end()) return false; + + // Grab the results. + entries = std::move(*known); + return true; +} + +SmallVector +SwiftLookupTableReader::getGlobalsAsMembersBaseNames() { + SmallVector results; + if (!GlobalsAsMembersTable) return {}; + + for (auto key : GlobalsAsMembersTable->keys()) { + results.push_back(key); + } + return results; +} + +bool SwiftLookupTableReader::lookupGlobalsAsMembers( + SerializedSwiftName baseName, + SmallVectorImpl &entries) { if (!GlobalsAsMembersTable) return false; // Look for an entry with this context name. - auto known = GlobalsAsMembersTable->find(context); + auto known = GlobalsAsMembersTable->find(baseName); if (known == GlobalsAsMembersTable->end()) return false; // Grab the results. diff --git a/lib/ClangImporter/SwiftLookupTable.h b/lib/ClangImporter/SwiftLookupTable.h index 3b8fc8c6a0b2d..fe980625c0ff8 100644 --- a/lib/ClangImporter/SwiftLookupTable.h +++ b/lib/ClangImporter/SwiftLookupTable.h @@ -276,7 +276,7 @@ const uint16_t SWIFT_LOOKUP_TABLE_VERSION_MAJOR = 1; /// Lookup table minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t SWIFT_LOOKUP_TABLE_VERSION_MINOR = 15; // Special names +const uint16_t SWIFT_LOOKUP_TABLE_VERSION_MINOR = 16; // Lazy Member Loading Index /// A lookup table that maps Swift names to the set of Clang /// declarations with that particular name. @@ -385,10 +385,19 @@ class SwiftLookupTable { } private: + using TableType = + llvm::DenseMap>; + using CacheCallback = void(SmallVectorImpl &, + SwiftLookupTableReader &, + SerializedSwiftName); + /// A table mapping from the base name of Swift entities to all of /// the C entities that have that name, in all contexts. - llvm::DenseMap> - LookupTable; + TableType LookupTable; + + /// A table mapping the base names of Swift entities to all of the C entities + /// that are remapped to that name by the globals-as-members utility, in all contexts. + TableType GlobalsAsMembers; /// The list of Objective-C categories and extensions. llvm::SmallVector Categories; @@ -398,7 +407,7 @@ class SwiftLookupTable { /// /// The values use the same representation as /// FullTableEntry::DeclsOrMacros. - llvm::DenseMap> GlobalsAsMembers; + llvm::DenseMap> GlobalsAsMembersIndex; /// The reader responsible for lazily loading the contents of this table. SwiftLookupTableReader *Reader; @@ -413,7 +422,9 @@ class SwiftLookupTable { /// Find or create the table entry for the given base name. llvm::DenseMap>::iterator - findOrCreate(SerializedSwiftName baseName); + findOrCreate(TableType &table, + SerializedSwiftName baseName, + llvm::function_ref create); /// Add the given entry to the list of entries, if it's not already /// present. @@ -474,8 +485,22 @@ class SwiftLookupTable { llvm::Optional searchContext); /// Retrieve the set of global declarations that are going to be - /// imported as members into the given context. - SmallVector lookupGlobalsAsMembers(StoredContext context); + /// imported as the given Swift name into the given context. + /// + /// \param baseName The base name to search for. All results will + /// have this base name. + /// + /// \param searchContext The context in which the resulting set of + /// entities should reside. This may be None to indicate that + /// all results from all contexts should be produced. + SmallVector + lookupGlobalsAsMembersImpl(SerializedSwiftName baseName, + llvm::Optional searchContext); + + /// Retrieve the set of global declarations that are going to be imported as + /// members in the given context. + SmallVector + allGlobalsAsMembersInContext(StoredContext context); public: /// Lookup an unresolved context name and resolve it to a Clang @@ -488,14 +513,16 @@ class SwiftLookupTable { /// have this base name. /// /// \param searchContext The context in which the resulting set of - /// entities should reside. This may be None to indicate that - /// all results from all contexts should be produced. + /// entities should reside. SmallVector lookup(SerializedSwiftName baseName, EffectiveClangContext searchContext); /// Retrieve the set of base names that are stored in the lookup table. SmallVector allBaseNames(); + /// Retrieve the set of base names that are stored in the globals-as-members lookup table. + SmallVector allGlobalsAsMembersBaseNames(); + /// Lookup Objective-C members with the given base name, regardless /// of context. SmallVector @@ -506,13 +533,28 @@ class SwiftLookupTable { /// Retrieve the set of global declarations that are going to be /// imported as members into the given context. + /// + /// \param baseName The base name to search for. All results will + /// have this base name. + /// + /// \param searchContext The context in which the resulting set of + /// entities should reside. SmallVector - lookupGlobalsAsMembers(EffectiveClangContext context); + lookupGlobalsAsMembers(SerializedSwiftName baseName, + Optional searchContext); + + SmallVector + allGlobalsAsMembersInContext(EffectiveClangContext context); /// Retrieve the set of global declarations that are going to be /// imported as members. SmallVector allGlobalsAsMembers(); + /// Retrieve the set of base names that are going to be imported as members in the + /// given context. + SmallVector + globalsAsMembersBaseNames(EffectiveClangContext context); + /// Deserialize all entries. void deserializeAll(); From b9f1e58b37531e55e87aa90aa2a136afbc721b24 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 8 Jan 2020 21:00:49 -0800 Subject: [PATCH 351/478] [CodeCompletion] Use GenericSignature methods to get associatedtype reqs --- lib/IDE/CodeCompletion.cpp | 31 +++++++++++---------------- test/IDE/complete_opaque_result.swift | 4 ++-- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 4f406327944f0..8834bafca281c 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -4201,28 +4201,21 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { // If resolved print it. return nullptr; - // Collect requirements on the associatedtype. - ProtocolDecl *protoD = - ResultT->castTo()->getAssocType()->getProtocol(); + auto genericSig = VD->getDeclContext()->getGenericSignatureOfContext(); + + if (genericSig->isConcreteType(ResultT)) + // If it has same type requrement, we will emit the concrete type. + return nullptr; + // Collect requirements on the associatedtype. SmallVector opaqueTypes; bool hasExplicitAnyObject = false; - for (auto req : protoD->getRequirementSignature()) { - if (!req.getFirstType()->isEqual(ResultT)) - continue; - - switch (req.getKind()) { - case RequirementKind::Conformance: - case RequirementKind::Superclass: - opaqueTypes.push_back(req.getSecondType()); - break; - case RequirementKind::Layout: - hasExplicitAnyObject |= req.getLayoutConstraint()->isClass(); - break; - case RequirementKind::SameType: - return nullptr; - } - } + if (auto superTy = genericSig->getSuperclassBound(ResultT)) + opaqueTypes.push_back(superTy); + for (auto proto : genericSig->getConformsTo(ResultT)) + opaqueTypes.push_back(proto->getDeclaredInterfaceType()); + if (auto layout = genericSig->getLayoutConstraint(ResultT)) + hasExplicitAnyObject = layout->isClass(); if (!hasExplicitAnyObject) { if (opaqueTypes.empty()) diff --git a/test/IDE/complete_opaque_result.swift b/test/IDE/complete_opaque_result.swift index fd77fcc0574e5..ef7accb15c7b2 100644 --- a/test/IDE/complete_opaque_result.swift +++ b/test/IDE/complete_opaque_result.swift @@ -110,8 +110,8 @@ protocol HasAssocWithConstraintOnProto where Self.AssocWithConstraintOnProto : M associatedtype AssocWithConstraintOnProto func returnAssocWithConstraintOnProto() -> AssocWithConstraintOnProto } -protocol HasAssocWithSameTypeConstraint where Self.AssocWithSameTypeConstraint == MyClass { - associatedtype AssocWithSameTypeConstraint +protocol HasAssocWithSameTypeConstraint where Self.AssocWithSameTypeConstraint == ConcreteMyProtocol { + associatedtype AssocWithSameTypeConstraint : MyProtocol func returnAssocWithSameTypeConstraint() -> AssocWithSameTypeConstraint } protocol HasAssocWithConformanceConstraintGeneric { From 90f7f34a35d3d4602d5e70b30ddb579ef5b2d014 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 8 Jan 2020 22:45:49 -0800 Subject: [PATCH 352/478] [CodeCompletion] Suggest local variables in a multi-clause guard statement Previously, local optional binding values aren't suggested in subsequent conditions in guard statement. That was because, if the condition contains code-completion token, the parser stops parsing and add implicit body with the end loc of the condition which is, in this case, the position of the code-completion token. As a result, since code-completion location is in the body range, namelookup::FindLocalVal refuses to emit valuses in conditions. This patch fixes the issue by ignoring implict body in `FindLocalVal`. rdar://problem/28482216 --- lib/AST/NameLookup.cpp | 3 ++- test/IDE/complete_stmt_controlling_expr.swift | 14 +++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index e7ad1f544c059..d2d411a8160a3 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -2507,7 +2507,8 @@ void FindLocalVal::visitGuardStmt(GuardStmt *S) { return; // Names in the guard aren't visible until after the body. - if (!isReferencePointInRange(S->getBody()->getSourceRange())) + if (S->getBody()->isImplicit() || + !isReferencePointInRange(S->getBody()->getSourceRange())) checkStmtCondition(S->getCond()); visit(S->getBody()); diff --git a/test/IDE/complete_stmt_controlling_expr.swift b/test/IDE/complete_stmt_controlling_expr.swift index 16a7b8323f948..37a3a5d039f8a 100644 --- a/test/IDE/complete_stmt_controlling_expr.swift +++ b/test/IDE/complete_stmt_controlling_expr.swift @@ -117,7 +117,8 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_4 | %FileCheck %s -check-prefix=FOOSTRUCT_DOT_BOOL // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_5 | %FileCheck %s -check-prefix=FOOSTRUCT_DOT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_6 | %FileCheck %s -check-prefix=FOOSTRUCT_NODOT - +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_7 | %FileCheck %s -check-prefix=FOOSTRUCT_LOCALVAL +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_8 | %FileCheck %s -check-prefix=FOOSTRUCT_LOCALVAL struct FooStruct { var instanceVar : Int @@ -605,6 +606,13 @@ func testGuardLetBinding5(x: FooStruct?) { func testGuardLetBinding5(x: FooStruct?) { guard let y = x, z = y#^GUARD_LET_BIND_6^# else {} } +func testGuardLetBinding7(x: FooStruct?) { + guard let boundVal = x, let other = #^GUARD_LET_BIND_7^# else {} +} +func testGuardLetBinding8(_ x: FooStruct?) { + guard let boundVal = x, let other = testGuardLetBinding8(#^GUARD_LET_BIND_8^#) else {} +} + // FOOSTRUCT_DOT: Begin completions // FOOSTRUCT_DOT-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]; @@ -623,3 +631,7 @@ func testGuardLetBinding5(x: FooStruct?) { // FOOSTRUCT_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .boolGen()[#Bool#]; // FOOSTRUCT_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .intGen()[#Int#]; // FOOSTRUCT_NODOT: End completions + +// FOOSTRUCT_LOCALVAL: Begin completions +// FOOSTRUCT_LOCALVAL-DAG: Decl[LocalVar]/Local{{(/TypeRelation\[Convertible\])?}}: boundVal[#FooStruct#]; +// FOOSTRUCT_LOCALVAL: End completions From a4105b1694d29ed2b2a35abb1f40da3d20d0effc Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 8 Jan 2020 23:23:53 -0800 Subject: [PATCH 353/478] [NFC] Clean the ClangImporter a bit Push some state closer to where it's actually needed and remove helper functions that aren't actually all that helpful. Replace these with an assertion for the real invariant here. --- lib/ClangImporter/ImportDecl.cpp | 58 +++++--------------------------- lib/ClangImporter/ImporterImpl.h | 12 ++++++- 2 files changed, 19 insertions(+), 51 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 8d3601813a1fe..cc6ff4324d3cd 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -4492,11 +4492,6 @@ namespace { const clang::ObjCProtocolList &clangProtocols, SmallVectorImpl &inheritedTypes); - /// Add conformances to the given Objective-C protocols to the - /// given declaration. - void addObjCProtocolConformances(Decl *decl, - ArrayRef protocols); - // Returns None on error. Returns nullptr if there is no type param list to // import or we suppress its import, as in the case of NSArray, NSSet, and // NSDictionary. @@ -4517,7 +4512,6 @@ namespace { /// methods become class methods on NSObject). void importMirroredProtocolMembers(const clang::ObjCContainerDecl *decl, DeclContext *dc, - ArrayRef protocols, SmallVectorImpl &members); void importNonOverriddenMirroredMethods(DeclContext *dc, @@ -6860,22 +6854,7 @@ void SwiftDeclConverter::importObjCProtocols( } } - addObjCProtocolConformances(decl, protocols); -} - -void SwiftDeclConverter::addObjCProtocolConformances( - Decl *decl, ArrayRef protocols) { - // Nothing to do for protocols. - if (isa(decl)) return; - Impl.recordImportedProtocols(decl, protocols); - - if (auto nominal = dyn_cast(decl)) { - nominal->setConformanceLoader(&Impl, 0); - } else { - auto ext = cast(decl); - ext->setConformanceLoader(&Impl, 0); - } } Optional SwiftDeclConverter::importObjCGenericParams( @@ -6940,22 +6919,18 @@ Optional SwiftDeclConverter::importObjCGenericParams( void SwiftDeclConverter::importMirroredProtocolMembers( const clang::ObjCContainerDecl *decl, DeclContext *dc, - ArrayRef protocols, SmallVectorImpl &members) { + SmallVectorImpl &members) { assert(dc); const clang::ObjCInterfaceDecl *interfaceDecl = nullptr; const ClangModuleUnit *declModule; const ClangModuleUnit *interfaceModule; - // 'protocols' is, for some reason, the full recursive expansion of - // the protocol hierarchy, so there's no need to recursively descend - // into inherited protocols. - // Try to import only the most specific methods with a particular name. // We use a MapVector to get deterministic iteration order later. llvm::MapVector> methodsByName; - for (auto proto : protocols) { + for (auto proto : Impl.getImportedProtocols(dc->getAsDecl())) { auto clangProto = cast_or_null(proto->getClangDecl()); if (!clangProto) @@ -8600,20 +8575,6 @@ bool ClangImporter::Implementation::addMemberAndAlternatesToExtension( return true; } -static ExtensionDecl * -figureOutTheDeclarationContextToImportInto(Decl *D, DeclContext *&DC, - IterableDeclContext *&IDC) { - if (auto *nominal = dyn_cast(D)) { - DC = nominal; - IDC = nominal; - return nullptr; - } - ExtensionDecl *ext = cast(D); - DC = ext; - IDC = ext; - return ext; -} - static void loadMembersOfBaseImportedFromClang(ExtensionDecl *ext) { const NominalTypeDecl *base = ext->getExtendedNominal(); auto *clangBase = base->getClangDecl(); @@ -8635,19 +8596,18 @@ void ClangImporter::Implementation::loadAllMembersOfObjcContainer( Instance->getSourceManager(), "loading members for"); - DeclContext *DC; - IterableDeclContext *IDC; - if (ExtensionDecl *ext = - figureOutTheDeclarationContextToImportInto(D, DC, IDC)) { - // If the base is also imported from Clang, load its members first. + assert(isa(D) || isa(D)); + if (auto *ext = dyn_cast(D)) { + // If the extended type is also imported from Clang, load its members first. loadMembersOfBaseImportedFromClang(ext); } startedImportingEntity(); SmallVector members; - collectMembersToAdd(objcContainer, D, DC, members); + collectMembersToAdd(objcContainer, D, cast(D), members); + auto *IDC = cast(D); for (auto member : members) { if (!isa(member)) IDC->addMember(member); @@ -8701,8 +8661,6 @@ void ClangImporter::Implementation::collectMembersToAdd( } SwiftDeclConverter converter(*this, CurrentVersion); - - auto protos = getImportedProtocols(D); if (auto clangClass = dyn_cast(objcContainer)) { objcContainer = clangClass = clangClass->getDefinition(); importInheritedConstructors(clangClass, cast(D), members); @@ -8713,7 +8671,7 @@ void ClangImporter::Implementation::collectMembersToAdd( // Import mirrored declarations for protocols to which this category // or extension conforms. // FIXME: This is supposed to be a short-term hack. - converter.importMirroredProtocolMembers(objcContainer, DC, protos, members); + converter.importMirroredProtocolMembers(objcContainer, DC, members); } void ClangImporter::Implementation::loadAllConformances( diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index bc4e7028c0158..11102dec231b6 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -1180,12 +1180,22 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// /// FIXME: This is all a hack; we should have lazier deserialization /// of protocols separate from their conformances. - void recordImportedProtocols(const Decl *decl, + void recordImportedProtocols(Decl *decl, ArrayRef protocols) { + // Nothing to do for protocols. + if (isa(decl)) return; + if (protocols.empty()) return; ImportedProtocols[decl] = SwiftContext.AllocateCopy(protocols); + + if (auto nominal = dyn_cast(decl)) { + nominal->setConformanceLoader(this, 0); + } else { + auto ext = cast(decl); + ext->setConformanceLoader(this, 0); + } } /// Retrieve the imported protocols for the given declaration. From 033ddffe425745d6c2f1b4fc802b87ac3f559243 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 9 Jan 2020 07:05:20 -0800 Subject: [PATCH 354/478] [AutoDiff] NFC: gardening. (#28928) Upstream minor changes from `tensorflow` branch. --- include/swift/AST/Attr.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 04f34ae1271f5..b20f08d2d30fa 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1755,10 +1755,10 @@ class DifferentiableAttr final // Print the attribute to the given stream. // If `omitWrtClause` is true, omit printing the `wrt:` clause. - // If `omitAssociatedFunctions` is true, omit printing associated functions. + // If `omitDerivativeFunctions` is true, omit printing derivative functions. void print(llvm::raw_ostream &OS, const Decl *D, bool omitWrtClause = false, - bool omitAssociatedFunctions = false) const; + bool omitDerivativeFunctions = false) const; static bool classof(const DeclAttribute *DA) { return DA->getKind() == DAK_Differentiable; @@ -1789,7 +1789,7 @@ class DerivativeAttr final private llvm::TrailingObjects { friend TrailingObjects; - /// The base type repr for the referenced original function. This field is + /// The base type for the referenced original declaration. This field is /// non-null only for parsed attributes that reference a qualified original /// declaration. This field is not serialized; type-checking uses it to /// resolve the original declaration, which is serialized. @@ -1883,7 +1883,7 @@ class TransposeAttr final private llvm::TrailingObjects { friend TrailingObjects; - /// The base type repr for the referenced original function. This field is + /// The base type for the referenced original declaration. This field is /// non-null only for parsed attributes that reference a qualified original /// declaration. This field is not serialized; type-checking uses it to /// resolve the original declaration, which is serialized. From ea3b6a02af77d16101f4e641afc4e554fe40a0d5 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 8 Jan 2020 13:16:16 +0100 Subject: [PATCH 355/478] stdlib: rewrite Array/ContiguousArray logic for reserving capacity and creating a new buffer. Share more code and avoid the large generic functions of ArrayProtocol. The result is a significant code size win. --- stdlib/public/core/Array.swift | 85 +++++++++++++++----- stdlib/public/core/ArrayShared.swift | 17 ++++ stdlib/public/core/ContiguousArray.swift | 86 ++++++++++++++++----- test/SILOptimizer/array_contentof_opt.swift | 11 +-- test/SILOptimizer/merge_exclusivity.swift | 3 - 5 files changed, 155 insertions(+), 47 deletions(-) diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index 93db94f22d136..425ff2a4378d8 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -346,7 +346,8 @@ extension Array { @_semantics("array.make_mutable") internal mutating func _makeMutableAndUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { - _buffer = _Buffer(copying: _buffer) + _createNewBuffer(bufferIsUnique: false, minimumCapacity: count, + growForAppend: false) } } @@ -1023,19 +1024,65 @@ extension Array: RangeReplaceableCollection { @inlinable @_semantics("array.mutate_unknown") public mutating func reserveCapacity(_ minimumCapacity: Int) { - if _buffer.requestUniqueMutableBackingBuffer( - minimumCapacity: minimumCapacity) == nil { + _reserveCapacityImpl(minimumCapacity: minimumCapacity, + growForAppend: false) + } - let newBuffer = _ContiguousArrayBuffer( - _uninitializedCount: count, minimumCapacity: minimumCapacity) + /// Reserves enough space to store `minimumCapacity` elements. + /// If a new buffer needs to be allocated and `growForAppend` is true, + /// the new capacity is calculated using `_growArrayCapacity`, but at least + /// kept at `minimumCapacity`. + @_alwaysEmitIntoClient + internal mutating func _reserveCapacityImpl( + minimumCapacity: Int, growForAppend: Bool + ) { + let isUnique = _buffer.isUniquelyReferenced() + if _slowPath(!isUnique || _getCapacity() < minimumCapacity) { + _createNewBuffer(bufferIsUnique: isUnique, + minimumCapacity: Swift.max(minimumCapacity, count), + growForAppend: growForAppend) + } + _internalInvariant(capacity >= minimumCapacity) + _internalInvariant(capacity == 0 || _buffer.isUniquelyReferenced()) + } + /// Creates a new buffer, replacing the current buffer. + /// + /// If `bufferIsUnique` is true, the buffer is assumed to be uniquely + /// referenced by this array and the elements are moved - instead of copied - + /// to the new buffer. + /// The `minimumCapacity` is the lower bound for the new capacity. + /// If `growForAppend` is true, the new capacity is calculated using + /// `_growArrayCapacity`, but at least kept at `minimumCapacity`. + @_alwaysEmitIntoClient + @inline(never) + internal mutating func _createNewBuffer( + bufferIsUnique: Bool, minimumCapacity: Int, growForAppend: Bool + ) { + let newCapacity = _growArrayCapacity(oldCapacity: _getCapacity(), + minimumCapacity: minimumCapacity, + growForAppend: growForAppend) + let count = _getCount() + _internalInvariant(newCapacity >= count) + + let newBuffer = _ContiguousArrayBuffer( + _uninitializedCount: count, minimumCapacity: newCapacity) + + if bufferIsUnique { + _internalInvariant(_buffer.isUniquelyReferenced()) + + // As an optimization, if the original buffer is unique, we can just move + // the elements instead of copying. + let dest = newBuffer.firstElementAddress + dest.moveInitialize(from: _buffer.firstElementAddress, + count: count) + _buffer.count = 0 + } else { _buffer._copyContents( - subRange: _buffer.indices, + subRange: 0..= minimumCapacity) + _buffer = _Buffer(_buffer: newBuffer, shiftedToStartIndex: 0) } /// Copy the contents of the current buffer to a new unique mutable buffer. @@ -1054,7 +1101,9 @@ extension Array: RangeReplaceableCollection { @_semantics("array.make_mutable") internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { - _copyToNewBuffer(oldCount: _buffer.count) + _createNewBuffer(bufferIsUnique: false, + minimumCapacity: count + 1, + growForAppend: true) } } @@ -1083,7 +1132,9 @@ extension Array: RangeReplaceableCollection { _buffer.isMutableAndUniquelyReferenced()) if _slowPath(oldCount + 1 > _buffer.capacity) { - _copyToNewBuffer(oldCount: oldCount) + _createNewBuffer(bufferIsUnique: true, + minimumCapacity: oldCount + 1, + growForAppend: true) } } @@ -1124,6 +1175,8 @@ extension Array: RangeReplaceableCollection { @inlinable @_semantics("array.append_element") public mutating func append(_ newElement: __owned Element) { + // Separating uniqueness check and capacity check allows hoisting the + // uniqueness check out of a loop. _makeUniqueAndReserveCapacityIfNotUnique() let oldCount = _getCount() _reserveCapacityAssumingUniqueBuffer(oldCount: oldCount) @@ -1183,16 +1236,10 @@ extension Array: RangeReplaceableCollection { @inlinable @_semantics("array.reserve_capacity_for_append") internal mutating func reserveCapacityForAppend(newElementsCount: Int) { - let oldCount = self.count - let oldCapacity = self.capacity - let newCount = oldCount + newElementsCount - // Ensure uniqueness, mutability, and sufficient storage. Note that // for consistency, we need unique self even if newElements is empty. - self.reserveCapacity( - newCount > oldCapacity ? - Swift.max(newCount, _growArrayCapacity(oldCapacity)) - : newCount) + _reserveCapacityImpl(minimumCapacity: self.count + newElementsCount, + growForAppend: true) } @inlinable diff --git a/stdlib/public/core/ArrayShared.swift b/stdlib/public/core/ArrayShared.swift index 1512260c55f3f..3e7d5939bac03 100644 --- a/stdlib/public/core/ArrayShared.swift +++ b/stdlib/public/core/ArrayShared.swift @@ -138,6 +138,23 @@ internal func _growArrayCapacity(_ capacity: Int) -> Int { return capacity * 2 } +@_alwaysEmitIntoClient +internal func _growArrayCapacity( + oldCapacity: Int, minimumCapacity: Int, growForAppend: Bool +) -> Int { + if growForAppend { + if oldCapacity < minimumCapacity { + // When appending to an array, grow exponentially. + return Swift.max(minimumCapacity, _growArrayCapacity(oldCapacity)) + } + return oldCapacity + } + // If not for append, just use the specified capacity, ignoring oldCapacity. + // This means that we "shrink" the buffer in case minimumCapacity is less + // than oldCapacity. + return minimumCapacity +} + //===--- generic helpers --------------------------------------------------===// extension _ArrayBufferProtocol { diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index ae1dc04163be5..69b93331389d8 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -67,7 +67,8 @@ extension ContiguousArray { @_semantics("array.make_mutable") internal mutating func _makeMutableAndUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { - _buffer = _Buffer(copying: _buffer) + _createNewBuffer(bufferIsUnique: false, minimumCapacity: count, + growForAppend: false) } } @@ -655,19 +656,64 @@ extension ContiguousArray: RangeReplaceableCollection { @inlinable @_semantics("array.mutate_unknown") public mutating func reserveCapacity(_ minimumCapacity: Int) { - if _buffer.requestUniqueMutableBackingBuffer( - minimumCapacity: minimumCapacity) == nil { + _reserveCapacityImpl(minimumCapacity: minimumCapacity, + growForAppend: false) + } + + /// Reserves enough space to store `minimumCapacity` elements. + /// If a new buffer needs to be allocated and `growForAppend` is true, + /// the new capacity is calculated using `_growArrayCapacity`. + @_alwaysEmitIntoClient + internal mutating func _reserveCapacityImpl( + minimumCapacity: Int, growForAppend: Bool + ) { + let isUnique = _buffer.isUniquelyReferenced() + if _slowPath(!isUnique || _getCapacity() < minimumCapacity) { + _createNewBuffer(bufferIsUnique: isUnique, + minimumCapacity: Swift.max(minimumCapacity, count), + growForAppend: growForAppend) + } + _internalInvariant(capacity >= minimumCapacity) + _internalInvariant(capacity == 0 || _buffer.isUniquelyReferenced()) + } + + /// Creates a new buffer, replacing the current buffer. + /// + /// If `bufferIsUnique` is true, the buffer is assumed to be uniquely + /// referenced by this array and the elements are moved - instead of copied - + /// to the new buffer. + /// The `minimumCapacity` is the lower bound for the new capacity. + /// If `growForAppend` is true, the new capacity is calculated using + /// `_growArrayCapacity`. + @_alwaysEmitIntoClient + @inline(never) + internal mutating func _createNewBuffer( + bufferIsUnique: Bool, minimumCapacity: Int, growForAppend: Bool + ) { + let newCapacity = _growArrayCapacity(oldCapacity: _getCapacity(), + minimumCapacity: minimumCapacity, + growForAppend: growForAppend) + let count = _getCount() + _internalInvariant(newCapacity >= count) - let newBuffer = _ContiguousArrayBuffer( - _uninitializedCount: count, minimumCapacity: minimumCapacity) + let newBuffer = _ContiguousArrayBuffer( + _uninitializedCount: count, minimumCapacity: newCapacity) + if bufferIsUnique { + _internalInvariant(_buffer.isUniquelyReferenced()) + + // As an optimization, if the original buffer is unique, we can just move + // the elements instead of copying. + let dest = newBuffer.firstElementAddress + dest.moveInitialize(from: _buffer.firstElementAddress, + count: count) + _buffer.count = 0 + } else { _buffer._copyContents( - subRange: _buffer.indices, + subRange: 0..= minimumCapacity) + _buffer = _Buffer(_buffer: newBuffer, shiftedToStartIndex: 0) } /// Copy the contents of the current buffer to a new unique mutable buffer. @@ -687,7 +733,9 @@ extension ContiguousArray: RangeReplaceableCollection { @_semantics("array.make_mutable") internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { - _copyToNewBuffer(oldCount: _buffer.count) + _createNewBuffer(bufferIsUnique: false, + minimumCapacity: count + 1, + growForAppend: true) } } @@ -716,7 +764,9 @@ extension ContiguousArray: RangeReplaceableCollection { _buffer.isMutableAndUniquelyReferenced()) if _slowPath(oldCount + 1 > _buffer.capacity) { - _copyToNewBuffer(oldCount: oldCount) + _createNewBuffer(bufferIsUnique: true, + minimumCapacity: oldCount + 1, + growForAppend: true) } } @@ -757,6 +807,8 @@ extension ContiguousArray: RangeReplaceableCollection { @inlinable @_semantics("array.append_element") public mutating func append(_ newElement: __owned Element) { + // Separating uniqueness check and capacity check allows hoisting the + // uniqueness check out of a loop. _makeUniqueAndReserveCapacityIfNotUnique() let oldCount = _getCount() _reserveCapacityAssumingUniqueBuffer(oldCount: oldCount) @@ -816,23 +868,17 @@ extension ContiguousArray: RangeReplaceableCollection { @inlinable @_semantics("array.reserve_capacity_for_append") internal mutating func reserveCapacityForAppend(newElementsCount: Int) { - let oldCount = self.count - let oldCapacity = self.capacity - let newCount = oldCount + newElementsCount - // Ensure uniqueness, mutability, and sufficient storage. Note that // for consistency, we need unique self even if newElements is empty. - self.reserveCapacity( - newCount > oldCapacity ? - Swift.max(newCount, _growArrayCapacity(oldCapacity)) - : newCount) + _reserveCapacityImpl(minimumCapacity: self.count + newElementsCount, + growForAppend: true) } @inlinable public mutating func _customRemoveLast() -> Element? { + _makeMutableAndUnique() let newCount = _getCount() - 1 _precondition(newCount >= 0, "Can't removeLast from an empty ContiguousArray") - _makeUniqueAndReserveCapacityIfNotUnique() let pointer = (_buffer.firstElementAddress + newCount) let element = pointer.move() _buffer.count = newCount diff --git a/test/SILOptimizer/array_contentof_opt.swift b/test/SILOptimizer/array_contentof_opt.swift index 7a0dfad4330ea..52aaa0c444e63 100644 --- a/test/SILOptimizer/array_contentof_opt.swift +++ b/test/SILOptimizer/array_contentof_opt.swift @@ -1,6 +1,10 @@ -// RUN: %target-swift-frontend -O -sil-verify-all -emit-sil -enforce-exclusivity=unchecked %s | %FileCheck %s +// RUN: %target-swift-frontend -O -sil-verify-all -emit-sil %s | %FileCheck %s // REQUIRES: swift_stdlib_no_asserts,optimized_stdlib +// Temporary disabled on linux. +// TODO: need to adapt some CHECK-lines for linux. +// REQUIRES: OS=macosx + // This is an end-to-end test of the array(contentsOf) -> array(Element) optimization // CHECK-LABEL: sil @{{.*}}testInt @@ -15,12 +19,9 @@ public func testInt(_ a: inout [Int]) { } // CHECK-LABEL: sil @{{.*}}testThreeInt -// CHECK-NOT: apply -// CHECK: [[FR:%[0-9]+]] = function_ref @$sSa15reserveCapacityyySiFSi_Tg5 +// CHECK: [[FR:%[0-9]+]] = function_ref @${{(sSa15reserveCapacityyySiFSi_Tg5|sSa16_createNewBuffer)}} // CHECK-NEXT: apply [[FR]] -// CHECK-NOT: apply // CHECK: [[F:%[0-9]+]] = function_ref @$sSa6appendyyxnFSi_Tg5 -// CHECK-NOT: apply // CHECK: apply [[F]] // CHECK-NEXT: apply [[F]] // CHECK-NEXT: apply [[F]] diff --git a/test/SILOptimizer/merge_exclusivity.swift b/test/SILOptimizer/merge_exclusivity.swift index 9e995df212d7d..986c69d1d7525 100644 --- a/test/SILOptimizer/merge_exclusivity.swift +++ b/test/SILOptimizer/merge_exclusivity.swift @@ -372,13 +372,10 @@ private struct EscapedTransforme: WriteProt { // TESTSIL-NEXT: [[B1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[REFADDR]] // TESTSIL: end_access [[B1]] // TESTSIL: [[BCONF:%.*]] = begin_access [modify] [dynamic] [[REFADDR]] -// TESTSIL: apply {{.*}} : $@convention(method) (Int, @inout Array) -> () // TESTSIL: end_access [[BCONF]] // TESTSIL: [[BCONF:%.*]] = begin_access [modify] [dynamic] [[REFADDR]] -// TESTSIL: apply {{.*}} : $@convention(method) (Int, @inout Array) -> () // TESTSIL: end_access [[BCONF]] // TESTSIL: [[BCONF:%.*]] = begin_access [modify] [dynamic] [[REFADDR]] -// TESTSIL: apply {{.*}} : $@convention(method) (Int, @inout Array) -> () // TESTSIL: end_access [[BCONF]] // TESTSIL-LABEL: } // end sil function '$s17merge_exclusivity14run_MergeTest9yySiF' @inline(never) From 88a9ebb9f806f60572f9d9d36fa1268cede20540 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 8 Jan 2020 13:21:45 +0100 Subject: [PATCH 356/478] stdlib: Don't request additional capacity in Array/ContiguousArray's remove. Just copy the buffer if it's not unique. This also implies that if there is a copy-on-write in remove, "shrink" the capacity of the new buffer to the required amount of elements (instead of copying the capacity of the original buffer). --- stdlib/public/core/Array.swift | 11 +++++----- stdlib/public/core/ContiguousArray.swift | 7 ++++--- test/stdlib/Inputs/CommonArrayTests.gyb | 26 ++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index 425ff2a4378d8..edeaea1cfafa4 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -1244,9 +1244,9 @@ extension Array: RangeReplaceableCollection { @inlinable public mutating func _customRemoveLast() -> Element? { + _makeMutableAndUnique() let newCount = _getCount() - 1 _precondition(newCount >= 0, "Can't removeLast from an empty Array") - _makeUniqueAndReserveCapacityIfNotUnique() let pointer = (_buffer.firstElementAddress + newCount) let element = pointer.move() _buffer.count = newCount @@ -1271,10 +1271,11 @@ extension Array: RangeReplaceableCollection { @inlinable @discardableResult public mutating func remove(at index: Int) -> Element { - _precondition(index < endIndex, "Index out of range") - _precondition(index >= startIndex, "Index out of range") - _makeUniqueAndReserveCapacityIfNotUnique() - let newCount = _getCount() - 1 + _makeMutableAndUnique() + let currentCount = _getCount() + _precondition(index < currentCount, "Index out of range") + _precondition(index >= 0, "Index out of range") + let newCount = currentCount - 1 let pointer = (_buffer.firstElementAddress + index) let result = pointer.move() pointer.moveInitialize(from: pointer + 1, count: newCount - index) diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index 69b93331389d8..6f865b407a809 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -903,9 +903,10 @@ extension ContiguousArray: RangeReplaceableCollection { @inlinable @discardableResult public mutating func remove(at index: Int) -> Element { - _precondition(index < endIndex, "Index out of range") - _precondition(index >= startIndex, "Index out of range") - _makeUniqueAndReserveCapacityIfNotUnique() + _makeMutableAndUnique() + let currentCount = _getCount() + _precondition(index < currentCount, "Index out of range") + _precondition(index >= 0, "Index out of range") let newCount = _getCount() - 1 let pointer = (_buffer.firstElementAddress + index) let result = pointer.move() diff --git a/test/stdlib/Inputs/CommonArrayTests.gyb b/test/stdlib/Inputs/CommonArrayTests.gyb index bd9d22118b165..e52a957d87dd5 100644 --- a/test/stdlib/Inputs/CommonArrayTests.gyb +++ b/test/stdlib/Inputs/CommonArrayTests.gyb @@ -106,12 +106,34 @@ ${Suite}.test("${ArrayType}/appendNonUnique") var x: ${ArrayType} = [] x.reserveCapacity(10002) let capacity = x.capacity - for _ in 1...10000 { + for _ in 1...100 { let y = x x.append(1) expectTrue(x.capacity == capacity) - let z = x + } +} + +% if ArrayType != 'ArraySlice': +${Suite}.test("${ArrayType}/removeNonUnique") + .code { + var x = ${ArrayType}(repeating: 27, count: 200) + x.reserveCapacity(10002) + for _ in 1...100 { + let y = x x.remove(at: 0) + expectTrue(x.capacity < 1000) + } +} +% end + +${Suite}.test("${ArrayType}/mutateNonUnique") + .code { + var x = ${ArrayType}(repeating: 27, count: 200) + x.reserveCapacity(10002) + for _ in 1...100 { + let y = x + x[0] = 0 + expectTrue(x.capacity < 1000) } } From bda8af0958e664d7a72ad8f7c8b631ac85952415 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 8 Jan 2020 13:48:01 +0100 Subject: [PATCH 357/478] stdlib: simplify Array/ContiguousArray's append(contentsOf:) Instead of calling ArrayBuffer's _arrayAppendSequence, inline the code. This saves some code size. --- stdlib/public/core/Array.swift | 19 +++++++++++++++++-- stdlib/public/core/ContiguousArray.swift | 19 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index edeaea1cfafa4..49cdacdd6e60c 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -1213,7 +1213,7 @@ extension Array: RangeReplaceableCollection { start: startNewElements, count: self.capacity - oldCount) - let (remainder,writtenUpTo) = buf.initialize(from: newElements) + var (remainder,writtenUpTo) = buf.initialize(from: newElements) // trap on underflow from the sequence's underestimate: let writtenCount = buf.distance(from: buf.startIndex, to: writtenUpTo) @@ -1229,7 +1229,22 @@ extension Array: RangeReplaceableCollection { if writtenUpTo == buf.endIndex { // there may be elements that didn't fit in the existing buffer, // append them in slow sequence-only mode - _buffer._arrayAppendSequence(IteratorSequence(remainder)) + var newCount = _getCount() + var nextItem = remainder.next() + while nextItem != nil { + reserveCapacityForAppend(newElementsCount: 1) + + let currentCapacity = _getCapacity() + let base = _buffer.firstElementAddress + + // fill while there is another item and spare capacity + while let next = nextItem, newCount < currentCapacity { + (base + newCount).initialize(to: next) + newCount += 1 + nextItem = remainder.next() + } + _buffer.count = newCount + } } } diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index 6f865b407a809..e4bb50f785242 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -845,7 +845,7 @@ extension ContiguousArray: RangeReplaceableCollection { start: startNewElements, count: self.capacity - oldCount) - let (remainder,writtenUpTo) = buf.initialize(from: newElements) + var (remainder,writtenUpTo) = buf.initialize(from: newElements) // trap on underflow from the sequence's underestimate: let writtenCount = buf.distance(from: buf.startIndex, to: writtenUpTo) @@ -861,7 +861,22 @@ extension ContiguousArray: RangeReplaceableCollection { if writtenUpTo == buf.endIndex { // there may be elements that didn't fit in the existing buffer, // append them in slow sequence-only mode - _buffer._arrayAppendSequence(IteratorSequence(remainder)) + var newCount = _getCount() + var nextItem = remainder.next() + while nextItem != nil { + reserveCapacityForAppend(newElementsCount: 1) + + let currentCapacity = _getCapacity() + let base = _buffer.firstElementAddress + + // fill while there is another item and spare capacity + while let next = nextItem, newCount < currentCapacity { + (base + newCount).initialize(to: next) + newCount += 1 + nextItem = remainder.next() + } + _buffer.count = newCount + } } } From 60a071892276f2253d80a42e8f085395a988dcef Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 8 Jan 2020 13:49:25 +0100 Subject: [PATCH 358/478] stdlib: simplify Array/ContiguousArray's withUnsafeMutableBufferPointer and withUnsafeMutableBufferPointer Again, to reduce code size --- stdlib/public/core/Array.swift | 14 +++----------- stdlib/public/core/ContiguousArray.swift | 14 +++----------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index 49cdacdd6e60c..5f3a3e3f0bc2e 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -1587,9 +1587,8 @@ extension Array { public mutating func withUnsafeMutableBufferPointer( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R { + _makeMutableAndUnique() let count = self.count - // Ensure unique storage - _buffer._outlinedMakeUniqueBuffer(bufferCount: count) // Ensure that body can't invalidate the storage or its bounds by // moving self into a temporary working array. @@ -1701,19 +1700,12 @@ extension Array { _precondition(subrange.upperBound <= _buffer.endIndex, "Array replace: subrange extends past the end") - let oldCount = _buffer.count let eraseCount = subrange.count let insertCount = newElements.count let growth = insertCount - eraseCount - if _buffer.requestUniqueMutableBackingBuffer( - minimumCapacity: oldCount + growth) != nil { - - _buffer.replaceSubrange( - subrange, with: insertCount, elementsOf: newElements) - } else { - _buffer._arrayOutOfPlaceReplace(subrange, with: newElements, count: insertCount) - } + reserveCapacityForAppend(newElementsCount: growth) + _buffer.replaceSubrange(subrange, with: insertCount, elementsOf: newElements) } } diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index e4bb50f785242..7520b18b8d9f2 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -1168,9 +1168,8 @@ extension ContiguousArray { public mutating func withUnsafeMutableBufferPointer( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R { + _makeMutableAndUnique() let count = self.count - // Ensure unique storage - _buffer._outlinedMakeUniqueBuffer(bufferCount: count) // Ensure that body can't invalidate the storage or its bounds by // moving self into a temporary working array. @@ -1283,19 +1282,12 @@ extension ContiguousArray { _precondition(subrange.upperBound <= _buffer.endIndex, "ContiguousArray replace: subrange extends past the end") - let oldCount = _buffer.count let eraseCount = subrange.count let insertCount = newElements.count let growth = insertCount - eraseCount - if _buffer.requestUniqueMutableBackingBuffer( - minimumCapacity: oldCount + growth) != nil { - - _buffer.replaceSubrange( - subrange, with: insertCount, elementsOf: newElements) - } else { - _buffer._arrayOutOfPlaceReplace(subrange, with: newElements, count: insertCount) - } + reserveCapacityForAppend(newElementsCount: growth) + _buffer.replaceSubrange(subrange, with: insertCount, elementsOf: newElements) } } From 27b01e3eb05c5cf2749bf307a0978a80c6019caa Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Thu, 9 Jan 2020 11:36:58 -0300 Subject: [PATCH 359/478] [test] Adding validation test for generic function match --- validation-test/Sema/generic_function_signature.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 validation-test/Sema/generic_function_signature.swift diff --git a/validation-test/Sema/generic_function_signature.swift b/validation-test/Sema/generic_function_signature.swift new file mode 100644 index 0000000000000..92eee65355e4e --- /dev/null +++ b/validation-test/Sema/generic_function_signature.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend -typecheck %s +enum E {} + +protocol P { + associatedtype T + var closure: () -> E { get } +} + +struct Concrete: P { + let closure: () -> E +} From 6a7f84048d01b30fec3c33f1c62a672c90b380fe Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 9 Jan 2020 10:33:22 -0800 Subject: [PATCH 360/478] [AutoDiff upstream] Store `@differentiable` original declaration. (#29082) Store in `DifferentiableAttr` the original declaration on which the attribute is declared. The original declaration is resolved during parsing and deserialization (not yet upstreamed). Progress towards TF-828: upstream `@differentiable` attribute type-checking. --- include/swift/AST/Attr.h | 13 +++++++++++-- lib/AST/Attr.cpp | 10 +++++++++- lib/Parse/ParseDecl.cpp | 29 ++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index b20f08d2d30fa..b42fdaf4da5a1 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1648,6 +1648,10 @@ class DifferentiableAttr final ParsedAutoDiffParameter> { friend TrailingObjects; + /// The declaration on which the `@differentiable` attribute is declared. + /// May not be a valid declaration for `@differentiable` attributes. + /// Resolved during parsing and deserialization. + Decl *OriginalDeclaration = nullptr; /// Whether this function is linear (optional). bool Linear; /// The number of parsed differentiability parameters specified in 'wrt:'. @@ -1703,6 +1707,12 @@ class DifferentiableAttr final Optional vjp, GenericSignature derivativeGenSig); + Decl *getOriginalDeclaration() const { return OriginalDeclaration; } + + /// Sets the original declaration on which this attribute is declared. + /// Should only be used by parsing and deserialization. + void setOriginalDeclaration(Decl *originalDeclaration); + /// Get the optional 'jvp:' function name and location. /// Use this instead of `getJVPFunction` to check whether the attribute has a /// registered JVP. @@ -1756,8 +1766,7 @@ class DifferentiableAttr final // Print the attribute to the given stream. // If `omitWrtClause` is true, omit printing the `wrt:` clause. // If `omitDerivativeFunctions` is true, omit printing derivative functions. - void print(llvm::raw_ostream &OS, const Decl *D, - bool omitWrtClause = false, + void print(llvm::raw_ostream &OS, const Decl *D, bool omitWrtClause = false, bool omitDerivativeFunctions = false) const; static bool classof(const DeclAttribute *DA) { diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 46d80f1c8c8d2..61e91cf14dc07 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1462,7 +1462,8 @@ DifferentiableAttr::DifferentiableAttr(Decl *original, bool implicit, Optional vjp, GenericSignature derivativeGenSig) : DeclAttribute(DAK_Differentiable, atLoc, baseRange, implicit), - Linear(linear), JVP(std::move(jvp)), VJP(std::move(vjp)) { + OriginalDeclaration(original), Linear(linear), JVP(std::move(jvp)), + VJP(std::move(vjp)) { setParameterIndices(parameterIndices); setDerivativeGenericSignature(derivativeGenSig); } @@ -1497,6 +1498,13 @@ DifferentiableAttr::create(AbstractFunctionDecl *original, bool implicit, std::move(vjp), derivativeGenSig); } +void DifferentiableAttr::setOriginalDeclaration(Decl *originalDeclaration) { + assert(originalDeclaration && "Original declaration must be non-null"); + assert(!OriginalDeclaration && + "Original declaration cannot have already been set"); + OriginalDeclaration = originalDeclaration; +} + void DifferentiableAttr::setJVPFunction(FuncDecl *decl) { JVPFunction = decl; if (decl && !JVP) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 471f0f13876ea..95f3052810952 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3473,6 +3473,18 @@ void Parser::setLocalDiscriminatorToParamList(ParameterList *PL) { } } +/// Set the original declaration in `@differentiable` attributes. +/// +/// Necessary because `Parser::parseNewDeclAttribute` (which calls +/// `Parser::parseDifferentiableAttribute`) does not have access to the +/// parent declaration of parsed attributes. +static void +setOriginalDeclarationForDifferentiableAttributes(DeclAttributes attrs, + Decl *D) { + for (auto *attr : attrs.getAttributes()) + const_cast(attr)->setOriginalDeclaration(D); +} + /// Parse a single syntactic declaration and return a list of decl /// ASTs. This can return multiple results for var decls that bind to multiple /// values, structs that define a struct decl and a constructor, etc. @@ -3873,7 +3885,8 @@ Parser::parseDecl(ParseDeclOptions Flags, if (DeclResult.isNonNull()) { Decl *D = DeclResult.get(); if (!declWasHandledAlready(D)) - Handler(DeclResult.get()); + Handler(D); + setOriginalDeclarationForDifferentiableAttributes(D->getAttrs(), D); } if (!DeclResult.isParseError()) { @@ -5581,6 +5594,11 @@ Parser::parseDeclVarGetSet(Pattern *pattern, ParseDeclOptions Flags, accessors.record(*this, PrimaryVar, Invalid); + // Set original declaration in `@differentiable` attributes. + for (auto *accessor : accessors.Accessors) + setOriginalDeclarationForDifferentiableAttributes(accessor->getAttrs(), + accessor); + return makeParserResult(PrimaryVar); } @@ -5836,6 +5854,10 @@ Parser::parseDeclVar(ParseDeclOptions Flags, VD->setStatic(StaticLoc.isValid()); VD->getAttrs() = Attributes; setLocalDiscriminator(VD); + + // Set original declaration in `@differentiable` attributes. + setOriginalDeclarationForDifferentiableAttributes(Attributes, VD); + Decls.push_back(VD); if (hasOpaqueReturnTy && sf && !InInactiveClauseEnvironment) { sf->addUnvalidatedDeclWithOpaqueResultType(VD); @@ -7083,6 +7105,11 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, accessors.record(*this, Subscript, (Invalid || !Status.isSuccess())); + // Set original declaration in `@differentiable` attributes. + for (auto *accessor : accessors.Accessors) + setOriginalDeclarationForDifferentiableAttributes(accessor->getAttrs(), + accessor); + // No need to setLocalDiscriminator because subscripts cannot // validly appear outside of type decls. return makeParserResult(Status, Subscript); From 9eca6153d646875c31978ee480c97a966bc34453 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Thu, 9 Jan 2020 13:43:05 -0800 Subject: [PATCH 361/478] [Update Checkout] Add master-rebranch scheme --- .../update-checkout-config.json | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index 476432e55bffa..969612f899647 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -268,6 +268,30 @@ "sourcekit-lsp": "swift-5.2-branch", "swift-format": "master" } + }, + "master-rebranch": { + "aliases": ["master-rebranch", "swift/master-rebranch"], + "repos": { + "llvm-project": "swift/master-rebranch", + "swift": "master-rebranch", + "cmark": "master", + "llbuild": "master", + "swift-tools-support-core": "master", + "swiftpm": "master", + "swift-syntax": "master", + "swift-stress-tester": "master", + "swift-corelibs-xctest": "master", + "swift-corelibs-foundation": "master", + "swift-corelibs-libdispatch": "master", + "swift-integration-tests": "master", + "swift-xcode-playground-support": "master", + "ninja": "release", + "icu": "release-65-1", + "cmake": "v3.15.1", + "indexstore-db": "master", + "sourcekit-lsp": "master", + "swift-format": "master" + } } } } From cdb4072e4b1fb4a42f8be5225186706cb9e2cbbc Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Thu, 9 Jan 2020 14:21:54 -0800 Subject: [PATCH 362/478] [build][gardening] obtain CMAKE_OSX_SYSROOT value once (#29098) Supports rdar://problem/58347344 --- utils/build-script-impl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 2cdb51aa8d8d0..4f28e5084133d 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -537,15 +537,17 @@ function set_build_options_for_host() { done fi + cmake_os_sysroot="$(xcrun --sdk ${platform} --show-sdk-path)" + cmark_cmake_options=( -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${platform} --show-sdk-path)" + -DCMAKE_OSX_SYSROOT:PATH="${cmake_os_sysroot}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${cmake_osx_deployment_target}" ) llvm_cmake_options=( -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING="${cmake_osx_deployment_target}" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${platform} --show-sdk-path)" + -DCMAKE_OSX_SYSROOT:PATH="${cmake_os_sysroot}" -DCOMPILER_RT_ENABLE_IOS:BOOL=FALSE -DCOMPILER_RT_ENABLE_WATCHOS:BOOL=FALSE -DCOMPILER_RT_ENABLE_TVOS:BOOL=FALSE From 5b320992ea24c32243c2fef565a86f2e31e9deb8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 9 Jan 2020 14:38:45 -0800 Subject: [PATCH 363/478] [Constraint solver] Use a constraint system to apply all function builders. When type checking the body of a function declaration that has a function builder on it (e.g., `@ViewBuilder var body: some View { ... }`), create a constraint system that is responsible for constraint generation and application, sending function declarations through the same code paths used by closures. --- include/swift/AST/DiagnosticsSema.def | 7 + lib/Sema/BuilderTransform.cpp | 184 ++++++++++++++---- lib/Sema/CSApply.cpp | 31 ++- lib/Sema/CSBindings.cpp | 3 +- lib/Sema/CSDiag.cpp | 4 + lib/Sema/CSSimplify.cpp | 15 +- lib/Sema/ConstraintSystem.h | 12 ++ lib/Sema/TypeCheckStmt.cpp | 23 ++- lib/Sema/TypeChecker.h | 13 +- test/Constraints/function_builder_diags.swift | 13 ++ 10 files changed, 249 insertions(+), 56 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 990e072795f72..95f370a9ab793 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4808,6 +4808,13 @@ NOTE(previous_function_builder_here, none, "previous function builder specified here", ()) ERROR(function_builder_arguments, none, "function builder attributes cannot have arguments", ()) +WARNING(function_builder_disabled_by_return, none, + "application of function builder %0 disabled by explicit 'return' " + "statement", (Type)) +NOTE(function_builder_remove_attr, none, + "remove the attribute to explicitly disable the function builder", ()) +NOTE(function_builder_remove_returns, none, + "remove 'return' statements to apply the function builder", ()) //------------------------------------------------------------------------------ // MARK: differentiable programming diagnostics diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index fdeec6c1c041b..be601b31c226d 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "ConstraintSystem.h" +#include "SolutionResult.h" #include "TypeChecker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" @@ -487,31 +488,128 @@ class BuilderClosureVisitor } // end anonymous namespace -BraceStmt * -TypeChecker::applyFunctionBuilderBodyTransform(FuncDecl *FD, - BraceStmt *body, - Type builderType) { - // Try to build a single result expression. - auto &ctx = FD->getASTContext(); - BuilderClosureVisitor visitor(ctx, nullptr, - /*wantExpr=*/true, builderType); - Expr *returnExpr = visitor.visit(body); - if (!returnExpr) - return nullptr; +/// Find the return statements in the given body, which block the application +/// of a function builder. +static std::vector findReturnStatements(AnyFunctionRef fn); + +Optional TypeChecker::applyFunctionBuilderBodyTransform( + FuncDecl *func, Type builderType) { + // Pre-check the body: pre-check any expressions in it and look + // for return statements. + // + // If we encountered an error or there was an explicit result type, + // bail out and report that to the caller. + auto &ctx = func->getASTContext(); + auto request = PreCheckFunctionBuilderRequest{func}; + switch (evaluateOrDefault( + ctx.evaluator, request, FunctionBuilderClosurePreCheck::Error)) { + case FunctionBuilderClosurePreCheck::Okay: + // If the pre-check was okay, apply the function-builder transform. + break; - // Make sure we have a usable result type for the body. - Type returnType = AnyFunctionRef(FD).getBodyResultType(); - if (!returnType || returnType->hasError()) + case FunctionBuilderClosurePreCheck::Error: return nullptr; - auto loc = returnExpr->getStartLoc(); - auto returnStmt = new (ctx) ReturnStmt(loc, returnExpr, /*implicit*/ true); - return BraceStmt::create(ctx, body->getLBraceLoc(), { returnStmt }, - body->getRBraceLoc()); + case FunctionBuilderClosurePreCheck::HasReturnStmt: { + // One or more explicit 'return' statements were encountered, which + // disables the function builder transform. Warn when we do this. + auto returnStmts = findReturnStatements(func); + assert(!returnStmts.empty()); + + ctx.Diags.diagnose( + returnStmts.front()->getReturnLoc(), + diag::function_builder_disabled_by_return, builderType); + + // Note that one can remove the function builder attribute. + auto attr = func->getAttachedFunctionBuilder(); + if (!attr) { + if (auto accessor = dyn_cast(func)) { + attr = accessor->getStorage()->getAttachedFunctionBuilder(); + } + } + + if (attr) { + ctx.Diags.diagnose( + attr->getLocation(), diag::function_builder_remove_attr) + .fixItRemove(attr->getRangeWithAt()); + attr->setInvalid(); + } + + // Note that one can remove all of the return statements. + { + auto diag = ctx.Diags.diagnose( + returnStmts.front()->getReturnLoc(), + diag::function_builder_remove_returns); + for (auto returnStmt : returnStmts) { + diag.fixItRemove(returnStmt->getReturnLoc()); + } + } + + return None; + } + } + + ConstraintSystemOptions options = ConstraintSystemFlags::AllowFixes; + auto resultInterfaceTy = func->getResultInterfaceType(); + auto resultContextType = func->mapTypeIntoContext(resultInterfaceTy); + + // Determine whether we're inferring the underlying type for the opaque + // result type of this function. + ConstraintKind resultConstraintKind = ConstraintKind::Conversion; + if (auto opaque = resultContextType->getAs()) { + if (opaque->getDecl()->isOpaqueReturnTypeOfFunction(func)) { + options |= ConstraintSystemFlags::UnderlyingTypeForOpaqueReturnType; + resultConstraintKind = ConstraintKind::OpaqueUnderlyingType; + } + } + + // Build a constraint system in which we can check the body of the function. + ConstraintSystem cs(func, options); + + // FIXME: check the result + cs.matchFunctionBuilder(func, builderType, resultContextType, + resultConstraintKind, + /*calleeLocator=*/nullptr, + /*FIXME:*/ConstraintLocatorBuilder(nullptr)); + + // Solve the constraint system. + SmallVector solutions; + if (cs.solve(solutions) || solutions.size() != 1) { + // Try to fix the system or provide a decent diagnostic. + auto salvagedResult = cs.salvage(); + switch (salvagedResult.getKind()) { + case SolutionResult::Kind::Success: + solutions.clear(); + solutions.push_back(std::move(salvagedResult).takeSolution()); + break; + + case SolutionResult::Kind::Error: + case SolutionResult::Kind::Ambiguous: + return nullptr; + + case SolutionResult::Kind::UndiagnosedError: + cs.diagnoseFailureFor(SolutionApplicationTarget(func)); + salvagedResult.markAsDiagnosed(); + return nullptr; + + case SolutionResult::Kind::TooComplex: + func->diagnose(diag::expression_too_complex) + .highlight(func->getBodySourceRange()); + salvagedResult.markAsDiagnosed(); + return nullptr; + } + + // The system was salvaged; continue on as if nothing happened. + } + + // Apply the solution to the function body. + return cast_or_null( + cs.applySolutionToBody(solutions.front(), func)); } ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder( AnyFunctionRef fn, Type builderType, Type bodyResultType, + ConstraintKind bodyResultConstraintKind, ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator) { auto builder = builderType->getAnyNominal(); assert(builder && "Bad function builder type"); @@ -524,7 +622,7 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder( return getTypeMatchSuccess(); } - // Pre-check the closure body: pre-check any expressions in it and look + // Pre-check the body: pre-check any expressions in it and look // for return statements. auto request = PreCheckFunctionBuilderRequest{fn}; switch (evaluateOrDefault(getASTContext().evaluator, request, @@ -614,11 +712,12 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder( }) == functionBuilderTransformed.end() && "already transformed this closure along this path!?!"); functionBuilderTransformed.push_back( - std::make_pair(fn, - AppliedBuilderTransform{builderType, singleExpr})); + std::make_pair( + fn, + AppliedBuilderTransform{builderType, singleExpr, bodyResultType})); // Bind the body result type to the type of the transformed expression. - addConstraint(ConstraintKind::Equal, bodyResultType, transformedType, + addConstraint(bodyResultConstraintKind, transformedType, bodyResultType, locator); return getTypeMatchSuccess(); } @@ -628,10 +727,17 @@ namespace { /// Pre-check all the expressions in the closure body. class PreCheckFunctionBuilderApplication : public ASTWalker { AnyFunctionRef Fn; - bool HasReturnStmt = false; + bool SkipPrecheck = false; + std::vector ReturnStmts; bool HasError = false; + + bool hasReturnStmt() const { return !ReturnStmts.empty(); } + public: - PreCheckFunctionBuilderApplication(AnyFunctionRef fn) : Fn(fn) {} + PreCheckFunctionBuilderApplication(AnyFunctionRef fn, bool skipPrecheck) + : Fn(fn), SkipPrecheck(skipPrecheck) {} + + const std::vector getReturnStmts() const { return ReturnStmts; } FunctionBuilderClosurePreCheck run() { Stmt *oldBody = Fn.getBody(); @@ -639,14 +745,13 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { Stmt *newBody = oldBody->walk(*this); // If the walk was aborted, it was because we had a problem of some kind. - assert((newBody == nullptr) == (HasError || HasReturnStmt) && + assert((newBody == nullptr) == HasError && "unexpected short-circuit while walking body"); - if (!newBody) { - if (HasError) - return FunctionBuilderClosurePreCheck::Error; + if (HasError) + return FunctionBuilderClosurePreCheck::Error; + if (hasReturnStmt()) return FunctionBuilderClosurePreCheck::HasReturnStmt; - } assert(oldBody == newBody && "pre-check walk wasn't in-place?"); @@ -657,7 +762,8 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { // Pre-check the expression. If this fails, abort the walk immediately. // Otherwise, replace the expression with the result of pre-checking. // In either case, don't recurse into the expression. - if (ConstraintSystem::preCheckExpression(E, /*DC*/ Fn.getAsDeclContext())) { + if (!SkipPrecheck && + ConstraintSystem::preCheckExpression(E, /*DC*/ Fn.getAsDeclContext())) { HasError = true; return std::make_pair(false, nullptr); } @@ -666,10 +772,12 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { } std::pair walkToStmtPre(Stmt *S) override { - // If we see a return statement, abort the walk immediately. - if (isa(S)) { - HasReturnStmt = true; - return std::make_pair(false, nullptr); + // If we see a return statement, note it.. + if (auto returnStmt = dyn_cast(S)) { + if (!returnStmt->isImplicit()) { + ReturnStmts.push_back(returnStmt); + return std::make_pair(false, S); + } } // Otherwise, recurse into the statement normally. @@ -688,5 +796,11 @@ PreCheckFunctionBuilderRequest::evaluate(Evaluator &eval, return FunctionBuilderClosurePreCheck::Okay; } - return PreCheckFunctionBuilderApplication(fn).run(); + return PreCheckFunctionBuilderApplication(fn, false).run(); +} + +std::vector findReturnStatements(AnyFunctionRef fn) { + PreCheckFunctionBuilderApplication precheck(fn, true); + (void)precheck.run(); + return precheck.getReturnStmts(); } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 760dcc5f2d71c..28bc2e78ecf28 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7239,15 +7239,30 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( if (auto expr = target.getAsExpr()) { result = expr->walk(walker); } else { - // FIXME: Implement this! - llvm_unreachable("Not yet implemented"); - -#if false auto fn = *target.getAsFunction(); - auto transform = rewriter.getAppliedBuilderTransform(fn); - assert(transform && "Not applying builder transform?"); - result = walker.applyFunctionBuilderBodyTransform(fn, transform); -#endif + + // Dig out the function builder transformation we applied. + auto transformed = solution.functionBuilderTransformed.find(fn); + assert(transformed != solution.functionBuilderTransformed.end()); + + auto singleExpr = transformed->second.singleExpr; + singleExpr = singleExpr->walk(walker); + if (!singleExpr) + return result; + + singleExpr = rewriter.coerceToType(singleExpr, + transformed->second.bodyResultType, + getConstraintLocator(singleExpr)); + if (!singleExpr) + return result; + + ASTContext &ctx = getASTContext(); + auto returnStmt = new (ctx) ReturnStmt( + singleExpr->getStartLoc(), singleExpr, /*implicit=*/true); + auto braceStmt = BraceStmt::create( + ctx, returnStmt->getStartLoc(), ASTNode(returnStmt), + returnStmt->getEndLoc(), /*implicit=*/true); + result = braceStmt; } if (result.isNull()) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index ab5bf7ffa7283..b7b5cbf4b0934 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -969,7 +969,8 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { if (Binding.hasDefaultedLiteralProtocol()) { type = cs.openUnboundGenericType(type, dstLocator); type = type->reconstituteSugar(/*recursive=*/false); - } else if (srcLocator->isLastElement() && + } else if (srcLocator && + srcLocator->isLastElement() && !type->hasTypeVariable() && cs.isCollectionType(type)) { // If the type binding comes from the argument conversion, let's // instead of binding collection types directly, try to bind diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index ccdcf68523974..246cbbe958edd 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -2455,6 +2455,10 @@ void ConstraintSystem::diagnoseFailureFor(SolutionApplicationTarget target) { // then it must be well-formed... but is ambiguous. Handle this by diagnostic // various cases that come up. diagnosis.diagnoseAmbiguity(expr); + } else { + // Emit a poor fallback message. + getASTContext().Diags.diagnose( + target.getAsFunction()->getLoc(), diag::failed_to_produce_diagnostic); } } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 2cfe967574bfd..6fd784efe00d3 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1149,7 +1149,7 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( auto result = cs.matchFunctionBuilder( closure, functionBuilderType, closureType->castTo()->getResult(), - calleeLocator, loc); + ConstraintKind::Conversion, calleeLocator, loc); if (result.isFailure()) return result; } @@ -6468,6 +6468,16 @@ ConstraintSystem::simplifyOneWayConstraint( return SolutionKind::Unsolved; } + // Propagate holes through one-way constraints. + if (secondSimplified->isHole()) { + first.visit([&](Type subType) { + if (auto *typeVar = subType->getAs()) + recordPotentialHole(typeVar); + }); + + return SolutionKind::Solved; + } + // Translate this constraint into a one-way binding constraint. return matchTypes(first, secondSimplified, ConstraintKind::Equal, flags, locator); @@ -7447,8 +7457,9 @@ ConstraintSystem::simplifyApplicableFnConstraint( } // If right-hand side is a type variable, the constraint is unsolved. - if (desugar2->isTypeVariableOrMember()) + if (desugar2->isTypeVariableOrMember()) { return formUnsolved(); + } // Strip the 'ApplyFunction' off the locator. // FIXME: Perhaps ApplyFunction can go away entirely? diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 3d376d096a83e..4e4610ea32f73 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -668,6 +668,10 @@ struct AppliedBuilderTransform { /// The single expression to which the closure was transformed. Expr *singleExpr; + + /// The result type of the body, to which the returned expression will be + /// converted. + Type bodyResultType; }; /// Describes the fixed score of a solution to the constraint system. @@ -3479,6 +3483,7 @@ class ConstraintSystem { /// Apply the given function builder to the closure expression. TypeMatchResult matchFunctionBuilder( AnyFunctionRef fn, Type builderType, Type bodyResultType, + ConstraintKind bodyResultConstraintKind, ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator); private: @@ -3993,6 +3998,13 @@ class ConstraintSystem { performingDiagnostics).get(); } + /// Apply a given solution to the body of the given function. + BraceStmt *applySolutionToBody(Solution &solution, AnyFunctionRef fn) { + return cast_or_null( + applySolutionImpl(solution, fn, Type(), false, false) + .dyn_cast()); + } + /// Reorder the disjunctive clauses for a given expression to /// increase the likelihood that a favored constraint will be successfully /// resolved before any others. diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index d8e0ea2eba617..20ec78994476b 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1980,12 +1980,17 @@ TypeCheckFunctionBodyUntilRequest::evaluate(Evaluator &evaluator, if (!body || AFD->isBodyTypeChecked()) return false; + bool alreadyTypeChecked = false; if (auto *func = dyn_cast(AFD)) { if (Type builderType = getFunctionBuilderType(func)) { - body = TypeChecker::applyFunctionBuilderBodyTransform(func, body, - builderType); - if (!body) - return true; + if (auto optBody = + TypeChecker::applyFunctionBuilderBodyTransform(func, builderType)) { + if (!*optBody) + return true; + + body = *optBody; + alreadyTypeChecked = true; + } } else if (func->hasSingleExpressionBody() && func->getResultInterfaceType()->isVoid()) { // The function returns void. We don't need an explicit return, no matter @@ -2013,9 +2018,13 @@ TypeCheckFunctionBodyUntilRequest::evaluate(Evaluator &evaluator, if (ctx.LangOpts.EnableASTScopeLookup) ASTScope::expandFunctionBody(AFD); - StmtChecker SC(AFD); - SC.EndTypeCheckLoc = endTypeCheckLoc; - bool hadError = SC.typeCheckBody(body); + // Type check the function body if needed. + bool hadError = false; + if (!alreadyTypeChecked) { + StmtChecker SC(AFD); + SC.EndTypeCheckLoc = endTypeCheckLoc; + hadError = SC.typeCheckBody(body); + } // If this was a function with a single expression body, let's see // if implicit return statement came out to be `Never` which means diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 13a5aac4f5ba2..bdd054f08e6cb 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -700,9 +700,16 @@ class TypeChecker final { SourceLoc EndTypeCheckLoc); static bool typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD); - static BraceStmt *applyFunctionBuilderBodyTransform(FuncDecl *FD, - BraceStmt *body, - Type builderType); + /// Try to apply the function builder transform of the given builder type + /// to the body of the function. + /// + /// \returns \c None if the builder transformation cannot be applied at all, + /// e.g., because of a \c return statement. Otherwise, returns either the + /// fully type-checked body of the function (on success) or a \c nullptr + /// value if an error occurred while type checking the transformed body. + static Optional applyFunctionBuilderBodyTransform( + FuncDecl *func, Type builderType); + static bool typeCheckClosureBody(ClosureExpr *closure); static bool typeCheckTapBody(TapExpr *expr, DeclContext *DC); diff --git a/test/Constraints/function_builder_diags.swift b/test/Constraints/function_builder_diags.swift index 9d66e0123796a..08930eff733d6 100644 --- a/test/Constraints/function_builder_diags.swift +++ b/test/Constraints/function_builder_diags.swift @@ -234,3 +234,16 @@ tuplify(true) { x in #warning("oops") // expected-warning{{oops}} 3.14159 } + +struct MyTuplifiedStruct { + var condition: Bool + + @TupleBuilder var computed: some Any { // expected-note{{remove the attribute to explicitly disable the function builder}}{{3-17=}} + if condition { + return 17 // expected-warning{{application of function builder 'TupleBuilder' disabled by explicit 'return' statement}} + // expected-note@-1{{remove 'return' statements to apply the function builder}}{{7-14=}}{{12-19=}} + } else { + return 42 + } + } +} From a3b68e6df55c5c6df2b0880bba94be53594a9f51 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Jan 2020 15:19:11 -0800 Subject: [PATCH 364/478] [silgen] When SILGenLValue accesses ref_elt_addr, emit unsafe access for immutable or non accessing uses instead of not emitting any begin_access. --- lib/SILGen/SILGenLValue.cpp | 17 ++++++++++------- test/SILGen/access_marker_gen.swift | 6 +++--- test/SILGen/guaranteed_self.swift | 11 ++++++++--- test/SILGen/properties.swift | 17 ++++++++++++----- test/SILGen/reabstract-tuple.swift | 4 +++- test/SILGen/super_init_refcounting.swift | 8 ++++++-- test/SILGen/unowned.swift | 3 ++- ...finite_init_failable_initializers_objc.swift | 4 +++- 8 files changed, 47 insertions(+), 23 deletions(-) diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 747c6a97d1922..8b3b7e0e825db 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -739,13 +739,16 @@ namespace { SGF.B.createRefElementAddr(loc, base.getUnmanagedValue(), Field, SubstFieldType); - // Avoid emitting access markers completely for non-accesses or immutable - // declarations. Access marker verification is aware of these cases. - if (!IsNonAccessing && !Field->isLet()) { - if (auto enforcement = SGF.getDynamicEnforcement(Field)) { - result = enterAccessScope(SGF, loc, result, getTypeData(), - getAccessKind(), *enforcement); - } + // Avoid emitting non-trivial access markers for non-accesses and + // immutable values. + auto enforcement = SGF.getDynamicEnforcement(Field); + if (enforcement && !IsNonAccessing && !Field->isLet()) { + result = enterAccessScope(SGF, loc, result, getTypeData(), + getAccessKind(), *enforcement); + } else { + result = + enterAccessScope(SGF, loc, result, getTypeData(), getAccessKind(), + SILAccessEnforcement::Unsafe); } return ManagedValue::forLValue(result); diff --git a/test/SILGen/access_marker_gen.swift b/test/SILGen/access_marker_gen.swift index 43d8761643695..a4608c0227149 100644 --- a/test/SILGen/access_marker_gen.swift +++ b/test/SILGen/access_marker_gen.swift @@ -124,9 +124,9 @@ func testClassLetProperty(c: C) -> Int { // CHECK-LABEL: sil hidden [ossa] @$s17access_marker_gen20testClassLetProperty1cSiAA1CC_tF : $@convention(thin) (@guaranteed C) -> Int { // CHECK: bb0(%0 : @guaranteed $C): // CHECK: [[ADR:%.*]] = ref_element_addr %{{.*}} : $C, #C.z -// CHECK-NOT: begin_access -// CHECK: %{{.*}} = load [trivial] [[ADR]] : $*Int -// CHECK-NOT: end_access +// CHECK: [[ADR_ACCESS:%.*]] = begin_access [read] [unsafe] [[ADR]] +// CHECK: %{{.*}} = load [trivial] [[ADR_ACCESS]] : $*Int +// CHECK: end_access [[ADR_ACCESS]] // CHECK-NOT: destroy_value %0 : $C // CHECK: return %{{.*}} : $Int // CHECK-LABEL: } // end sil function '$s17access_marker_gen20testClassLetProperty1cSiAA1CC_tF' diff --git a/test/SILGen/guaranteed_self.swift b/test/SILGen/guaranteed_self.swift index 3a0c807867cc8..2395e824c282e 100644 --- a/test/SILGen/guaranteed_self.swift +++ b/test/SILGen/guaranteed_self.swift @@ -451,11 +451,14 @@ class LetFieldClass { // CHECK-LABEL: sil hidden [ossa] @$s15guaranteed_self13LetFieldClassC10letkMethod{{[_0-9a-zA-Z]*}}F : $@convention(method) (@guaranteed LetFieldClass) -> () { // CHECK: bb0([[CLS:%.*]] : @guaranteed $LetFieldClass): // CHECK: [[KRAKEN_ADDR:%.*]] = ref_element_addr [[CLS]] : $LetFieldClass, #LetFieldClass.letk - // CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR]] + // CHECK-NEXT: [[KRAKEN_ADDR_ACCESS:%.*]] = begin_access [read] [unsafe] [[KRAKEN_ADDR]] + // CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR_ACCESS]] + // CHECK-NEXT: end_access [[KRAKEN_ADDR_ACCESS]] // CHECK-NEXT: [[KRAKEN_METH:%.*]] = class_method [[KRAKEN]] // CHECK-NEXT: apply [[KRAKEN_METH]]([[KRAKEN]]) // CHECK: [[KRAKEN_ADDR:%.*]] = ref_element_addr [[CLS]] : $LetFieldClass, #LetFieldClass.letk - // CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR]] + // CHECK-NEXT: [[KRAKEN_ADDR_ACCESS:%.*]] = begin_access [read] [unsafe] [[KRAKEN_ADDR]] + // CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR_ACCESS]] // CHECK: [[REBORROWED_KRAKEN:%.*]] = begin_borrow [[KRAKEN]] // CHECK: [[DESTROY_SHIP_FUN:%.*]] = function_ref @$s15guaranteed_self11destroyShipyyAA6KrakenCF : $@convention(thin) (@guaranteed Kraken) -> () // CHECK-NEXT: apply [[DESTROY_SHIP_FUN]]([[REBORROWED_KRAKEN]]) @@ -464,7 +467,9 @@ class LetFieldClass { // CHECK-NEXT: [[KRAKEN_BOX:%.*]] = alloc_box ${ var Kraken } // CHECK-NEXT: [[PB:%.*]] = project_box [[KRAKEN_BOX]] // CHECK-NEXT: [[KRAKEN_ADDR:%.*]] = ref_element_addr [[CLS]] : $LetFieldClass, #LetFieldClass.letk - // CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR]] + // CHECK-NEXT: [[KRAKEN_ADDR_ACCESS:%.*]] = begin_access [read] [unsafe] [[KRAKEN_ADDR]] + // CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR_ACCESS]] + // CHECK-NEXT: end_access [[KRAKEN_ADDR_ACCESS]] // CHECK-NEXT: store [[KRAKEN]] to [init] [[PB]] // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [unknown] [[PB]] : $*Kraken // CHECK-NEXT: [[KRAKEN_COPY:%.*]] = load [copy] [[READ]] diff --git a/test/SILGen/properties.swift b/test/SILGen/properties.swift index 6c4e4aba474c3..0d000477cc559 100644 --- a/test/SILGen/properties.swift +++ b/test/SILGen/properties.swift @@ -616,7 +616,9 @@ func genericProps(_ x: GenericClass) { // CHECK: apply {{.*}}([[ARG]]) : $@convention(method) <τ_0_0> (@guaranteed GenericClass<τ_0_0>) -> Int let _ = x.y // CHECK: [[Z:%.*]] = ref_element_addr [[ARG]] : $GenericClass, #GenericClass.z - // CHECK: [[LOADED_Z:%.*]] = load [copy] [[Z]] : $*String + // CHECK: [[Z_ACCESS:%.*]] = begin_access [read] [unsafe] [[Z]] + // CHECK: [[LOADED_Z:%.*]] = load [copy] [[Z_ACCESS]] : $*String + // CHECK: end_access [[Z_ACCESS]] // CHECK: destroy_value [[LOADED_Z]] // CHECK-NOT: destroy_value [[ARG]] let _ = x.z @@ -626,7 +628,8 @@ func genericProps(_ x: GenericClass) { func genericPropsInGenericContext(_ x: GenericClass) { // CHECK: bb0([[ARG:%.*]] : @guaranteed $GenericClass): // CHECK: [[Z:%.*]] = ref_element_addr [[ARG]] : $GenericClass, #GenericClass.z - // CHECK: copy_addr [[Z]] {{.*}} : $*U + // CHECK: [[Z_ACCESS:%.*]] = begin_access [read] [unsafe] [[Z]] + // CHECK: copy_addr [[Z_ACCESS]] {{.*}} : $*U let _ = x.z } @@ -642,7 +645,9 @@ class ClassWithLetProperty { // CHECK: bb0([[ARG:%.*]] : @guaranteed $ClassWithLetProperty): // CHECK-NEXT: debug_value // CHECK-NEXT: [[PTR:%[0-9]+]] = ref_element_addr [[ARG]] : $ClassWithLetProperty, #ClassWithLetProperty.p -// CHECK-NEXT: [[VAL:%[0-9]+]] = load [trivial] [[PTR]] : $*Int +// CHECK-NEXT: [[PTR_ACCESS:%.*]] = begin_access [read] [unsafe] [[PTR]] +// CHECK-NEXT: [[VAL:%[0-9]+]] = load [trivial] [[PTR_ACCESS]] : $*Int +// CHECK-NEXT: end_access [[PTR_ACCESS]] // CHECK-NEXT: return [[VAL]] : $Int @@ -670,7 +675,8 @@ class r19254812Derived: r19254812Base{ // Initialization of the pi field: no copy_values/releases. // CHECK: [[SELF:%[0-9]+]] = load_borrow [[PB_BOX]] : $*r19254812Derived // CHECK-NEXT: [[PIPTR:%[0-9]+]] = ref_element_addr [[SELF]] : $r19254812Derived, #r19254812Derived.pi -// CHECK-NEXT: assign {{.*}} to [[PIPTR]] : $*Double +// CHECK-NEXT: [[PIPTR_ACCESS:%.*]] = begin_access [modify] [unsafe] [[PIPTR]] +// CHECK-NEXT: assign {{.*}} to [[PIPTR_ACCESS]] : $*Double // CHECK-NOT: destroy_value // CHECK-NOT: copy_value @@ -678,7 +684,8 @@ class r19254812Derived: r19254812Base{ // Load of the pi field: no copy_values/releases. // CHECK: [[SELF:%[0-9]+]] = load_borrow [[PB_BOX]] : $*r19254812Derived // CHECK-NEXT: [[PIPTR:%[0-9]+]] = ref_element_addr [[SELF]] : $r19254812Derived, #r19254812Derived.pi -// CHECK-NEXT: {{.*}} = load [trivial] [[PIPTR]] : $*Double +// CHECK-NEXT: [[PIPTR_ACCESS:%.*]] = begin_access [read] [unsafe] [[PIPTR]] +// CHECK-NEXT: {{.*}} = load [trivial] [[PIPTR_ACCESS]] : $*Double // CHECK: return } diff --git a/test/SILGen/reabstract-tuple.swift b/test/SILGen/reabstract-tuple.swift index f04ff22b83494..faca9a7862ee2 100644 --- a/test/SILGen/reabstract-tuple.swift +++ b/test/SILGen/reabstract-tuple.swift @@ -27,10 +27,12 @@ class Box { // CHECK: [[CALL:%.*]] = apply [[INIT_F]]<(Int, () -> ())>(%{{.*}}, %{{.*}}) : $@convention(method) <τ_0_0> (@in τ_0_0, @thick Box<τ_0_0>.Type) -> @owned Box<τ_0_0> // CHECK: [[BORROW_CALL:%.*]] = begin_borrow [[CALL]] : $Box<(Int, () -> ())> // CHECK: [[REF:%.*]] = ref_element_addr [[BORROW_CALL]] : $Box<(Int, () -> ())>, #Box.value -// CHECK: [[TUPLEC:%.*]] = load [copy] [[REF]] : $*(Int, @callee_guaranteed () -> @out ()) +// CHECK: [[REF_ACCESS:%.*]] = begin_access [read] [unsafe] [[REF]] +// CHECK: [[TUPLEC:%.*]] = load [copy] [[REF_ACCESS]] : $*(Int, @callee_guaranteed () -> @out ()) // CHECK: ([[TUPLEC_0:%.*]], [[TUPLEC_1:%.*]]) = destructure_tuple [[TUPLEC]] // CHECK: [[THUNK2:%.*]] = function_ref @$sytIegr_Ieg_TR : $@convention(thin) (@guaranteed @callee_guaranteed () -> @out ()) -> () // CHECK: [[PA2:%.*]] = partial_apply [callee_guaranteed] [[THUNK2]]([[TUPLEC_1]]) : $@convention(thin) (@guaranteed @callee_guaranteed () -> @out ()) -> () +// CHECK: end_access [[REF_ACCESS]] // CHECK: destroy_value [[PA2]] : $@callee_guaranteed () -> () // CHECK: end_borrow [[BORROW_CALL]] : $Box<(Int, () -> ())> // CHECK-LABEL: } // end sil function '$s4main7testBoxyyF' diff --git a/test/SILGen/super_init_refcounting.swift b/test/SILGen/super_init_refcounting.swift index 3872ffea7cfca..ac405f68e01ff 100644 --- a/test/SILGen/super_init_refcounting.swift +++ b/test/SILGen/super_init_refcounting.swift @@ -78,13 +78,17 @@ class Good: Foo { // CHECK: store %0 to [init] [[PB_SELF_BOX]] // CHECK: [[SELF_OBJ:%.*]] = load_borrow [[PB_SELF_BOX]] // CHECK: [[X_ADDR:%.*]] = ref_element_addr [[SELF_OBJ]] : $Good, #Good.x - // CHECK: assign {{.*}} to [[X_ADDR]] : $*Int + // CHECK: [[X_ADDR_ACCESS:%.*]] = begin_access [modify] [unsafe] [[X_ADDR]] + // CHECK: assign {{.*}} to [[X_ADDR_ACCESS]] : $*Int + // CHECK: end_access [[X_ADDR_ACCESS]] // CHECK: [[SELF_OBJ:%.*]] = load [take] [[PB_SELF_BOX]] : $*Good // CHECK: [[SUPER_OBJ:%.*]] = upcast [[SELF_OBJ]] : $Good to $Foo // CHECK: [[BORROWED_SUPER:%.*]] = begin_borrow [[SUPER_OBJ]] // CHECK: [[DOWNCAST_BORROWED_SUPER:%.*]] = unchecked_ref_cast [[BORROWED_SUPER]] : $Foo to $Good // CHECK: [[X_ADDR:%.*]] = ref_element_addr [[DOWNCAST_BORROWED_SUPER]] : $Good, #Good.x - // CHECK: [[X:%.*]] = load [trivial] [[X_ADDR]] : $*Int + // CHECK: [[X_ADDR_ACCESS:%.*]] = begin_access [read] [unsafe] [[X_ADDR]] + // CHECK: [[X:%.*]] = load [trivial] [[X_ADDR_ACCESS]] : $*Int + // CHECK: end_access [[X_ADDR_ACCESS]] // CHECK: end_borrow [[BORROWED_SUPER]] // CHECK: [[SUPER_INIT:%.*]] = function_ref @$s22super_init_refcounting3FooCyACSicfc : $@convention(method) (Int, @owned Foo) -> @owned Foo // CHECK: apply [[SUPER_INIT]]([[X]], [[SUPER_OBJ]]) diff --git a/test/SILGen/unowned.swift b/test/SILGen/unowned.swift index ebffa09cef626..cfaf7b396bfc7 100644 --- a/test/SILGen/unowned.swift +++ b/test/SILGen/unowned.swift @@ -138,9 +138,10 @@ class TestUnownedMember { // CHECK: [[BORROWED_ARG1:%.*]] = begin_borrow [[ARG1]] // CHECK: [[ARG1_COPY:%.*]] = copy_value [[BORROWED_ARG1]] // CHECK: [[FIELDPTR:%.*]] = ref_element_addr [[BORROWED_SELF]] : $TestUnownedMember, #TestUnownedMember.member +// CHECK: [[FIELDPTR_ACCESS:%.*]] = begin_access [modify] [unsafe] [[FIELDPTR]] // CHECK: [[INVAL:%.*]] = ref_to_unowned [[ARG1_COPY]] : $C to $@sil_unowned C // CHECK: [[INVAL_COPY:%.*]] = copy_value [[INVAL]] : $@sil_unowned C -// CHECK: assign [[INVAL_COPY]] to [[FIELDPTR]] : $*@sil_unowned C +// CHECK: assign [[INVAL_COPY]] to [[FIELDPTR_ACCESS]] : $*@sil_unowned C // CHECK: destroy_value [[ARG1_COPY]] : $C // CHECK: end_borrow [[BORROWED_ARG1]] // CHECK: end_borrow [[BORROWED_SELF]] diff --git a/test/SILOptimizer/definite_init_failable_initializers_objc.swift b/test/SILOptimizer/definite_init_failable_initializers_objc.swift index f1be59b403c2d..c8adf1618c29f 100644 --- a/test/SILOptimizer/definite_init_failable_initializers_objc.swift +++ b/test/SILOptimizer/definite_init_failable_initializers_objc.swift @@ -42,7 +42,9 @@ class Cat : FakeNSObject { // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $Cat // CHECK: store [[ARG2]] to [[SELF_BOX]] : $*Cat // CHECK: [[FIELD_ADDR:%.*]] = ref_element_addr [[ARG2]] : $Cat, #Cat.x - // CHECK-NEXT: store {{%.*}} to [[FIELD_ADDR]] : $*LifetimeTracked + // CHECK-NEXT: [[FIELD_ADDR_ACCESS:%.*]] = begin_access [modify] [unsafe] [[FIELD_ADDR]] + // CHECK-NEXT: store {{%.*}} to [[FIELD_ADDR_ACCESS]] : $*LifetimeTracked + // CHECK-NEXT: end_access [[FIELD_ADDR_ACCESS]] // CHECK-NEXT: strong_release [[ARG2]] // CHECK-NEXT: [[COND:%.*]] = struct_extract %1 : $Bool, #Bool._value // CHECK-NEXT: cond_br [[COND]], bb1, bb2 From 8dda0193a688b05747430302e3eaf0a022e3be35 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 9 Jan 2020 13:27:20 -0800 Subject: [PATCH 365/478] ModuleInterface: lock .swiftinterface while generating module cache This ensures only one process is generating module cache from an interface file so that we don't blow up memory usage when multiple processes are doing the same. The locking mechanism is similar to that of Clang's. A better approach is that the build system takes care of the module building step as a formal dependency. rdar://52839445 --- include/swift/AST/DiagnosticsFrontend.def | 6 ++ lib/Frontend/ModuleInterfaceBuilder.cpp | 70 ++++++++++++++++++++++- lib/Frontend/ModuleInterfaceBuilder.h | 5 +- lib/Frontend/ModuleInterfaceLoader.cpp | 10 ++-- test/Driver/lock_interface.swift | 13 +++++ 5 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 test/Driver/lock_interface.swift diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index c3beb96e145fb..049527c6fbb7f 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -343,6 +343,12 @@ ERROR(unknown_forced_module_loading_mode,none, "unknown value for SWIFT_FORCE_MODULE_LOADING variable: '%0'", (StringRef)) +REMARK(interface_file_lock_failure,none, + "could not acquire lock file for module interface '%0'", (StringRef)) + +REMARK(interface_file_lock_timed_out,none, + "timed out waiting to acquire lock file for module interface '%0'", (StringRef)) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index f1bfcea821bc6..538e88f1ed401 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -36,6 +36,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/Regex.h" #include "llvm/Support/StringSaver.h" +#include "llvm/Support/LockFileManager.h" using namespace swift; using FileDependency = SerializationOptions::FileDependency; @@ -240,7 +241,7 @@ bool ModuleInterfaceBuilder::collectDepsForSerialization( return false; } -bool ModuleInterfaceBuilder::buildSwiftModule( +bool ModuleInterfaceBuilder::buildSwiftModuleInternal( StringRef OutPath, bool ShouldSerializeDeps, std::unique_ptr *ModuleBuffer) { bool SubError = false; @@ -382,3 +383,70 @@ bool ModuleInterfaceBuilder::buildSwiftModule( }); return !RunSuccess || SubError; } + +bool ModuleInterfaceBuilder::buildSwiftModule(StringRef OutPath, + bool ShouldSerializeDeps, + std::unique_ptr *ModuleBuffer, + llvm::function_ref RemarkRebuild) { + + while (1) { + // Attempt to lock the interface file. Only one process is allowed to build + // module from the interface so we don't consume too much memory when multiple + // processes are doing the same. + // FIXME: We should surface the module building step to the build system so + // we don't need to synchronize here. + llvm::LockFileManager Locked(interfacePath); + switch (Locked) { + case llvm::LockFileManager::LFS_Error:{ + // ModuleInterfaceBuilder takes care of correctness and locks are only + // necessary for performance. Fallback to building the module in case of any lock + // related errors. + if (RemarkRebuild) { + diags.diagnose(SourceLoc(), diag::interface_file_lock_failure, + interfacePath); + } + // Clear out any potential leftover. + Locked.unsafeRemoveLockFile(); + LLVM_FALLTHROUGH; + } + case llvm::LockFileManager::LFS_Owned: { + if (RemarkRebuild) { + RemarkRebuild(); + } + return buildSwiftModuleInternal(OutPath, ShouldSerializeDeps, ModuleBuffer); + } + case llvm::LockFileManager::LFS_Shared: { + // Someone else is responsible for building the module. Wait for them to + // finish. + switch (Locked.waitForUnlock()) { + case llvm::LockFileManager::Res_Success: { + // This process may have a different module output path. If the other + // process doesn't build the interface to this output path, we should try + // building ourselves. + auto bufferOrError = llvm::MemoryBuffer::getFile(OutPath); + if (!bufferOrError) + continue; + *ModuleBuffer = std::move(bufferOrError.get()); + return false; + } + case llvm::LockFileManager::Res_OwnerDied: { + continue; // try again to get the lock. + } + case llvm::LockFileManager::Res_Timeout: { + // Since ModuleInterfaceBuilder takes care of correctness, we try waiting for + // another process to complete the build so swift does not do it done + // twice. If case of timeout, build it ourselves. + if (RemarkRebuild) { + diags.diagnose(SourceLoc(), diag::interface_file_lock_timed_out, + interfacePath); + } + // Clear the lock file so that future invocations can make progress. + Locked.unsafeRemoveLockFile(); + continue; + } + } + break; + } + } + } +} diff --git a/lib/Frontend/ModuleInterfaceBuilder.h b/lib/Frontend/ModuleInterfaceBuilder.h index f8f096a39538a..b5de3c64b8a8e 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.h +++ b/lib/Frontend/ModuleInterfaceBuilder.h @@ -67,6 +67,8 @@ class ModuleInterfaceBuilder { version::Version &Vers, llvm::StringSaver &SubArgSaver, SmallVectorImpl &SubArgs); + bool buildSwiftModuleInternal(StringRef OutPath, bool ShouldSerializeDeps, + std::unique_ptr *ModuleBuffer); public: ModuleInterfaceBuilder(SourceManager &sourceMgr, DiagnosticEngine &diags, const SearchPathOptions &searchPathOpts, @@ -102,7 +104,8 @@ class ModuleInterfaceBuilder { } bool buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps, - std::unique_ptr *ModuleBuffer); + std::unique_ptr *ModuleBuffer, + llvm::function_ref RemarkRebuild = nullptr); }; } // end namespace swift diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 4b238525dd14e..0e9b13b533412 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -950,13 +950,11 @@ class ModuleInterfaceLoaderImpl { std::unique_ptr moduleBuffer; // We didn't discover a module corresponding to this interface. - // Diagnose that we didn't find a loadable module, if we were asked to. - if (remarkOnRebuildFromInterface) { + auto remarkRebuild = [&]() { rebuildInfo.diagnose(ctx, diagnosticLoc, moduleName, interfacePath); - } - + }; // If we found an out-of-date .swiftmodule, we still want to add it as // a dependency of the .swiftinterface. That way if it's updated, but // the .swiftinterface remains the same, we invalidate the cache and @@ -966,7 +964,9 @@ class ModuleInterfaceLoaderImpl { builder.addExtraDependency(modulePath); if (builder.buildSwiftModule(cachedOutputPath, /*shouldSerializeDeps*/true, - &moduleBuffer)) + &moduleBuffer, + remarkOnRebuildFromInterface ? remarkRebuild: + llvm::function_ref())) return std::make_error_code(std::errc::invalid_argument); assert(moduleBuffer && diff --git a/test/Driver/lock_interface.swift b/test/Driver/lock_interface.swift new file mode 100644 index 0000000000000..cf41dc730d13e --- /dev/null +++ b/test/Driver/lock_interface.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) +// RUN: echo 'public func foo() {}' > %t/Foo.swift +// RUN: %target-swift-frontend-typecheck -emit-module-interface-path %t/Foo.swiftinterface %t/Foo.swift -enable-library-evolution +// RUN: touch %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift +// RUN: echo 'import Foo' > %t/file-01.swift +// RUN: echo 'import Foo' > %t/file-02.swift +// RUN: echo 'import Foo' > %t/file-03.swift +// RUN: %swiftc_driver -j20 %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift -I %t -Xfrontend -Rmodule-interface-rebuild &> %t/result.txt +// RUN: %FileCheck %s -check-prefix=CHECK-REBUILD < %t/result.txt + +// Ensure we only build Foo module once from the interface +// CHECK-REBUILD: rebuilding module 'Foo' from interface +// CHECK-REBUILD-NOT: rebuilding module 'Foo' from interface From 142b3ad538f54c92ecfd577669bbce06a9a0162c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 9 Jan 2020 16:33:13 -0800 Subject: [PATCH 366/478] [Constraint system] Minor fixes / test updates for function builders. --- lib/Sema/CSApply.cpp | 2 +- test/IDE/complete_function_builder.swift | 6 +++--- test/Profiler/coverage_function_builder.swift | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 28bc2e78ecf28..90d0af6790a58 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7261,7 +7261,7 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( singleExpr->getStartLoc(), singleExpr, /*implicit=*/true); auto braceStmt = BraceStmt::create( ctx, returnStmt->getStartLoc(), ASTNode(returnStmt), - returnStmt->getEndLoc(), /*implicit=*/true); + returnStmt->getEndLoc(), /*implicit=*/false); result = braceStmt; } diff --git a/test/IDE/complete_function_builder.swift b/test/IDE/complete_function_builder.swift index 8f97cf8e05414..480a63359c530 100644 --- a/test/IDE/complete_function_builder.swift +++ b/test/IDE/complete_function_builder.swift @@ -51,14 +51,14 @@ let globalStringVal: String = "" func testAcceptColorTagged(paramIntVal: Int, paramStringVal: String) { let taggedValue = paramIntVal.tag(Color.red) - + acceptColorTagged { color in #^IN_CLOSURE_TOP^# // IN_CLOSURE_TOP_CONTEXT: Begin completions -// IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: taggedValue[#Tagged#]; name=taggedValue +// IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local{{.*}}: taggedValue[#Tagged#]; name=taggedValue // IN_CLOSURE_TOP-DAG: Decl[GlobalVar]/CurrModule: globalIntVal[#Int#]; name=globalIntVal // IN_CLOSURE_TOP-DAG: Decl[GlobalVar]/CurrModule: globalStringVal[#String#]; name=globalStringVal -// IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: color; name=color +// IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: color{{.*}}; name=color // IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: paramIntVal[#Int#]; name=paramIntVal // IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: paramStringVal[#String#]; name=paramStringVal // IN_CLOSURE_TOP: End completions diff --git a/test/Profiler/coverage_function_builder.swift b/test/Profiler/coverage_function_builder.swift index a08237f268eb8..a3cfb30c139ba 100644 --- a/test/Profiler/coverage_function_builder.swift +++ b/test/Profiler/coverage_function_builder.swift @@ -13,7 +13,7 @@ struct Summer { // CHECK-LABEL: sil_coverage_map {{.*}} "$s24coverage_functon_builder5test0SiyF" @Summer func test0() -> Int { - // CHECK: [[@LINE-1]]:21 -> [[@LINE+3]]:2 : 0 + // CHECK: [[@LINE-1]]:21 -> [[@LINE+2]]:5 : 0 18 12 } @@ -21,7 +21,7 @@ func test0() -> Int { // CHECK-LABEL: sil_coverage_map {{.*}} "$s24coverage_functon_builder5test1SiyF" @Summer func test1() -> Int { - // CHECK: [[@LINE-1]]:21 -> [[@LINE+7]]:2 : 0 + // CHECK: [[@LINE-1]]:21 -> [[@LINE+6]]:4 : 0 18 12 if 7 < 23 { From fbfb67a218849e9e2f34b47b1aa6115857649619 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 9 Jan 2020 17:10:57 -0800 Subject: [PATCH 367/478] [AutoDiff upstream] Define `AutoDiffConfig`. (#29099) Define `AutoDiffConfig`: the configuration for a derivative function: - Parameter indices. - Result indices. - Derivative generic signature (optional). Progress towards TF-828: upstream `@differentiable` attribute type-checking. --- include/swift/AST/AutoDiff.h | 69 ++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/include/swift/AST/AutoDiff.h b/include/swift/AST/AutoDiff.h index fc2059f907eed..cc0f8ec97b105 100644 --- a/include/swift/AST/AutoDiff.h +++ b/include/swift/AST/AutoDiff.h @@ -19,6 +19,7 @@ #include +#include "swift/AST/GenericSignature.h" #include "swift/AST/Identifier.h" #include "swift/AST/IndexSubset.h" #include "swift/AST/Type.h" @@ -70,6 +71,25 @@ struct AutoDiffDerivativeFunctionKind { } }; +/// Identifies an autodiff derivative function configuration: +/// - Parameter indices. +/// - Result indices. +/// - Derivative generic signature (optional). +struct AutoDiffConfig { + IndexSubset *parameterIndices; + IndexSubset *resultIndices; + GenericSignature derivativeGenericSignature; + + /*implicit*/ AutoDiffConfig(IndexSubset *parameterIndices, + IndexSubset *resultIndices, + GenericSignature derivativeGenericSignature) + : parameterIndices(parameterIndices), resultIndices(resultIndices), + derivativeGenericSignature(derivativeGenericSignature) {} + + void print(llvm::raw_ostream &s = llvm::outs()) const; + SWIFT_DEBUG_DUMP; +}; + class ParsedAutoDiffParameter { public: enum class Kind { Named, Ordered, Self }; @@ -148,10 +168,59 @@ void getSubsetParameterTypes(IndexSubset *indices, AnyFunctionType *type, namespace llvm { +using swift::AutoDiffConfig; using swift::AutoDiffDerivativeFunctionKind; +using swift::GenericSignature; +using swift::IndexSubset; template struct DenseMapInfo; +template <> struct DenseMapInfo { + static AutoDiffConfig getEmptyKey() { + auto *ptr = llvm::DenseMapInfo::getEmptyKey(); + // The `derivativeGenericSignature` component must be `nullptr` so that + // `getHashValue` and `isEqual` do not try to call + // `GenericSignatureImpl::getCanonicalSignature()` on an invalid pointer. + return {static_cast(ptr), static_cast(ptr), + nullptr}; + } + + static AutoDiffConfig getTombstoneKey() { + auto *ptr = llvm::DenseMapInfo::getTombstoneKey(); + // The `derivativeGenericSignature` component must be `nullptr` so that + // `getHashValue` and `isEqual` do not try to call + // `GenericSignatureImpl::getCanonicalSignature()` on an invalid pointer. + return {static_cast(ptr), static_cast(ptr), + nullptr}; + } + + static unsigned getHashValue(const AutoDiffConfig &Val) { + auto canGenSig = + Val.derivativeGenericSignature + ? Val.derivativeGenericSignature->getCanonicalSignature() + : nullptr; + unsigned combinedHash = hash_combine( + ~1U, DenseMapInfo::getHashValue(Val.parameterIndices), + DenseMapInfo::getHashValue(Val.resultIndices), + DenseMapInfo::getHashValue(canGenSig)); + return combinedHash; + } + + static bool isEqual(const AutoDiffConfig &LHS, const AutoDiffConfig &RHS) { + auto lhsCanGenSig = + LHS.derivativeGenericSignature + ? LHS.derivativeGenericSignature->getCanonicalSignature() + : nullptr; + auto rhsCanGenSig = + RHS.derivativeGenericSignature + ? RHS.derivativeGenericSignature->getCanonicalSignature() + : nullptr; + return LHS.parameterIndices == RHS.parameterIndices && + LHS.resultIndices == RHS.resultIndices && + DenseMapInfo::isEqual(lhsCanGenSig, rhsCanGenSig); + } +}; + template <> struct DenseMapInfo { static AutoDiffDerivativeFunctionKind getEmptyKey() { return static_cast( From 9d019ed300b0ad6c5b1e550f561293701c41d9d6 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 13 Nov 2019 15:05:48 -0800 Subject: [PATCH 368/478] [gardening] Corrected comment. The comment said that a function was defined in a file where in fact it was no longer defined. The comment now refers to the correct file. --- include/swift/ABI/Metadata.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 58e5ce74784cc..e7cb08be3849f 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -3575,7 +3575,7 @@ struct TargetSingletonMetadataInitialization { } /// This method can only be called from the runtime itself. It is defined - /// in MetadataCache.h. + /// in Metadata.cpp. TargetMetadata *allocate( const TargetTypeContextDescriptor *description) const; }; From a9f16c7757fee11259995cadf49ff6654afc1717 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 3 Dec 2019 17:10:38 -0800 Subject: [PATCH 369/478] [gardening] Fixed typo. --- include/swift/IRGen/Linking.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index f7cd87e7f063d..56b76c07be474 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -65,7 +65,7 @@ class UniversalLinkageInfo { bool shouldAllPrivateDeclsBeVisibleFromOtherFiles() const { return HasMultipleIGMs; } - /// In case of multipe llvm modules, private lazy protocol + /// In case of multiple llvm modules, private lazy protocol /// witness table accessors could be emitted by two different IGMs during /// IRGen into different object files and the linker would complain about /// duplicate symbols. From 52cf7497641d7353dbc28e60db195e6bcf69ff99 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 4 Dec 2019 11:32:41 -0800 Subject: [PATCH 370/478] [gardening] Corrected comment. --- include/swift/AST/Decl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index d8ba393ded6fb..cc7705db0d160 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2862,7 +2862,7 @@ class GenericTypeDecl : public GenericContext, public TypeDecl { /// code. One is formed implicitly when a declaration is written with an opaque /// result type, as in: /// -/// func foo() -> opaque SignedInteger { return 1 } +/// func foo() -> some SignedInteger { return 1 } /// /// The declared type is a special kind of ArchetypeType representing the /// abstracted underlying type. From cdf3817c047a700e91df87140eb4ed86cd53fcc3 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 4 Dec 2019 12:45:16 -0800 Subject: [PATCH 371/478] [gardening] Extracted common expression to value. --- lib/IRGen/GenDecl.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index dea6cd90f8594..bc62e0b270e7e 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -3682,9 +3682,11 @@ ConstantReference IRGenModule::getAddrOfTypeMetadata(CanType concreteType, llvm::Type *defaultVarTy; unsigned adjustmentIndex; - + + bool fullMetadata = (nominal && requiresForeignTypeMetadata(nominal)); + // Foreign classes reference the full metadata with a GEP. - if (nominal && requiresForeignTypeMetadata(nominal)) { + if (fullMetadata) { defaultVarTy = FullTypeMetadataStructTy; adjustmentIndex = MetadataAdjustmentIndex::ValueType; // The symbol for other nominal type metadata is generated at the address @@ -3712,7 +3714,7 @@ ConstantReference IRGenModule::getAddrOfTypeMetadata(CanType concreteType, Optional entity; DebugTypeInfo DbgTy; - if (nominal && requiresForeignTypeMetadata(nominal)) { + if (fullMetadata) { entity = LinkEntity::forTypeMetadata(concreteType, TypeMetadataAddress::FullMetadata); DbgTy = DebugTypeInfo::getMetadata(MetatypeType::get(concreteType), From edab098067885b2564d852d5271504bbce04cc98 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 4 Dec 2019 11:34:19 -0800 Subject: [PATCH 372/478] [runtime] Metadata can be flagged static. Added a new flag to the GenericMetadataPatternFlags flagset for whether the metadata has a set of flags at its tail. When that flag is set, there will be an extra uint64_t flagset at the end of the metadata. For struct metadata, the type of that flagset will be StructMetadataTrailingFlags. The first flag in that trailing flagset indicates whether the metadata was statically specialized. The second flag in that trailing flagset indicates whether the metadata is statically canonical. When verifying the metadata cache, a check is done for whether the metadata was statically specialized and whether it was known to be canonical statically. If so, verification is skipped. Skipping it is necessary because the known-canonical statically specialized metadata will not be in the cache. In that case, the canonical statically specialized metadata will be returned from the metadata accessor and never be cached. --- include/swift/ABI/Metadata.h | 30 ++++++++++++++++++++++++++++ include/swift/ABI/MetadataValues.h | 32 ++++++++++++++++++++++++++++++ stdlib/public/runtime/Metadata.cpp | 16 +++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index e7cb08be3849f..951abc2cc3070 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -697,6 +697,8 @@ struct TargetMetadata { bool satisfiesClassConstraint() const; + bool isCanonicalStaticallySpecializedGenericMetadata() const; + #if SWIFT_OBJC_INTEROP /// Get the ObjC class object for this type if it has one, or return null if /// the type is not a class (or not a class with a class object). @@ -1348,6 +1350,34 @@ struct TargetStructMetadata : public TargetValueMetadata { return reinterpret_cast(asWords + offset); } + bool isCanonicalStaticallySpecializedGenericMetadata() const { + auto *description = getDescription(); + if (!description->isGeneric()) + return false; + + auto *trailingFlags = getTrailingFlags(); + if (trailingFlags == nullptr) + return false; + + return trailingFlags->isCanonicalStaticSpecialization(); + } + + const MetadataTrailingFlags *getTrailingFlags() const { + auto description = getDescription(); + auto flags = description->getFullGenericContextHeader() + .DefaultInstantiationPattern->PatternFlags; + if (!flags.hasTrailingFlags()) + return nullptr; + auto fieldOffset = description->FieldOffsetVectorOffset; + auto offset = + fieldOffset + + // Pad to the nearest pointer. + ((description->NumFields * sizeof(uint32_t) + sizeof(void *) - 1) / + sizeof(void *)); + auto asWords = reinterpret_cast(this); + return reinterpret_cast(asWords + offset); + } + static constexpr int32_t getGenericArgumentOffset() { return sizeof(TargetStructMetadata) / sizeof(StoredPointer); } diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index e6c869c1ad97a..6077c0019ed4b 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -1511,6 +1511,10 @@ class GenericMetadataPatternFlags : public FlagSet { /// Does this pattern have an extra-data pattern? HasExtraDataPattern = 0, + /// Do instances of this pattern have a bitset of flags that occur at the + /// end of the metadata, after the extra data if there is any? + HasTrailingFlags = 1, + // Class-specific flags. /// Does this pattern have an immediate-members pattern? @@ -1535,6 +1539,10 @@ class GenericMetadataPatternFlags : public FlagSet { hasExtraDataPattern, setHasExtraDataPattern) + FLAGSET_DEFINE_FLAG_ACCESSORS(HasTrailingFlags, + hasTrailingFlags, + setHasTrailingFlags) + FLAGSET_DEFINE_FIELD_ACCESSORS(Value_MetadataKind, Value_MetadataKind_width, MetadataKind, @@ -1667,6 +1675,30 @@ class MetadataRequest : public FlagSet { } }; +struct MetadataTrailingFlags : public FlagSet { + enum { + /// Whether this metadata is a specialization of a generic metadata pattern + /// which was created during compilation. + IsStaticSpecialization = 0, + + /// Whether this metadata is a specialization of a generic metadata pattern + /// which was created during compilation and made to be canonical by + /// modifying the metadata accessor. + IsCanonicalStaticSpecialization = 1, + }; + + explicit MetadataTrailingFlags(uint64_t bits) : FlagSet(bits) {} + constexpr MetadataTrailingFlags() {} + + FLAGSET_DEFINE_FLAG_ACCESSORS(IsStaticSpecialization, + isStaticSpecialization, + setIsStaticSpecialization) + + FLAGSET_DEFINE_FLAG_ACCESSORS(IsCanonicalStaticSpecialization, + isCanonicalStaticSpecialization, + setIsCanonicalStaticSpecialization) +}; + /// Flags for Builtin.IntegerLiteral values. class IntegerLiteralFlags { public: diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 9d5f3729d89ab..2546d90dec5cd 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -4630,6 +4630,14 @@ const WitnessTable *swift::swift_getAssociatedConformanceWitness( template static Result performOnMetadataCache(const Metadata *metadata, Callbacks &&callbacks) { + // TODO: Once more than just structs have canonical statically specialized + // metadata, calling an updated + // isCanonicalStaticallySpecializedGenericMetadata would entail + // dyn_casting to the same type more than once. Avoid that by combining + // that function's implementation with the dyn_casts below. + if (metadata->isCanonicalStaticallySpecializedGenericMetadata()) + return std::move(callbacks).forOtherMetadata(metadata); + // Handle different kinds of type that can delay their metadata. const TypeContextDescriptor *description; if (auto classMetadata = dyn_cast(metadata)) { @@ -5211,6 +5219,14 @@ bool Metadata::satisfiesClassConstraint() const { return isAnyClass(); } +template <> +bool Metadata::isCanonicalStaticallySpecializedGenericMetadata() const { + if (auto *metadata = dyn_cast(this)) + return metadata->isCanonicalStaticallySpecializedGenericMetadata(); + + return false; +} + #if !NDEBUG static bool referencesAnonymousContext(Demangle::Node *node) { if (node->getKind() == Demangle::Node::Kind::AnonymousContext) From 525e25603fb0f32471ee39330f56b7e359f07e09 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 4 Dec 2019 12:33:56 -0800 Subject: [PATCH 373/478] [IRGen] Pass argument/table to builders. Previously, the various generic builders implemented the methods addGenericArgument and addGenericWitnessTable without being provided what argument or witness table they were to add (because it was not previously needed). Now, the GenericRequirement is passed along to both methods. --- lib/IRGen/ClassMetadataVisitor.h | 9 +++++++-- lib/IRGen/EnumMetadataVisitor.h | 4 ++-- lib/IRGen/GenMeta.cpp | 20 ++++++++++++-------- lib/IRGen/MetadataLayout.cpp | 10 ++++++---- lib/IRGen/NominalMetadataVisitor.h | 4 ++-- lib/IRGen/StructMetadataVisitor.h | 4 ++-- 6 files changed, 31 insertions(+), 20 deletions(-) diff --git a/lib/IRGen/ClassMetadataVisitor.h b/lib/IRGen/ClassMetadataVisitor.h index 65492f9625ec1..3c82701e78448 100644 --- a/lib/IRGen/ClassMetadataVisitor.h +++ b/lib/IRGen/ClassMetadataVisitor.h @@ -204,8 +204,13 @@ class ClassMetadataScanner : public ClassMetadataVisitor { addPointer(); } } - void addGenericArgument(ClassDecl *forClass) { addPointer(); } - void addGenericWitnessTable(ClassDecl *forClass) { addPointer(); } + void addGenericArgument(GenericRequirement requirement, ClassDecl *forClass) { + addPointer(); + } + void addGenericWitnessTable(GenericRequirement requirement, + ClassDecl *forClass) { + addPointer(); + } void addPlaceholder(MissingMemberDecl *MMD) { for (auto i : range(MMD->getNumberOfVTableEntries())) { (void)i; diff --git a/lib/IRGen/EnumMetadataVisitor.h b/lib/IRGen/EnumMetadataVisitor.h index 30bd8da890121..ab6690c42dac8 100644 --- a/lib/IRGen/EnumMetadataVisitor.h +++ b/lib/IRGen/EnumMetadataVisitor.h @@ -82,8 +82,8 @@ class EnumMetadataScanner : public EnumMetadataVisitor { void addMetadataFlags() { addPointer(); } void addValueWitnessTable() { addPointer(); } void addNominalTypeDescriptor() { addPointer(); } - void addGenericArgument() { addPointer(); } - void addGenericWitnessTable() { addPointer(); } + void addGenericArgument(GenericRequirement requirement) { addPointer(); } + void addGenericWitnessTable(GenericRequirement requirement) { addPointer(); } void addPayloadSize() { addPointer(); } void noteStartOfTypeSpecificMembers() {} diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 1dcf7f01a534c..4744438489f4c 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -2869,11 +2869,13 @@ namespace { llvm_unreachable("Fixed class metadata cannot have missing members"); } - void addGenericArgument(ClassDecl *forClass) { + void addGenericArgument(GenericRequirement requirement, + ClassDecl *forClass) { llvm_unreachable("Fixed class metadata cannot have generic parameters"); } - void addGenericWitnessTable(ClassDecl *forClass) { + void addGenericWitnessTable(GenericRequirement requirement, + ClassDecl *forClass) { llvm_unreachable("Fixed class metadata cannot have generic requirements"); } }; @@ -2908,12 +2910,14 @@ namespace { } } - void addGenericArgument(ClassDecl *forClass) { + void addGenericArgument(GenericRequirement requirement, + ClassDecl *forClass) { // Filled in at runtime. B.addNullPointer(IGM.TypeMetadataPtrTy); } - void addGenericWitnessTable(ClassDecl *forClass) { + void addGenericWitnessTable(GenericRequirement requirement, + ClassDecl *forClass) { // Filled in at runtime. B.addNullPointer(IGM.WitnessTablePtrTy); } @@ -3499,11 +3503,11 @@ namespace { B.addAlignmentPadding(super::IGM.getPointerAlignment()); } - void addGenericArgument() { + void addGenericArgument(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic parameters"); } - void addGenericWitnessTable() { + void addGenericWitnessTable(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic requirements"); } }; @@ -3733,11 +3737,11 @@ namespace { B.add(emitNominalTypeDescriptor()); } - void addGenericArgument() { + void addGenericArgument(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic parameters"); } - void addGenericWitnessTable() { + void addGenericWitnessTable(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic requirements"); } }; diff --git a/lib/IRGen/MetadataLayout.cpp b/lib/IRGen/MetadataLayout.cpp index 795529c5f8a41..8b54e77503875 100644 --- a/lib/IRGen/MetadataLayout.cpp +++ b/lib/IRGen/MetadataLayout.cpp @@ -314,18 +314,20 @@ ClassMetadataLayout::ClassMetadataLayout(IRGenModule &IGM, ClassDecl *decl) super::noteStartOfGenericRequirements(forClass); } - void addGenericWitnessTable(ClassDecl *forClass) { + void addGenericWitnessTable(GenericRequirement requirement, + ClassDecl *forClass) { if (forClass == Target) { Layout.NumImmediateMembers++; } - super::addGenericWitnessTable(forClass); + super::addGenericWitnessTable(requirement, forClass); } - void addGenericArgument(ClassDecl *forClass) { + void addGenericArgument(GenericRequirement requirement, + ClassDecl *forClass) { if (forClass == Target) { Layout.NumImmediateMembers++; } - super::addGenericArgument(forClass); + super::addGenericArgument(requirement, forClass); } void addMethod(SILDeclRef fn) { diff --git a/lib/IRGen/NominalMetadataVisitor.h b/lib/IRGen/NominalMetadataVisitor.h index 6ac633abf6d31..212d7c65640c9 100644 --- a/lib/IRGen/NominalMetadataVisitor.h +++ b/lib/IRGen/NominalMetadataVisitor.h @@ -53,9 +53,9 @@ template class NominalMetadataVisitor GenericTypeRequirements requirements(super::IGM, typeDecl); for (auto reqt : requirements.getRequirements()) { if (reqt.Protocol) { - asImpl().addGenericWitnessTable(args...); + asImpl().addGenericWitnessTable(reqt, args...); } else { - asImpl().addGenericArgument(args...); + asImpl().addGenericArgument(reqt, args...); } } diff --git a/lib/IRGen/StructMetadataVisitor.h b/lib/IRGen/StructMetadataVisitor.h index 837ea00e5087e..04195e44f4ad2 100644 --- a/lib/IRGen/StructMetadataVisitor.h +++ b/lib/IRGen/StructMetadataVisitor.h @@ -87,8 +87,8 @@ class StructMetadataScanner : public StructMetadataVisitor { void addValueWitnessTable() { addPointer(); } void addNominalTypeDescriptor() { addPointer(); } void addFieldOffset(VarDecl *) { addInt32(); } - void addGenericArgument() { addPointer(); } - void addGenericWitnessTable() { addPointer(); } + void addGenericArgument(GenericRequirement requirement) { addPointer(); } + void addGenericWitnessTable(GenericRequirement requirement) { addPointer(); } void noteStartOfTypeSpecificMembers() {} void noteEndOfFieldOffsets() { From e45b05476ea2cbf2feb2f8bc0b0d9476e9ed5f70 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 4 Dec 2019 14:48:52 -0800 Subject: [PATCH 374/478] [IRGen] Added gate for metadata prespecialization. The new frontend flag -prespecialize-generic-metadata must be passed in order for generic metadata to be specialized statically. rdar://problem/56984885 --- include/swift/AST/IRGenOptions.h | 9 +++++++-- include/swift/Option/FrontendOptions.td | 4 ++++ lib/Frontend/CompilerInvocation.cpp | 4 ++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 30aae5b3438e7..9b9db78a3b8c7 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -190,6 +190,10 @@ class IRGenOptions { /// Passing this flag completely disables this behavior. unsigned DisableLegacyTypeInfo : 1; + /// Create metadata specializations for generic types at statically known type + /// arguments. + unsigned PrespecializeGenericMetadata : 1; + /// The path to load legacy type layouts from. StringRef ReadLegacyTypeInfoPath; @@ -255,8 +259,9 @@ class IRGenOptions { EnableAnonymousContextMangledNames(false), ForcePublicLinkage(false), LazyInitializeClassMetadata(false), LazyInitializeProtocolConformances(false), DisableLegacyTypeInfo(false), - UseIncrementalLLVMCodeGen(true), UseSwiftCall(false), - GenerateProfile(false), EnableDynamicReplacementChaining(false), + PrespecializeGenericMetadata(false), UseIncrementalLLVMCodeGen(true), + UseSwiftCall(false), GenerateProfile(false), + EnableDynamicReplacementChaining(false), DisableRoundTripDebugTypes(false), DisableDebuggerShadowCopies(false), CmdArgs(), SanitizeCoverage(llvm::SanitizerCoverageOptions()), TypeInfoFilter(TypeInfoDumpFilter::All) {} diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 3601598937269..7206520f310af 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -644,6 +644,10 @@ def disable_verify_exclusivity : Flag<["-"], "disable-verify-exclusivity">, def disable_legacy_type_info : Flag<["-"], "disable-legacy-type-info">, HelpText<"Completely disable legacy type layout">; +def prespecialize_generic_metadata : Flag<["-"], "prespecialize-generic-metadata">, + HelpText<"Statically specialize metadata for generic types at types that " + "are known to be used in source.">; + def read_legacy_type_info_path_EQ : Joined<["-"], "read-legacy-type-info-path=">, HelpText<"Read legacy type layout from the given path instead of default path">; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 4a4b47653ed8e..01a7e1d7c1002 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1249,6 +1249,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, Opts.DisableLegacyTypeInfo = true; } + if (Args.hasArg(OPT_prespecialize_generic_metadata)) { + Opts.PrespecializeGenericMetadata = true; + } + if (const Arg *A = Args.getLastArg(OPT_read_legacy_type_info_path_EQ)) { Opts.ReadLegacyTypeInfoPath = A->getValue(); } From 89278f8cbfb08fd192e0b51a02a55da6e7e3b402 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 3 Jan 2020 16:01:58 -0800 Subject: [PATCH 375/478] [metadata prespecialization] Target only >=5.2. Compatibility with earlier swift runtimes would require modifying the runtime compatibility libraries to adjust the behavior of checkMetadataState by way of typeForMangledNode or even typeForMangledName. For now, simply require that a version of swift whose runtime knows about prespecialized metadata is being targeted. --- include/swift/AST/ASTContext.h | 8 ++++++++ lib/AST/Availability.cpp | 8 ++++++++ lib/IRGen/IRGenModule.cpp | 9 +++++++++ lib/IRGen/IRGenModule.h | 2 ++ 4 files changed, 27 insertions(+) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 5f18776a7a886..53bf0aafb0d21 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -614,6 +614,14 @@ class ASTContext final { /// swift_getTypeByMangledNameInContextInMetadataState. AvailabilityContext getTypesInAbstractMetadataStateAvailability(); + /// Get the runtime availability of support for prespecialized generic + /// metadata. + AvailabilityContext getPrespecializedGenericMetadataAvailability(); + + /// Get the runtime availability of features introduced in the Swift 5.2 + /// compiler for the target platform. + AvailabilityContext getSwift52Availability(); + //===--------------------------------------------------------------------===// // Diagnostics Helper functions diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 912e5617867a4..be788259457fe 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -239,6 +239,14 @@ AvailabilityContext ASTContext::getSwift51Availability() { } AvailabilityContext ASTContext::getTypesInAbstractMetadataStateAvailability() { + return getSwift52Availability(); +} + +AvailabilityContext ASTContext::getPrespecializedGenericMetadataAvailability() { + return getSwift52Availability(); +} + +AvailabilityContext ASTContext::getSwift52Availability() { auto target = LangOpts.Target; if (target.isMacOSX() ) { diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 56d802ed1f3d1..c55957c377239 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -1329,6 +1329,15 @@ void IRGenModule::error(SourceLoc loc, const Twine &message) { bool IRGenModule::useDllStorage() { return ::useDllStorage(Triple); } +bool IRGenModule::shouldPrespecializeGenericMetadata() { + auto &context = getSwiftModule()->getASTContext(); + auto deploymentAvailability = + AvailabilityContext::forDeploymentTarget(context); + return IRGen.Opts.PrespecializeGenericMetadata && + deploymentAvailability.isContainedIn( + context.getPrespecializedGenericMetadataAvailability()); +} + void IRGenerator::addGenModule(SourceFile *SF, IRGenModule *IGM) { assert(GenModules.count(SF) == 0); GenModules[SF] = IGM; diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 128079828db7e..1e43521dcd60c 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -744,6 +744,8 @@ class IRGenModule { void error(SourceLoc loc, const Twine &message); bool useDllStorage(); + + bool shouldPrespecializeGenericMetadata(); Size getAtomicBoolSize() const { return AtomicBoolSize; } Alignment getAtomicBoolAlignment() const { return AtomicBoolAlign; } From 9ea71d11149e00be131b70b72471650460852a69 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 4 Dec 2019 15:09:12 -0800 Subject: [PATCH 376/478] [IRGen] Prepare to refer to prespecializations. When emitting a reference to the metadata for a generic type, prepare to, rather than always inserting calls to the type metadata access function, emit direct references to static specializations when possible and emit calls to the forthcoming swift_getCanonicalSpecializedMetadata when not possible. For now, the metadata access function is always called. --- lib/IRGen/MetadataRequest.cpp | 88 +++++++++++++++++++++++++++++++++-- lib/IRGen/MetadataRequest.h | 4 ++ 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 57d708745c648..9445292dad7c4 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -692,6 +692,76 @@ static MetadataResponse emitNominalMetadataRef(IRGenFunction &IGF, return response; } +bool irgen::isNominalGenericContextTypeMetadataAccessTrivial( + IRGenModule &IGM, NominalTypeDecl &nominal, CanType type) { + // TODO: Once prespecialized generic metadata can be lazily emitted, eliminate + // this early return. + return false; + + assert(nominal.isGenericContext()); + + if (!IGM.shouldPrespecializeGenericMetadata()) { + return false; + } + + if (type->hasArchetype()) { + return false; + } + + if (nominal.getModuleContext() != IGM.getSwiftModule() || + nominal.isResilient(IGM.getSwiftModule(), ResilienceExpansion::Minimal)) { + return false; + } + + if (isa(type) || isa(type)) { + // TODO: Support enums. + return false; + } + + if (isa(type) || isa(type)) { + // TODO: Support classes. + return false; + } + + auto *generic = type.getAnyGeneric(); + assert(generic); + auto *environment = generic->getGenericEnvironment(); + assert(environment); + auto substitutions = + type->getContextSubstitutionMap(IGM.getSwiftModule(), &nominal); + + return llvm::all_of(environment->getGenericParams(), [&](auto parameter) { + auto conformances = + environment->getGenericSignature()->getConformsTo(parameter); + auto witnessTablesAreReferenceable = + llvm::all_of(conformances, [&](ProtocolDecl *conformance) { + return conformance->getModuleContext() == IGM.getSwiftModule() && + !conformance->isResilient(IGM.getSwiftModule(), + ResilienceExpansion::Minimal); + }); + auto argument = ((Type *)parameter)->subst(substitutions); + auto genericArgument = argument->getAnyGeneric(); + // For now, to avoid statically specializing generic protocol witness + // tables, don't statically specialize metadata for types any of whose + // arguments are generic. + // + // TODO: This is more pessimistic than necessary. Specialize even in + // the face of generic arguments so long as those arguments + // aren't required to conform to any protocols. + // + // TODO: Once witness tables are statically specialized, check whether the + // ConformanceInfo returns nullptr from tryGetConstantTable. + // early return. + auto isGeneric = genericArgument && genericArgument->isGenericContext(); + auto isNominal = argument->getNominalOrBoundGenericNominal(); + auto isExistential = argument->isExistentialType(); + return isNominal && !isGeneric && !isExistential && + witnessTablesAreReferenceable && + irgen::isTypeMetadataAccessTrivial(IGM, + argument->getCanonicalType()); + }) && IGM.getTypeInfoForUnlowered(type).isFixedSize(ResilienceExpansion::Maximal); +} + /// Is it basically trivial to access the given metadata? If so, we don't /// need a cache variable in its accessor. bool irgen::isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type) { @@ -707,14 +777,14 @@ bool irgen::isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type) { if (isa(nominalDecl->getModuleScopeContext())) return false; - // Generic type metadata always requires an accessor. if (nominalDecl->isGenericContext()) - return false; + return isNominalGenericContextTypeMetadataAccessTrivial(IGM, *nominalDecl, + type); auto expansion = ResilienceExpansion::Maximal; // Resiliently-sized metadata access always requires an accessor. - return (IGM.getTypeInfoForUnlowered(type).isFixedSize(expansion)); + return IGM.getTypeInfoForUnlowered(type).isFixedSize(expansion); } // The empty tuple type has a singleton metadata. @@ -739,6 +809,18 @@ bool irgen::isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type) { if (type->hasDynamicSelfType()) return true; + if (isa(type) || isa(type)) { + auto nominalType = cast(type); + auto *nominalDecl = nominalType->getDecl(); + + // Imported type metadata always requires an accessor. + if (isa(nominalDecl->getModuleScopeContext())) + return false; + + return isNominalGenericContextTypeMetadataAccessTrivial(IGM, *nominalDecl, + type); + } + return false; } diff --git a/lib/IRGen/MetadataRequest.h b/lib/IRGen/MetadataRequest.h index d9a0471d8e14b..5cfb836335959 100644 --- a/lib/IRGen/MetadataRequest.h +++ b/lib/IRGen/MetadataRequest.h @@ -503,6 +503,10 @@ static inline bool isAccessorLazilyGenerated(MetadataAccessStrategy strategy) { /// need a cache variable in its accessor. bool isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type); +bool isNominalGenericContextTypeMetadataAccessTrivial(IRGenModule &IGM, + NominalTypeDecl &nominal, + CanType type); + /// Determine how the given type metadata should be accessed. MetadataAccessStrategy getTypeMetadataAccessStrategy(CanType type); From c2d5d60d65732557cf994f126d46850d88b90e86 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 4 Dec 2019 15:19:01 -0800 Subject: [PATCH 377/478] [IRGen] Prepare to lazily emit prespecializations. Added worklist of prespecializations awaiting lazy emission to IRGenModule. Added map from type decl to list of bound types for which prespecializations will be emitted. For now, no specializations are emitted. --- lib/IRGen/GenDecl.cpp | 18 ++++++++++++++++++ lib/IRGen/IRGenModule.h | 15 +++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index bc62e0b270e7e..94f7ff323958f 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1129,6 +1129,7 @@ void IRGenerator::emitTypeMetadataRecords() { /// else) that we require. void IRGenerator::emitLazyDefinitions() { while (!LazyTypeMetadata.empty() || + !LazySpecializedTypeMetadataRecords.empty() || !LazyTypeContextDescriptors.empty() || !LazyOpaqueTypeDescriptors.empty() || !LazyFieldDescriptors.empty() || @@ -1145,6 +1146,11 @@ void IRGenerator::emitLazyDefinitions() { CurrentIGMPtr IGM = getGenModule(type->getDeclContext()); emitLazyTypeMetadata(*IGM.get(), type); } + while (!LazySpecializedTypeMetadataRecords.empty()) { + CanType type = LazySpecializedTypeMetadataRecords.pop_back_val(); + auto *nominal = type->getNominalOrBoundGenericNominal(); + CurrentIGMPtr IGM = getGenModule(nominal->getDeclContext()); + } while (!LazyTypeContextDescriptors.empty()) { NominalTypeDecl *type = LazyTypeContextDescriptors.pop_back_val(); auto &entry = LazyTypeGlobals.find(type)->second; @@ -1362,6 +1368,18 @@ void IRGenerator::noteUseOfFieldDescriptor(NominalTypeDecl *type) { LazyFieldDescriptors.push_back(type); } +void IRGenerator::noteUseOfSpecializedGenericTypeMetadata(CanType type) { + auto key = type->getAnyNominal(); + assert(key); + auto &enqueuedSpecializedTypes = this->SpecializationsForGenericTypes[key]; + if (llvm::all_of(enqueuedSpecializedTypes, + [&](CanType enqueued) { return enqueued != type; })) { + assert(!FinishedEmittingLazyDefinitions); + this->LazySpecializedTypeMetadataRecords.push_back(type); + enqueuedSpecializedTypes.push_back(type); + } +} + void IRGenerator::noteUseOfOpaqueTypeDescriptor(OpaqueTypeDecl *opaque) { if (!opaque) return; diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 1e43521dcd60c..e4cae9fd479e3 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -244,6 +244,15 @@ class IRGenerator { /// queued up. llvm::SmallPtrSet LazilyEmittedFieldMetadata; + /// Maps every generic type that is specialized within the module to its + /// specializations. + llvm::DenseMap> + SpecializationsForGenericTypes; + + /// The queue of specialized generic types whose prespecialized metadata to + /// emit. + llvm::SmallVector LazySpecializedTypeMetadataRecords; + struct LazyOpaqueInfo { bool IsDescriptorUsed = false; bool IsDescriptorEmitted = false; @@ -375,10 +384,16 @@ class IRGenerator { void ensureRelativeSymbolCollocation(SILDefaultWitnessTable &wt); + llvm::SmallVector specializationsForType(NominalTypeDecl *type) { + return SpecializationsForGenericTypes.lookup(type); + } + void noteUseOfTypeMetadata(NominalTypeDecl *type) { noteUseOfTypeGlobals(type, true, RequireMetadata); } + void noteUseOfSpecializedGenericTypeMetadata(CanType type); + void noteUseOfTypeMetadata(CanType type) { type.visit([&](Type t) { if (auto *nominal = t->getAnyNominal()) From 840ded49237454e55862d3db1d069abfb284edd2 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 4 Dec 2019 15:35:45 -0800 Subject: [PATCH 378/478] [IRGen] Accessor returns prespecializations. For every prespecialization of generic metadata that exists in the module where the generic type is defined, the metadata accessor gains code with the following effect switch arguments { case prespecialization1.genericArguments: return prespecialization1 case prespecialization2.genericArguments: return prespecialization2 ... default: return swift_getGenericMetadata(...) } rdar://problem/56961700 --- lib/IRGen/MetadataRequest.cpp | 98 ++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 9445292dad7c4..566669fc084da 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -19,24 +19,29 @@ #include "ConstantBuilder.h" #include "Explosion.h" #include "FixedTypeInfo.h" -#include "GenericRequirement.h" #include "GenArchetype.h" #include "GenClass.h" #include "GenMeta.h" #include "GenProto.h" #include "GenType.h" +#include "GenericRequirement.h" #include "IRGenDebugInfo.h" #include "IRGenFunction.h" #include "IRGenMangler.h" #include "IRGenModule.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ExistentialLayout.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/SubstitutionMap.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/IRGen/Linking.h" #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/TypeLowering.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/IR/Constant.h" +#include "llvm/Support/FormatVariadic.h" +#include using namespace swift; using namespace irgen; @@ -1765,6 +1770,74 @@ IRGenFunction::emitGenericTypeMetadataAccessFunctionCall( return MetadataResponse::handle(*this, request, call); } +static void emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( + IRGenFunction &IGF, Explosion ¶ms, NominalTypeDecl *nominal, + GenericArguments &genericArgs, + std::function valueAtIndex) { + auto &IGM = IGF.IGM; + auto specializations = IGF.IGM.IRGen.specializationsForType(nominal); + if (specializations.size() > 0) { + SmallVector conditionBlocks; + for (size_t index = 0; index < specializations.size(); ++index) { + conditionBlocks.push_back(llvm::BasicBlock::Create(IGM.getLLVMContext())); + } + + IGF.Builder.CreateBr(conditionBlocks[0]); + + SmallVector, 4> + specializationBlocks; + auto switchDestination = llvm::BasicBlock::Create(IGM.getLLVMContext()); + unsigned long index = 0; + for (auto specialization : specializations) { + auto conditionBlock = conditionBlocks[index]; + IGF.Builder.emitBlock(conditionBlock); + auto successorBlock = index < conditionBlocks.size() - 1 + ? conditionBlocks[index + 1] + : switchDestination; + auto specializationBlock = llvm::BasicBlock::Create(IGM.getLLVMContext()); + auto substitutions = specialization->getContextSubstitutionMap( + IGM.getSwiftModule(), nominal); + + llvm::Value *condition = llvm::ConstantInt::get(IGM.Int1Ty, 1); + auto generic = specialization->getAnyGeneric(); + auto parameters = generic->getGenericEnvironment()->getGenericParams(); + for (size_t index = 0; index < parameters.size(); ++index) { + auto parameter = parameters[index]; + auto argument = ((Type *)parameter)->subst(substitutions); + llvm::Constant *addr = + IGM.getAddrOfTypeMetadata(argument->getCanonicalType()); + auto addrInt = IGF.Builder.CreateBitCast(addr, IGM.Int8PtrTy); + condition = IGF.Builder.CreateAnd( + condition, IGF.Builder.CreateICmpEQ(addrInt, valueAtIndex(index))); + } + IGF.Builder.CreateCondBr(condition, specializationBlock, successorBlock); + + auto specializedMetadataAddress = + IGM.getAddrOfTypeMetadata(specialization); + // Construct a MetadataResponse. It has three fields in the following + // order: + // - const Metadata *Metadata; + // - MetadataState (i32) StaticState; + llvm::Value *response = llvm::UndefValue::get(IGM.TypeMetadataResponseTy); + response = IGF.Builder.CreateInsertValue( + response, specializedMetadataAddress, 0, + "insert metadata address into response"); + auto state = + llvm::ConstantInt::get(IGM.SizeTy, (uint32_t)MetadataState::Complete); + response = IGF.Builder.CreateInsertValue( + response, state, 1, "insert metadata state into response"); + specializationBlocks.push_back({specializationBlock, response}); + ++index; + } + + for (auto pair : specializationBlocks) { + IGF.Builder.emitBlock(pair.first); + IGF.Builder.CreateRet(pair.second); + } + IGF.Builder.emitBlock(switchDestination); + } +} + static MetadataResponse emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, Explosion ¶ms, @@ -1788,6 +1861,21 @@ emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, llvm::Value *arguments = IGF.Builder.CreateBitCast(argsBuffer.getAddress(), IGM.Int8PtrTy); + llvm::Value *argumentsBuffer = + IGF.Builder.CreateBitCast(argsBuffer.getAddress(), IGM.Int8PtrPtrTy); + + emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( + IGF, params, nominal, genericArgs, [&](int index) { + llvm::Value *indexValue = llvm::ConstantInt::get(IGM.Int64Ty, index); + llvm::SmallVector indices{indexValue}; + llvm::Value *elementPointer = + IGF.Builder.CreateGEP(argumentsBuffer, indexValue); + llvm::LoadInst *retval = IGF.Builder.CreateLoad( + elementPointer, Alignment(), + llvm::formatv("load argument at index {0} from buffer", index)); + return retval; + }); + // Make the call. auto call = IGF.Builder.CreateCall(IGM.getGetGenericMetadataFn(), {request, arguments, descriptor}); @@ -1873,7 +1961,13 @@ emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, auto arg2 = numArguments >= 3 ? IGF.Builder.CreateBitCast(params.claimNext(), IGM.Int8PtrTy) : llvm::UndefValue::get(IGM.Int8PtrTy); - + + std::array argValues{arg0, arg1, arg2}; + + emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( + IGF, params, nominal, genericArgs, + [&](int index) { return argValues[index]; }); + auto call = IGF.Builder.CreateCall(thunkFn, {request, arg0, arg1, arg2, descriptor}); call->setDoesNotAccessMemory(); From d9205fafd334e3833fe2c640972bfc7e878d0c18 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 4 Dec 2019 15:47:49 -0800 Subject: [PATCH 379/478] [IRGen] Prepare to emit prespecializations. --- lib/IRGen/GenDecl.cpp | 1 + lib/IRGen/GenMeta.cpp | 9 +++++++++ lib/IRGen/GenMeta.h | 3 +++ 3 files changed, 13 insertions(+) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 94f7ff323958f..d8e86bdd74fc7 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1150,6 +1150,7 @@ void IRGenerator::emitLazyDefinitions() { CanType type = LazySpecializedTypeMetadataRecords.pop_back_val(); auto *nominal = type->getNominalOrBoundGenericNominal(); CurrentIGMPtr IGM = getGenModule(nominal->getDeclContext()); + emitLazySpecializedGenericTypeMetadata(*IGM.get(), type); } while (!LazyTypeContextDescriptors.empty()) { NominalTypeDecl *type = LazyTypeContextDescriptors.pop_back_val(); diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 4744438489f4c..375b5d0c394a7 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -1834,6 +1834,11 @@ void irgen::emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type) { } } +void irgen::emitLazySpecializedGenericTypeMetadata(IRGenModule &IGM, + CanType type) { + emitSpecializedGenericStructMetadata(IGM, type); +} + llvm::Constant * IRGenModule::getAddrOfSharedContextDescriptor(LinkEntity entity, ConstantInit definition, @@ -3678,6 +3683,10 @@ void irgen::emitStructMetadata(IRGenModule &IGM, StructDecl *structDecl) { init.finishAndCreateFuture()); } +/// Emit the type metadata or metadata template for a struct. +void irgen::emitSpecializedGenericStructMetadata(IRGenModule &IGM, + CanType type) {} + // Enums static Optional getConstantPayloadSize(IRGenModule &IGM, diff --git a/lib/IRGen/GenMeta.h b/lib/IRGen/GenMeta.h index 4d56cc52f7a80..973e63c9f1f33 100644 --- a/lib/IRGen/GenMeta.h +++ b/lib/IRGen/GenMeta.h @@ -71,6 +71,7 @@ namespace irgen { /// generated definitions. void emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type); + void emitLazySpecializedGenericTypeMetadata(IRGenModule &IGM, CanType type); /// Emit metadata for a foreign struct, enum or class. void emitForeignTypeMetadata(IRGenModule &IGM, NominalTypeDecl *decl); @@ -81,6 +82,8 @@ namespace irgen { /// Emit the metadata associated with the given enum declaration. void emitEnumMetadata(IRGenModule &IGM, EnumDecl *theEnum); + void emitSpecializedGenericStructMetadata(IRGenModule &IGM, CanType type); + /// Get what will be the index into the generic type argument array at the end /// of a nominal type's metadata. int32_t getIndexOfGenericArgument(IRGenModule &IGM, From 7068141770772f915b3dca1045b1d666d7ccbec1 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 4 Dec 2019 18:48:07 -0800 Subject: [PATCH 380/478] [IRGen] Emit prespecialized metadata records. Prespecialized records contain direct references to the generic arguments and protocol witnesses with which it is specialized for now. Both prespecialized records and the records that are specialized at runtime gain a trailing pointer-sized flagset. For now, the flags in it include whether the record was prespecialized and whether it was known to be canonical at compile time (which is true for prespecialized records within the module which defines the type whose metadata is specialized since in those cases the metadata accessor can be modified). rdar://problem/56960307 --- lib/IRGen/GenMeta.cpp | 154 ++++++++++++++++++++++++++++-- lib/IRGen/GenValueWitness.cpp | 10 ++ lib/IRGen/IRGenModule.h | 3 + lib/IRGen/StructMetadataVisitor.h | 11 +++ 4 files changed, 170 insertions(+), 8 deletions(-) diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 375b5d0c394a7..a15f0d5d06c4a 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -18,9 +18,10 @@ #include "swift/ABI/TypeIdentity.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" +#include "swift/AST/Attr.h" #include "swift/AST/CanTypeVisitor.h" #include "swift/AST/Decl.h" -#include "swift/AST/Attr.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/SubstitutionMap.h" @@ -31,13 +32,13 @@ #include "swift/SIL/SILModule.h" #include "swift/SIL/TypeLowering.h" #include "swift/Strings.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" #include "llvm/ADT/SmallString.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Module.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclObjC.h" #include "Address.h" #include "Callee.h" @@ -3474,8 +3475,12 @@ namespace { return descriptor; } + llvm::Constant *getNominalTypeDescriptor() { + return emitNominalTypeDescriptor(); + } + void addNominalTypeDescriptor() { - B.add(emitNominalTypeDescriptor()); + B.add(asImpl().getNominalTypeDescriptor()); } ConstantReference emitValueWitnessTable(bool relativeReference) { @@ -3483,14 +3488,18 @@ namespace { return irgen::emitValueWitnessTable(IGM, type, false, relativeReference); } + ConstantReference getValueWitnessTable(bool relativeReference) { + return emitValueWitnessTable(relativeReference); + } + void addValueWitnessTable() { - B.add(emitValueWitnessTable(false).getValue()); + B.add(asImpl().getValueWitnessTable(false).getValue()); } void addFieldOffset(VarDecl *var) { assert(var->hasStorage() && "storing field offset for computed property?!"); - SILType structType = getLoweredType(); + SILType structType = asImpl().getLoweredType(); llvm::Constant *offset = emitPhysicalStructMemberFixedOffset(IGM, structType, var); @@ -3515,6 +3524,22 @@ namespace { void addGenericWitnessTable(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic requirements"); } + + bool hasTrailingFlags() { + return IGM.shouldPrespecializeGenericMetadata(); + } + + void addTrailingFlags() { + auto flags = asImpl().getTrailingFlags(); + + B.addInt(IGM.Int64Ty, flags.getOpaqueValue()); + } + + MetadataTrailingFlags getTrailingFlags() { + MetadataTrailingFlags flags; + + return flags; + } }; class StructMetadataBuilder : @@ -3587,6 +3612,16 @@ namespace { return StructContextDescriptorBuilder(IGM, Target, RequireMetadata).emit(); } + GenericMetadataPatternFlags getPatternFlags() { + auto flags = super::getPatternFlags(); + + if (IGM.shouldPrespecializeGenericMetadata()) { + flags.setHasTrailingFlags(true); + } + + return flags; + } + ConstantReference emitValueWitnessTable(bool relativeReference) { assert(relativeReference && "should only relative reference"); return getValueWitnessTableForGenericValueType(IGM, Target, @@ -3646,10 +3681,96 @@ namespace { vectorSize }; } + void addTrailingFlags() { this->B.addInt(IGM.Int64Ty, 0); } + bool hasCompletionFunction() { return !isa(IGM.getTypeInfo(getLoweredType())); } }; + + class SpecializedGenericStructMetadataBuilder + : public StructMetadataBuilderBase< + SpecializedGenericStructMetadataBuilder> { + using super = + StructMetadataBuilderBase; + CanType type; + bool HasUnfilledFieldOffset = false; + + protected: + using super::asImpl; + using super::getLoweredType; + using super::IGM; + using super::Target; + + public: + SpecializedGenericStructMetadataBuilder(IRGenModule &IGM, CanType type, + StructDecl &decl, + ConstantStructBuilder &B) + : super(IGM, &decl, B), type(type) {} + + void noteStartOfTypeSpecificMembers() {} + + llvm::Constant *getNominalTypeDescriptor() { + return IGM.getAddrOfTypeContextDescriptor(Target, RequireMetadata); + } + + SILType getLoweredType() { return SILType::getPrimitiveObjectType(type); } + + ConstantReference getValueWitnessTable(bool relativeReference) { + auto type = this->Target->getDeclaredType()->getCanonicalType(); + return ConstantReference(IGM.getAddrOfEffectiveValueWitnessTable(type), + irgen::ConstantReference::Direct); + } + + void addGenericArgument(GenericRequirement requirement) { + auto t = requirement.TypeParameter.subst(genericSubstitutions()); + ConstantReference ref = IGM.getAddrOfTypeMetadata( + CanType(t), SymbolReferenceKind::Relative_Direct); + B.add(ref.getDirectValue()); + } + + void addGenericWitnessTable(GenericRequirement requirement) { + auto conformance = genericSubstitutions().lookupConformance( + requirement.TypeParameter->getCanonicalType(), requirement.Protocol); + ProtocolConformance *concreteConformance = conformance.getConcrete(); + + llvm::Constant *addr; + + Type argument = requirement.TypeParameter.subst(genericSubstitutions()); + auto argumentNominal = argument->getAnyNominal(); + if (argumentNominal && argumentNominal->isGenericContext()) { + // TODO: Statically specialize the witness table pattern for t's + // conformance. + llvm_unreachable("Statically specializing metadata at generic types is " + "not supported."); + } else { + RootProtocolConformance *rootConformance = + concreteConformance->getRootConformance(); + addr = IGM.getAddrOfWitnessTable(rootConformance); + } + + B.add(addr); + } + + SubstitutionMap genericSubstitutions() { + return type->getContextSubstitutionMap(IGM.getSwiftModule(), + type->getAnyNominal()); + } + + MetadataTrailingFlags getTrailingFlags() { + MetadataTrailingFlags flags = super::getTrailingFlags(); + + flags.setIsStaticSpecialization(true); + flags.setIsCanonicalStaticSpecialization(true); + + return flags; + } + + void flagUnfilledFieldOffset() { HasUnfilledFieldOffset = true; } + + bool canBeConstant() { return !HasUnfilledFieldOffset; } + }; + } // end anonymous namespace /// Emit the type metadata or metadata template for a struct. @@ -3683,9 +3804,26 @@ void irgen::emitStructMetadata(IRGenModule &IGM, StructDecl *structDecl) { init.finishAndCreateFuture()); } -/// Emit the type metadata or metadata template for a struct. void irgen::emitSpecializedGenericStructMetadata(IRGenModule &IGM, - CanType type) {} + CanType type) { + Type ty = type.getPointer(); + auto &context = type->getNominalOrBoundGenericNominal()->getASTContext(); + PrettyStackTraceType stackTraceRAII( + context, "emitting prespecialized metadata for", ty); + ConstantInitBuilder initBuilder(IGM); + auto init = initBuilder.beginStruct(); + init.setPacked(true); + + bool isPattern = false; + + auto &decl = *type.getStructOrBoundGenericStruct(); + SpecializedGenericStructMetadataBuilder builder(IGM, type, decl, init); + builder.layout(); + + bool canBeConstant = builder.canBeConstant(); + IGM.defineTypeMetadata(type, isPattern, canBeConstant, + init.finishAndCreateFuture()); +} // Enums diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index 05024f6abd84e..504251e533846 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -1023,6 +1023,16 @@ getAddrOfKnownValueWitnessTable(IRGenModule &IGM, CanType type, return {}; } +llvm::Constant * +IRGenModule::getAddrOfEffectiveValueWitnessTable(CanType concreteType, + ConstantInit init) { + if (auto known = + getAddrOfKnownValueWitnessTable(*this, concreteType, false)) { + return known.getValue(); + } + return getAddrOfValueWitnessTable(concreteType); +} + /// Emit a value-witness table for the given type. ConstantReference irgen::emitValueWitnessTable(IRGenModule &IGM, CanType abstractType, diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index e4cae9fd479e3..d0392ad32d0f9 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1300,6 +1300,9 @@ private: \ ForDefinition_t forDefinition); llvm::Constant *getAddrOfValueWitnessTable(CanType concreteType, ConstantInit init = ConstantInit()); + llvm::Constant * + getAddrOfEffectiveValueWitnessTable(CanType concreteType, + ConstantInit init = ConstantInit()); Optional getAddrOfIVarInitDestroy(ClassDecl *cd, bool isDestroyer, bool isForeign, diff --git a/lib/IRGen/StructMetadataVisitor.h b/lib/IRGen/StructMetadataVisitor.h index 04195e44f4ad2..147d2672bb162 100644 --- a/lib/IRGen/StructMetadataVisitor.h +++ b/lib/IRGen/StructMetadataVisitor.h @@ -18,6 +18,7 @@ #define SWIFT_IRGEN_STRUCTMETADATALAYOUT_H #include "NominalMetadataVisitor.h" +#include "swift/AST/IRGenOptions.h" namespace swift { namespace irgen { @@ -61,6 +62,9 @@ template class StructMetadataVisitor asImpl().addFieldOffset(prop); asImpl().noteEndOfFieldOffsets(); + + if (asImpl().hasTrailingFlags()) + asImpl().addTrailingFlags(); } // Note the start of the field offset vector. @@ -68,6 +72,11 @@ template class StructMetadataVisitor // Note the end of the field offset vector. void noteEndOfFieldOffsets() {} + + bool hasTrailingFlags() { + return Target->isGenericContext() && + IGM.shouldPrespecializeGenericMetadata(); + } }; /// An "implementation" of StructMetadataVisitor that just scans through @@ -95,6 +104,8 @@ class StructMetadataScanner : public StructMetadataVisitor { NextOffset = NextOffset.roundUpToAlignment(super::IGM.getPointerAlignment()); } + void addTrailingFlags() { addPointer(); } + private: void addPointer() { NextOffset += super::IGM.getPointerSize(); From c4d13e4b4b28bb23027bc18b2d1251536b344aca Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 4 Dec 2019 15:31:02 -0800 Subject: [PATCH 381/478] [IRGen] Directly reference prespecializations. When possible, directly reference metadata prespecializations. Doing so is possible when the type is defined in the same module, because in those cases the metadata accessor can be modified to ensure that the prespecialized metadata is canonical. rdar://problem/56994171 --- lib/AST/Availability.cpp | 7 +- lib/IRGen/GenDecl.cpp | 8 ++ lib/IRGen/MetadataRequest.cpp | 27 ++++++- test/IRGen/lit.local.cfg | 4 + ...within-class-1argument-1distinct_use.swift | 29 +++++++ .../struct-inmodule-0argument.swift | 19 +++++ ...uct-inmodule-1argument-0distinct_use.swift | 25 ++++++ ...1argument-1conformance-1distinct_use.swift | 40 ++++++++++ ...mance_stdlib_equatable-1distinct_use.swift | 30 +++++++ ...dule-1argument-1distinct_generic_use.swift | 35 ++++++++ ...uct-inmodule-1argument-1distinct_use.swift | 36 +++++++++ ...1argument-2conformance-1distinct_use.swift | 43 ++++++++++ ...uct-inmodule-1argument-2distinct_use.swift | 45 +++++++++++ ...1argument-3conformance-1distinct_use.swift | 45 +++++++++++ ...uct-inmodule-1argument-3distinct_use.swift | 54 +++++++++++++ ...1argument-4conformance-1distinct_use.swift | 47 +++++++++++ ...uct-inmodule-1argument-4distinct_use.swift | 63 +++++++++++++++ ...1argument-5conformance-1distinct_use.swift | 49 ++++++++++++ ...uct-inmodule-1argument-5distinct_use.swift | 71 ++++++++++++++++ ...e-1argument-clang_node-1distinct_use.swift | 38 +++++++++ ...within-class-1argument-1distinct_use.swift | 43 ++++++++++ ...-within-enum-1argument-1distinct_use.swift | 41 ++++++++++ ...ithin-struct-1argument-1distinct_use.swift | 41 ++++++++++ ...uct-inmodule-2argument-0distinct_use.swift | 27 +++++++ ...uct-inmodule-2argument-1distinct_use.swift | 40 ++++++++++ ...uct-inmodule-2argument-2distinct_use.swift | 51 ++++++++++++ ...uct-inmodule-2argument-3distinct_use.swift | 62 ++++++++++++++ ...uct-inmodule-2argument-4distinct_use.swift | 73 +++++++++++++++++ ...uct-inmodule-2argument-5distinct_use.swift | 80 +++++++++++++++++++ ...within-class-1argument-1distinct_use.swift | 45 +++++++++++ test/lit.cfg | 13 +++ 31 files changed, 1225 insertions(+), 6 deletions(-) create mode 100644 test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index be788259457fe..579fe7f0f3c24 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -252,9 +252,12 @@ AvailabilityContext ASTContext::getSwift52Availability() { if (target.isMacOSX() ) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(10, 99, 0))); - } else if (target.isiOS() || target.isWatchOS()) { + } else if (target.isiOS()) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(99, 0, 0))); + } else if (target.isWatchOS()) { return AvailabilityContext( - VersionRange::allGTE(llvm::VersionTuple(9999, 0, 0))); + VersionRange::allGTE(llvm::VersionTuple(9, 99, 0))); } else { return AvailabilityContext::alwaysAvailable(); } diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index d8e86bdd74fc7..564e38ac575c5 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -3730,6 +3730,14 @@ ConstantReference IRGenModule::getAddrOfTypeMetadata(CanType concreteType, IRGen.noteUseOfTypeMetadata(nominal); } + if (shouldPrespecializeGenericMetadata()) { + if (auto nominal = concreteType->getAnyNominal()) { + if (nominal->isGenericContext()) { + IRGen.noteUseOfSpecializedGenericTypeMetadata(concreteType); + } + } + } + Optional entity; DebugTypeInfo DbgTy; diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 566669fc084da..3ce8b2c2619ce 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -40,6 +40,7 @@ #include "swift/SIL/TypeLowering.h" #include "llvm/ADT/STLExtras.h" #include "llvm/IR/Constant.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/FormatVariadic.h" #include @@ -643,6 +644,22 @@ llvm::Value *irgen::emitObjCHeapMetadataRef(IRGenFunction &IGF, classObject); } +static MetadataResponse emitNominalPrespecializedGenericMetadataRef( + IRGenFunction &IGF, NominalTypeDecl *theDecl, CanType theType, + DynamicMetadataRequest request) { + assert(isNominalGenericContextTypeMetadataAccessTrivial(IGF.IGM, *theDecl, + theType)); + // We are applying generic parameters to a generic type. + assert(theType->getAnyNominal() == theDecl); + + // Check to see if we've maybe got a local reference already. + if (auto cache = IGF.tryGetLocalTypeMetadata(theType, request)) + return cache; + + auto metadata = IGF.IGM.getAddrOfTypeMetadata(theType); + return MetadataResponse::forComplete(metadata); +} + /// Returns a metadata reference for a nominal type. /// /// This is only valid in a couple of special cases: @@ -683,6 +700,12 @@ static MetadataResponse emitNominalMetadataRef(IRGenFunction &IGF, theDecl->getGenericSignature()->areAllParamsConcrete()) && "no generic args?!"); + if (isNominalGenericContextTypeMetadataAccessTrivial(IGF.IGM, *theDecl, + theType)) { + return emitNominalPrespecializedGenericMetadataRef(IGF, theDecl, theType, + request); + } + // Call the generic metadata accessor function. llvm::Function *accessor = IGF.IGM.getAddrOfGenericTypeMetadataAccessFunction(theDecl, @@ -699,10 +722,6 @@ static MetadataResponse emitNominalMetadataRef(IRGenFunction &IGF, bool irgen::isNominalGenericContextTypeMetadataAccessTrivial( IRGenModule &IGM, NominalTypeDecl &nominal, CanType type) { - // TODO: Once prespecialized generic metadata can be lazily emitted, eliminate - // this early return. - return false; - assert(nominal.isGenericContext()); if (!IGM.shouldPrespecializeGenericMetadata()) { diff --git a/test/IRGen/lit.local.cfg b/test/IRGen/lit.local.cfg index ff48ffce548be..0597958565874 100644 --- a/test/IRGen/lit.local.cfg +++ b/test/IRGen/lit.local.cfg @@ -5,6 +5,10 @@ config.substitutions.insert(0, ('%build-irgen-test-overlays', '%target-swift-frontend -enable-objc-interop -disable-objc-attr-requires-foundation-module -emit-module -o %t -sdk %S/Inputs %S/Inputs/ObjectiveC.swift && ' '%target-swift-frontend -enable-objc-interop -emit-module -o %t -sdk %S/Inputs %S/Inputs/Foundation.swift -I %t')) +config.substitutions.insert(0, ('%build-irgen-test-overlays\(mock-sdk-directory: ([^)]+)\)', + SubstituteCaptures(r'%target-swift-frontend -enable-objc-interop -disable-objc-attr-requires-foundation-module -emit-module -o %t -sdk \1 \1/ObjectiveC.swift && ' + r'%target-swift-frontend -enable-objc-interop -emit-module -o %t -sdk \1 \1/Foundation.swift -I %t'))) + def get_target_os(): import re (run_cpu, run_vendor, run_os, run_version) = re.match('([^-]+)-([^-]+)-([^0-9]+)(.*)', config.variant_triple).groups() diff --git a/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..146714030f3aa --- /dev/null +++ b/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift @@ -0,0 +1,29 @@ +// RUN: %swift -target %module-target-future -parse-stdlib -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +precedencegroup AssignmentPrecedence {} + +class Namespace {} + +class Zang { +} + +extension Namespace where T == Zang { + class ExtensionNonGeneric {} +} + +@inline(never) +func consume(_ t: T) { + Builtin.fixLifetime(t) +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[METADATA_RESPONSE:%[0-9]+]] = call swiftcc %swift.metadata_response @"$s4main9NamespaceCA2A4ZangCRszlE19ExtensionNonGenericCyAE_GMa"(i64 0) #{{[0-9]+}} +// CHECK: [[METADATA:%[0-9]+]] = extractvalue %swift.metadata_response [[METADATA_RESPONSE]], 0 +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %5, %swift.type* [[METADATA]]) +// CHECK: } +func doit() { + consume( Namespace.ExtensionNonGeneric() ) +} + +doit() diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift new file mode 100644 index 0000000000000..c5a6c25d900b8 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift @@ -0,0 +1,19 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +struct Value { + let first: Int +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #0 { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, <{ i32, i32, i32, i32, i32, i32, i32 }>*, i32, [4 x i8], i64 }>, <{ i8**, i64, <{ i32, i32, i32, i32, i32, i32, i32 }>*, i32, [4 x i8], i64 }>* @"$s4main5ValueVMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift new file mode 100644 index 0000000000000..3cbf0bc99e36e --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift @@ -0,0 +1,25 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: } +func doit() { +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift new file mode 100644 index 0000000000000..48b477d590d27 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift @@ -0,0 +1,40 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$sytN" = external global %swift.full_type +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +protocol P {} +extension Int : P {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TABLE:%[0-9]+]] = bitcast i8** %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* [[ERASED_TABLE]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #12 +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift new file mode 100644 index 0000000000000..4ae70ef36a4ce --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift @@ -0,0 +1,30 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK-NOT: @"$s4main5ValueVyAA7IntegerVGMf" +struct Value { + let first: First +} + +struct Integer : Hashable { + let value: Int +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +func doit() { + consume( Value(first: Integer(value: 13)) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_CONFORMANCE:%[0-9]+]] = bitcast i8** %2 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* [[ERASED_CONFORMANCE]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift new file mode 100644 index 0000000000000..c2ee6f3a4c88a --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift @@ -0,0 +1,35 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +struct Outer { + let first: First +} + +struct Inner { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// TODO: Once prespecialization is done for generic arguments which are +// themselves generic (Outer>, here), a direct reference to +// the prespecialized metadata should be emitted here. +// CHECK: [[TYPE:%[0-9]+]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s4main5OuterVyAA5InnerVySiGGMD") #11 +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* [[TYPE]]) +// CHECK: } +func doit() { + consume( Outer(first: Inner(first: 13)) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5OuterVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5OuterVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..6938f323fd629 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift @@ -0,0 +1,36 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift new file mode 100644 index 0000000000000..01f8167d65448 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift @@ -0,0 +1,43 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$sytN" = external global %swift.full_type +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +protocol P {} +protocol Q {} +extension Int : P {} +extension Int : Q {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, i8**, i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TABLE_1:%[0-9]+]] = bitcast i8** %2 to i8* +// CHECK: [[ERASED_TABLE_2:%[0-9]+]] = bitcast i8** %3 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* [[ERASED_TABLE_1]], i8* [[ERASED_TABLE_2]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift new file mode 100644 index 0000000000000..fbcd94d17aee5 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift @@ -0,0 +1,45 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) + consume( Value(first: 13.0) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1]] +// CHECK: br i1 [[EQUAL_TYPES_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] +// CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift new file mode 100644 index 0000000000000..a419b9a77302e --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift @@ -0,0 +1,45 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$sytN" = external global %swift.full_type +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +protocol P {} +protocol Q {} +protocol R {} +extension Int : P {} +extension Int : Q {} +extension Int : R {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_ARGUMENT_BUFFER:%[0-9]+]] = bitcast i8** %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[ERASED_TYPE_ADDRESS:%[0-9]+]] = getelementptr i8*, i8** %1, i64 0 +// CHECK: %"load argument at index 0 from buffer" = load i8*, i8** [[ERASED_TYPE_ADDRESS]] +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), %"load argument at index 0 from buffer" +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata(i64 %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift new file mode 100644 index 0000000000000..fd91112b12430 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift @@ -0,0 +1,54 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) + consume( Value(first: 13.0) ) + consume( Value(first: "13") ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1]] +// CHECK: br i1 [[EQUAL_TYPES_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] +// CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_3:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3]] +// CHECK: br i1 [[EQUAL_TYPES_3]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift new file mode 100644 index 0000000000000..a79eb619a4677 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift @@ -0,0 +1,47 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$sytN" = external global %swift.full_type +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1SAAWP", i32 0, i32 0), i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +protocol P {} +protocol Q {} +protocol R {} +protocol S {} +extension Int : P {} +extension Int : Q {} +extension Int : R {} +extension Int : S {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_ARGUMENT_BUFFER:%[0-9]+]] = bitcast i8** %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[ERASED_TYPE_ADDRESS:%[0-9]+]] = getelementptr i8*, i8** %1, i64 0 +// CHECK: %"load argument at index 0 from buffer" = load i8*, i8** [[ERASED_TYPE_ADDRESS]] +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), %"load argument at index 0 from buffer" +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata(i64 %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift new file mode 100644 index 0000000000000..de12dda34713e --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift @@ -0,0 +1,63 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main5ValueVys5UInt8VGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) + consume( Value(first: 13.0) ) + consume( Value(first: "13") ) + consume( Value(first: 13 as UInt8) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1]] +// CHECK: br i1 [[EQUAL_TYPES_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] +// CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_3:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3]] +// CHECK: br i1 [[EQUAL_TYPES_3]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[TYPE_COMPARISON_4:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_4]]: +// CHECK: [[EQUAL_TYPE_4:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_4:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4]] +// CHECK: br i1 [[EQUAL_TYPES_4]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_4]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift new file mode 100644 index 0000000000000..074ea7cd9363d --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift @@ -0,0 +1,49 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$sytN" = external global %swift.full_type +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1SAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1TAAWP", i32 0, i32 0), i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +protocol P {} +protocol Q {} +protocol R {} +protocol S {} +protocol T {} +extension Int : P {} +extension Int : Q {} +extension Int : R {} +extension Int : S {} +extension Int : T {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_ARGUMENT_BUFFER:%[0-9]+]] = bitcast i8** %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[ERASED_TYPE_ADDRESS:%[0-9]+]] = getelementptr i8*, i8** %1, i64 0 +// CHECK: %"load argument at index 0 from buffer" = load i8*, i8** [[ERASED_TYPE_ADDRESS]] +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), %"load argument at index 0 from buffer" +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata(i64 %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift new file mode 100644 index 0000000000000..5e871a3f8b737 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift @@ -0,0 +1,71 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main5ValueVys4Int8VGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss4Int8VN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVys5UInt8VGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) + consume( Value(first: 13.0) ) + consume( Value(first: "13") ) + consume( Value(first: 13 as UInt8) ) + consume( Value(first: 13 as Int8) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1]] +// CHECK: br i1 [[EQUAL_TYPES_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] +// CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_3:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3]] +// CHECK: br i1 [[EQUAL_TYPES_3]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[TYPE_COMPARISON_4:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_4]]: +// CHECK: [[EQUAL_TYPE_4:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_4:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4]] +// CHECK: br i1 [[EQUAL_TYPES_4]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[TYPE_COMPARISON_5:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_5]]: +// CHECK: [[EQUAL_TYPE_5:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss4Int8VN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_5:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_5]] +// CHECK: br i1 [[EQUAL_TYPES_5]], label %[[EXIT_PRESPECIALIZED_5:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_4]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_5]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys4Int8VGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift new file mode 100644 index 0000000000000..7fca0f2b184de --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift @@ -0,0 +1,38 @@ +// RUN: %empty-directory(%t) +// RUN: %build-irgen-test-overlays(mock-sdk-directory: %S/../Inputs) +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs -I %t) -target %module-target-future -primary-file %s -emit-ir | %FileCheck %s + +// REQUIRES: objc_interop + +import Foundation + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +struct Value { + let value: T + + init(_ value: T) { + self.value = value + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: [[TYPE:%[0-9]+]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s4main5ValueVySo12NSDictionaryCGMD") #{{[0-9]+}} +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %12, %swift.type* [[TYPE]]) +// CHECK: } +func doit() { + consume(Value(NSDictionary())) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..d06d9366114de --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift @@ -0,0 +1,43 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// REQUIRES: OS=macosx + +// CHECK: @"$s4main9NamespaceC5ValueVySS_SiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceC5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +final class Namespace { + struct Value { + let first: First + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceC5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceC5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..7d375e98035f5 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift @@ -0,0 +1,41 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main9NamespaceO5ValueVySS_SiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceO5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceO5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +enum Namespace { + struct Value { + let first: First + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceO5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceO5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceO5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceO5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..dd71000739ebe --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift @@ -0,0 +1,41 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main9NamespaceV5ValueVySS_SiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceV5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceV5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +struct Namespace { + struct Value { + let first: First + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceV5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceV5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceV5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceV5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift new file mode 100644 index 0000000000000..cae7f9f84b1ad --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift @@ -0,0 +1,27 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: } +func doit() { +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift new file mode 100644 index 0000000000000..fcec7f43c4685 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift @@ -0,0 +1,40 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift new file mode 100644 index 0000000000000..ec54261b0ab90 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift @@ -0,0 +1,51 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) + consume( Value(first: 13.0, second: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_2_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2_1]] +// CHECK: [[EQUAL_TYPE_2_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] +// CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift new file mode 100644 index 0000000000000..e6cc6b7d021c9 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift @@ -0,0 +1,62 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) + consume( Value(first: 13.0, second: 13) ) + consume( Value(first: "13.0", second: 13.0) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_2_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2_1]] +// CHECK: [[EQUAL_TYPE_2_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] +// CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_3_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3_1]] +// CHECK: [[EQUAL_TYPE_3_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_3_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_3_1]], [[EQUAL_TYPE_3_2]] +// CHECK: br i1 [[EQUAL_TYPES_3_2]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift new file mode 100644 index 0000000000000..89f601fa48447 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift @@ -0,0 +1,73 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main5ValueVys5UInt8VSSGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", %swift.type* @"$sSSN", i32 0, i32 8, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) + consume( Value(first: 13.0, second: 13) ) + consume( Value(first: "13.0", second: 13.0) ) + consume( Value(first: 13 as UInt8, second: "13.0") ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_2_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2_1]] +// CHECK: [[EQUAL_TYPE_2_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] +// CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_3_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3_1]] +// CHECK: [[EQUAL_TYPE_3_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_3_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_3_1]], [[EQUAL_TYPE_3_2]] +// CHECK: br i1 [[EQUAL_TYPES_3_2]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[TYPE_COMPARISON_4:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_4]]: +// CHECK: [[EQUAL_TYPE_4_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_4_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4_1]] +// CHECK: [[EQUAL_TYPE_4_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_4_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_4_1]], [[EQUAL_TYPE_4_2]] +// CHECK: br i1 [[EQUAL_TYPES_4_2]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_4]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift new file mode 100644 index 0000000000000..623821b1b2c02 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift @@ -0,0 +1,80 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main5ValueVys5UInt8VSSGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", %swift.type* @"$sSSN", i32 0, i32 8, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) + consume( Value(first: 13.0, second: 13) ) + consume( Value(first: "13.0", second: 13.0) ) + consume( Value(first: 13 as UInt8, second: "13.0") ) + consume( Value(first: 13 as Int8, second: 13 as UInt8) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_2_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2_1]] +// CHECK: [[EQUAL_TYPE_2_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] +// CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_3_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3_1]] +// CHECK: [[EQUAL_TYPE_3_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_3_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_3_1]], [[EQUAL_TYPE_3_2]] +// CHECK: br i1 [[EQUAL_TYPES_3_2]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[TYPE_COMPARISON_4:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_4]]: +// CHECK: [[EQUAL_TYPE_4_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_4_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4_1]] +// CHECK: [[EQUAL_TYPE_4_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_4_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_4_1]], [[EQUAL_TYPE_4_2]] +// CHECK: br i1 [[EQUAL_TYPES_4_2]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[TYPE_COMPARISON_5:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_5]]: +// CHECK: [[EQUAL_TYPE_5_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss4Int8VN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_5_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_5_1]] +// CHECK: [[EQUAL_TYPE_5_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_5_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_5_1]], [[EQUAL_TYPE_5_2]] +// CHECK: br i1 [[EQUAL_TYPES_5_2]], label %[[EXIT_PRESPECIALIZED_5:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_4]]: +// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..fdeedd3131e8e --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift @@ -0,0 +1,45 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main9NamespaceC5ValueVySS_SiSdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceC5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", %swift.type* @"$sSdN", i32 0, i32 8, i64 3 }>, align 8 +final class Namespace { + struct Value { + let first: First + let second: Second + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13, second: 13.0) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"(i64, %swift.type*, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: [[ERASED_TYPE_3:%[0-9]+]] = bitcast %swift.type* %3 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: [[EQUAL_TYPE_1_3:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_3]] +// CHECK: [[EQUAL_TYPES_1_3:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_2]], [[EQUAL_TYPE_1_3]] +// CHECK: br i1 [[EQUAL_TYPES_1_3]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiSdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* [[ERASED_TYPE_3]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/lit.cfg b/test/lit.cfg index 1b43328020d69..954031e68f7fb 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -670,6 +670,7 @@ config.substitutions.append(('%target-cpu', run_cpu)) config.substitutions.append(('%target-endian', run_endian)) config.substitutions.append(('%target-os', run_os)) config.substitutions.append(('%target-ptrsize', run_ptrsize)) +config.substitutions.append(('%target-alignment', "%d" % (int(run_ptrsize)/8))) # Enable Darwin SDK-dependent tests if we have an SDK. # On Linux, assume that SDK path does not point to the Darwin SDK. @@ -735,6 +736,7 @@ def use_interpreter_for_simple_runs(): config.available_features.add('interpret') target_specific_module_triple = config.variant_triple +target_future = target_specific_module_triple config.target_run = "" @@ -766,6 +768,7 @@ if run_vendor == 'apple': "-target %s %s %s" % (config.variant_triple, stdlib_resource_dir_opt, mcp_opt)) target_options_for_mock_sdk_after = sdk_overlay_dir_opt + target_future_version = '' if 'arm' in run_cpu and swift_test_mode != 'only_non_executable': raise RuntimeError('Device tests are currently only supported when ' @@ -777,12 +780,15 @@ if run_vendor == 'apple': if run_os == 'ios': lit_config.note('Testing iOS ' + config.variant_triple) xcrun_sdk_name = "iphoneos" + target_future_version = "99.0" elif run_os == 'tvos': lit_config.note('Testing AppleTV ' + config.variant_triple) xcrun_sdk_name = "appletvos" + target_future_version = "99.0" elif run_os == 'watchos': lit_config.note('Testing watchOS ' + config.variant_triple) xcrun_sdk_name = "watchos" + target_future_version = "9.99.0" config.target_cc_options = ( "-arch %s -m%s-version-min=%s %s" % @@ -808,14 +814,17 @@ if run_vendor == 'apple': config.available_features.add('DARWIN_SIMULATOR=ios') lit_config.note("Testing iOS simulator " + config.variant_triple) xcrun_sdk_name = "iphonesimulator" + target_future_version = "99.0" elif run_os == 'watchos': config.available_features.add('DARWIN_SIMULATOR=watchos') lit_config.note("Testing watchOS simulator " + config.variant_triple) xcrun_sdk_name = "watchsimulator" + target_future_version = "9.99.0" else: config.available_features.add('DARWIN_SIMULATOR=tvos') lit_config.note("Testing AppleTV simulator " + config.variant_triple) xcrun_sdk_name = "appletvsimulator" + target_future_version = "99.0" target_specific_module_triple += "-simulator" @@ -868,6 +877,7 @@ if run_vendor == 'apple': swift_execution_tests_extra_flags, sourcekitd_framework_dir, sourcekitd_framework_dir)) config.target_run = "" + target_future_version = "10.99" if 'interpret' in lit_config.params: use_interpreter_for_simple_runs() @@ -925,6 +935,8 @@ if run_vendor == 'apple': % (config.target_build_swift)) config.target_add_rpath = r'-Xlinker -rpath -Xlinker \1' + target_future = format('%s-apple-%s%s' % (run_cpu, run_os, target_future_version)) + elif run_os in ['windows-msvc']: lit_config.note('Testing Windows ' + config.variant_triple) config.environment['NUMBER_OF_PROCESSORS'] = os.environ['NUMBER_OF_PROCESSORS'] @@ -1227,6 +1239,7 @@ subst_target_swift_frontend_mock_sdk += " -typo-correction-limit 10 " config.substitutions.append(('%module-target-triple', target_specific_module_triple)) +config.substitutions.append(('%module-target-future', target_future)) # Add 'target-sdk-name' as the name for platform-specific directories config.substitutions.append(('%target-sdk-name', config.target_sdk_name)) From 9e2e090623172f95b5df92fbf6f1c56813e96828 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 5 Dec 2019 16:17:37 -0800 Subject: [PATCH 382/478] [IRGen] Emit metadata accessors last. Metadata accessors are dependent on prespecializations of the metadata of generic, in-module types. Those prespecializations are themselves dependent on usages of the types in functions. Consequently, the accessors must be emitted after all the functions are emitted. --- lib/IRGen/GenDecl.cpp | 6 +++ lib/IRGen/GenMeta.cpp | 24 +++++++++ lib/IRGen/GenMeta.h | 3 ++ lib/IRGen/GenericArguments.h | 98 +++++++++++++++++++++++++++++++++++ lib/IRGen/IRGenModule.h | 12 +++++ lib/IRGen/MetadataRequest.cpp | 76 ++------------------------- lib/IRGen/MetadataRequest.h | 5 ++ 7 files changed, 153 insertions(+), 71 deletions(-) create mode 100644 lib/IRGen/GenericArguments.h diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 564e38ac575c5..9d4b611e76aeb 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1192,6 +1192,12 @@ void IRGenerator::emitLazyDefinitions() { } } + while (!LazyMetadataAccessors.empty()) { + NominalTypeDecl *nominal = LazyMetadataAccessors.pop_back_val(); + CurrentIGMPtr IGM = getGenModule(nominal->getDeclContext()); + emitLazyMetadataAccessor(*IGM.get(), nominal); + } + FinishedEmittingLazyDefinitions = true; } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index a15f0d5d06c4a..8c07fada76d95 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -54,6 +54,7 @@ #include "GenPoly.h" #include "GenStruct.h" #include "GenValueWitness.h" +#include "GenericArguments.h" #include "HeapTypeInfo.h" #include "IRGenDebugInfo.h" #include "IRGenMangler.h" @@ -1835,6 +1836,29 @@ void irgen::emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type) { } } +void irgen::emitLazyMetadataAccessor(IRGenModule &IGM, + NominalTypeDecl *nominal) { + GenericArguments genericArgs; + genericArgs.collectTypes(IGM, nominal); + + llvm::Function *accessor = IGM.getAddrOfGenericTypeMetadataAccessFunction( + nominal, genericArgs.Types, ForDefinition); + + if (IGM.getOptions().optimizeForSize()) + accessor->addFnAttr(llvm::Attribute::NoInline); + + bool isReadNone = (genericArgs.Types.size() <= + NumDirectGenericTypeMetadataAccessFunctionArgs); + + emitCacheAccessFunction( + IGM, accessor, /*cache*/ nullptr, CacheStrategy::None, + [&](IRGenFunction &IGF, Explosion ¶ms) { + return emitGenericTypeMetadataAccessFunction(IGF, params, nominal, + genericArgs); + }, + isReadNone); +} + void irgen::emitLazySpecializedGenericTypeMetadata(IRGenModule &IGM, CanType type) { emitSpecializedGenericStructMetadata(IGM, type); diff --git a/lib/IRGen/GenMeta.h b/lib/IRGen/GenMeta.h index 973e63c9f1f33..f62e2995946f1 100644 --- a/lib/IRGen/GenMeta.h +++ b/lib/IRGen/GenMeta.h @@ -71,6 +71,9 @@ namespace irgen { /// generated definitions. void emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type); + /// Emit the type metadata accessor for a type for which it might be used. + void emitLazyMetadataAccessor(IRGenModule &IGM, NominalTypeDecl *type); + void emitLazySpecializedGenericTypeMetadata(IRGenModule &IGM, CanType type); /// Emit metadata for a foreign struct, enum or class. diff --git a/lib/IRGen/GenericArguments.h b/lib/IRGen/GenericArguments.h new file mode 100644 index 0000000000000..5365888e7cedd --- /dev/null +++ b/lib/IRGen/GenericArguments.h @@ -0,0 +1,98 @@ +//===--- MetadataRequest.cpp - IR generation for metadata requests --------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements IR generation for accessing metadata. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IRGEN_GENERICARGUMENTS_H +#define SWIFT_IRGEN_GENERICARGUMENTS_H + +#include "Explosion.h" +#include "FixedTypeInfo.h" +#include "GenArchetype.h" +#include "GenClass.h" +#include "GenMeta.h" +#include "GenProto.h" +#include "GenType.h" +#include "GenericRequirement.h" +#include "IRGenDebugInfo.h" +#include "IRGenFunction.h" +#include "IRGenMangler.h" +#include "IRGenModule.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/ExistentialLayout.h" +#include "swift/AST/GenericEnvironment.h" +#include "swift/AST/IRGenOptions.h" +#include "swift/AST/SubstitutionMap.h" +#include "swift/ClangImporter/ClangModule.h" +#include "swift/IRGen/Linking.h" +#include "llvm/ADT/STLExtras.h" + +namespace swift { +namespace irgen { + +/// A structure for collecting generic arguments for emitting a +/// nominal metadata reference. The structure produced here is +/// consumed by swift_getGenericMetadata() and must correspond to +/// the fill operations that the compiler emits for the bound decl. +struct GenericArguments { + /// The values to use to initialize the arguments structure. + SmallVector Values; + SmallVector Types; + + static unsigned getNumGenericArguments(IRGenModule &IGM, + NominalTypeDecl *nominal) { + GenericTypeRequirements requirements(IGM, nominal); + return requirements.getNumTypeRequirements(); + } + + void collectTypes(IRGenModule &IGM, NominalTypeDecl *nominal) { + GenericTypeRequirements requirements(IGM, nominal); + collectTypes(IGM, requirements); + } + + void collectTypes(IRGenModule &IGM, + const GenericTypeRequirements &requirements) { + for (auto &requirement : requirements.getRequirements()) { + if (requirement.Protocol) { + Types.push_back(IGM.WitnessTablePtrTy); + } else { + Types.push_back(IGM.TypeMetadataPtrTy); + } + } + } + + void collect(IRGenFunction &IGF, CanType type) { + auto *decl = type.getNominalOrBoundGenericNominal(); + GenericTypeRequirements requirements(IGF.IGM, decl); + + auto subs = type->getContextSubstitutionMap(IGF.IGM.getSwiftModule(), decl); + requirements.enumerateFulfillments( + IGF.IGM, subs, + [&](unsigned reqtIndex, CanType type, ProtocolConformanceRef conf) { + if (conf) { + Values.push_back(emitWitnessTableRef(IGF, type, conf)); + } else { + Values.push_back(IGF.emitAbstractTypeMetadataRef(type)); + } + }); + + collectTypes(IGF.IGM, decl); + assert(Types.size() == Values.size()); + } +}; + +} // namespace irgen +} // namespace swift + +#endif diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index d0392ad32d0f9..7f7e4a7889698 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -253,6 +253,12 @@ class IRGenerator { /// emit. llvm::SmallVector LazySpecializedTypeMetadataRecords; + /// The queue of metadata accessors to emit. + /// + /// The accessors must be emitted after everything else which might result in + /// a statically-known-canonical prespecialization. + llvm::SmallSetVector LazyMetadataAccessors; + struct LazyOpaqueInfo { bool IsDescriptorUsed = false; bool IsDescriptorEmitted = false; @@ -388,6 +394,12 @@ class IRGenerator { return SpecializationsForGenericTypes.lookup(type); } + void noteUseOfMetadataAccessor(NominalTypeDecl *decl) { + if (LazyMetadataAccessors.count(decl) == 0) { + LazyMetadataAccessors.insert(decl); + } + } + void noteUseOfTypeMetadata(NominalTypeDecl *type) { noteUseOfTypeGlobals(type, true, RequireMetadata); } diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 3ce8b2c2619ce..75926952f8a4d 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -24,6 +24,7 @@ #include "GenMeta.h" #include "GenProto.h" #include "GenType.h" +#include "GenericArguments.h" #include "GenericRequirement.h" #include "IRGenDebugInfo.h" #include "IRGenFunction.h" @@ -438,60 +439,6 @@ static llvm::Value *emitObjCMetadataRef(IRGenFunction &IGF, return emitObjCMetadataRefForMetadata(IGF, classPtr); } -namespace { - /// A structure for collecting generic arguments for emitting a - /// nominal metadata reference. The structure produced here is - /// consumed by swift_getGenericMetadata() and must correspond to - /// the fill operations that the compiler emits for the bound decl. - struct GenericArguments { - /// The values to use to initialize the arguments structure. - SmallVector Values; - SmallVector Types; - - static unsigned getNumGenericArguments(IRGenModule &IGM, - NominalTypeDecl *nominal) { - GenericTypeRequirements requirements(IGM, nominal); - return requirements.getNumTypeRequirements(); - } - - void collectTypes(IRGenModule &IGM, NominalTypeDecl *nominal) { - GenericTypeRequirements requirements(IGM, nominal); - collectTypes(IGM, requirements); - } - - void collectTypes(IRGenModule &IGM, - const GenericTypeRequirements &requirements) { - for (auto &requirement : requirements.getRequirements()) { - if (requirement.Protocol) { - Types.push_back(IGM.WitnessTablePtrTy); - } else { - Types.push_back(IGM.TypeMetadataPtrTy); - } - } - } - - void collect(IRGenFunction &IGF, CanType type) { - auto *decl = type.getNominalOrBoundGenericNominal(); - GenericTypeRequirements requirements(IGF.IGM, decl); - - auto subs = - type->getContextSubstitutionMap(IGF.IGM.getSwiftModule(), decl); - requirements.enumerateFulfillments( - IGF.IGM, subs, - [&](unsigned reqtIndex, CanType type, ProtocolConformanceRef conf) { - if (conf) { - Values.push_back(emitWitnessTableRef(IGF, type, conf)); - } else { - Values.push_back(IGF.emitAbstractTypeMetadataRef(type)); - } - }); - - collectTypes(IGF.IGM, decl); - assert(Types.size() == Values.size()); - } - }; -} // end anonymous namespace - static bool isTypeErasedGenericClass(NominalTypeDecl *ntd) { // ObjC classes are type erased. // TODO: Unless they have magic methods... @@ -1857,11 +1804,9 @@ static void emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( } } -static MetadataResponse -emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, - Explosion ¶ms, - NominalTypeDecl *nominal, - GenericArguments &genericArgs) { +MetadataResponse irgen::emitGenericTypeMetadataAccessFunction( + IRGenFunction &IGF, Explosion ¶ms, NominalTypeDecl *nominal, + GenericArguments &genericArgs) { auto &IGM = IGF.IGM; llvm::Constant *descriptor = @@ -2181,18 +2126,7 @@ irgen::getGenericTypeMetadataAccessFunction(IRGenModule &IGM, if (!shouldDefine || !accessor->empty()) return accessor; - if (IGM.getOptions().optimizeForSize()) - accessor->addFnAttr(llvm::Attribute::NoInline); - - bool isReadNone = - (genericArgs.Types.size() <= NumDirectGenericTypeMetadataAccessFunctionArgs); - - emitCacheAccessFunction(IGM, accessor, /*cache*/nullptr, CacheStrategy::None, - [&](IRGenFunction &IGF, Explosion ¶ms) { - return emitGenericTypeMetadataAccessFunction( - IGF, params, nominal, genericArgs); - }, - isReadNone); + IGM.IRGen.noteUseOfMetadataAccessor(nominal); return accessor; } diff --git a/lib/IRGen/MetadataRequest.h b/lib/IRGen/MetadataRequest.h index 5cfb836335959..4c39a03e8199d 100644 --- a/lib/IRGen/MetadataRequest.h +++ b/lib/IRGen/MetadataRequest.h @@ -34,6 +34,7 @@ enum ForDefinition_t : bool; namespace irgen { class ConstantReference; class Explosion; +struct GenericArguments; class IRGenFunction; class IRGenModule; class MetadataDependencyCollector; @@ -587,6 +588,10 @@ void emitCacheAccessFunction(IRGenModule &IGM, CacheStrategy cacheStrategy, CacheEmitter getValue, bool isReadNone = true); +MetadataResponse +emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, Explosion ¶ms, + NominalTypeDecl *nominal, + GenericArguments &genericArgs); /// Emit a declaration reference to a metatype object. void emitMetatypeRef(IRGenFunction &IGF, CanMetatypeType type, From 686c49aec32200af4dbe9d066f0f66bf4ef405da Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 6 Dec 2019 17:52:45 -0800 Subject: [PATCH 383/478] [IRGen] Don't add prespecialized metadata to ABI. The usages of a type that happen to be made in a module are implementation details of that module and should not be exported. --- lib/IRGen/GenDecl.cpp | 11 ++++-- ...ate-inmodule-1argument-1distinct_use.swift | 36 ++++++++++++++++++ ...within-class-1argument-1distinct_use.swift | 38 +++++++++++++++++++ ...1argument-1conformance-1distinct_use.swift | 4 +- ...uct-inmodule-1argument-1distinct_use.swift | 4 +- ...1argument-2conformance-1distinct_use.swift | 4 +- ...uct-inmodule-1argument-2distinct_use.swift | 8 ++-- ...1argument-3conformance-1distinct_use.swift | 4 +- ...uct-inmodule-1argument-3distinct_use.swift | 12 +++--- ...1argument-4conformance-1distinct_use.swift | 4 +- ...uct-inmodule-1argument-4distinct_use.swift | 16 ++++---- ...1argument-5conformance-1distinct_use.swift | 4 +- ...uct-inmodule-1argument-5distinct_use.swift | 19 +++++----- ...uct-inmodule-2argument-1distinct_use.swift | 4 +- ...uct-inmodule-2argument-2distinct_use.swift | 8 ++-- ...uct-inmodule-2argument-3distinct_use.swift | 12 +++--- ...uct-inmodule-2argument-4distinct_use.swift | 16 ++++---- ...uct-inmodule-2argument-5distinct_use.swift | 19 ++++++---- ...lic-inmodule-1argument-1distinct_use.swift | 37 ++++++++++++++++++ 19 files changed, 190 insertions(+), 70 deletions(-) create mode 100644 test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift create mode 100644 test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 9d4b611e76aeb..86a9ec5c11fd0 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -3671,8 +3671,11 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(CanType concreteType, if (nominal) addRuntimeResolvableType(nominal); - // Don't define the alias for foreign type metadata, since it's not ABI. - if (nominal && requiresForeignTypeMetadata(nominal)) + // Don't define the alias for foreign type metadata or prespecialized generic + // metadata, since neither is ABI. + if ((nominal && requiresForeignTypeMetadata(nominal)) || + (concreteType->getAnyGeneric() && + concreteType->getAnyGeneric()->isGenericContext())) return var; // For concrete metadata, declare the alias to its address point. @@ -3708,7 +3711,9 @@ ConstantReference IRGenModule::getAddrOfTypeMetadata(CanType concreteType, llvm::Type *defaultVarTy; unsigned adjustmentIndex; - bool fullMetadata = (nominal && requiresForeignTypeMetadata(nominal)); + bool fullMetadata = (nominal && requiresForeignTypeMetadata(nominal)) || + (concreteType->getAnyGeneric() && + concreteType->getAnyGeneric()->isGenericContext()); // Foreign classes reference the full metadata with a GEP. if (fullMetadata) { diff --git a/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..bb3d77542a3ca --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift @@ -0,0 +1,36 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main5Value[[UNIQUE_ID_1:[0-9A-Z_]+]]VySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5Value[[UNIQUE_ID_1]]VWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5Value[[UNIQUE_ID_1]]VMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +fileprivate struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5Value[[UNIQUE_ID_1]]VySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define internal swiftcc %swift.metadata_response @"$s4main5Value[[UNIQUE_ID_1]]VMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5Value[[UNIQUE_ID_1]]VySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5Value[[UNIQUE_ID_1]]VMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..aed3227797d20 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift @@ -0,0 +1,38 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main9NamespaceC5ValueVySi_GMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceC5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +final class Namespace { + struct Value { + let first: Arg + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceC5ValueVySi_GMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: br i1 [[EQUAL_TYPES_1_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceC5ValueVySi_GMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift index 48b477d590d27..2cdaf6b4935fb 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift @@ -15,7 +15,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -33,7 +33,7 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* [[ERASED_TABLE]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #12 // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift index 6938f323fd629..5238da4234c2e 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift @@ -12,7 +12,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -29,7 +29,7 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift index 01f8167d65448..1bb69b22f6730 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift @@ -17,7 +17,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -36,7 +36,7 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* [[ERASED_TABLE_1]], i8* [[ERASED_TABLE_2]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift index fbcd94d17aee5..ab3ca42678890 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift @@ -13,8 +13,8 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -36,9 +36,9 @@ doit() // CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] // CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift index a419b9a77302e..b33247c6ec6ab 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift @@ -19,7 +19,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -38,7 +38,7 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata(i64 %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift index fd91112b12430..a24d7067a2c88 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift @@ -14,9 +14,9 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -43,11 +43,11 @@ doit() // CHECK: [[EQUAL_TYPES_3:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3]] // CHECK: br i1 [[EQUAL_TYPES_3]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_3]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift index a79eb619a4677..a24da11c6632a 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift @@ -21,7 +21,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -40,7 +40,7 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata(i64 %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift index de12dda34713e..5b069b4fa62ae 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift @@ -15,10 +15,10 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -50,13 +50,13 @@ doit() // CHECK: [[EQUAL_TYPES_4:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4]] // CHECK: br i1 [[EQUAL_TYPES_4]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_3]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_4]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift index 074ea7cd9363d..0cb9c99a0bf46 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift @@ -23,7 +23,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -42,7 +42,7 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata(i64 %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift index 5e871a3f8b737..cc3e35f959677 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift @@ -16,10 +16,11 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys4Int8VGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -56,15 +57,15 @@ doit() // CHECK: [[EQUAL_TYPES_5:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_5]] // CHECK: br i1 [[EQUAL_TYPES_5]], label %[[EXIT_PRESPECIALIZED_5:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_3]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_4]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_5]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys4Int8VGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys4Int8VGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift index fcec7f43c4685..d70f8ac6288ff 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift @@ -13,7 +13,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13, second: 13) ) @@ -33,7 +33,7 @@ doit() // CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] // CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift index ec54261b0ab90..9e7c6b41daa92 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift @@ -14,8 +14,8 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13, second: 13) ) @@ -42,9 +42,9 @@ doit() // CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] // CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift index e6cc6b7d021c9..7de5c7606fe75 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift @@ -15,9 +15,9 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13, second: 13) ) @@ -51,11 +51,11 @@ doit() // CHECK: [[EQUAL_TYPES_3_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_3_1]], [[EQUAL_TYPE_3_2]] // CHECK: br i1 [[EQUAL_TYPES_3_2]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_3]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift index 89f601fa48447..84550705779d6 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift @@ -16,10 +16,10 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13, second: 13) ) @@ -60,13 +60,13 @@ doit() // CHECK: [[EQUAL_TYPES_4_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_4_1]], [[EQUAL_TYPE_4_2]] // CHECK: br i1 [[EQUAL_TYPES_4_2]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_3]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_4]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift index 623821b1b2c02..788c390def7a7 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift @@ -16,10 +16,11 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf", i32 0, i32 1) to %swift.type*)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys4Int8Vs5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13, second: 13) ) @@ -67,13 +68,15 @@ doit() // CHECK: [[EQUAL_TYPES_5_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_5_1]], [[EQUAL_TYPE_5_2]] // CHECK: br i1 [[EQUAL_TYPES_5_2]], label %[[EXIT_PRESPECIALIZED_5:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_3]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_PRESPECIALIZED_4]]: -// CHECK: ret %swift.metadata_response { %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>, <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf", i32 0, i32 1) to %swift.type*), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: [[EXIT_PRESPECIALIZED_5]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys4Int8Vs5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } // CHECK: [[EXIT_NORMAL]]: // CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} diff --git a/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..70647ed395a80 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift @@ -0,0 +1,37 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +@frozen +public struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define{{[ protected]*}} swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } From d78dc038c43e26985e985d3c38b0f2eb3bcbead4 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 19 Dec 2019 13:23:22 -0800 Subject: [PATCH 384/478] [metadata prespecialization] Specialize VWTs. Prespecialized metadata records must refer to value witness tables that have correct values for size and stride. In order to do that, a prespecialized value witness table must emitted and referred to. Here, a fully specialized value witness table is emitted. For the moment, the value witness table is fully specialized. Having it be specialized, though, will likely cause serious code size problems, since it results in specialized value witness functions being generated. rdar://problem/58088270 --- lib/IRGen/GenDecl.cpp | 4 -- lib/IRGen/GenMeta.cpp | 8 ++-- lib/IRGen/GenValueWitness.cpp | 4 -- ...within-class-1argument-1distinct_use.swift | 8 +++- ...ate-inmodule-1argument-1distinct_use.swift | 17 ++++--- ...within-class-1argument-1distinct_use.swift | 17 ++++--- .../struct-inmodule-0argument.swift | 8 +++- ...uct-inmodule-1argument-0distinct_use.swift | 10 +++-- ...1argument-1conformance-1distinct_use.swift | 17 ++++--- ...mance_stdlib_equatable-1distinct_use.swift | 10 +++-- ...dule-1argument-1distinct_generic_use.swift | 10 +++-- ...uct-inmodule-1argument-1distinct_use.swift | 17 ++++--- ...1argument-2conformance-1distinct_use.swift | 17 ++++--- ...uct-inmodule-1argument-2distinct_use.swift | 23 ++++++---- ...1argument-3conformance-1distinct_use.swift | 17 ++++--- ...uct-inmodule-1argument-3distinct_use.swift | 30 ++++++++----- ...1argument-4conformance-1distinct_use.swift | 17 ++++--- ...uct-inmodule-1argument-4distinct_use.swift | 36 ++++++++------- ...1argument-5conformance-1distinct_use.swift | 17 ++++--- ...uct-inmodule-1argument-5distinct_use.swift | 44 +++++++++++-------- ...e-1argument-clang_node-1distinct_use.swift | 10 +++-- ...within-class-1argument-1distinct_use.swift | 17 ++++--- ...-within-enum-1argument-1distinct_use.swift | 17 ++++--- ...ithin-struct-1argument-1distinct_use.swift | 17 ++++--- ...uct-inmodule-2argument-0distinct_use.swift | 10 +++-- ...uct-inmodule-2argument-1distinct_use.swift | 17 ++++--- ...uct-inmodule-2argument-2distinct_use.swift | 24 ++++++---- ...uct-inmodule-2argument-3distinct_use.swift | 31 ++++++++----- ...uct-inmodule-2argument-4distinct_use.swift | 38 +++++++++------- ...uct-inmodule-2argument-5distinct_use.swift | 42 +++++++++++------- ...within-class-1argument-1distinct_use.swift | 17 ++++--- ...lic-inmodule-1argument-1distinct_use.swift | 17 ++++--- 32 files changed, 366 insertions(+), 222 deletions(-) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 86a9ec5c11fd0..51fb697e355f6 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -4069,10 +4069,6 @@ Optional IRGenModule::getAddrOfIVarInitDestroy( llvm::Function *IRGenModule::getAddrOfValueWitness(CanType abstractType, ValueWitness index, ForDefinition_t forDefinition) { - // We shouldn't emit value witness symbols for generic type instances. - assert(!isa(abstractType) && - "emitting value witness for generic type instance?!"); - LinkEntity entity = LinkEntity::forValueWitness(abstractType, index); llvm::Function *&entry = GlobalFuncs[entity]; diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 8c07fada76d95..182b329efb513 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -3740,10 +3740,12 @@ namespace { SILType getLoweredType() { return SILType::getPrimitiveObjectType(type); } + ConstantReference emitValueWitnessTable(bool relativeReference) { + return irgen::emitValueWitnessTable(IGM, type, false, relativeReference); + } + ConstantReference getValueWitnessTable(bool relativeReference) { - auto type = this->Target->getDeclaredType()->getCanonicalType(); - return ConstantReference(IGM.getAddrOfEffectiveValueWitnessTable(type), - irgen::ConstantReference::Direct); + return emitValueWitnessTable(relativeReference); } void addGenericArgument(GenericRequirement requirement) { diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index 504251e533846..d3b77b9625e73 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -1038,10 +1038,6 @@ ConstantReference irgen::emitValueWitnessTable(IRGenModule &IGM, CanType abstractType, bool isPattern, bool relativeReference) { - // We shouldn't emit global value witness tables for generic type instances. - assert(!isa(abstractType) && - "emitting VWT for generic instance"); - // See if we can use a prefab witness table from the runtime. if (!isPattern) { if (auto known = getAddrOfKnownValueWitnessTable(IGM, abstractType, diff --git a/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift index 146714030f3aa..8c77d62e50823 100644 --- a/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift @@ -1,4 +1,8 @@ -// RUN: %swift -target %module-target-future -parse-stdlib -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -parse-stdlib -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios precedencegroup AssignmentPrecedence {} @@ -18,7 +22,7 @@ func consume(_ t: T) { // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { // CHECK: entry: -// CHECK: [[METADATA_RESPONSE:%[0-9]+]] = call swiftcc %swift.metadata_response @"$s4main9NamespaceCA2A4ZangCRszlE19ExtensionNonGenericCyAE_GMa"(i64 0) #{{[0-9]+}} +// CHECK: [[METADATA_RESPONSE:%[0-9]+]] = call swiftcc %swift.metadata_response @"$s4main9NamespaceCA2A4ZangCRszlE19ExtensionNonGenericCyAE_GMa"([[INT]] 0) #{{[0-9]+}} // CHECK: [[METADATA:%[0-9]+]] = extractvalue %swift.metadata_response [[METADATA_RESPONSE]], 0 // CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %5, %swift.type* [[METADATA]]) // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift index bb3d77542a3ca..4337bf632f36e 100644 --- a/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift @@ -1,6 +1,11 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main5Value[[UNIQUE_ID_1:[0-9A-Z_]+]]VySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5Value[[UNIQUE_ID_1]]VWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5Value[[UNIQUE_ID_1]]VMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5Value[[UNIQUE_ID_1:[0-9A-Z_]+]]VySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5Value[[UNIQUE_ID_1:[0-9A-Z_]+]]VMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] fileprivate struct Value { let first: First } @@ -12,7 +17,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5Value[[UNIQUE_ID_1]]VySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5Value[[UNIQUE_ID_1]]VySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -20,7 +25,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define internal swiftcc %swift.metadata_response @"$s4main5Value[[UNIQUE_ID_1]]VMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: define internal swiftcc %swift.metadata_response @"$s4main5Value[[UNIQUE_ID_1]]VMa"([[INT]], %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] @@ -29,8 +34,8 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5Value[[UNIQUE_ID_1]]VySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5Value[[UNIQUE_ID_1]]VySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5Value[[UNIQUE_ID_1]]VMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5Value[[UNIQUE_ID_1]]VMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift index aed3227797d20..1db12cbcb0991 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift @@ -1,6 +1,11 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main9NamespaceC5ValueVySi_GMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceC5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main9NamespaceC5ValueVySi_GMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] final class Namespace { struct Value { let first: Arg @@ -14,7 +19,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceC5ValueVySi_GMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceC5ValueVySi_GMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Namespace.Value(first: 13) ) @@ -22,7 +27,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] @@ -31,8 +36,8 @@ doit() // CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] // CHECK: br i1 [[EQUAL_TYPES_1_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceC5ValueVySi_GMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceC5ValueVySi_GMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift index c5a6c25d900b8..13da1ee56a613 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift @@ -1,4 +1,8 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios struct Value { let first: Int @@ -11,7 +15,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #0 { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, <{ i32, i32, i32, i32, i32, i32, i32 }>*, i32, [4 x i8], i64 }>, <{ i8**, i64, <{ i32, i32, i32, i32, i32, i32, i32 }>*, i32, [4 x i8], i64 }>* @"$s4main5ValueVMf", i32 0, i32 1) to %swift.type*)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast ([[INT]]* getelementptr inbounds (<{ i8**, [[INT]], <{ i32, i32, i32, i32, i32, i32, i32 }>*, i32{{(, \[4 x i8\])?}}, i64 }>, <{ i8**, [[INT]], <{ i32, i32, i32, i32, i32, i32, i32 }>*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVMf", i32 0, i32 1) to %swift.type*)) // CHECK: } func doit() { consume( Value(first: 13) ) diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift index 3cbf0bc99e36e..42dff49c51270 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift @@ -1,4 +1,8 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios struct Value { let first: First @@ -17,9 +21,9 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift index 2cdaf6b4935fb..156d2e1539550 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift @@ -1,7 +1,12 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$sytN" = external global %swift.full_type -// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] protocol P {} extension Int : P {} struct Value { @@ -15,7 +20,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -23,7 +28,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, i8**) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, i8**) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_TABLE:%[0-9]+]] = bitcast i8** %2 to i8* @@ -33,8 +38,8 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* [[ERASED_TABLE]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #12 +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* [[ERASED_TABLE]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #12 // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift index 4ae70ef36a4ce..054f4cf84b7ea 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift @@ -1,4 +1,8 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios // CHECK-NOT: @"$s4main5ValueVyAA7IntegerVGMf" struct Value { @@ -21,10 +25,10 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, i8**) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, i8**) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_CONFORMANCE:%[0-9]+]] = bitcast i8** %2 to i8* -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* [[ERASED_CONFORMANCE]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* [[ERASED_CONFORMANCE]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift index c2ee6f3a4c88a..6fc3aedf3621a 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift @@ -1,4 +1,8 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios struct Outer { let first: First @@ -27,9 +31,9 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5OuterVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5OuterVMa"([[INT]], %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5OuterVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5OuterVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift index 5238da4234c2e..7447b96f1481a 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift @@ -1,6 +1,11 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] struct Value { let first: First } @@ -12,7 +17,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -20,7 +25,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] @@ -29,8 +34,8 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift index 1bb69b22f6730..f3b4dfd36c7a2 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift @@ -1,7 +1,12 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$sytN" = external global %swift.full_type -// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] protocol P {} protocol Q {} extension Int : P {} @@ -17,7 +22,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -25,7 +30,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, i8**, i8**) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, i8**, i8**) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_TABLE_1:%[0-9]+]] = bitcast i8** %2 to i8* @@ -36,8 +41,8 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* [[ERASED_TABLE_1]], i8* [[ERASED_TABLE_2]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* [[ERASED_TABLE_1]], i8* [[ERASED_TABLE_2]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift index ab3ca42678890..817344f003dca 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift @@ -1,7 +1,12 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sBi64_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi64_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] struct Value { let first: First } @@ -13,8 +18,8 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -23,7 +28,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] @@ -36,10 +41,10 @@ doit() // CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] // CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift index b33247c6ec6ab..d363392ae0f82 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift @@ -1,7 +1,12 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$sytN" = external global %swift.full_type -// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] protocol P {} protocol Q {} protocol R {} @@ -19,7 +24,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -27,7 +32,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, i8**) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], i8**) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_ARGUMENT_BUFFER:%[0-9]+]] = bitcast i8** %1 to i8* // CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] @@ -38,8 +43,8 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata(i64 %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata([[INT]] %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift index a24d7067a2c88..ac0d1129f8af7 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift @@ -1,8 +1,14 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVySSGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_[[ALIGNMENT]] to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$sBi64_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi64_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] struct Value { let first: First } @@ -14,9 +20,9 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -26,7 +32,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] @@ -43,12 +49,12 @@ doit() // CHECK: [[EQUAL_TYPES_3:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3]] // CHECK: br i1 [[EQUAL_TYPES_3]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_3]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift index a24da11c6632a..67efa8458ada3 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift @@ -1,7 +1,12 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$sytN" = external global %swift.full_type -// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1SAAWP", i32 0, i32 0), i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1SAAWP", i32 0, i32 0), i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] protocol P {} protocol Q {} protocol R {} @@ -21,7 +26,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -29,7 +34,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, i8**) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], i8**) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_ARGUMENT_BUFFER:%[0-9]+]] = bitcast i8** %1 to i8* // CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] @@ -40,8 +45,8 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata(i64 %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata([[INT]] %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift index 5b069b4fa62ae..546b8cc53d5d7 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift @@ -1,9 +1,15 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main5ValueVys5UInt8VGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sBi8_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVys5UInt8VGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi8_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$sBi64_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi64_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] struct Value { let first: First } @@ -15,10 +21,10 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -29,7 +35,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] @@ -50,14 +56,14 @@ doit() // CHECK: [[EQUAL_TYPES_4:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4]] // CHECK: br i1 [[EQUAL_TYPES_4]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_3]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_4]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift index 0cb9c99a0bf46..6e799b7585fab 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift @@ -1,7 +1,12 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$sytN" = external global %swift.full_type -// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1SAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1TAAWP", i32 0, i32 0), i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1SAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1TAAWP", i32 0, i32 0), i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] protocol P {} protocol Q {} protocol R {} @@ -23,7 +28,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -31,7 +36,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, i8**) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], i8**) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_ARGUMENT_BUFFER:%[0-9]+]] = bitcast i8** %1 to i8* // CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] @@ -42,8 +47,8 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata(i64 %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata([[INT]] %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift index cc3e35f959677..e7081cca80aab 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift @@ -1,10 +1,18 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main5ValueVys4Int8VGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss4Int8VN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVys5UInt8VGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + + +// CHECK: @"$sBi8_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVys4Int8VGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi8_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss4Int8VN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVys5UInt8VGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi8_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_[[ALIGNMENT]] to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$sBi64_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi64_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] struct Value { let first: First } @@ -16,11 +24,11 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys4Int8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys4Int8VGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -32,7 +40,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] @@ -57,16 +65,16 @@ doit() // CHECK: [[EQUAL_TYPES_5:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_5]] // CHECK: br i1 [[EQUAL_TYPES_5]], label %[[EXIT_PRESPECIALIZED_5:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_3]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_4]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_5]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVys4Int8VGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys4Int8VGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift index 7fca0f2b184de..447889caac128 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift @@ -1,6 +1,10 @@ // RUN: %empty-directory(%t) // RUN: %build-irgen-test-overlays(mock-sdk-directory: %S/../Inputs) -// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs -I %t) -target %module-target-future -primary-file %s -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs -I %t) -target %module-target-future -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios // REQUIRES: objc_interop @@ -30,9 +34,9 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift index d06d9366114de..8eb07e2ff33bf 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift @@ -1,8 +1,11 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// REQUIRES: OS=macosx +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios -// CHECK: @"$s4main9NamespaceC5ValueVySS_SiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceC5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main9NamespaceC5ValueVySS_SiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] final class Namespace { struct Value { let first: First @@ -16,7 +19,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceC5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Namespace.Value(first: 13) ) @@ -24,7 +27,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* @@ -36,8 +39,8 @@ doit() // CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] // CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceC5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift index 7d375e98035f5..4fc44026d2c15 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift @@ -1,6 +1,11 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main9NamespaceO5ValueVySS_SiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceO5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceO5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main9NamespaceO5ValueVySS_SiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceO5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] enum Namespace { struct Value { let first: First @@ -14,7 +19,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceO5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceO5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Namespace.Value(first: 13) ) @@ -22,7 +27,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceO5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceO5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* @@ -34,8 +39,8 @@ doit() // CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] // CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceO5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceO5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceO5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceO5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift index dd71000739ebe..036f66e87f7d1 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift @@ -1,6 +1,11 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main9NamespaceV5ValueVySS_SiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceV5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceV5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main9NamespaceV5ValueVySS_SiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceV5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] struct Namespace { struct Value { let first: First @@ -14,7 +19,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceV5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceV5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Namespace.Value(first: 13) ) @@ -22,7 +27,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceV5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceV5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* @@ -34,8 +39,8 @@ doit() // CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] // CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main9NamespaceV5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceV5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceV5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceV5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift index cae7f9f84b1ad..00c7687d1ea0c 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift @@ -1,4 +1,8 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios struct Value { let first: First @@ -18,10 +22,10 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift index d70f8ac6288ff..7c78050b67d67 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift @@ -1,6 +1,11 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] struct Value { let first: First let second: Second @@ -13,7 +18,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13, second: 13) ) @@ -21,7 +26,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* @@ -33,8 +38,8 @@ doit() // CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] // CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift index 9e7c6b41daa92..06973e4e5bf3d 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift @@ -1,7 +1,13 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySdSiGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySdSiGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdSiGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] struct Value { let first: First let second: Second @@ -14,8 +20,8 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13, second: 13) ) @@ -24,7 +30,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* @@ -42,10 +48,10 @@ doit() // CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] // CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift index 7de5c7606fe75..a65955302f1db 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift @@ -1,8 +1,15 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVySSSdGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSSdGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSSdGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSSdGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSSdGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySdSiGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySdSiGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdSiGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] struct Value { let first: First let second: Second @@ -15,9 +22,9 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13, second: 13) ) @@ -27,7 +34,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* @@ -51,12 +58,12 @@ doit() // CHECK: [[EQUAL_TYPES_3_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_3_1]], [[EQUAL_TYPE_3_2]] // CHECK: br i1 [[EQUAL_TYPES_3_2]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_3]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift index 84550705779d6..67000561d9fc9 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift @@ -1,9 +1,17 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main5ValueVys5UInt8VSSGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", %swift.type* @"$sSSN", i32 0, i32 8, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVys5UInt8VSSGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVys5UInt8VSSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVys5UInt8VSSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", %swift.type* @"$sSSN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSSdGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSSdGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSSdGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSSdGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSSdGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySdSiGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySdSiGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdSiGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] struct Value { let first: First let second: Second @@ -16,10 +24,10 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13, second: 13) ) @@ -30,7 +38,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* @@ -60,14 +68,14 @@ doit() // CHECK: [[EQUAL_TYPES_4_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_4_1]], [[EQUAL_TYPE_4_2]] // CHECK: br i1 [[EQUAL_TYPES_4_2]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_3]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_4]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift index 788c390def7a7..89c6302e01989 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift @@ -1,9 +1,17 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main5ValueVys5UInt8VSSGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", %swift.type* @"$sSSN", i32 0, i32 8, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 -// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVys5UInt8VSSGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVys5UInt8VSSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVys5UInt8VSSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", %swift.type* @"$sSSN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSSdGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSSdGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSSdGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSSdGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSSdGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySdSiGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySdSiGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdSiGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] struct Value { let first: First let second: Second @@ -16,11 +24,11 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1)) -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys4Int8Vs5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys4Int8Vs5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13, second: 13) ) @@ -32,7 +40,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* @@ -68,16 +76,16 @@ doit() // CHECK: [[EQUAL_TYPES_5_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_5_1]], [[EQUAL_TYPE_5_2]] // CHECK: br i1 [[EQUAL_TYPES_5_2]], label %[[EXIT_PRESPECIALIZED_5:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_2]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_3]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_4]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_PRESPECIALIZED_5]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys4Int8Vs5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys4Int8Vs5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift index fdeedd3131e8e..9aa4da19582af 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift @@ -1,6 +1,11 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main9NamespaceC5ValueVySS_SiSdGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceC5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", %swift.type* @"$sSdN", i32 0, i32 8, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main9NamespaceC5ValueVySS_SiSdGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main9NamespaceC5ValueVySS_SiSdGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main9NamespaceC5ValueVySS_SiSdGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main9NamespaceC5ValueVySS_SiSdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceC5ValueVySS_SiSdGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", %swift.type* @"$sSdN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] final class Namespace { struct Value { let first: First @@ -15,7 +20,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiSdGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Namespace.Value(first: 13, second: 13.0) ) @@ -23,7 +28,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"(i64, %swift.type*, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"([[INT]], %swift.type*, %swift.type*, %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* @@ -38,8 +43,8 @@ doit() // CHECK: [[EQUAL_TYPES_1_3:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_2]], [[EQUAL_TYPE_1_3]] // CHECK: br i1 [[EQUAL_TYPES_1_3]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED_1]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiSdGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiSdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* [[ERASED_TYPE_3]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* [[ERASED_TYPE_3]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift index 70647ed395a80..64d762b608f13 100644 --- a/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift @@ -1,6 +1,11 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment -// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVWV", i32 0, i32 0), i64 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0, [4 x i8] zeroinitializer, i64 3 }>, align 8 +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] @frozen public struct Value { let first: First @@ -13,7 +18,7 @@ func consume(_ t: T) { } // CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) // CHECK: } func doit() { consume( Value(first: 13) ) @@ -21,7 +26,7 @@ func doit() { doit() // CHECK: ; Function Attrs: noinline nounwind readnone -// CHECK: define{{[ protected]*}} swiftcc %swift.metadata_response @"$s4main5ValueVMa"(i64, %swift.type*) #{{[0-9]+}} { +// CHECK: define{{[ protected]*}} swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* // CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] @@ -30,8 +35,8 @@ doit() // CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] // CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] // CHECK: [[EXIT_PRESPECIALIZED]]: -// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, i64, %swift.type_descriptor*, %swift.type*, i32, [4 x i8], i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), i64 0 } +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } // CHECK: [[EXIT_NORMAL]]: -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata(i64 %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } From b483047afef75c8b0ea8e47178cb57cde89e0e0e Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 9 Jan 2020 19:14:47 -0800 Subject: [PATCH 385/478] Revert "[Incremental] Dependency fixes in preparation for fine-grained dependencies" --- include/swift/AST/FineGrainedDependencies.h | 64 +- include/swift/Basic/LangOptions.h | 10 +- include/swift/Driver/Action.h | 17 - include/swift/Driver/Compilation.h | 12 - .../Driver/FineGrainedDependencyDriverGraph.h | 148 ++-- include/swift/Driver/Job.h | 9 - include/swift/Option/Options.td | 11 +- lib/AST/FineGrainedDependencies.cpp | 61 +- ...endenciesSourceFileDepGraphConstructor.cpp | 435 +++------ lib/Driver/Compilation.cpp | 97 +- lib/Driver/Driver.cpp | 3 +- .../FineGrainedDependencyDriverGraph.cpp | 218 ++--- lib/Driver/Job.cpp | 5 - lib/Driver/ToolChain.cpp | 51 +- lib/Driver/ToolChains.cpp | 8 - lib/Frontend/CompilerInvocation.cpp | 10 +- lib/FrontendTool/FrontendTool.cpp | 4 +- ...h_mode_dependencies_make_wrong_order.swift | 12 +- unittests/Driver/CMakeLists.txt | 3 - .../FineGrainedDependencyGraphTests.cpp | 833 ------------------ 20 files changed, 347 insertions(+), 1664 deletions(-) delete mode 100644 unittests/Driver/FineGrainedDependencyGraphTests.cpp diff --git a/include/swift/AST/FineGrainedDependencies.h b/include/swift/AST/FineGrainedDependencies.h index 19bfd16c05ef6..60ef8b24a7bdf 100644 --- a/include/swift/AST/FineGrainedDependencies.h +++ b/include/swift/AST/FineGrainedDependencies.h @@ -325,7 +325,7 @@ class BiIndexedTwoStageMap { /// Write out the .swiftdeps file for a frontend compilation of a primary file. bool emitReferenceDependencies(DiagnosticEngine &diags, SourceFile *SF, const DependencyTracker &depTracker, - StringRef outputPath, bool alsoEmitDotFile); + StringRef outputPath); //============================================================================== // MARK: Enums //============================================================================== @@ -438,8 +438,8 @@ class DependencyKey { name() {} /// For constructing a key in the frontend. - DependencyKey(NodeKind kind, DeclAspect aspect, const std::string &context, - const std::string &name) + DependencyKey(NodeKind kind, DeclAspect aspect, std::string context, + std::string name) : kind(kind), aspect(aspect), context(context), name(name) { assert(verify()); } @@ -449,7 +449,7 @@ class DependencyKey { StringRef getContext() const { return context; } StringRef getName() const { return name; } - StringRef getSwiftDepsFromASourceFileProvideNodeKey() const { + StringRef getSwiftDepsFromSourceFileProvide() const { assert(getKind() == NodeKind::sourceFileProvide && "Receiver must be sourceFileProvide."); return getName(); @@ -494,19 +494,11 @@ class DependencyKey { static std::string computeNameForProvidedEntity(Entity); /// Given some type of depended-upon entity create the key. - static DependencyKey createDependedUponKey(StringRef mangledHolderName, - StringRef memberBaseName); - - template - static DependencyKey createDependedUponKey(StringRef); - - static DependencyKey - createTransitiveKeyForWholeSourceFile(StringRef swiftDeps); + template + static DependencyKey createDependedUponKey(const Entity &); std::string humanReadableName() const; - StringRef aspectName() const { return DeclAspectNames[size_t(aspect)]; } - void dump(llvm::raw_ostream &os) const { os << asString() << "\n"; } SWIFT_DEBUG_DUMP { dump(llvm::errs()); } @@ -543,13 +535,6 @@ struct std::hash { } }; -namespace swift { -namespace fine_grained_dependencies { -using ContextNameFingerprint = - std::tuple>; -} -} // namespace swift - //============================================================================== // MARK: DepGraphNode //============================================================================== @@ -699,9 +684,6 @@ class SourceFileDepGraphNode : public DepGraphNode { } /// Record the sequence number, \p n, of another use. - /// The relationship between an interface and its implementation is NOT - /// included here. See \c - /// SourceFileDepGraph::findExistingNodePairOrCreateAndAddIfNew. void addDefIDependUpon(size_t n) { if (n != getSequenceNumber()) defsIDependUpon.insert(n); @@ -745,25 +727,6 @@ class SourceFileDepGraph { SourceFileDepGraph(const SourceFileDepGraph &g) = delete; SourceFileDepGraph(SourceFileDepGraph &&g) = default; - /// Simulate loading for unit testing: - /// \param swiftDepsFileName The name of the swiftdeps file of the phony job - /// \param includePrivateDeps Whether the graph includes intra-file arcs - /// \param hadCompilationError Simulate a compilation error - /// \param interfaceHash The interface hash of the simulated graph - /// \param simpleNamesByRDK A map of vectors of names keyed by reference - /// dependency key \param compoundNamesByRDK A map of (mangledHolder, - /// baseName) pairs keyed by reference dependency key. For single-name - /// dependencies, an initial underscore indicates that the name does not - /// cascade. For compound names, it is the first name, the holder which - /// indicates non-cascading. For member names, an initial underscore indicates - /// file-privacy. - static SourceFileDepGraph - simulateLoad(std::string swiftDepsFileName, const bool includePrivateDeps, - const bool hadCompilationError, std::string interfaceHash, - llvm::StringMap> simpleNamesByRDK, - llvm::StringMap>> - compoundNamesByRDK); - /// Nodes are owned by the graph. ~SourceFileDepGraph() { forEachNode([&](SourceFileDepGraphNode *n) { delete n; }); @@ -783,7 +746,7 @@ class SourceFileDepGraph { InterfaceAndImplementationPair getSourceFileNodePair() const; - StringRef getSwiftDepsOfJobThatProducedThisGraph() const; + StringRef getSwiftDepsFromSourceFileProvide() const; std::string getGraphID() const { return getSourceFileNodePair().getInterface()->getKey().humanReadableName(); @@ -807,13 +770,12 @@ class SourceFileDepGraph { /// The frontend creates a pair of nodes for every tracked Decl and the source /// file itself. InterfaceAndImplementationPair - findExistingNodePairOrCreateAndAddIfNew( - NodeKind k, const ContextNameFingerprint &contextNameFingerprint); + findExistingNodePairOrCreateAndAddIfNew(NodeKind k, StringRef context, + StringRef name, + Optional fingerprint); - SourceFileDepGraphNode * - findExistingNodeOrCreateIfNew(DependencyKey key, - const Optional &fingerprint, - bool isProvides); + SourceFileDepGraphNode *findExistingNodeOrCreateIfNew( + DependencyKey key, Optional fingerprint, bool isProvides); /// \p Use is the Node that must be rebuilt when \p def changes. /// Record that fact in the graph. @@ -930,7 +892,7 @@ template class DotFileEmitter { } void emitArcs() { g.forEachArc([&](const NodeT *def, const NodeT *use) { - if (includeGraphArc(def, use)) + if (includeGraphArc(use, def)) emitGraphArc(def, use); }); } diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 3d54ece63ff4f..0795566e66c58 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -285,14 +285,10 @@ namespace swift { /// Whether to verify the parsed syntax tree and emit related diagnostics. bool VerifySyntaxTree = false; - /// Emit the newer, finer-grained swiftdeps file. Eventually will support - /// faster rebuilds. + /// Scaffolding to permit experimentation with finer-grained dependencies + /// and faster rebuilds. bool EnableFineGrainedDependencies = false; - - /// When using fine-grained dependencies, emit dot files for every swiftdeps - /// file. - bool EmitFineGrainedDependencySourcefileDotFiles = false; - + /// To mimic existing system, set to false. /// To experiment with including file-private and private dependency info, /// set to true. diff --git a/include/swift/Driver/Action.h b/include/swift/Driver/Action.h index 5bc3c26175470..4dae1eb336ba0 100644 --- a/include/swift/Driver/Action.h +++ b/include/swift/Driver/Action.h @@ -168,23 +168,6 @@ class CompileJobAction : public JobAction { static bool classof(const Action *A) { return A->getKind() == Action::Kind::CompileJob; } - - /// Return a _single_ TY_Swift InputAction, if one exists; - /// if 0 or >1 such inputs exist, return nullptr. - const InputAction *findSingleSwiftInput() const { - auto Inputs = getInputs(); - auto isSwiftInput = [](const Action *A) -> const InputAction* { - if (auto const *S = dyn_cast(A)) - return S->getType() == file_types::TY_Swift ? S : nullptr; - return nullptr; - }; - const auto loc1 = std::find_if(Inputs.begin(), Inputs.end(), isSwiftInput); - if (loc1 == Inputs.end()) - return nullptr; // none found - // Ensure uniqueness - const auto loc2 = std::find_if(loc1 + 1, Inputs.end(), isSwiftInput); - return loc2 == Inputs.end() ? dyn_cast(*loc1) : nullptr; - } }; class InterpretJobAction : public JobAction { diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index 0d9f56f4abb45..eaff74cfce578 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -521,18 +521,6 @@ class Compilation { /// How many .swift input files? unsigned countSwiftInputs() const; - /// Unfortunately the success or failure of a Swift compilation is currently - /// sensitive to the order in which files are processed, at least in terms of - /// the order of processing extensions (and likely other ways we haven't - /// discovered yet). So long as this is true, we need to make sure any batch - /// job we build names its inputs in an order that's a subsequence of the - /// sequence of inputs the driver was initially invoked with. - /// - /// Also use to write out information in a consistent order. - void sortJobsToMatchCompilationInputs( - ArrayRef unsortedJobs, - SmallVectorImpl &sortedJobs) const; - private: /// Perform all jobs. /// diff --git a/include/swift/Driver/FineGrainedDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h index b07a5f33f9f1f..237a361784b19 100644 --- a/include/swift/Driver/FineGrainedDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -48,9 +48,9 @@ namespace fine_grained_dependencies { /// Keep separate type from Node for type-checking. class ModuleDepGraphNode : public DepGraphNode { - /// The swiftDeps file that holds this entity iff this is a provides node. + /// The swiftDeps file that holds this entity. /// If more than one source file has the same DependencyKey, then there - /// will be one node for each in the driver, distinguished by this field. + /// will be one node for each in the driver. Optional swiftDeps; public: @@ -76,18 +76,26 @@ class ModuleDepGraphNode : public DepGraphNode { const Optional &getSwiftDeps() const { return swiftDeps; } - std::string getSwiftDepsOrEmpty() const { - return getSwiftDeps().getValueOr(std::string()); + bool assertImplementationMustBeInAFile() const { + assert((getSwiftDeps().hasValue() || !getKey().isImplementation()) && + "Implementations must be in some file."); + return true; } - std::string getSwiftDepsForMapKey() const { - // Use the empty string for nodes whose source file is unknown, - // i.e. depends. (Known depends are represented by arcs, not nodes.) - return getSwiftDepsOrEmpty(); + std::string humanReadableName() const { + StringRef where = + !getSwiftDeps().hasValue() + ? "" + : llvm::sys::path::filename(getSwiftDeps().getValue()); + return DepGraphNode::humanReadableName(where); } - const std::string &getSwiftDepsOfProvides() const { - return getSwiftDeps().getValue(); + SWIFT_DEBUG_DUMP; + + bool assertProvidedEntityMustBeInAFile() const { + assert((getSwiftDeps().hasValue() || !getKey().isImplementation()) && + "Implementations must be in some file."); + return true; } /// Nodes can move from file to file when the driver reads the result of a @@ -95,28 +103,15 @@ class ModuleDepGraphNode : public DepGraphNode { void setSwiftDeps(Optional s) { swiftDeps = s; } bool getIsProvides() const { return getSwiftDeps().hasValue(); } +}; - /// Return true if this node describes a definition for which the job is known - bool isDefinedInAKnownFile() const { return getIsProvides(); } - - bool doesNodeProvideAnInterface() const { - return getKey().isInterface() && getIsProvides(); - } - - bool assertImplementationMustBeInAFile() const { - assert((isDefinedInAKnownFile() || !getKey().isImplementation()) && - "Implementations must be in some file."); - return true; - } - - std::string humanReadableName() const { - StringRef where = !getIsProvides() - ? "" - : llvm::sys::path::filename(getSwiftDepsOfProvides()); - return DepGraphNode::humanReadableName(where); - } - - SWIFT_DEBUG_DUMP; +/// A placeholder allowing the experimental system to fit into the driver +/// without changing as much code. +class CoarseGrainedDependencyGraphImpl { +public: + /// Use the status quo LoadResult for now. + using LoadResult = + typename swift::CoarseGrainedDependencyGraphImpl::LoadResult; }; //============================================================================== @@ -164,10 +159,18 @@ class ModuleDepGraph { std::unordered_set externalDependencies; /// The new version of "Marked." - /// Aka "isMarked". Holds the swiftDeps paths for jobs the driver has or will - /// schedule. - /// TODO: Move scheduledJobs out of the graph, ultimately. - std::unordered_set swiftDepsOfJobsThatNeedRunning; + /// Record cascading jobs by swiftDepsFilename because that's what + /// nodes store directly. + /// + /// The status quo system uses "cascade" for the following: + /// Def1 -> def2 -> def3, where arrows are uses, so 3 depends on 2 which + /// depends on 1. The first use is said to "cascade" if when def1 changes, + /// def3 is dirtied. + /// TODO: Move cascadingJobs out of the graph, ultimately. + /// If marked, any Job that depends on me must be rebuilt after compiling me + /// if I have changed. + + std::unordered_set cascadingJobs; /// Keyed by swiftdeps filename, so we can get back to Jobs. std::unordered_map jobsBySwiftDeps; @@ -187,12 +190,10 @@ class ModuleDepGraph { const bool verifyFineGrainedDependencyGraphAfterEveryImport; const bool emitFineGrainedDependencyDotFileAfterEveryImport; - /// If tracing dependencies, holds a vector used to hold the current path - /// def - use/def - use/def - ... + /// If tracing dependencies, holds the current node traversal path Optional> currentPathIfTracing; - /// If tracing dependencies, holds the sequence of defs used to get to the job - /// that is the key + /// If tracing dependencies, record the node sequence std::unordered_multimap> dependencyPathsToJobs; @@ -203,7 +204,7 @@ class ModuleDepGraph { /// Encapsulate the invariant between where the node resides in /// nodesBySwiftDepsFile and the swiftDeps node instance variable here. void addToMap(ModuleDepGraphNode *n) { - nodeMap.insert(n->getSwiftDepsForMapKey(), n->getKey(), n); + nodeMap.insert(n->getSwiftDeps().getValueOr(std::string()), n->getKey(), n); } /// When integrating a SourceFileDepGraph, there might be a node representing @@ -222,7 +223,8 @@ class ModuleDepGraph { /// Remove node from nodeMap, check invariants. ModuleDepGraphNode *eraseNodeFromMap(ModuleDepGraphNode *nodeToErase) { ModuleDepGraphNode *nodeActuallyErased = nodeMap.findAndErase( - nodeToErase->getSwiftDepsForMapKey(), nodeToErase->getKey()); + nodeToErase->getSwiftDeps().getValueOr(std::string()), + nodeToErase->getKey()); (void)nodeActuallyErased; assert( nodeToErase == nodeActuallyErased || @@ -230,28 +232,6 @@ class ModuleDepGraph { return nodeToErase; } - void eraseNodeFromUsesByDef(ModuleDepGraphNode *nodeToErase) { - for (auto &defAndUses : usesByDef) - defAndUses.second.erase(nodeToErase); - } - - void eraseNodeFromCurrentPathIfTracing(ModuleDepGraphNode *nodeToErase) { - if (currentPathIfTracing) - eraseNodeFromVector(currentPathIfTracing.getValue(), nodeToErase); - } - - void eraseNodeFromDependencyPathToJobs(ModuleDepGraphNode *nodeToErase) { - for (auto &jobAndPath : dependencyPathsToJobs) - eraseNodeFromVector(jobAndPath.second, nodeToErase); - } - - static void eraseNodeFromVector(std::vector &v, - const ModuleDepGraphNode *n) { - const auto where = std::find(v.begin(), v.end(), n); - if (where != v.end()) - v.erase(where); - } - static StringRef getSwiftDeps(const driver::Job *cmd) { return cmd->getOutput().getAdditionalOutputForType( file_types::TY_SwiftDeps); @@ -288,8 +268,6 @@ class ModuleDepGraph { assert(verify() && "ModuleDepGraph should be fine when created"); } - ModuleDepGraph() : ModuleDepGraph(false, false, false, nullptr) {} - /// Unlike the standard \c CoarseGrainedDependencyGraph, returns \c /// CoarseGrainedDependencyGraphImpl::LoadResult::AffectsDownstream when /// loading a new file, i.e. when determining the initial set. Caller @@ -297,13 +275,6 @@ class ModuleDepGraph { CoarseGrainedDependencyGraphImpl::LoadResult loadFromPath(const driver::Job *, StringRef, DiagnosticEngine &); - CoarseGrainedDependencyGraphImpl::LoadResult - loadFromString(const driver::Job *cmd, StringRef data); - - CoarseGrainedDependencyGraphImpl::LoadResult - loadFromSourceFileDepGraph(const driver::Job *cmd, - const SourceFileDepGraph &); - /// For the dot file. std::string getGraphID() const { return "driver"; } @@ -331,12 +302,8 @@ class ModuleDepGraph { /// 1. Return value (via visited) is the set of jobs needing recompilation /// after this one, and /// 2. Jobs not previously known to need dependencies reexamined after they - /// are recompiled. Such jobs are added to the \ref scheduledJobs set, and + /// are recompiled. Such jobs are added to the \ref cascadingJobs set, and /// accessed via \ref isMarked. - /// - /// Only return jobs marked that were previously unmarked. Not required for - /// the driver because it won't run a job twice, but required for the unit - /// test. std::vector markTransitive( const driver::Job *jobToBeRecompiled, const void *ignored = nullptr); @@ -348,8 +315,6 @@ class ModuleDepGraph { std::vector getExternalDependencies() const; - /// Find jobs that were previously not known to need compilation but that - /// depend on \c externalDependency. std::vector markExternal(StringRef externalDependency); void forEachUnmarkedJobDirectlyDependentOnExternalSwiftdeps( @@ -463,14 +428,14 @@ class ModuleDepGraph { /// Given a definition node, and a list of already found dependents, /// recursively add transitive closure of dependents of the definition /// into the already found dependents. - void findDependentNodes( + /// Also record any dependents that "cascade", i.e. whose dependencies must be + /// recomputed after recompilation so that its dependents can be recompiled. + void findDependentNodesAndRecordCascadingOnes( std::unordered_set &foundDependents, const ModuleDepGraphNode *definition); - /// Givien a set of nodes, return the set of swiftDeps for the jobs those - /// nodes are in. - llvm::StringSet<> computeSwiftDepsFromInterfaceNodes( - ArrayRef nodes); + std::vector computeUniqueJobsFromNodes( + const std::unordered_set &nodes); /// Record a visit to this node for later dependency printing size_t traceArrival(const ModuleDepGraphNode *visitedNode); @@ -482,12 +447,12 @@ class ModuleDepGraph { const std::vector &pathToJob, const driver::Job *dependentJob); - /// Return true if job was not scheduled before - bool recordJobNeedsRunning(StringRef swiftDeps) { - return swiftDepsOfJobsThatNeedRunning.insert(swiftDeps).second; + /// Return true if job did not cascade before + bool rememberThatJobCascades(StringRef swiftDeps) { + return cascadingJobs.insert(swiftDeps).second; } - /// For debugging and visualization, write out the graph to a dot file. + /// For debugging, write out the graph to a dot file. /// \p diags may be null if no diagnostics are needed. void emitDotFileForJob(DiagnosticEngine &, const driver::Job *); void emitDotFile(DiagnosticEngine &, StringRef baseName); @@ -504,13 +469,6 @@ class ModuleDepGraph { void printPath(raw_ostream &out, const driver::Job *node) const; private: - /// Get a printable filename, given a node's swiftDeps. - StringRef getProvidingFilename(Optional swiftDeps) const; - - /// Print one node on the dependency path. - static void printOneNodeOfPath(raw_ostream &out, const DependencyKey &key, - const StringRef filename); - bool isCurrentPathForTracingEmpty() const { return !currentPathIfTracing.hasValue() || currentPathIfTracing->empty(); } diff --git a/include/swift/Driver/Job.h b/include/swift/Driver/Job.h index e57952545bb64..d36d3f3b3ff2d 100644 --- a/include/swift/Driver/Job.h +++ b/include/swift/Driver/Job.h @@ -150,9 +150,6 @@ class CommandOutput { public: CommandOutput(file_types::ID PrimaryOutputType, OutputFileMap &Derived); - /// For testing dependency graphs that use Jobs - CommandOutput(StringRef dummyBaseName, OutputFileMap &); - /// Return the primary output type for this CommandOutput. file_types::ID getPrimaryOutputType() const; @@ -322,12 +319,6 @@ class Job { ExtraEnvironment(std::move(ExtraEnvironment)), FilelistFileInfos(std::move(Infos)), ResponseFile(ResponseFile) {} - /// For testing dependency graphs that use Jobs - Job(OutputFileMap &OFM, StringRef dummyBaseName) - : Job(CompileJobAction(file_types::TY_Object), - SmallVector(), - std::make_unique(dummyBaseName, OFM), nullptr, {}) {} - virtual ~Job(); const JobAction &getSource() const { diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index a5a7813b4f17c..bdb5a2e9e362c 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -137,11 +137,7 @@ def driver_always_rebuild_dependents : def enable_fine_grained_dependencies : Flag<["-"], "enable-fine-grained-dependencies">, Flags<[FrontendOption, HelpHidden]>, -HelpText<"Be more selective about incremental recompilation">; - -def disable_fine_grained_dependencies : -Flag<["-"], "disable-fine-grained-dependencies">, Flags<[FrontendOption, HelpHidden]>, -HelpText<"Don't be more selective about incremental recompilation">; +HelpText<"Experimental work-in-progress to be more selective about incremental recompilation">; def enable_only_one_dependency_file : @@ -187,11 +183,6 @@ Flag<["-"], "fine-grained-dependency-include-intrafile">, InternalDebugOpt, HelpText<"Include within-file dependencies.">; -def emit_fine_grained_dependency_sourcefile_dot_files : -Flag<["-"], "emit-fine-grained-dependency-sourcefile-dot-files">, -InternalDebugOpt, -HelpText<"Emit dot files for every source file.">; - def driver_mode : Joined<["--"], "driver-mode=">, Flags<[HelpHidden]>, HelpText<"Set the driver mode to either 'swift' or 'swiftc'">; diff --git a/lib/AST/FineGrainedDependencies.cpp b/lib/AST/FineGrainedDependencies.cpp index f61dbd3851dee..c69c4623d79db 100644 --- a/lib/AST/FineGrainedDependencies.cpp +++ b/lib/AST/FineGrainedDependencies.cpp @@ -57,11 +57,11 @@ SourceFileDepGraph::getSourceFileNodePair() const { getNode(1)); } -StringRef SourceFileDepGraph::getSwiftDepsOfJobThatProducedThisGraph() const { +StringRef SourceFileDepGraph::getSwiftDepsFromSourceFileProvide() const { return getSourceFileNodePair() .getInterface() ->getKey() - .getSwiftDepsFromASourceFileProvideNodeKey(); + .getSwiftDepsFromSourceFileProvide(); } void SourceFileDepGraph::forEachArc( @@ -77,11 +77,8 @@ void SourceFileDepGraph::forEachArc( InterfaceAndImplementationPair SourceFileDepGraph::findExistingNodePairOrCreateAndAddIfNew( - NodeKind k, const ContextNameFingerprint &contextNameFingerprint) { - const std::string &context = std::get<0>(contextNameFingerprint); - const std::string &name = std::get<1>(contextNameFingerprint); - const Optional &fingerprint = - std::get<2>(contextNameFingerprint); + NodeKind k, StringRef context, StringRef name, + Optional fingerprint) { InterfaceAndImplementationPair nodePair{ findExistingNodeOrCreateIfNew( DependencyKey(k, DeclAspect::interface, context, name), fingerprint, @@ -89,25 +86,13 @@ SourceFileDepGraph::findExistingNodePairOrCreateAndAddIfNew( findExistingNodeOrCreateIfNew( DependencyKey(k, DeclAspect::implementation, context, name), fingerprint, true /* = isProvides */)}; - // if interface changes, have to rebuild implementation. - // This dependency used to be represented by - // addArc(nodePair.getInterface(), nodePair.getImplementation()); - // However, recall that the dependency scheme as of 1/2020 chunks - // declarations together by base name. - // So if the arc were added, a dirtying of a same-based-named interface - // in a different file would dirty the implementation in this file, - // causing the needless recompilation of this file. - // But, if an arc is added for this, then *any* change that causes - // a same-named interface to be dirty will dirty this implementation, - // even if that interface is in another file. - // Therefor no such arc is added here, and any dirtying of either - // the interface or implementation of this declaration will cause - // the driver to recompile this source file. + // if interface changes, have to rebuild implementation + addArc(nodePair.getInterface(), nodePair.getImplementation()); return nodePair; } SourceFileDepGraphNode *SourceFileDepGraph::findExistingNodeOrCreateIfNew( - DependencyKey key, const Optional &fingerprint, + DependencyKey key, Optional fingerprint, const bool isProvides) { SourceFileDepGraphNode *result = memoizedNodes.findExistingOrCreateIfNew( key, [&](DependencyKey key) -> SourceFileDepGraphNode * { @@ -116,22 +101,11 @@ SourceFileDepGraphNode *SourceFileDepGraph::findExistingNodeOrCreateIfNew( addNode(n); return n; }); - assert(result->getKey() == key && "Keys must match."); - if (!isProvides) - return result; // If have provides and depends with same key, result is one node that // isProvides - if (!result->getIsProvides() && fingerprint) { + if (isProvides) result->setIsProvides(); - assert(!result->getFingerprint() && "Depends should not have fingerprints"); - result->setFingerprint(fingerprint); - return result; - } - // If there are two Decls with same base name but differ only in fingerprint, - // since we won't be able to tell which Decl is depended-upon (is this right?) - // just use the one node, but erase its print: - if (fingerprint != result->getFingerprint()) - result->setFingerprint(None); + assert(result->getKey() == key && "Keys must match."); return result; } @@ -139,18 +113,6 @@ std::string DependencyKey::demangleTypeAsContext(StringRef s) { return swift::Demangle::demangleTypeAsString(s.str()); } -DependencyKey DependencyKey::createTransitiveKeyForWholeSourceFile( - const StringRef swiftDeps) { - assert(!swiftDeps.empty()); - const auto context = DependencyKey::computeContextForProvidedEntity< - NodeKind::sourceFileProvide>(swiftDeps); - const auto name = - DependencyKey::computeNameForProvidedEntity( - swiftDeps); - return DependencyKey(NodeKind::sourceFileProvide, DeclAspect::interface, - context, name); -} - //============================================================================== // MARK: Debugging //============================================================================== @@ -213,8 +175,9 @@ std::string DependencyKey::humanReadableName() const { } std::string DependencyKey::asString() const { - return NodeKindNames[size_t(kind)] + " " + "aspect: " + aspectName().str() + - ", " + humanReadableName(); + return NodeKindNames[size_t(kind)] + " " + + "aspect: " + DeclAspectNames[size_t(aspect)] + ", " + + humanReadableName(); } /// Needed for TwoStageMap::verify: diff --git a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp index d2c4abd459093..853aaaaade902 100644 --- a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp +++ b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp @@ -28,7 +28,6 @@ #include "swift/AST/Types.h" #include "swift/Basic/FileSystem.h" #include "swift/Basic/LLVM.h" -#include "swift/Basic/ReferenceDependencyKeys.h" #include "swift/Demangling/Demangle.h" #include "swift/Frontend/FrontendOptions.h" #include "llvm/ADT/MapVector.h" @@ -215,11 +214,8 @@ struct SourceFileDeclFinder { private: /// Extensions may contain nominals and operators. void findNominalsFromExtensions() { - for (auto *ED : extensions) { - const auto *const NTD = ED->getExtendedNominal(); - if (NTD) - findNominalsAndOperatorsIn(NTD, ED); - } + for (auto *ED : extensions) + findNominalsAndOperatorsIn(ED->getExtendedNominal(), ED); } /// Top-level nominals may contain nominals and operators. void findNominalsInTopNominals() { @@ -264,19 +260,16 @@ struct SourceFileDeclFinder { /// Extensions may contain ValueDecls. void findValuesInExtensions() { for (const auto *ED : extensions) { - const auto *const NTD = ED->getExtendedNominal(); - if (!NTD || excludeIfPrivate(NTD)) + if (excludeIfPrivate(ED->getExtendedNominal())) continue; if (!includePrivateDecls && (!allInheritedProtocolsArePrivate(ED) || allMembersArePrivate(ED))) continue; for (const auto *member : ED->getMembers()) if (const auto *VD = dyn_cast(member)) - if (VD->hasName() && (includePrivateDecls || !declIsPrivate(VD))) { - const auto *const NTD = ED->getExtendedNominal(); - if (NTD) - valuesInExtensions.push_back(std::make_pair(NTD, VD)); - } + if (VD->hasName() && (includePrivateDecls || !declIsPrivate(VD))) + valuesInExtensions.push_back( + std::make_pair(ED->getExtendedNominal(), VD)); } } @@ -361,12 +354,6 @@ std::string DependencyKey::computeContextForProvidedEntity< return mangleTypeAsContext(holderAndMember.first); } -// Linux compiler requires the following: -template -std::string -DependencyKey::computeContextForProvidedEntity(StringRef); - //============================================================================== // MARK: computeNameForProvidedEntity //============================================================================== @@ -437,38 +424,46 @@ std::string DependencyKey::computeNameForProvidedEntity< template <> DependencyKey -DependencyKey::createDependedUponKey(StringRef name) { - return DependencyKey(NodeKind::topLevel, DeclAspect::interface, "", name); +DependencyKey::createDependedUponKey( + const DeclBaseName &dbn) { + return DependencyKey(NodeKind::topLevel, DeclAspect::interface, "", + dbn.userFacingName()); } template <> DependencyKey -DependencyKey::createDependedUponKey(StringRef name) { +DependencyKey::createDependedUponKey( + const DeclBaseName &dbn) { return DependencyKey(NodeKind::dynamicLookup, DeclAspect::interface, "", - name); + dbn.userFacingName()); } template <> -DependencyKey -DependencyKey::createDependedUponKey(StringRef name) { - return DependencyKey(NodeKind::externalDepend, DeclAspect::interface, "", - name); +DependencyKey DependencyKey::createDependedUponKey< + NodeKind::nominal, std::pair>( + const std::pair &p) { + return DependencyKey(NodeKind::nominal, DeclAspect::interface, + mangleTypeAsContext(p.first), ""); } template <> -DependencyKey -DependencyKey::createDependedUponKey(StringRef mangledName) { - return DependencyKey(NodeKind::nominal, DeclAspect::interface, mangledName, - ""); -} - -DependencyKey DependencyKey::createDependedUponKey(StringRef mangledHolderName, - StringRef memberBaseName) { - const bool isMemberBlank = memberBaseName.empty(); +DependencyKey DependencyKey::createDependedUponKey< + NodeKind::member, std::pair>( + const std::pair &p) { + const bool isMemberBlank = p.second.empty(); const auto kind = isMemberBlank ? NodeKind::potentialMember : NodeKind::member; - return DependencyKey(kind, DeclAspect::interface, mangledHolderName, - isMemberBlank ? "" : memberBaseName); + return DependencyKey(kind, DeclAspect::interface, + mangleTypeAsContext(p.first), + isMemberBlank ? "" : p.second.userFacingName()); +} + +template <> +DependencyKey +DependencyKey::createDependedUponKey( + const std::string &file) { + return DependencyKey(NodeKind::externalDepend, DeclAspect::interface, "", + file); } //============================================================================== @@ -480,6 +475,12 @@ namespace { /// Reads the information provided by the frontend and builds the /// SourceFileDepGraph class SourceFileDepGraphConstructor { + /// The SourceFile containing the Decls. + SourceFile *SF; + + /// Furnishes depended-upon names resulting from lookups. + const DependencyTracker &depTracker; + /// Name of the swiftDeps file, for inclusion in the constructed graph. StringRef swiftDeps; // TODO rm? @@ -490,133 +491,18 @@ class SourceFileDepGraphConstructor { /// If there was an error, cannot get accurate info. const bool hadCompilationError; - /// Functions as the fingerprint of the entire file - const std::string interfaceHash; - - /// Top-level base names of decls that are depended-upon and a flag indicating - /// if the dependency "cascades" - const std::vector> topLevelDepends; - - /// A mangled nominal name and the member base name that are depended-upon, - /// a flag indicating if the member is private to its enclosing file, and - /// a flag indicating if the dependency cascades. - const std::vector, bool>> - memberDepends; - - /// The base name of a class member depended-upon for dynamic lookup, and a - /// cascades flag. - const std::vector> dynamicLookupDepends; - - /// The paths of swiftdeps files of other modules that are depended-upon. - const std::vector externalDependencies; - - /// Provided names - std::vector precedenceGroups; - std::vector memberOperatorDecls; - std::vector operators; - std::vector topNominals; - std::vector topValues; - std::vector allNominals; - std::vector potentialMemberHolders; - std::vector valuesInExtensions; - std::vector classMembers; - /// Graph under construction SourceFileDepGraph g; public: - /// Expose this layer to enable faking up a constructor for testing. - /// See the instance variable comments for explanation. - // clang-format off - SourceFileDepGraphConstructor( - StringRef swiftDeps, - bool includePrivateDeps, - bool hadCompilationError, - const std::string &interfaceHash, - ArrayRef> topLevelDepends, - ArrayRef, bool>> memberDepends, - ArrayRef> dynamicLookupDepends, - ArrayRef externalDependencies, - - ArrayRef precedenceGroups, - ArrayRef memberOperatorDecls, - ArrayRef operators, - ArrayRef topNominals, - ArrayRef topValues, - ArrayRef allNominals, - ArrayRef potentialMemberHolders, - ArrayRef valuesInExtensions, - ArrayRef classMembers - ) : - swiftDeps(swiftDeps), - includePrivateDeps(includePrivateDeps), - hadCompilationError(hadCompilationError), - - interfaceHash(interfaceHash), - topLevelDepends(topLevelDepends), - memberDepends(memberDepends), - dynamicLookupDepends(dynamicLookupDepends), - externalDependencies(externalDependencies), - - precedenceGroups(precedenceGroups), - memberOperatorDecls(memberOperatorDecls), - operators(operators), - topNominals(topNominals), - topValues(topValues), - allNominals(allNominals), - potentialMemberHolders(potentialMemberHolders), - valuesInExtensions(valuesInExtensions), - classMembers(classMembers) - {} - - SourceFileDepGraphConstructor static forSourceFile(SourceFile *SF, + SourceFileDepGraphConstructor(SourceFile *SF, const DependencyTracker &depTracker, StringRef swiftDeps, const bool includePrivateDeps, - const bool hadCompilationError) { - - SourceFileDeclFinder declFinder(SF, includePrivateDeps); - std::vector> topLevelDepends; - for (const auto p: SF->getReferencedNameTracker()->getTopLevelNames()) - topLevelDepends.push_back(std::make_pair(p.getFirst().userFacingName(), p.getSecond())); - - std::vector> dynamicLookupDepends; - for (const auto p: SF->getReferencedNameTracker()->getDynamicLookupNames()) - dynamicLookupDepends.push_back(std::make_pair(p.getFirst().userFacingName(), p.getSecond())); - - std::vector, bool>> memberDepends; - for (const auto &p: SF->getReferencedNameTracker()->getUsedMembers()) - memberDepends.push_back( - std::make_pair( - std::make_tuple( - mangleTypeAsContext(p.getFirst().first), - p.getFirst().second.userFacingName(), - declIsPrivate(p.getFirst().first)), - p.getSecond())); - - return SourceFileDepGraphConstructor( - swiftDeps, - includePrivateDeps, - hadCompilationError, - - getInterfaceHash(SF), - topLevelDepends, - memberDepends, - dynamicLookupDepends, - depTracker.getDependencies(), - - namesForProvidersOfAGivenType(declFinder.precedenceGroups), - namesForProvidersOfAGivenType(declFinder.memberOperatorDecls), - namesForProvidersOfAGivenType(declFinder.operators), - namesForProvidersOfAGivenType(declFinder.topNominals), - namesForProvidersOfAGivenType(declFinder.topValues), - namesForProvidersOfAGivenType(declFinder.allNominals), - namesForProvidersOfAGivenType(declFinder.potentialMemberHolders), - namesForProvidersOfAGivenType(declFinder.valuesInExtensions), - namesForProvidersOfAGivenType(declFinder.classMembers) - ); - } - // clang-format on + const bool hadCompilationError) + : SF(SF), depTracker(depTracker), swiftDeps(swiftDeps), + includePrivateDeps(includePrivateDeps), + hadCompilationError(hadCompilationError) {} /// Construct the graph and return it. SourceFileDepGraph construct() { @@ -631,7 +517,7 @@ class SourceFileDepGraphConstructor { } private: - std::string getSourceFileFingerprint() const { return interfaceHash; } + std::string getSourceFileFingerprint() const { return getInterfaceHash(SF); } static std::string getInterfaceHash(SourceFile *SF) { llvm::SmallString<32> interfaceHash; @@ -647,26 +533,19 @@ class SourceFileDepGraphConstructor { void addDependencyArcsToGraph(); /// Given an array of Decls or pairs of them in \p declsOrPairs - /// create string pairs for context and name + /// create nodes if needed and add the new nodes to the graph. template - static std::vector - namesForProvidersOfAGivenType(std::vector &contentsVec) { - std::vector result; - for (const auto declOrPair : contentsVec) - result.push_back(ContextNameFingerprint( + void addAllProviderNodesOfAGivenType(std::vector &contentsVec) { + for (const auto declOrPair : contentsVec) { + // No fingerprints for providers (Decls) yet. + // Someday ... + const Optional fingerprint = None; + auto p = g.findExistingNodePairOrCreateAndAddIfNew( + kind, DependencyKey::computeContextForProvidedEntity(declOrPair), DependencyKey::computeNameForProvidedEntity(declOrPair), - Optional())); - return result; - } - - template - void addAllProviderNodesOfAGivenType( - ArrayRef contextNameFingerprints) { - for (const auto &contextNameFingerprint : contextNameFingerprints) { - auto p = g.findExistingNodePairOrCreateAndAddIfNew( - kind, contextNameFingerprint); - // When we don't have a fingerprint yet, must rebuild every provider when + fingerprint); + // Since we don't have fingerprints yet, must rebuild every provider when // interfaceHash changes. So when interface (i.e. interface hash) of // sourceFile changes, every provides is dirty. And since we don't know // what happened, dirtyness might affect the interface. @@ -678,8 +557,8 @@ class SourceFileDepGraphConstructor { /// Given a map of names and isCascades, add the resulting dependencies to the /// graph. template - void addAllDependenciesFrom(ArrayRef> names) { - for (const auto &p : names) + void addAllDependenciesFrom(const llvm::DenseMap &map) { + for (const auto &p : map) recordThatThisWholeFileDependsOn( DependencyKey::createDependedUponKey(p.first), p.second); } @@ -687,7 +566,8 @@ class SourceFileDepGraphConstructor { /// Given a map of holder-and-member-names and isCascades, add the resulting /// dependencies to the graph. void addAllDependenciesFrom( - ArrayRef, bool>>); + const llvm::DenseMap, + bool> &); /// Given an array of external swiftDeps files, add the resulting external /// dependencies to the graph. @@ -707,27 +587,28 @@ class SourceFileDepGraphConstructor { }; } // namespace +using UsedMembersMap = + llvm::DenseMap, bool>; void SourceFileDepGraphConstructor::addAllDependenciesFrom( - ArrayRef, bool>> - members) { + const UsedMembersMap &map) { + + UsedMembersMap filteredMap; + for (const auto &entry : map) + if (includePrivateDeps || !declIsPrivate(entry.first.first)) + filteredMap[entry.getFirst()] = entry.getSecond(); - llvm::StringSet<> holdersOfCascadingMembers; - for (const auto &entry : members) { - if (!includePrivateDeps && std::get<2>(entry.first)) - continue; + std::unordered_set holdersOfCascadingMembers; + for (auto &entry : filteredMap) if (entry.second) - holdersOfCascadingMembers.insert(std::get<0>(entry.first)); - } - for (const auto &entry : members) { - if (!includePrivateDeps && std::get<2>(entry.first)) - continue; + holdersOfCascadingMembers.insert(entry.first.first); + + for (auto &entry : filteredMap) { + // mangles twice in the name of symmetry recordThatThisWholeFileDependsOn( - DependencyKey::createDependedUponKey( - std::get<0>(entry.first)), - holdersOfCascadingMembers.count(std::get<0>(entry.first)) != 0); + DependencyKey::createDependedUponKey(entry.first), + holdersOfCascadingMembers.count(entry.first.first) != 0); recordThatThisWholeFileDependsOn( - DependencyKey::createDependedUponKey(std::get<0>(entry.first), - std::get<1>(entry.first)), + DependencyKey::createDependedUponKey(entry.first), entry.second); } } @@ -739,40 +620,47 @@ void SourceFileDepGraphConstructor::addAllDependenciesFrom( void SourceFileDepGraphConstructor::addSourceFileNodesToGraph() { g.findExistingNodePairOrCreateAndAddIfNew( NodeKind::sourceFileProvide, - ContextNameFingerprint(DependencyKey::computeContextForProvidedEntity< - NodeKind::sourceFileProvide>(swiftDeps), - DependencyKey::computeNameForProvidedEntity< - NodeKind::sourceFileProvide>(swiftDeps), - getSourceFileFingerprint())); + DependencyKey::computeContextForProvidedEntity< + NodeKind::sourceFileProvide>(swiftDeps), + DependencyKey::computeNameForProvidedEntity( + swiftDeps), + getSourceFileFingerprint()); } void SourceFileDepGraphConstructor::addProviderNodesToGraph() { + SourceFileDeclFinder declFinder(SF, includePrivateDeps); // TODO: express the multiple provides and depends streams with variadic // templates // Many kinds of Decls become top-level depends. - addAllProviderNodesOfAGivenType(precedenceGroups); - addAllProviderNodesOfAGivenType(memberOperatorDecls); - addAllProviderNodesOfAGivenType(operators); - addAllProviderNodesOfAGivenType(topNominals); - addAllProviderNodesOfAGivenType(topValues); + addAllProviderNodesOfAGivenType( + declFinder.precedenceGroups); + addAllProviderNodesOfAGivenType( + declFinder.memberOperatorDecls); + addAllProviderNodesOfAGivenType(declFinder.operators); + addAllProviderNodesOfAGivenType(declFinder.topNominals); + addAllProviderNodesOfAGivenType(declFinder.topValues); - addAllProviderNodesOfAGivenType(allNominals); + addAllProviderNodesOfAGivenType(declFinder.allNominals); addAllProviderNodesOfAGivenType( - potentialMemberHolders); - addAllProviderNodesOfAGivenType(valuesInExtensions); + declFinder.potentialMemberHolders); + addAllProviderNodesOfAGivenType( + declFinder.valuesInExtensions); - addAllProviderNodesOfAGivenType(classMembers); + addAllProviderNodesOfAGivenType( + declFinder.classMembers); } void SourceFileDepGraphConstructor::addDependencyArcsToGraph() { // TODO: express the multiple provides and depends streams with variadic // templates - addAllDependenciesFrom(topLevelDepends); - addAllDependenciesFrom(memberDepends); - addAllDependenciesFrom(dynamicLookupDepends); - addAllDependenciesFrom(externalDependencies); + addAllDependenciesFrom( + SF->getReferencedNameTracker()->getTopLevelNames()); + addAllDependenciesFrom(SF->getReferencedNameTracker()->getUsedMembers()); + addAllDependenciesFrom( + SF->getReferencedNameTracker()->getDynamicLookupNames()); + addAllDependenciesFrom(depTracker.getDependencies()); } void SourceFileDepGraphConstructor::recordThatThisWholeFileDependsOn( @@ -788,8 +676,7 @@ void SourceFileDepGraphConstructor::recordThatThisWholeFileDependsOn( bool swift::fine_grained_dependencies::emitReferenceDependencies( DiagnosticEngine &diags, SourceFile *const SF, - const DependencyTracker &depTracker, StringRef outputPath, - const bool alsoEmitDotFile) { + const DependencyTracker &depTracker, StringRef outputPath) { // Before writing to the dependencies file path, preserve any previous file // that may have been there. No error handling -- this is just a nicety, it @@ -798,8 +685,8 @@ bool swift::fine_grained_dependencies::emitReferenceDependencies( const bool includeIntrafileDeps = SF->getASTContext().LangOpts.FineGrainedDependenciesIncludeIntrafileOnes; const bool hadCompilationError = SF->getASTContext().hadError(); - auto gc = SourceFileDepGraphConstructor::forSourceFile( - SF, depTracker, outputPath, includeIntrafileDeps, hadCompilationError); + SourceFileDepGraphConstructor gc(SF, depTracker, outputPath, + includeIntrafileDeps, hadCompilationError); SourceFileDepGraph g = gc.construct(); const bool hadError = @@ -812,114 +699,10 @@ bool swift::fine_grained_dependencies::emitReferenceDependencies( assert(g.verifyReadsWhatIsWritten(outputPath)); - if (alsoEmitDotFile) { - std::string dotFileName = outputPath.str() + ".dot"; - withOutputFile(diags, dotFileName, [&](llvm::raw_pwrite_stream &out) { - DotFileEmitter(out, g, false, false).emit(); - return false; - }); - } + std::string dotFileName = outputPath.str() + ".dot"; + withOutputFile(diags, dotFileName, [&](llvm::raw_pwrite_stream &out) { + DotFileEmitter(out, g, false, false).emit(); + return false; + }); return hadError; } - -//============================================================================== -// Entry point from the unit tests -//============================================================================== - -static std::vector -getBaseNameProvides(ArrayRef simpleNames) { - std::vector result; - for (StringRef n : simpleNames) - result.push_back(ContextNameFingerprint("", n.str(), None)); - return result; -} - -static std::vector -getMangledHolderProvides(ArrayRef simpleNames) { - std::vector result; - for (StringRef n : simpleNames) - result.push_back(ContextNameFingerprint(n.str(), "", None)); - return result; -} - -static std::vector getCompoundProvides( - ArrayRef> compoundNames) { - std::vector result; - for (const auto &p : compoundNames) - result.push_back(ContextNameFingerprint(p.first, p.second, None)); - return result; -} - -// Use '_' as a prefix indicating non-cascading -static bool cascades(const std::string &s) { return s.empty() || s[0] != '_'; } - -// Use '_' as a prefix for a file-private member -static bool isPrivate(const std::string &s) { - return !s.empty() && s[0] == '_'; -} - -static std::vector> -getSimpleDepends(ArrayRef simpleNames) { - std::vector> result; - for (std::string n : simpleNames) - result.push_back({n, cascades((n))}); - return result; -} - -static std::vector -getExternalDepends(ArrayRef simpleNames) { - return simpleNames; -} - -static std::vector, bool>> -getCompoundDepends( - ArrayRef simpleNames, - ArrayRef> compoundNames) { - std::vector, bool>> - result; - for (std::string n : simpleNames) { - // (On Linux, the compiler needs more verbosity than: - // result.push_back({{n, "", false}, cascades(n)}); - result.push_back( - std::make_pair(std::make_tuple(n, std::string(), false), cascades(n))); - } - for (auto &p : compoundNames) { - // Likewise, for Linux expand the following out: - // result.push_back( - // {{p.first, p.second, isPrivate(p.second)}, cascades(p.first)}); - result.push_back( - std::make_pair(std::make_tuple(p.first, p.second, isPrivate(p.second)), - cascades(p.first))); - } - return result; -} - -SourceFileDepGraph SourceFileDepGraph::simulateLoad( - std::string swiftDepsFilename, const bool includePrivateDeps, - const bool hadCompilationError, std::string interfaceHash, - llvm::StringMap> simpleNamesByRDK, - llvm::StringMap>> - compoundNamesByRDK) { - - using namespace reference_dependency_keys; - - // clang-format off - SourceFileDepGraphConstructor c( - swiftDepsFilename, includePrivateDeps, hadCompilationError, interfaceHash, - getSimpleDepends(simpleNamesByRDK[dependsTopLevel]), - getCompoundDepends(simpleNamesByRDK[dependsNominal], compoundNamesByRDK[dependsMember]), - getSimpleDepends(simpleNamesByRDK[dependsDynamicLookup]), - getExternalDepends(simpleNamesByRDK[dependsExternal]), - {}, // precedence groups - {}, // memberOperatorDecls - {}, // operators - getMangledHolderProvides(simpleNamesByRDK[providesNominal]), // topNominals - getBaseNameProvides(simpleNamesByRDK[providesTopLevel]), // topValues - getMangledHolderProvides(simpleNamesByRDK[providesNominal]), // allNominals - getMangledHolderProvides(simpleNamesByRDK[providesNominal]), // potentialMemberHolders - getCompoundProvides(compoundNamesByRDK[providesMember]), // valuesInExtensions - getBaseNameProvides(simpleNamesByRDK[providesDynamicLookup]) // classMembers - ); - // clang-format on - return c.construct(); -} diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index dcf6724576844..b830e04ea68d4 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -272,7 +272,7 @@ namespace driver { void noteBuilding(const Job *cmd, const bool willBeBuilding, const bool isTentative, const bool forRanges, - StringRef reason) const { + StringRef reason) { if (!Comp.getShowIncrementalBuildDecisions()) return; if (ScheduledCommands.count(cmd)) @@ -300,23 +300,6 @@ namespace driver { }); } - template - void noteBuildingJobs(const JobsCollection &unsortedJobsArg, - const bool forRanges, const StringRef reason) const { - if (!Comp.getShowIncrementalBuildDecisions() && - !Comp.getShowJobLifecycle()) - return; - // Sigh, must manually convert SmallPtrSet to ArrayRef-able container - llvm::SmallVector unsortedJobs; - for (const Job *j : unsortedJobsArg) - unsortedJobs.push_back(j); - llvm::SmallVector sortedJobs; - Comp.sortJobsToMatchCompilationInputs(unsortedJobs, sortedJobs); - for (const Job *j : sortedJobs) - noteBuilding(j, /*willBeBuilding=*/true, /*isTentative=*/false, - forRanges, reason); - } - const Job *findUnfinishedJob(ArrayRef JL) { for (const Job *Cmd : JL) { if (!FinishedCommands.count(Cmd)) @@ -449,7 +432,7 @@ namespace driver { diag::warn_unable_to_load_dependencies, DependenciesFile); Comp.disableIncrementalBuild( - Twine("malformed swift dependencies file ' ") + DependenciesFile + + Twine("Malformed swift dependencies file ' ") + DependenciesFile + "'"); } @@ -485,7 +468,7 @@ namespace driver { // other recompilations. It is possible that the current code marks // things that do not need to be marked. Unecessary compilation would // result if that were the case. - bool wasKnownToNeedRunning = isMarkedInDepGraph(FinishedCmd, forRanges); + bool wasCascading = isMarkedInDepGraph(FinishedCmd, forRanges); switch (loadDepGraphFromPath(FinishedCmd, DependenciesFile, Comp.getDiags(), forRanges)) { @@ -501,7 +484,7 @@ namespace driver { break; case CoarseGrainedDependencyGraph::LoadResult::UpToDate: - if (!wasKnownToNeedRunning) + if (!wasCascading) break; LLVM_FALLTHROUGH; case CoarseGrainedDependencyGraph::LoadResult::AffectsDownstream: @@ -691,22 +674,14 @@ namespace driver { const CommandSet &DependentsInEffect = useRangesForScheduling ? DependentsWithRanges : DependentsWithoutRanges; - - noteBuildingJobs(DependentsInEffect, useRangesForScheduling, - "because of dependencies discovered later"); - - // Sort dependents for more deterministic behavior - llvm::SmallVector UnsortedDependents; - for (const Job *j : DependentsInEffect) - UnsortedDependents.push_back(j); - llvm::SmallVector SortedDependents; - Comp.sortJobsToMatchCompilationInputs(UnsortedDependents, - SortedDependents); - - for (const Job *Cmd : SortedDependents) { + for (const Job *Cmd : DependentsInEffect) { DeferredCommands.erase(Cmd); + noteBuilding(Cmd, /*willBeBuilding=*/true, useRangesForScheduling, + /*isTentative=*/false, + "because of dependencies discovered later"); scheduleCommandIfNecessaryAndPossible(Cmd); } + return TaskFinishedResponse::ContinueExecution; } @@ -717,7 +692,6 @@ namespace driver { // Store this task's ReturnCode as our Result if we haven't stored // anything yet. - if (Result == EXIT_SUCCESS) Result = ReturnCode; @@ -1033,6 +1007,10 @@ namespace driver { const bool isCascading = isCascadingJobAccordingToCondition( Cmd, Cond, HasDependenciesFileName); + + if (Comp.getEnableFineGrainedDependencies()) + assert(getFineGrainedDepGraph(/*forRanges=*/false) + .emitDotFileAndVerify(Comp.getDiags())); return std::make_pair(shouldSched, isCascading); } @@ -1077,10 +1055,6 @@ namespace driver { const Job *const Cmd, const Job::Condition Condition, const bool hasDependenciesFileName, const bool forRanges) { - // When using ranges may still decide not to schedule the job. - const bool isTentative = - Comp.getEnableSourceRangeDependencies() || forRanges; - switch (Condition) { case Job::Condition::Always: case Job::Condition::NewlyAdded: @@ -1096,11 +1070,11 @@ namespace driver { } LLVM_FALLTHROUGH; case Job::Condition::RunWithoutCascading: - noteBuilding(Cmd, /*willBeBuilding=*/true, /*isTentative=*/isTentative, + noteBuilding(Cmd, /*willBeBuilding=*/true, /*isTentative=*/true, forRanges, "(initial)"); return true; case Job::Condition::CheckDependencies: - noteBuilding(Cmd, /*willBeBuilding=*/false, /*isTentative=*/isTentative, + noteBuilding(Cmd, /*willBeBuilding=*/false, /*isTentative=*/true, forRanges, "file is up-to-date and output exists"); return false; } @@ -1144,7 +1118,11 @@ namespace driver { IncrementalTracer)) CascadedJobs.insert(transitiveCmd); } - noteBuildingJobs(CascadedJobs, forRanges, "because of the initial set"); + for (auto *transitiveCmd : CascadedJobs) + noteBuilding(transitiveCmd, /*willBeBuilding=*/true, + /*isTentative=*/false, forRanges, + "because of the initial set"); + return CascadedJobs; } @@ -1160,8 +1138,11 @@ namespace driver { for (const Job * marked: markExternalInDepGraph(dependency, forRanges)) ExternallyDependentJobs.push_back(marked); }); - noteBuildingJobs(ExternallyDependentJobs, forRanges, - "because of external dependencies"); + for (auto *externalCmd : ExternallyDependentJobs) { + noteBuilding(externalCmd, /*willBeBuilding=*/true, + /*isTentative=*/false, forRanges, + "because of external dependencies"); + } return ExternallyDependentJobs; } @@ -1521,11 +1502,11 @@ namespace driver { continue; // Be conservative, in case we use ranges this time but not next. - bool mightBeCascading = true; + bool isCascading = true; if (Comp.getIncrementalBuildEnabled()) - mightBeCascading = isMarkedInDepGraph( + isCascading = isMarkedInDepGraph( Cmd, /*forRanges=*/Comp.getEnableSourceRangeDependencies()); - UnfinishedCommands.insert({Cmd, mightBeCascading}); + UnfinishedCommands.insert({Cmd, isCascading}); } } } @@ -1852,6 +1833,8 @@ int Compilation::performJobsImpl(bool &abnormalExit, CompilationRecordPath + "~moduleonly"); } } + if (getEnableFineGrainedDependencies()) + assert(State.FineGrainedDepGraph.emitDotFileAndVerify(getDiags())); abnormalExit = State.hadAnyAbnormalExit(); return State.getResult(); } @@ -2079,23 +2062,3 @@ void Compilation::addDependencyPathOrCreateDummy( llvm::raw_fd_ostream(depPath, EC, llvm::sys::fs::F_None); } } - -void Compilation::sortJobsToMatchCompilationInputs( - const ArrayRef unsortedJobs, - SmallVectorImpl &sortedJobs) const { - llvm::DenseMap jobsByInput; - for (const Job *J : unsortedJobs) { - const CompileJobAction *CJA = cast(&J->getSource()); - const InputAction *IA = CJA->findSingleSwiftInput(); - auto R = - jobsByInput.insert(std::make_pair(IA->getInputArg().getValue(), J)); - assert(R.second); - (void)R; - } - for (const InputPair &P : getInputFiles()) { - auto I = jobsByInput.find(P.second->getValue()); - if (I != jobsByInput.end()) { - sortedJobs.push_back(I->second); - } - } -} diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index ef99128d3a3a8..e3a6e2a3361cf 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -958,8 +958,7 @@ Driver::buildCompilation(const ToolChain &TC, // relies on the new dependency graph const bool EnableFineGrainedDependencies = - ArgList->hasFlag(options::OPT_enable_fine_grained_dependencies, - options::OPT_disable_fine_grained_dependencies, false); + ArgList->hasArg(options::OPT_enable_fine_grained_dependencies); const bool VerifyFineGrainedDependencyGraphAfterEveryImport = ArgList->hasArg( options:: diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index 6564e17dbf719..dc307fea0fab6 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -42,7 +42,8 @@ using namespace swift::driver; // MARK: Interfacing to Compilation //============================================================================== -using LoadResult = CoarseGrainedDependencyGraphImpl::LoadResult; +using LoadResult = + fine_grained_dependencies::CoarseGrainedDependencyGraphImpl::LoadResult; LoadResult ModuleDepGraph::loadFromPath(const Job *Cmd, StringRef path, DiagnosticEngine &diags) { @@ -65,11 +66,6 @@ LoadResult ModuleDepGraph::loadFromPath(const Job *Cmd, StringRef path, return r; } -LoadResult ModuleDepGraph::loadFromString(const Job *cmd, StringRef data) { - auto buffer = llvm::MemoryBuffer::getMemBuffer(data); - return loadFromBuffer(cmd, *buffer.get()); -} - LoadResult ModuleDepGraph::loadFromBuffer(const Job *job, llvm::MemoryBuffer &buffer) { @@ -77,73 +73,50 @@ LoadResult ModuleDepGraph::loadFromBuffer(const Job *job, SourceFileDepGraph::loadFromBuffer(buffer); if (!sourceFileDepGraph) return CoarseGrainedDependencyGraphImpl::LoadResult::HadError; - return loadFromSourceFileDepGraph(job, sourceFileDepGraph.getValue()); -} - -LoadResult ModuleDepGraph::loadFromSourceFileDepGraph( - const Job *job, const SourceFileDepGraph &sourceFileDepGraph) { addIndependentNode(job); - return integrate(sourceFileDepGraph); + return integrate(sourceFileDepGraph.getValue()); } bool ModuleDepGraph::isMarked(const Job *cmd) const { - return swiftDepsOfJobsThatNeedRunning.count(getSwiftDeps(cmd)); + return cascadingJobs.count(getSwiftDeps(cmd)); } std::vector ModuleDepGraph::markTransitive( const Job *jobToBeRecompiled, const void *ignored) { FrontendStatsTracer tracer(stats, "fine-grained-dependencies-markTransitive"); - assert(jobToBeRecompiled && "Ensure there is really a job"); std::unordered_set dependentNodes; const StringRef swiftDepsToBeRecompiled = getSwiftDeps(jobToBeRecompiled); - assert(!swiftDepsToBeRecompiled.empty() && "Must have a swift deps"); - // Caller already knows to run this job, no need to return it. - recordJobNeedsRunning(swiftDepsToBeRecompiled); - - // Do the traversal for every node in the job to be recompiled. + // Do the traversal. for (auto &fileAndNode : nodeMap[swiftDepsToBeRecompiled]) { assert(isCurrentPathForTracingEmpty()); - findDependentNodes(dependentNodes, fileAndNode.second); + findDependentNodesAndRecordCascadingOnes(dependentNodes, + fileAndNode.second); } - std::vector newJobsToCompile; - // The job containing the interface "cascades", in other words - // whenever that job gets recompiled, anything depending on it - // (since we don't have interface-specific dependency info as of Dec. - // 2018) must be recompiled. - std::vector dependentNodesVec{ - dependentNodes.begin(), dependentNodes.end()}; - for (const auto &entry : - computeSwiftDepsFromInterfaceNodes(dependentNodesVec)) { - const StringRef swiftDeps = entry.getKey(); - if (recordJobNeedsRunning(swiftDeps)) { - const Job *j = getJob(swiftDeps.str()); - newJobsToCompile.push_back(j); - } - } - return newJobsToCompile; + return computeUniqueJobsFromNodes(dependentNodes); } -llvm::StringSet<> ModuleDepGraph::computeSwiftDepsFromInterfaceNodes( - const ArrayRef nodes) { +std::vector ModuleDepGraph::computeUniqueJobsFromNodes( + const std::unordered_set &nodes) { + + std::vectorjobs; - llvm::StringSet<> swiftDepsOfNodes; + std::unordered_set swiftDepsOfNodes; for (const ModuleDepGraphNode *n : nodes) { - // if (!n->doesNodeProvideAnInterface()) - // continue; - if (!n->getIsProvides()) + if (!n->getSwiftDeps().hasValue()) continue; - const std::string &swiftDeps = n->getSwiftDepsOfProvides(); + const std::string &swiftDeps = n->getSwiftDeps().getValue(); if (swiftDepsOfNodes.insert(swiftDeps).second) { assert(n->assertImplementationMustBeInAFile()); - assert(ensureJobIsTracked(swiftDeps)); + ensureJobIsTracked(swiftDeps); + jobs.push_back(getJob(swiftDeps)); } } - return swiftDepsOfNodes; + return jobs; } -bool ModuleDepGraph::markIntransitive(const Job *job) { - return recordJobNeedsRunning(getSwiftDeps(job)); +bool ModuleDepGraph::markIntransitive(const Job *node) { + return rememberThatJobCascades(getSwiftDeps(node)); } void ModuleDepGraph::addIndependentNode(const Job *job) { @@ -177,10 +150,11 @@ void ModuleDepGraph::forEachUnmarkedJobDirectlyDependentOnExternalSwiftdeps( DependencyKey key = DependencyKey::createDependedUponKey( externalSwiftDeps.str()); + // collect answers into useSet + std::unordered_set visitedSet; for (const ModuleDepGraphNode *useNode : usesByDef[key]) { - const auto swiftDepsOfUse = useNode->getSwiftDepsOfProvides(); - const Job *job = getJob(swiftDepsOfUse); - if (isMarked(job)) + const Job *job = getJob(useNode->getSwiftDeps()); + if (!isMarked(job)) continue; fn(job); } @@ -193,7 +167,7 @@ void ModuleDepGraph::forEachUnmarkedJobDirectlyDependentOnExternalSwiftdeps( LoadResult ModuleDepGraph::integrate(const SourceFileDepGraph &g) { FrontendStatsTracer tracer(stats, "fine-grained-dependencies-integrate"); - StringRef swiftDeps = g.getSwiftDepsOfJobThatProducedThisGraph(); + StringRef swiftDeps = g.getSwiftDepsFromSourceFileProvide(); // When done, disappearedNodes contains the nodes which no longer exist. auto disappearedNodes = nodeMap[swiftDeps]; // When done, changeDependencyKeys contains a list of keys that changed @@ -255,14 +229,10 @@ bool ModuleDepGraph::integrateSourceFileDepGraphNode( if (integrand->getKey().getKind() == NodeKind::externalDepend) return externalDependencies.insert(integrand->getKey().getName()).second; - // Since dependencies are modeled as arcs in both SourceFile and Module - // dependency graphs, no more integration need be done for a depends node. The - // information will be obtained front the using node's arcs. if (integrand->isDepends()) - return false; + return false; // dependency will be handled by the use node - StringRef swiftDepsOfSourceFileGraph = - g.getSwiftDepsOfJobThatProducedThisGraph(); + StringRef swiftDepsOfSourceFileGraph = g.getSwiftDepsFromSourceFileProvide(); auto changedAndUseNode = integrateSourceFileDeclNode( integrand, swiftDepsOfSourceFileGraph, preexistingMatch); recordWhatUseDependsUpon(g, integrand, changedAndUseNode.second); @@ -322,10 +292,6 @@ void ModuleDepGraph::recordWhatUseDependsUpon( void ModuleDepGraph::removeNode(ModuleDepGraphNode *n) { eraseNodeFromMap(n); - eraseNodeFromUsesByDef(n); - eraseNodeFromCurrentPathIfTracing(n); - eraseNodeFromDependencyPathToJobs(n); - delete n; } @@ -341,15 +307,6 @@ void ModuleDepGraph::forEachUseOf( return; for (const ModuleDepGraphNode *useNode : iter->second) fn(useNode); - // Add in implicit interface->implementation dependency - if (def->getKey().isInterface() && def->getSwiftDeps()) { - const auto &dk = def->getKey(); - const DependencyKey key(dk.getKind(), DeclAspect::interface, - dk.getContext(), dk.getName()); - if (const auto interfaceNode = - nodeMap.find(def->getSwiftDeps().getValue(), dk)) - fn(interfaceNode.getValue()); - } } void ModuleDepGraph::forEachNode( @@ -383,22 +340,31 @@ void ModuleDepGraph::forEachArc( // Could be faster by passing in a file, not a node, but we are trying for // generality. -void ModuleDepGraph::findDependentNodes( +void ModuleDepGraph::findDependentNodesAndRecordCascadingOnes( std::unordered_set &foundDependents, const ModuleDepGraphNode *definition) { - size_t pathLengthAfterArrival = traceArrival(definition); + size_t pathLengthAfterArrival = traceArrival(definition); // Moved this out of the following loop for effieciency. - assert(definition->getIsProvides() && "Should only call me for Decl nodes."); + assert(definition->getSwiftDeps().hasValue() && + "Should only call me for Decl nodes."); forEachUseOf(definition, [&](const ModuleDepGraphNode *u) { // Cycle recording and check. if (!foundDependents.insert(u).second) return; - // If this use also provides something, follow it - if (u->getIsProvides()) - findDependentNodes(foundDependents, u); + if (u->getKey().isInterface() && u->getSwiftDeps().hasValue()) { + // An interface depends on something. Thus, if that something changes + // the interface must be recompiled. But if an interface changes, then + // anything using that interface must also be recompiled. + // So, the job containing the interface "cascades", in other words + // whenever that job gets recompiled, anything depending on it + // (since we don't have interface-specific dependency info as of Dec. + // 2018) must be recompiled. + rememberThatJobCascades(u->getSwiftDeps().getValue()); + findDependentNodesAndRecordCascadingOnes(foundDependents, u); + } }); traceDeparture(pathLengthAfterArrival); } @@ -407,9 +373,9 @@ size_t ModuleDepGraph::traceArrival(const ModuleDepGraphNode *visitedNode) { if (!currentPathIfTracing.hasValue()) return 0; auto ¤tPath = currentPathIfTracing.getValue(); + recordDependencyPathToJob(currentPath, getJob(visitedNode->getSwiftDeps())); + currentPath.push_back(visitedNode); - const auto visitedSwiftDepsIfAny = visitedNode->getSwiftDeps(); - recordDependencyPathToJob(currentPath, getJob(visitedSwiftDepsIfAny)); return currentPath.size(); } @@ -428,9 +394,8 @@ void ModuleDepGraph::traceDeparture(size_t pathLengthAfterArrival) { currentPath.pop_back(); } -// ============================================================================= -// MARK: Emitting Dot file for ModuleDepGraph -// ============================================================================= +// Emitting Dot file for ModuleDepGraph +// =========================================== void ModuleDepGraph::emitDotFileForJob(DiagnosticEngine &diags, const Job *job) { @@ -439,8 +404,7 @@ void ModuleDepGraph::emitDotFileForJob(DiagnosticEngine &diags, void ModuleDepGraph::emitDotFile(DiagnosticEngine &diags, StringRef baseName) { unsigned seqNo = dotFileSequenceNumber[baseName]++; - std::string fullName = - baseName.str() + "-post-integration." + std::to_string(seqNo) + ".dot"; + std::string fullName = baseName.str() + "." + std::to_string(seqNo) + ".dot"; withOutputFile(diags, fullName, [&](llvm::raw_ostream &out) { emitDotFile(out); return false; @@ -458,8 +422,8 @@ void ModuleDepGraph::emitDotFile(llvm::raw_ostream &out) { void ModuleDepGraphNode::dump() const { DepGraphNode::dump(); - if (getIsProvides()) - llvm::errs() << " swiftDeps: <" << getSwiftDepsOfProvides() << ">\n"; + if (getSwiftDeps().hasValue()) + llvm::errs() << " swiftDeps: <" << getSwiftDeps().getValue() << ">\n"; else llvm::errs() << " no swiftDeps\n"; } @@ -514,7 +478,9 @@ void ModuleDepGraph::verifyNodeIsUniqueWithinSubgraph( assert(submapIndex < nodesSeenInNodeMap.size() && "submapIndex is out of bounds."); auto iterInserted = nodesSeenInNodeMap[submapIndex][n->getKey()].insert( - std::make_pair(n->getSwiftDepsForMapKey(), n)); + std::make_pair(n->getSwiftDeps().hasValue() ? n->getSwiftDeps().getValue() + : std::string(), + n)); if (!iterInserted.second) { llvm_unreachable("duplicate driver keys"); } @@ -557,75 +523,23 @@ void ModuleDepGraph::verifyEachJobInGraphIsTracked() const { }); } -/// Dump the path(s) that led to \p node. -/// TODO: break up +bool ModuleDepGraph::emitDotFileAndVerify(DiagnosticEngine &diags) { + if (!driverDotFileBasePath.empty()) + emitDotFile(diags, driverDotFileBasePath); + return verify(); +} + +/// Dump the path that led to \p node. +/// TODO: make output more like existing system's void ModuleDepGraph::printPath(raw_ostream &out, const driver::Job *jobToBeBuilt) const { assert(currentPathIfTracing.hasValue() && "Cannot print paths of paths weren't tracked."); - - for (auto paths = dependencyPathsToJobs.find(jobToBeBuilt); - paths != dependencyPathsToJobs.end() && paths->first == jobToBeBuilt; - ++paths) { - const auto &path = paths->second; - bool first = true; - out << "\t"; - for (const ModuleDepGraphNode *n : path) { - if (first) - first = false; - else - out << " -> "; - - const StringRef providerName = getProvidingFilename(n->getSwiftDeps()); - printOneNodeOfPath(out, n->getKey(), providerName); - } - out << "\n"; - } -} - -StringRef ModuleDepGraph::getProvidingFilename( - const Optional swiftDeps) const { - if (!swiftDeps) - return "getFirstSwiftPrimaryInput()); - // FineGrainedDependencyGraphTests work with simulated jobs with empty - // input names. - return !inputName.empty() ? inputName : StringRef(swiftDeps.getValue()); -} - -void ModuleDepGraph::printOneNodeOfPath(raw_ostream &out, - const DependencyKey &key, - const StringRef filename) { - switch (key.getKind()) { - case NodeKind::topLevel: - out << key.aspectName() << " of top-level name '" << key.humanReadableName() - << "' in " << filename; - break; - case NodeKind::nominal: - out << key.aspectName() << " of type '" << key.humanReadableName() - << "' in " << filename; - break; - case NodeKind::potentialMember: - out << key.aspectName() << " of non-private members '" - << key.humanReadableName() << "' in " << filename; - break; - case NodeKind::member: - out << key.aspectName() << " of member '" << key.humanReadableName() - << "' in " << filename; - break; - case NodeKind::dynamicLookup: - out << key.aspectName() << " of AnyObject member '" - << key.humanReadableName() << "' in " << filename; - break; - case NodeKind::externalDepend: - out << filename << " depends on " << key.aspectName() << " of module '" - << key.humanReadableName() << "'"; - break; - case NodeKind::sourceFileProvide: - out << key.aspectName() << " of source file " << key.humanReadableName(); - break; - default: - llvm_unreachable("unknown NodeKind"); + auto const allPaths = dependencyPathsToJobs.find(jobToBeBuilt); + if (allPaths == dependencyPathsToJobs.cend()) + return; + for (const auto *n : allPaths->second) { + out << n->humanReadableName() << "\n"; } + out << "\n"; } diff --git a/lib/Driver/Job.cpp b/lib/Driver/Job.cpp index c6362335de7d8..a4e21ad0c9b6b 100644 --- a/lib/Driver/Job.cpp +++ b/lib/Driver/Job.cpp @@ -25,11 +25,6 @@ using namespace swift; using namespace swift::driver; -CommandOutput::CommandOutput(StringRef dummyBase, OutputFileMap &dummyOFM) - : Inputs({CommandInputPair(dummyBase, "")}), DerivedOutputMap(dummyOFM) { - setAdditionalOutputForType(file_types::TY_SwiftDeps, dummyBase); -} - StringRef CommandOutput::getOutputForInputAndType(StringRef PrimaryInputFile, file_types::ID Type) const { if (Type == file_types::TY_Nothing) diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index 87398eecadb9b..c8b8ab791692b 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -178,6 +178,26 @@ file_types::ID ToolChain::lookupTypeForExtension(StringRef Ext) const { return file_types::lookupTypeForExtension(Ext); } +/// Return a _single_ TY_Swift InputAction, if one exists; +/// if 0 or >1 such inputs exist, return nullptr. +static const InputAction *findSingleSwiftInput(const CompileJobAction *CJA) { + auto Inputs = CJA->getInputs(); + const InputAction *IA = nullptr; + for (auto const *I : Inputs) { + if (auto const *S = dyn_cast(I)) { + if (S->getType() == file_types::TY_Swift) { + if (IA == nullptr) { + IA = S; + } else { + // Already found one, two is too many. + return nullptr; + } + } + } + } + return IA; +} + static bool jobsHaveSameExecutableNames(const Job *A, const Job *B) { // Jobs that get here (that are derived from CompileJobActions) should always // have the same executable name -- it should always be SWIFT_EXECUTABLE_NAME @@ -223,7 +243,7 @@ bool ToolChain::jobIsBatchable(const Compilation &C, const Job *A) const { if (C.OnlyOneDependencyFile && A->getOutput().hasAdditionalOutputForType(file_types::TY_Dependencies)) return false; - return CJActA->findSingleSwiftInput() != nullptr; + return findSingleSwiftInput(CJActA) != nullptr; } bool ToolChain::jobsAreBatchCombinable(const Compilation &C, const Job *A, @@ -285,6 +305,33 @@ mergeBatchInputs(ArrayRef jobs, return false; } +/// Unfortunately the success or failure of a Swift compilation is currently +/// sensitive to the order in which files are processed, at least in terms of +/// the order of processing extensions (and likely other ways we haven't +/// discovered yet). So long as this is true, we need to make sure any batch job +/// we build names its inputs in an order that's a subsequence of the sequence +/// of inputs the driver was initially invoked with. +static void +sortJobsToMatchCompilationInputs(ArrayRef unsortedJobs, + SmallVectorImpl &sortedJobs, + Compilation &C) { + llvm::DenseMap jobsByInput; + for (const Job *J : unsortedJobs) { + const CompileJobAction *CJA = cast(&J->getSource()); + const InputAction *IA = findSingleSwiftInput(CJA); + auto R = + jobsByInput.insert(std::make_pair(IA->getInputArg().getValue(), J)); + assert(R.second); + (void)R; + } + for (const InputPair &P : C.getInputFiles()) { + auto I = jobsByInput.find(P.second->getValue()); + if (I != jobsByInput.end()) { + sortedJobs.push_back(I->second); + } + } +} + /// Construct a \c BatchJob by merging the constituent \p jobs' CommandOutput, /// input \c Job and \c Action members. Call through to \c constructInvocation /// on \p BatchJob, to build the \c InvocationInfo. @@ -296,7 +343,7 @@ ToolChain::constructBatchJob(ArrayRef unsortedJobs, return nullptr; llvm::SmallVector sortedJobs; - C.sortJobsToMatchCompilationInputs(unsortedJobs, sortedJobs); + sortJobsToMatchCompilationInputs(unsortedJobs, sortedJobs, C); // Synthetic OutputInfo is a slightly-modified version of the initial // compilation's OI. diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 0cb8248242a83..087d819cbb9c7 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -230,8 +230,6 @@ static void addCommonFrontendArgs(const ToolChain &TC, const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_AssumeSingleThreaded); inputArgs.AddLastArg(arguments, options::OPT_enable_fine_grained_dependencies); - inputArgs.AddLastArg(arguments, - options::OPT_disable_fine_grained_dependencies); inputArgs.AddLastArg(arguments, options::OPT_fine_grained_dependency_include_intrafile); inputArgs.AddLastArg(arguments, options::OPT_package_description_version); @@ -643,12 +641,6 @@ void ToolChain::JobContext::addFrontendCommandLineInputArguments( if ((!isPrimary || usePrimaryFileList) && !useFileList) arguments.push_back(inputName); } - if (C.getEnableFineGrainedDependencies()) - arguments.push_back("-enable-fine-grained-dependencies"); - - if (Args.hasArg( - options::OPT_emit_fine_grained_dependency_sourcefile_dot_files)) - arguments.push_back("-emit-fine-grained-dependency-sourcefile-dot-files"); } void ToolChain::JobContext::addFrontendSupplementaryOutputArguments( diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 4a4b47653ed8e..dd0af19225e90 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -359,13 +359,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.BuildSyntaxTree = true; Opts.VerifySyntaxTree = true; } - - Opts.EnableFineGrainedDependencies = - Args.hasFlag(options::OPT_enable_fine_grained_dependencies, - options::OPT_disable_fine_grained_dependencies, false); - - if (Args.hasArg(OPT_emit_fine_grained_dependency_sourcefile_dot_files)) - Opts.EmitFineGrainedDependencySourcefileDotFiles = true; + + if (Args.hasArg(OPT_enable_fine_grained_dependencies)) + Opts.EnableFineGrainedDependencies = true; if (Args.hasArg(OPT_fine_grained_dependency_include_intrafile)) Opts.FineGrainedDependenciesIncludeIntrafileOnes = true; diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 42c02a84dc50e..5ae5755ed69e1 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -979,9 +979,7 @@ static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( if (Invocation.getLangOptions().EnableFineGrainedDependencies) (void)fine_grained_dependencies::emitReferenceDependencies( Instance.getASTContext().Diags, SF, - *Instance.getDependencyTracker(), referenceDependenciesFilePath, - Invocation.getLangOptions() - .EmitFineGrainedDependencySourcefileDotFiles); + *Instance.getDependencyTracker(), referenceDependenciesFilePath); else (void)emitReferenceDependencies(Instance.getASTContext().Diags, SF, *Instance.getDependencyTracker(), diff --git a/test/Driver/batch_mode_dependencies_make_wrong_order.swift b/test/Driver/batch_mode_dependencies_make_wrong_order.swift index 8e26f32a3e87e..6888ee6461564 100644 --- a/test/Driver/batch_mode_dependencies_make_wrong_order.swift +++ b/test/Driver/batch_mode_dependencies_make_wrong_order.swift @@ -16,13 +16,13 @@ // RUN: cd %t && %swiftc_driver -enable-batch-mode -incremental -output-file-map %S/Inputs/abcd_filemap.yaml -module-name main -j 1 d.swift c.swift b.swift a.swift main.swift -driver-show-incremental -driver-show-job-lifecycle >%t/out.txt 2>&1 // RUN: %FileCheck %s <%t/out.txt // -// Check that we saw invalidation happen in command-line argument order -// CHECK: Queuing because of dependencies discovered later: {compile: d.o <= d.swift} -// CHECK: Queuing because of dependencies discovered later: {compile: c.o <= c.swift} +// Check that we saw invalidation happen in alphabetic order // CHECK: Queuing because of dependencies discovered later: {compile: b.o <= b.swift} -// CHECK: Batchable: {compile: d.o <= d.swift} -// CHECK: Batchable: {compile: c.o <= c.swift} +// CHECK: Queuing because of dependencies discovered later: {compile: c.o <= c.swift} +// CHECK: Queuing because of dependencies discovered later: {compile: d.o <= d.swift} // CHECK: Batchable: {compile: b.o <= b.swift} +// CHECK: Batchable: {compile: c.o <= c.swift} +// CHECK: Batchable: {compile: d.o <= d.swift} // -// Check that we still issued the job in reverse-alphabetic order +// But check that we still issued the job in reverse-alphabetic order // CHECK: Adding batch job to task queue: {compile: d.o c.o b.o <= d.swift c.swift b.swift} diff --git a/unittests/Driver/CMakeLists.txt b/unittests/Driver/CMakeLists.txt index fb50d3d15a858..425fa364b84e2 100644 --- a/unittests/Driver/CMakeLists.txt +++ b/unittests/Driver/CMakeLists.txt @@ -1,11 +1,8 @@ add_swift_unittest(SwiftDriverTests CoarseGrainedDependencyGraphTests.cpp - FineGrainedDependencyGraphTests.cpp ) target_link_libraries(SwiftDriverTests PRIVATE swiftDriver - swiftClangImporter - swiftAST ) diff --git a/unittests/Driver/FineGrainedDependencyGraphTests.cpp b/unittests/Driver/FineGrainedDependencyGraphTests.cpp deleted file mode 100644 index 953ac78885839..0000000000000 --- a/unittests/Driver/FineGrainedDependencyGraphTests.cpp +++ /dev/null @@ -1,833 +0,0 @@ -#include "swift/Basic/ReferenceDependencyKeys.h" -#include "swift/Driver/CoarseGrainedDependencyGraph.h" -#include "swift/Driver/FineGrainedDependencyDriverGraph.h" -#include "swift/Driver/Job.h" -#include "gtest/gtest.h" - -// This file adapts the unit tests from the older, coarse-grained, dependency -// graph to the new fine-grained graph. - -// \c markTransitive and \c markExternal may include jobs in their result -// that would be excluded in the coarse-grained graph. But since these will be -// jobs that have already been scheduled, downstream mechanisms will filter -// them out. - -using namespace swift; -using LoadResult = CoarseGrainedDependencyGraphImpl::LoadResult; -using namespace reference_dependency_keys; -using namespace fine_grained_dependencies; -using Job = driver::Job; - -/// Initial underscore makes non-cascading, on member means private. -static LoadResult -simulateLoad(ModuleDepGraph &dg, const Job *cmd, - llvm::StringMap> simpleNames, - llvm::StringMap>> - compoundNames = {}, - const bool includePrivateDeps = true, - const bool hadCompilationError = false) { - StringRef swiftDeps = - cmd->getOutput().getAdditionalOutputForType(file_types::TY_SwiftDeps); - assert(!swiftDeps.empty()); - StringRef interfaceHash = swiftDeps; - auto sfdg = SourceFileDepGraph::simulateLoad( - swiftDeps, includePrivateDeps, hadCompilationError, interfaceHash, - simpleNames, compoundNames); - - return dg.loadFromSourceFileDepGraph(cmd, sfdg); -} - -LLVM_ATTRIBUTE_UNUSED -static std::vector -printForDebugging(std::vector jobs) { - llvm::errs() << "\nprintForDebugging: "; - for (auto *j : jobs) { - const auto swiftDeps = - j->getOutput().getAdditionalOutputForType(file_types::TY_SwiftDeps); - assert(!swiftDeps.empty()); - llvm::errs() << "job" << swiftDeps << ", "; - } - llvm::errs() << "\n"; - return jobs; -} - -static OutputFileMap OFM; - -static Job job0(OFM, "0"), job1(OFM, "1"), job2(OFM, "2"), job3(OFM, "3"), - job4(OFM, "4"), job5(OFM, "5"), job6(OFM, "6"), job7(OFM, "7"), - job8(OFM, "8"), job9(OFM, "9"), job10(OFM, "10"), job11(OFM, "11"), - job12(OFM, "12"); - -template -static bool contains(const Range &range, const T &value) { - return std::find(std::begin(range), std::end(range), value) != - std::end(range); -} - -TEST(ModuleDepGraph, BasicLoad) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{dependsTopLevel, {"a", "b"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"c", "d"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job2, {{providesTopLevel, {"e", "f"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job3, {{providesNominal, {"g", "h"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job4, {{providesDynamicLookup, {"i", "j"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job5, {{dependsDynamicLookup, {"k", "l"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job6, {}, - {{providesMember, {{"m", "mm"}, {"n", "nn"}}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job7, {}, - {{dependsMember, {{"o", "oo"}, {"p", "pp"}}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job8, {{dependsExternal, {"/foo", "/bar"}}}), - LoadResult::AffectsDownstream); - - EXPECT_EQ(simulateLoad(graph, &job9, - {{providesNominal, {"a", "b"}}, - {providesTopLevel, {"b", "c"}}, - {dependsNominal, {"c", "d"}}, - {dependsTopLevel, {"d", "a"}}}), - LoadResult::AffectsDownstream); -} - -TEST(ModuleDepGraph, IndependentNodes) { - ModuleDepGraph graph; - - EXPECT_EQ( - simulateLoad(graph, &job0, - {{dependsTopLevel, {"a"}}, {providesTopLevel, {"a0"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ( - simulateLoad(graph, &job1, - {{dependsTopLevel, {"b"}}, {providesTopLevel, {"b0"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ( - simulateLoad(graph, &job2, - {{dependsTopLevel, {"c"}}, {providesTopLevel, {"c0"}}}), - LoadResult::AffectsDownstream); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_FALSE(graph.isMarked(&job1)); - EXPECT_FALSE(graph.isMarked(&job2)); - - // Mark 0 again -- should be no change. - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_FALSE(graph.isMarked(&job1)); - EXPECT_FALSE(graph.isMarked(&job2)); - - EXPECT_EQ(0u, graph.markTransitive(&job2).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_FALSE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); - - EXPECT_EQ(0u, graph.markTransitive(&job1).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); -} - -TEST(ModuleDepGraph, IndependentDepKinds) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, - {{dependsNominal, {"a"}}, {providesNominal, {"b"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, - {{dependsTopLevel, {"b"}}, {providesTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_FALSE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, IndependentDepKinds2) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, - {{dependsNominal, {"a"}}, {providesNominal, {"b"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, - {{dependsTopLevel, {"b"}}, {providesTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - - EXPECT_EQ(0u, graph.markTransitive(&job1).size()); - EXPECT_FALSE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, IndependentMembers) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {}, {{providesMember, {{"a", "aa"}}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {}, {{dependsMember, {{"a", "bb"}}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job2, {}, {{dependsMember, {{"a", ""}}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job3, {}, {{dependsMember, {{"b", "aa"}}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job4, {}, {{dependsMember, {{"b", "bb"}}}}), - LoadResult::AffectsDownstream); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_FALSE(graph.isMarked(&job1)); - EXPECT_FALSE(graph.isMarked(&job2)); - EXPECT_FALSE(graph.isMarked(&job3)); - EXPECT_FALSE(graph.isMarked(&job4)); -} - -TEST(ModuleDepGraph, SimpleDependent) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesTopLevel, {"a", "b", "c"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{dependsTopLevel, {"x", "b", "z"}}}), - LoadResult::AffectsDownstream); - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job1, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependentReverse) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{dependsTopLevel, {"a", "b", "c"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{providesTopLevel, {"x", "b", "z"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job1); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job0, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependent2) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a", "b", "c"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"x", "b", "z"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job1, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependent3) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, - {{providesNominal, {"a"}}, {providesTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"a"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job1, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependent4) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, - {{dependsNominal, {"a"}}, {dependsTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job1, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependent5) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, - {{providesNominal, {"a"}}, {providesTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, - {{dependsNominal, {"a"}}, {dependsTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job1, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependent6) { - ModuleDepGraph graph; - - EXPECT_EQ( - simulateLoad(graph, &job0, {{providesDynamicLookup, {"a", "b", "c"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ( - simulateLoad(graph, &job1, {{dependsDynamicLookup, {"x", "b", "z"}}}), - LoadResult::AffectsDownstream); - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job1, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependentMember) { - ModuleDepGraph graph; - - EXPECT_EQ( - simulateLoad(graph, &job0, {}, - {{providesMember, {{"a", "aa"}, {"b", "bb"}, {"c", "cc"}}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ( - simulateLoad(graph, &job1, {}, - {{dependsMember, {{"x", "xx"}, {"b", "bb"}, {"z", "zz"}}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job1, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, MultipleDependentsSame) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a", "b", "c"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"x", "b", "z"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"q", "b", "s"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(2u, marked.size()); - EXPECT_TRUE(contains(marked, &job1)); - EXPECT_TRUE(contains(marked, &job2)); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); -} - -TEST(ModuleDepGraph, MultipleDependentsDifferent) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a", "b", "c"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"x", "b", "z"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"q", "r", "c"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(2u, marked.size()); - EXPECT_TRUE(contains(marked, &job1)); - EXPECT_TRUE(contains(marked, &job2)); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); -} - -TEST(ModuleDepGraph, ChainedDependents) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a", "b", "c"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ( - simulateLoad(graph, &job1, - {{dependsNominal, {"x", "b"}}, {providesNominal, {"z"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"z"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(2u, marked.size()); - EXPECT_TRUE(contains(marked, &job1)); - EXPECT_TRUE(contains(marked, &job2)); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); -} - -TEST(ModuleDepGraph, ChainedNoncascadingDependents) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a", "b", "c"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ( - simulateLoad(graph, &job1, - {{dependsNominal, {"x", "b"}}, {providesNominal, {"_z"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"_z"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(2u, marked.size()); - EXPECT_TRUE(contains(marked, &job1)); - EXPECT_TRUE(contains(marked, &job2)); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); -} - -TEST(ModuleDepGraph, ChainedNoncascadingDependents2) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesTopLevel, {"a", "_b", "c"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ( - simulateLoad(graph, &job1, - {{dependsTopLevel, {"x", "_b"}}, {providesNominal, {"z"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"z"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_TRUE(contains(marked, &job1)); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_FALSE(graph.isMarked(&job2)); -} - -TEST(ModuleDepGraph, MarkTwoNodes) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesTopLevel, {"a", "b"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, - {{dependsTopLevel, {"a"}}, {providesTopLevel, {"z"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job2, {{dependsTopLevel, {"z"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ( - simulateLoad(graph, &job10, - {{providesTopLevel, {"y", "z"}}, {dependsTopLevel, {"q"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job11, {{dependsTopLevel, {"y"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job12, - {{dependsTopLevel, {"q"}}, {providesTopLevel, {"q"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(2u, marked.size()); - EXPECT_TRUE(contains(marked, &job1)); - EXPECT_TRUE(contains(marked, &job2)); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); - EXPECT_FALSE(graph.isMarked(&job10)); - EXPECT_FALSE(graph.isMarked(&job11)); - EXPECT_FALSE(graph.isMarked(&job12)); - - { - auto marked = graph.markTransitive(&job10); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job11, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); - EXPECT_TRUE(graph.isMarked(&job10)); - EXPECT_TRUE(graph.isMarked(&job11)); - EXPECT_FALSE(graph.isMarked(&job12)); -} - -TEST(ModuleDepGraph, MarkOneNodeTwice) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"b"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job1, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_FALSE(graph.isMarked(&job2)); - - // Reload 0. - EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"b"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job2, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); -} - -TEST(ModuleDepGraph, MarkOneNodeTwice2) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"b"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job1, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_FALSE(graph.isMarked(&job2)); - - // Reload 0. - EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a", "b"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job2, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); -} - -TEST(ModuleDepGraph, ReloadDetectsChange) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesNominal, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{dependsNominal, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job2, {{dependsNominal, {"b"}}}), - LoadResult::AffectsDownstream); - - EXPECT_EQ(0u, graph.markTransitive(&job1).size()); - EXPECT_FALSE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_FALSE(graph.isMarked(&job2)); - - // Reload 1. - EXPECT_EQ(simulateLoad(graph, &job1, - {{dependsNominal, {"a"}}, {providesNominal, {"b"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job2, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); - - // Re-mark 1. - EXPECT_EQ(0u, graph.markTransitive(&job1).size()); - - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); -} - -TEST(ModuleDepGraph, DependencyLoops) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, - {{providesTopLevel, {"a", "b", "c"}}, - {dependsTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, - {{providesTopLevel, {"x"}}, - {dependsTopLevel, {"x", "b", "z"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job2, {{dependsTopLevel, {"x"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(2u, marked.size()); - EXPECT_TRUE(contains(marked, &job1)); - EXPECT_TRUE(contains(marked, &job2)); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - EXPECT_TRUE(graph.isMarked(&job2)); -} - -TEST(ModuleDepGraph, MarkIntransitive) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesTopLevel, {"a", "b", "c"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{dependsTopLevel, {"x", "b", "z"}}}), - LoadResult::AffectsDownstream); - - EXPECT_TRUE(graph.markIntransitive(&job0)); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_FALSE(graph.isMarked(&job1)); - - { - auto marked = graph.markTransitive(&job0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job1, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, MarkIntransitiveTwice) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesTopLevel, {"a", "b", "c"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{dependsTopLevel, {"x", "b", "z"}}}), - LoadResult::AffectsDownstream); - - EXPECT_TRUE(graph.markIntransitive(&job0)); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_FALSE(graph.isMarked(&job1)); - - EXPECT_FALSE(graph.markIntransitive(&job0)); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_FALSE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, MarkIntransitiveThenIndirect) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{providesTopLevel, {"a", "b", "c"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ(simulateLoad(graph, &job1, {{dependsTopLevel, {"x", "b", "z"}}}), - LoadResult::AffectsDownstream); - - EXPECT_TRUE(graph.markIntransitive(&job1)); - EXPECT_FALSE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - - EXPECT_EQ(0u, graph.markTransitive(&job0).size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, SimpleExternal) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{dependsExternal, {"/foo", "/bar"}}}), - LoadResult::AffectsDownstream); - - EXPECT_TRUE(contains(graph.getExternalDependencies(), "/foo")); - EXPECT_TRUE(contains(graph.getExternalDependencies(), "/bar")); - - EXPECT_EQ(1u, graph.markExternal("/foo").size()); - EXPECT_TRUE(graph.isMarked(&job0)); - - EXPECT_EQ(0u, graph.markExternal("/foo").size()); - EXPECT_TRUE(graph.isMarked(&job0)); -} - -TEST(ModuleDepGraph, SimpleExternal2) { - ModuleDepGraph graph; - - EXPECT_EQ(simulateLoad(graph, &job0, {{dependsExternal, {"/foo", "/bar"}}}), - LoadResult::AffectsDownstream); - - EXPECT_EQ(1u, graph.markExternal("/bar").size()); - EXPECT_TRUE(graph.isMarked(&job0)); - - EXPECT_EQ(0u, graph.markExternal("/bar").size()); - EXPECT_TRUE(graph.isMarked(&job0)); -} - -TEST(ModuleDepGraph, ChainedExternal) { - ModuleDepGraph graph; - - EXPECT_EQ( - simulateLoad(graph, &job0, - {{dependsExternal, {"/foo"}}, {providesTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ( - simulateLoad(graph, &job1, - {{dependsExternal, {"/bar"}}, {dependsTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - - EXPECT_TRUE(contains(graph.getExternalDependencies(), "/foo")); - EXPECT_TRUE(contains(graph.getExternalDependencies(), "/bar")); - - EXPECT_EQ(2u, graph.markExternal("/foo").size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - - EXPECT_EQ(0u, graph.markExternal("/foo").size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, ChainedExternalReverse) { - ModuleDepGraph graph; - - EXPECT_EQ( - simulateLoad(graph, &job0, - {{dependsExternal, {"/foo"}}, {providesTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ( - simulateLoad(graph, &job1, - {{dependsExternal, {"/bar"}}, {dependsTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - - { - auto marked = graph.markExternal("/bar"); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job1, marked.front()); - } - EXPECT_FALSE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - - EXPECT_EQ(0u, graph.markExternal("/bar").size()); - EXPECT_FALSE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); - - { - auto marked = graph.markExternal("/foo"); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(&job0, marked.front()); - } - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_TRUE(graph.isMarked(&job1)); -} - -TEST(ModuleDepGraph, ChainedExternalPreMarked) { - ModuleDepGraph graph; - - EXPECT_EQ( - simulateLoad(graph, &job0, - {{dependsExternal, {"/foo"}}, {providesTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - EXPECT_EQ( - simulateLoad(graph, &job1, - {{dependsExternal, {"/bar"}}, {dependsTopLevel, {"a"}}}), - LoadResult::AffectsDownstream); - - graph.markIntransitive(&job0); - - EXPECT_EQ(0u, graph.markExternal("/foo").size()); - EXPECT_TRUE(graph.isMarked(&job0)); - EXPECT_FALSE(graph.isMarked(&job1)); -} From 8ad49ffbc5746cd25653f83ad90b48a439511396 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 9 Jan 2020 19:53:39 -0800 Subject: [PATCH 386/478] [master-rebranch] Change a check to be CHECK-DAG to avoid issues around these two attributes being printed in the wrong order. This makes this test pass after we rebranch and before. --- test/IRGen/generic_metatypes.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/IRGen/generic_metatypes.swift b/test/IRGen/generic_metatypes.swift index e454e0fd5c6c1..f43d5cfbfa266 100644 --- a/test/IRGen/generic_metatypes.swift +++ b/test/IRGen/generic_metatypes.swift @@ -145,5 +145,5 @@ func makeGenericMetatypes() { // CHECK-NOT: call void @llvm.lifetime.end // CHECK: ret %swift.metadata_response -// CHECK: attributes [[NOUNWIND_READNONE]] = { nounwind readnone } -// CHECK: attributes [[NOUNWIND_OPT]] = { noinline nounwind "frame-pointer"="none" "target-cpu" +// CHECK-DAG: attributes [[NOUNWIND_READNONE]] = { nounwind readnone } +// CHECK-DAG: attributes [[NOUNWIND_OPT]] = { noinline nounwind "frame-pointer"="none" "target-cpu" From f883fff0b8e77d1f2e8ab02431b8f3341f5f4b4b Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 9 Jan 2020 21:30:56 -0500 Subject: [PATCH 387/478] ClangImporter: Fix quadratic behavior with property overrides We would add all imported members to a per-nominal vector, and then perform shadowing checks on the entire list, followed by visiting each one to look for a matching member with the right name. We were spending a lot of time inside this function as a result. Instead, change Impl.MembersForNominal to store a mapping from names to lists of members having that name, changing this into an O(1) lookup. Fixes . --- lib/ClangImporter/ImportDecl.cpp | 77 ++++++++++++++++++-------------- lib/ClangImporter/ImporterImpl.h | 11 +++-- 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index cc6ff4324d3cd..0d2b5f31555ef 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2149,45 +2149,46 @@ namespace { } }; - /// Search the member tables for this class and its superclasses and try to identify the nearest VarDecl - /// that serves as a base for an override. We have to do this ourselves because Objective-C has no - /// semantic notion of overrides, and freely allows users to refine the type of any member property in a - /// derived class. + /// Search the member tables for this class and its superclasses and try to + /// identify the nearest VarDecl that serves as a base for an override. We + /// have to do this ourselves because Objective-C has no semantic notion of + /// overrides, and freely allows users to refine the type of any member + /// property in a derived class. /// - /// The override must be the nearest possible one so there are not breaks in the override chain. That is, - /// suppose C refines B refines A and each successively redeclares a member with a different type. It - /// should be the case that the nearest override from C is B and from B is A. If the override point from - /// C were A, then B would record an override on A as well and we would introduce a semantic ambiguity. + /// The override must be the nearest possible one so there are not breaks + /// in the override chain. That is, suppose C refines B refines A and each + /// successively redeclares a member with a different type. It should be + /// the case that the nearest override from C is B and from B is A. If the + /// override point from C were A, then B would record an override on A as + /// well and we would introduce a semantic ambiguity. /// - /// There is also a special case for finding a method that stomps over a getter. If this is the case and no - /// override point is identified, we will not import the property to force users to explicitly call the method. + /// There is also a special case for finding a method that stomps over a + /// getter. If this is the case and no override point is identified, we will + /// not import the property to force users to explicitly call the method. static std::pair - identifyNearestOverridenDecl(ClangImporter::Implementation &Impl, - DeclContext *dc, - const clang::ObjCPropertyDecl *decl, - Identifier name, - ClassDecl *subject) { + identifyNearestOverriddenDecl(ClangImporter::Implementation &Impl, + DeclContext *dc, + const clang::ObjCPropertyDecl *decl, + Identifier name, + ClassDecl *subject) { bool foundMethod = false; for (; subject; (subject = subject->getSuperclassDecl())) { llvm::SmallVector lookup; - auto found = Impl.MembersForNominal.find(subject); - if (found != Impl.MembersForNominal.end()) { - lookup.append(found->second.begin(), found->second.end()); - namelookup::pruneLookupResultSet(dc, NL_QualifiedDefault, lookup); + auto foundNames = Impl.MembersForNominal.find(subject); + if (foundNames != Impl.MembersForNominal.end()) { + auto foundDecls = foundNames->second.find(name); + if (foundDecls != foundNames->second.end()) { + lookup.append(foundDecls->second.begin(), foundDecls->second.end()); + } } for (auto *&result : lookup) { - // Skip declarations that don't match the name we're looking for. - if (result->getBaseName() != name) - continue; - if (auto *fd = dyn_cast(result)) { if (fd->isInstanceMember() != decl->isInstanceProperty()) continue; - if (fd->getFullName().getArgumentNames().empty()) { - foundMethod = true; - } + assert(fd->getFullName().getArgumentNames().empty()); + foundMethod = true; } else { auto *var = cast(result); if (var->isInstanceMember() != decl->isInstanceProperty()) @@ -2231,11 +2232,11 @@ namespace { return getVersion() == getActiveSwiftVersion(); } - template - T *recordMemberInContext(DeclContext *dc, T *member) { + void recordMemberInContext(DeclContext *dc, ValueDecl *member) { assert(member && "Attempted to record null member!"); - Impl.MembersForNominal[dc->getSelfNominalTypeDecl()].push_back(member); - return member; + auto *nominal = dc->getSelfNominalTypeDecl(); + auto name = member->getBaseName(); + Impl.MembersForNominal[nominal][name].push_back(member); } /// Import the name of the given entity. @@ -3798,7 +3799,7 @@ namespace { if (correctSwiftName) markAsVariant(result, *correctSwiftName); - return recordMemberInContext(dc, result); + return result; } void finishFuncDecl(const clang::FunctionDecl *decl, @@ -4408,7 +4409,14 @@ namespace { } } - return recordMemberInContext(dc, result); + // We only care about recording methods with no arguments here, because + // they can shadow imported properties. + if (!isa(result) && + result->getFullName().getArgumentNames().empty()) { + recordMemberInContext(dc, result); + } + + return result; } public: @@ -5070,7 +5078,7 @@ namespace { bool foundMethod = false; std::tie(overridden, foundMethod) - = identifyNearestOverridenDecl(Impl, dc, decl, name, subject); + = identifyNearestOverriddenDecl(Impl, dc, decl, name, subject); if (foundMethod && !overridden) return nullptr; @@ -5165,7 +5173,8 @@ namespace { if (correctSwiftName) markAsVariant(result, *correctSwiftName); - return recordMemberInContext(dc, result); + recordMemberInContext(dc, result); + return result; } Decl * diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 11102dec231b6..8f1bd96b4000f 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -447,10 +447,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation // Mapping from imported types to their raw value types. llvm::DenseMap RawTypes; - /// Keep track of all member declarations that have been imported into a nominal type. - llvm::DenseMap> - MembersForNominal; - clang::CompilerInstance *getClangInstance() { return Instance.get(); } @@ -500,6 +496,13 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation llvm::DenseMap> ConstructorsForNominal; + /// Keep track of all member declarations that have been imported into + /// a nominal type. + llvm::DenseMap>> + MembersForNominal; + /// Keep track of the nested 'Code' enum for imported error wrapper /// structs. llvm::DenseMap ErrorCodeEnums; From 837e331f16d19ba0f4c8ec1e2b962a32d63be137 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 9 Jan 2020 21:48:25 -0800 Subject: [PATCH 388/478] IRGen: handle ASAN better with importing on ELF ELF's lack of linker directives is worked around by a custom section (`.swift1_autolink_entries`). This is metadata that is not intended to be emitted into the linked binary. A previous change introduced the use of a module (global) assembly gadget to discard the section. However, this interacts poorly with ASAN which would instrument the section, resulting in a strong reference. This reference would persist to a discarded symbol. lld would object to this. Blacklist the symbol to ensure that ASAN + autolinking can co-exist. --- lib/IRGen/IRGenModule.cpp | 2 ++ test/IRGen/ELF-remove-autolink-section.swift | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 56d802ed1f3d1..6b78becd2c1d3 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -50,6 +50,7 @@ #include "llvm/Support/MD5.h" #include "ConformanceDescription.h" +#include "GenDecl.h" #include "GenEnum.h" #include "GenIntegerLiteral.h" #include "GenType.h" @@ -1190,6 +1191,7 @@ void IRGenModule::emitAutolinkInfo() { var->setSection(".swift1_autolink_entries"); var->setAlignment(getPointerAlignment().getValue()); + disableAddressSanitizer(*this, var); addUsedGlobal(var); } diff --git a/test/IRGen/ELF-remove-autolink-section.swift b/test/IRGen/ELF-remove-autolink-section.swift index d74d51e10d867..3a2b30ff3c415 100644 --- a/test/IRGen/ELF-remove-autolink-section.swift +++ b/test/IRGen/ELF-remove-autolink-section.swift @@ -13,5 +13,15 @@ print("Hi from Swift!") // ELF: module asm ".section .swift1_autolink_entries,\220x80000000\22" + +// Find the metadata entry for the blacklisting of the metadata symbol +// Ensure that it is in the ASAN metadata + +// ELF-DAG: !llvm.asan.globals = !{ +// ELF-SAME: [[MD:![0-9]+]] +// ELF-SAME: } + +// ELF-DAG: [[MD]] = !{[37 x i8]* @_swift1_autolink_entries, null, null, i1 false, i1 true} + // SECTION: .swift1_autolink_entries // NOSECTION-NOT: .swift1_autolink_entries From c769e491e2da4347f1136ac1e0b92d92cf9f40bb Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Thu, 9 Jan 2020 22:54:41 -0800 Subject: [PATCH 389/478] Revert "ModuleInterface: lock .swiftinterface while generating module cache" --- include/swift/AST/DiagnosticsFrontend.def | 6 -- lib/Frontend/ModuleInterfaceBuilder.cpp | 70 +---------------------- lib/Frontend/ModuleInterfaceBuilder.h | 5 +- lib/Frontend/ModuleInterfaceLoader.cpp | 10 ++-- test/Driver/lock_interface.swift | 13 ----- 5 files changed, 7 insertions(+), 97 deletions(-) delete mode 100644 test/Driver/lock_interface.swift diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 049527c6fbb7f..c3beb96e145fb 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -343,12 +343,6 @@ ERROR(unknown_forced_module_loading_mode,none, "unknown value for SWIFT_FORCE_MODULE_LOADING variable: '%0'", (StringRef)) -REMARK(interface_file_lock_failure,none, - "could not acquire lock file for module interface '%0'", (StringRef)) - -REMARK(interface_file_lock_timed_out,none, - "timed out waiting to acquire lock file for module interface '%0'", (StringRef)) - #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index a95d0e9ba72dd..59af6d226681e 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -36,7 +36,6 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/Regex.h" #include "llvm/Support/StringSaver.h" -#include "llvm/Support/LockFileManager.h" using namespace swift; using FileDependency = SerializationOptions::FileDependency; @@ -241,7 +240,7 @@ bool ModuleInterfaceBuilder::collectDepsForSerialization( return false; } -bool ModuleInterfaceBuilder::buildSwiftModuleInternal( +bool ModuleInterfaceBuilder::buildSwiftModule( StringRef OutPath, bool ShouldSerializeDeps, std::unique_ptr *ModuleBuffer) { bool SubError = false; @@ -385,70 +384,3 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( }); return !RunSuccess || SubError; } - -bool ModuleInterfaceBuilder::buildSwiftModule(StringRef OutPath, - bool ShouldSerializeDeps, - std::unique_ptr *ModuleBuffer, - llvm::function_ref RemarkRebuild) { - - while (1) { - // Attempt to lock the interface file. Only one process is allowed to build - // module from the interface so we don't consume too much memory when multiple - // processes are doing the same. - // FIXME: We should surface the module building step to the build system so - // we don't need to synchronize here. - llvm::LockFileManager Locked(interfacePath); - switch (Locked) { - case llvm::LockFileManager::LFS_Error:{ - // ModuleInterfaceBuilder takes care of correctness and locks are only - // necessary for performance. Fallback to building the module in case of any lock - // related errors. - if (RemarkRebuild) { - diags.diagnose(SourceLoc(), diag::interface_file_lock_failure, - interfacePath); - } - // Clear out any potential leftover. - Locked.unsafeRemoveLockFile(); - LLVM_FALLTHROUGH; - } - case llvm::LockFileManager::LFS_Owned: { - if (RemarkRebuild) { - RemarkRebuild(); - } - return buildSwiftModuleInternal(OutPath, ShouldSerializeDeps, ModuleBuffer); - } - case llvm::LockFileManager::LFS_Shared: { - // Someone else is responsible for building the module. Wait for them to - // finish. - switch (Locked.waitForUnlock()) { - case llvm::LockFileManager::Res_Success: { - // This process may have a different module output path. If the other - // process doesn't build the interface to this output path, we should try - // building ourselves. - auto bufferOrError = llvm::MemoryBuffer::getFile(OutPath); - if (!bufferOrError) - continue; - *ModuleBuffer = std::move(bufferOrError.get()); - return false; - } - case llvm::LockFileManager::Res_OwnerDied: { - continue; // try again to get the lock. - } - case llvm::LockFileManager::Res_Timeout: { - // Since ModuleInterfaceBuilder takes care of correctness, we try waiting for - // another process to complete the build so swift does not do it done - // twice. If case of timeout, build it ourselves. - if (RemarkRebuild) { - diags.diagnose(SourceLoc(), diag::interface_file_lock_timed_out, - interfacePath); - } - // Clear the lock file so that future invocations can make progress. - Locked.unsafeRemoveLockFile(); - continue; - } - } - break; - } - } - } -} diff --git a/lib/Frontend/ModuleInterfaceBuilder.h b/lib/Frontend/ModuleInterfaceBuilder.h index b5de3c64b8a8e..f8f096a39538a 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.h +++ b/lib/Frontend/ModuleInterfaceBuilder.h @@ -67,8 +67,6 @@ class ModuleInterfaceBuilder { version::Version &Vers, llvm::StringSaver &SubArgSaver, SmallVectorImpl &SubArgs); - bool buildSwiftModuleInternal(StringRef OutPath, bool ShouldSerializeDeps, - std::unique_ptr *ModuleBuffer); public: ModuleInterfaceBuilder(SourceManager &sourceMgr, DiagnosticEngine &diags, const SearchPathOptions &searchPathOpts, @@ -104,8 +102,7 @@ class ModuleInterfaceBuilder { } bool buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps, - std::unique_ptr *ModuleBuffer, - llvm::function_ref RemarkRebuild = nullptr); + std::unique_ptr *ModuleBuffer); }; } // end namespace swift diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 87ecf51cb370d..ceb0cb7ae7ad8 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -950,11 +950,13 @@ class ModuleInterfaceLoaderImpl { std::unique_ptr moduleBuffer; // We didn't discover a module corresponding to this interface. + // Diagnose that we didn't find a loadable module, if we were asked to. - auto remarkRebuild = [&]() { + if (remarkOnRebuildFromInterface) { rebuildInfo.diagnose(ctx, diagnosticLoc, moduleName, interfacePath); - }; + } + // If we found an out-of-date .swiftmodule, we still want to add it as // a dependency of the .swiftinterface. That way if it's updated, but // the .swiftinterface remains the same, we invalidate the cache and @@ -964,9 +966,7 @@ class ModuleInterfaceLoaderImpl { builder.addExtraDependency(modulePath); if (builder.buildSwiftModule(cachedOutputPath, /*shouldSerializeDeps*/true, - &moduleBuffer, - remarkOnRebuildFromInterface ? remarkRebuild: - llvm::function_ref())) + &moduleBuffer)) return std::make_error_code(std::errc::invalid_argument); assert(moduleBuffer && diff --git a/test/Driver/lock_interface.swift b/test/Driver/lock_interface.swift deleted file mode 100644 index cf41dc730d13e..0000000000000 --- a/test/Driver/lock_interface.swift +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: echo 'public func foo() {}' > %t/Foo.swift -// RUN: %target-swift-frontend-typecheck -emit-module-interface-path %t/Foo.swiftinterface %t/Foo.swift -enable-library-evolution -// RUN: touch %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift -// RUN: echo 'import Foo' > %t/file-01.swift -// RUN: echo 'import Foo' > %t/file-02.swift -// RUN: echo 'import Foo' > %t/file-03.swift -// RUN: %swiftc_driver -j20 %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift -I %t -Xfrontend -Rmodule-interface-rebuild &> %t/result.txt -// RUN: %FileCheck %s -check-prefix=CHECK-REBUILD < %t/result.txt - -// Ensure we only build Foo module once from the interface -// CHECK-REBUILD: rebuilding module 'Foo' from interface -// CHECK-REBUILD-NOT: rebuilding module 'Foo' from interface From 66852e2bb78dc31f3e085383a132c7393f314557 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 10 Jan 2020 09:50:34 +0100 Subject: [PATCH 390/478] tests: fix Array test The Array/removeNonUnique test is not working with an old runtime. rdar://problem/58471030 --- test/stdlib/Inputs/CommonArrayTests.gyb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/stdlib/Inputs/CommonArrayTests.gyb b/test/stdlib/Inputs/CommonArrayTests.gyb index e52a957d87dd5..20924efb2f138 100644 --- a/test/stdlib/Inputs/CommonArrayTests.gyb +++ b/test/stdlib/Inputs/CommonArrayTests.gyb @@ -116,12 +116,14 @@ ${Suite}.test("${ArrayType}/appendNonUnique") % if ArrayType != 'ArraySlice': ${Suite}.test("${ArrayType}/removeNonUnique") .code { - var x = ${ArrayType}(repeating: 27, count: 200) - x.reserveCapacity(10002) - for _ in 1...100 { - let y = x - x.remove(at: 0) - expectTrue(x.capacity < 1000) + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + var x = ${ArrayType}(repeating: 27, count: 200) + x.reserveCapacity(10002) + for _ in 1...100 { + let y = x + x.remove(at: 0) + expectTrue(x.capacity < 1000) + } } } % end From 68f0816daa181712cfec804651a618dbd2f68b4c Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Thu, 9 Jan 2020 11:42:39 +0000 Subject: [PATCH 391/478] [stdlib] Add withContiguousStorageIfAvailable to SubString.UTF8View Due to an oversight it seems that we never added a withContigousStorageIfAvailable implementation to SubString.UTF8View, which meant that if you sliced a String you lost the ability to get fast access to the backing storage. There's no good reason for this functionality to be missing, so this patch adds it in by delegating to the Slice implementation. Resolves SR-11999. --- stdlib/public/core/Substring.swift | 7 +++++++ test/stdlib/subString.swift | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/stdlib/public/core/Substring.swift b/stdlib/public/core/Substring.swift index 9786f34dfb148..c187129c51b0c 100644 --- a/stdlib/public/core/Substring.swift +++ b/stdlib/public/core/Substring.swift @@ -389,6 +389,13 @@ extension Substring.UTF8View: BidirectionalCollection { return _slice.distance(from: start, to: end) } + @_alwaysEmitIntoClient + public func withContiguousStorageIfAvailable( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R? { + return try _slice.withContiguousStorageIfAvailable(body) + } + @inlinable public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { _slice._failEarlyRangeCheck(index, bounds: bounds) diff --git a/test/stdlib/subString.swift b/test/stdlib/subString.swift index 17e0d41e98edc..1fa936649baf7 100644 --- a/test/stdlib/subString.swift +++ b/test/stdlib/subString.swift @@ -12,6 +12,19 @@ func checkMatch(_ x: S, _ y: T, _ i: S.Index) expectEqual(x[i], y[i]) } +func checkMatchContiguousStorage(_ x: S, _ y: T) + where S.Element == T.Element, S.Element: Equatable +{ + let xElement = x.withContiguousStorageIfAvailable { $0.first } + let yElement = y.withContiguousStorageIfAvailable { $0.first } + expectEqual(xElement, yElement) +} + +func checkHasContiguousStorage(_ x: S) { + let hasStorage = x.withContiguousStorageIfAvailable { _ in true } ?? false + expectTrue(hasStorage) +} + SubstringTests.test("Equality") { let s = "abcdefg" let s1 = s[s.index(s.startIndex, offsetBy: 2) ..< @@ -228,6 +241,13 @@ SubstringTests.test("UTF8View") { expectEqual("", String(t.dropLast(100))!) expectEqual("", String(u.dropFirst(100))!) expectEqual("", String(u.dropLast(100))!) + + checkHasContiguousStorage(s.utf8) + checkHasContiguousStorage(t) + checkHasContiguousStorage(u) + checkMatchContiguousStorage(Array(s.utf8), s.utf8) + checkMatchContiguousStorage(Array(t), t) + checkMatchContiguousStorage(Array(u), u) } } From 64b83286e68228fb9dbf8d4da76ef5cce51cdd1e Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 10 Jan 2020 13:58:24 +0100 Subject: [PATCH 392/478] tests: fix and re-enable array_contentof_opt test for linux --- test/SILOptimizer/array_contentof_opt.swift | 22 +++++++++------------ 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/test/SILOptimizer/array_contentof_opt.swift b/test/SILOptimizer/array_contentof_opt.swift index 52aaa0c444e63..cb4b73a37a874 100644 --- a/test/SILOptimizer/array_contentof_opt.swift +++ b/test/SILOptimizer/array_contentof_opt.swift @@ -1,10 +1,6 @@ // RUN: %target-swift-frontend -O -sil-verify-all -emit-sil %s | %FileCheck %s // REQUIRES: swift_stdlib_no_asserts,optimized_stdlib -// Temporary disabled on linux. -// TODO: need to adapt some CHECK-lines for linux. -// REQUIRES: OS=macosx - // This is an end-to-end test of the array(contentsOf) -> array(Element) optimization // CHECK-LABEL: sil @{{.*}}testInt @@ -18,15 +14,15 @@ public func testInt(_ a: inout [Int]) { a += [1] } -// CHECK-LABEL: sil @{{.*}}testThreeInt -// CHECK: [[FR:%[0-9]+]] = function_ref @${{(sSa15reserveCapacityyySiFSi_Tg5|sSa16_createNewBuffer)}} -// CHECK-NEXT: apply [[FR]] -// CHECK: [[F:%[0-9]+]] = function_ref @$sSa6appendyyxnFSi_Tg5 -// CHECK: apply [[F]] -// CHECK-NEXT: apply [[F]] -// CHECK-NEXT: apply [[F]] -// CHECK-NEXT: tuple -// CHECK-NEXT: return +// CHECK-LABEL: sil @{{.*}}testThreeInts +// CHECK-DAG: [[FR:%[0-9]+]] = function_ref @${{(sSa15reserveCapacityyySiFSi_Tg5|sSa16_createNewBuffer)}} +// CHECK-DAG: apply [[FR]] +// CHECK-DAG: [[F:%[0-9]+]] = function_ref @$sSa6appendyyxnFSi_Tg5 +// CHECK-DAG: apply [[F]] +// CHECK-DAG: apply [[F]] +// CHECK-DAG: apply [[F]] +// CHECK: } // end sil function '{{.*}}testThreeInts{{.*}}' + public func testThreeInts(_ a: inout [Int]) { a += [1, 2, 3] } From dfc5b06819df217ff51ad0d7ff5e1c70b9b44244 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 10 Jan 2020 17:08:53 +0100 Subject: [PATCH 393/478] stdlib: annotate Array's remove functions with semantic attributes. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All mutating Array functions must be annotated with semantics, because otherwise some high level optimizations get confused. The semantic attributes prevent inlining those functions in high-level-sil. This is need so that the optimizer sees that the Array is taken as inout and can reason that it's modified. This restriction is not needed anymore when we’ll have COW representation in SIL. rdar://problem/58478089 --- stdlib/public/core/Array.swift | 2 ++ stdlib/public/core/ContiguousArray.swift | 2 ++ 2 files changed, 4 insertions(+) diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index 5f3a3e3f0bc2e..e143420e9120e 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -1258,6 +1258,7 @@ extension Array: RangeReplaceableCollection { } @inlinable + @_semantics("array.mutate_unknown") public mutating func _customRemoveLast() -> Element? { _makeMutableAndUnique() let newCount = _getCount() - 1 @@ -1285,6 +1286,7 @@ extension Array: RangeReplaceableCollection { /// - Complexity: O(*n*), where *n* is the length of the array. @inlinable @discardableResult + @_semantics("array.mutate_unknown") public mutating func remove(at index: Int) -> Element { _makeMutableAndUnique() let currentCount = _getCount() diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index 7520b18b8d9f2..13275086d2f26 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -890,6 +890,7 @@ extension ContiguousArray: RangeReplaceableCollection { } @inlinable + @_semantics("array.mutate_unknown") public mutating func _customRemoveLast() -> Element? { _makeMutableAndUnique() let newCount = _getCount() - 1 @@ -917,6 +918,7 @@ extension ContiguousArray: RangeReplaceableCollection { /// - Complexity: O(*n*), where *n* is the length of the array. @inlinable @discardableResult + @_semantics("array.mutate_unknown") public mutating func remove(at index: Int) -> Element { _makeMutableAndUnique() let currentCount = _getCount() From e606f52ae8939ddf4708b4d29cb4c841db8fc1c9 Mon Sep 17 00:00:00 2001 From: Shoaib Meenai Date: Thu, 9 Jan 2020 20:30:06 -0800 Subject: [PATCH 394/478] [build] Check for unicode functions in libedit Some versions of libedit may have histedit.h but not the Unicode functions. Explicitly check for the Unicode functions in the found libedit to ensure the check is accurate. I considered making this a component of the LibEdit package in our find module, but CMake's documentation [1] says "Packages that find multiple semi-independent parts (like bundles of libraries) should search for the components...", and the "multiple semi-independent parts" definitely isn't the case here; we're just interested in determining if the found library supports a particular feature. [1] https://cmake.org/cmake/help/v3.16/manual/cmake-developer.7.html#find-modules --- CMakeLists.txt | 13 +++++++++++++ lib/Immediate/CMakeLists.txt | 2 +- tools/SourceKit/tools/CMakeLists.txt | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a34a7733fd13..bc318be7bb31b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -930,6 +930,19 @@ else() find_package(LibEdit) endif() +if(LibEdit_FOUND) + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_INCLUDES ${LibEdit_INCLUDE_DIRS}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${LibEdit_LIBRARIES}) + check_symbol_exists(el_wgets "histedit.h" HAVE_EL_WGETS) + if(HAVE_EL_WGETS) + set(LibEdit_HAS_UNICODE YES) + else() + set(LibEdit_HAS_UNICODE NO) + endif() + cmake_pop_check_state() +endif() + check_symbol_exists(wait4 "sys/wait.h" HAVE_WAIT4) check_symbol_exists(proc_pid_rusage "libproc.h" HAVE_PROC_PID_RUSAGE) diff --git a/lib/Immediate/CMakeLists.txt b/lib/Immediate/CMakeLists.txt index f95071a3853d6..01bbe6ae504f3 100644 --- a/lib/Immediate/CMakeLists.txt +++ b/lib/Immediate/CMakeLists.txt @@ -12,7 +12,7 @@ target_link_libraries(swiftImmediate PRIVATE swiftIRGen swiftSILGen swiftSILOptimizer) -if(LibEdit_FOUND) +if(LibEdit_FOUND AND LibEdit_HAS_UNICODE) target_compile_definitions(swiftImmediate PRIVATE HAVE_LIBEDIT) target_link_libraries(swiftImmediate PRIVATE diff --git a/tools/SourceKit/tools/CMakeLists.txt b/tools/SourceKit/tools/CMakeLists.txt index f49f58be095cd..ad651d0d6fb78 100644 --- a/tools/SourceKit/tools/CMakeLists.txt +++ b/tools/SourceKit/tools/CMakeLists.txt @@ -6,7 +6,7 @@ include_directories( add_swift_lib_subdirectory(sourcekitd) add_swift_tool_subdirectory(sourcekitd-test) -if(LibEdit_FOUND) +if(LibEdit_FOUND AND LibEdit_HAS_UNICODE) add_swift_tool_subdirectory(sourcekitd-repl) endif() add_swift_tool_subdirectory(complete-test) From 7a3a0a9e23abe3626d3821cbcf522b2bdddd740b Mon Sep 17 00:00:00 2001 From: Ashley Garland Date: Fri, 27 Sep 2019 16:25:53 -0700 Subject: [PATCH 395/478] Symbol graph support Adds a tool `swift-symbolgraph-extract` that reads an existing Swift module and prints a platform- and language-agnostic JSON description of the module, primarly for documentation. Adds a small sub-library `SymbolGraphGen` which houses the core implementation for collecting relevant information about declarations. The main entry point is integrated directly into the driver as a mode: the tool is meant to be run outside of the normal edit-compile-run/test workflow to avoid impacting build times. Along with common options for other tools, unique options include `pretty-print` for debugging, and a `minimum-access-level` options for including internal documentation. A symbol graph is a directed graph where the nodes are symbols in a module and the edges are relationships between them. For example, a `struct S` may have a member `var x`. The graph would have two nodes for `S` and `x`, and one "member-of" relationship edge. Other relationship kinds include "inherits-from" or "conforms to". The data format for a symbol graph is still under development and may change without notice until a specificiation and versioning scheme is published. Various aspects about a symbol are recorded in the nodes, such as availability, documentation comments, or data needed for printing the shapes of declarations without having to understand specifics about the langauge. Implicit and public-underscored stdlib declarations are not included by default. rdar://problem/55346798 --- include/swift/AST/Decl.h | 2 + include/swift/AST/PrintOptions.h | 7 + include/swift/Driver/Driver.h | 3 +- include/swift/SymbolGraphGen/SymbolGraphGen.h | 41 ++ lib/AST/ASTPrinter.cpp | 5 +- lib/AST/Decl.cpp | 87 ++-- lib/CMakeLists.txt | 1 + lib/Driver/Driver.cpp | 2 + lib/Markup/LineList.cpp | 2 - lib/SymbolGraphGen/CMakeLists.txt | 13 + .../DeclarationFragmentPrinter.cpp | 140 +++++++ .../DeclarationFragmentPrinter.h | 123 ++++++ lib/SymbolGraphGen/Edge.cpp | 40 ++ lib/SymbolGraphGen/Edge.h | 167 ++++++++ lib/SymbolGraphGen/FormatVersion.h | 20 + lib/SymbolGraphGen/JSON.cpp | 57 +++ lib/SymbolGraphGen/JSON.h | 45 +++ lib/SymbolGraphGen/Symbol.cpp | 380 ++++++++++++++++++ lib/SymbolGraphGen/Symbol.h | 128 ++++++ lib/SymbolGraphGen/SymbolGraph.cpp | 68 ++++ lib/SymbolGraphGen/SymbolGraph.h | 64 +++ lib/SymbolGraphGen/SymbolGraphASTWalker.cpp | 364 +++++++++++++++++ lib/SymbolGraphGen/SymbolGraphASTWalker.h | 161 ++++++++ lib/SymbolGraphGen/SymbolGraphGen.cpp | 56 +++ test/IDE/comment_brief.swift | 4 +- test/SymbolGraph/Module.swift | 20 + .../Relationships/ConformsTo.swift | 16 + .../DefaultImplementationOf.swift | 18 + .../Relationships/InheritsFrom.swift | 11 + test/SymbolGraph/Relationships/MemberOf.swift | 12 + .../SymbolGraph/Relationships/Overrides.swift | 20 + .../Relationships/RequirementOf.swift | 12 + .../Relationships/TargetFallback.swift | 16 + .../AccessLevelFilter/IncludeInternal.swift | 20 + .../AccessLevelFilter/PublicDefault.swift | 15 + test/SymbolGraph/Symbols/AccessLevels.swift | 12 + test/SymbolGraph/Symbols/DocComment.swift | 54 +++ test/SymbolGraph/Symbols/Kinds.swift | 42 ++ .../Mixins/Availability/Availability.swift | 24 ++ .../UnconditionallyDeprecated.swift | 10 + .../UnconditionallyUnavailable.swift | 10 + .../Symbols/Mixins/DeclarationFragments.swift | 54 +++ .../Symbols/Mixins/FunctionSignature.swift | 37 ++ test/SymbolGraph/Symbols/Names.swift | 9 + .../Symbols/SkipsPublicUnderscore.swift | 11 + test/lit.cfg | 17 + tools/driver/CMakeLists.txt | 14 +- tools/driver/driver.cpp | 6 + .../driver/swift_symbolgraph_extract_main.cpp | 177 ++++++++ 49 files changed, 2574 insertions(+), 43 deletions(-) create mode 100644 include/swift/SymbolGraphGen/SymbolGraphGen.h create mode 100644 lib/SymbolGraphGen/CMakeLists.txt create mode 100644 lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp create mode 100644 lib/SymbolGraphGen/DeclarationFragmentPrinter.h create mode 100644 lib/SymbolGraphGen/Edge.cpp create mode 100644 lib/SymbolGraphGen/Edge.h create mode 100644 lib/SymbolGraphGen/FormatVersion.h create mode 100644 lib/SymbolGraphGen/JSON.cpp create mode 100644 lib/SymbolGraphGen/JSON.h create mode 100644 lib/SymbolGraphGen/Symbol.cpp create mode 100644 lib/SymbolGraphGen/Symbol.h create mode 100644 lib/SymbolGraphGen/SymbolGraph.cpp create mode 100644 lib/SymbolGraphGen/SymbolGraph.h create mode 100644 lib/SymbolGraphGen/SymbolGraphASTWalker.cpp create mode 100644 lib/SymbolGraphGen/SymbolGraphASTWalker.h create mode 100644 lib/SymbolGraphGen/SymbolGraphGen.cpp create mode 100644 test/SymbolGraph/Module.swift create mode 100644 test/SymbolGraph/Relationships/ConformsTo.swift create mode 100644 test/SymbolGraph/Relationships/DefaultImplementationOf.swift create mode 100644 test/SymbolGraph/Relationships/InheritsFrom.swift create mode 100644 test/SymbolGraph/Relationships/MemberOf.swift create mode 100644 test/SymbolGraph/Relationships/Overrides.swift create mode 100644 test/SymbolGraph/Relationships/RequirementOf.swift create mode 100644 test/SymbolGraph/Relationships/TargetFallback.swift create mode 100644 test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift create mode 100644 test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift create mode 100644 test/SymbolGraph/Symbols/AccessLevels.swift create mode 100644 test/SymbolGraph/Symbols/DocComment.swift create mode 100644 test/SymbolGraph/Symbols/Kinds.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift create mode 100644 test/SymbolGraph/Symbols/Names.swift create mode 100644 test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift create mode 100644 tools/driver/swift_symbolgraph_extract_main.cpp diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index d8ba393ded6fb..57514cb26bd90 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -875,6 +875,8 @@ class alignas(1 << DeclAlignInBits) Decl { LLVM_READONLY const GenericContext *getAsGenericContext() const; + bool hasUnderscoredNaming() const; + bool isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic = true) const; AvailabilityContext getAvailabilityForLinkage() const; diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 6a142e02da510..55aff0da73aca 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -380,6 +380,13 @@ struct PrintOptions { ArgAndParamPrintingMode ArgAndParamPrinting = ArgAndParamPrintingMode::MatchSource; + /// Whether to print the default argument value string + /// representation. + bool PrintDefaultArgumentValue = true; + + /// Whether to print "_" placeholders for empty arguments. + bool PrintEmptyArgumentNames = true; + /// Whether to print documentation comments attached to declarations. /// Note that this may print documentation comments from related declarations /// (e.g. the overridden method in the superclass) if such comment is found. diff --git a/include/swift/Driver/Driver.h b/include/swift/Driver/Driver.h index b72397379bb68..d927f3908a14f 100644 --- a/include/swift/Driver/Driver.h +++ b/include/swift/Driver/Driver.h @@ -158,7 +158,8 @@ class Driver { Interactive, // swift Batch, // swiftc AutolinkExtract, // swift-autolink-extract - SwiftIndent // swift-indent + SwiftIndent, // swift-indent + SymbolGraph // swift-symbolgraph }; class InputInfoMap; diff --git a/include/swift/SymbolGraphGen/SymbolGraphGen.h b/include/swift/SymbolGraphGen/SymbolGraphGen.h new file mode 100644 index 0000000000000..324a505c45e39 --- /dev/null +++ b/include/swift/SymbolGraphGen/SymbolGraphGen.h @@ -0,0 +1,41 @@ +//===--- swift_indent_main.cpp - Swift code formatting tool ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Triple.h" +#include "swift/AST/AttrKind.h" + +namespace swift { + +class ModuleDecl; + +namespace symbolgraphgen { + +struct SymbolGraphOptions { + /// The path to output the symbol graph JSON. + StringRef OutputPath; + + /// The target of the module. + llvm::Triple Target; + + /// Pretty-print the JSON with newlines and indentation. + bool PrettyPrint; + + /// The minimum access level that symbols must have in order to be + /// included in the graph. + AccessLevel MinimumAccessLevel; +}; + +/// Emit a Symbol Graph JSON file for a module. +int emitSymbolGraphForModule(ModuleDecl *M, const SymbolGraphOptions &Options); + +} // end namespace symbolgraphgen +} // end namespace swift diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 99a0ca8c982a8..992f27e7b8c19 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2632,6 +2632,9 @@ void PrintAST::printOneParameter(const ParamDecl *param, // Else, print the argument only. LLVM_FALLTHROUGH; case PrintOptions::ArgAndParamPrintingMode::ArgumentOnly: + if (ArgName.empty() && !Options.PrintEmptyArgumentNames) { + return; + } Printer.printName(ArgName, PrintNameContext::FunctionParameterExternal); if (!ArgNameIsAPIByDefault && !ArgName.empty()) @@ -2686,7 +2689,7 @@ void PrintAST::printOneParameter(const ParamDecl *param, if (param->isVariadic()) Printer << "..."; - if (param->isDefaultArgument()) { + if (param->isDefaultArgument() && Options.PrintDefaultArgumentValue) { SmallString<128> scratch; auto defaultArgStr = param->getDefaultValueStringRepresentation(scratch); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 73b488298f153..ecc8f250d82ed 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -697,6 +697,56 @@ bool ParameterList::hasInternalParameter(StringRef Prefix) const { return false; } +bool Decl::hasUnderscoredNaming() const { + const Decl *D = this; + if (const auto AFD = dyn_cast(D)) { + // If it's a function with a parameter with leading underscore, it's a + // private function. + if (AFD->getParameters()->hasInternalParameter("_")) { + return true; + } + } + + if (const auto SubscriptD = dyn_cast(D)) { + if (SubscriptD->getIndices()->hasInternalParameter("_")) { + return true; + } + } + + if (const auto PD = dyn_cast(D)) { + if (PD->getAttrs().hasAttribute()) { + return false; + } + StringRef NameStr = PD->getNameStr(); + if (NameStr.startswith("_Builtin")) { + return true; + } + if (NameStr.startswith("_ExpressibleBy")) { + return true; + } + } + + if (const auto ImportD = dyn_cast(D)) { + if (const auto *Mod = ImportD->getModule()) { + if (Mod->isSwiftShimsModule()) { + return true; + } + } + } + + const auto VD = dyn_cast(D); + if (!VD || !VD->hasName()) { + return false; + } + + if (!VD->getBaseName().isSpecial() && + VD->getBaseName().getIdentifier().str().startswith("_")) { + return true; + } + + return false; +} + bool Decl::isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic) const { const Decl *D = this; if (auto ExtD = dyn_cast(D)) { @@ -718,47 +768,12 @@ bool Decl::isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic) const { FU->getKind() != FileUnitKind::SerializedAST) return false; - if (auto AFD = dyn_cast(D)) { - // If it's a function with a parameter with leading underscore, it's a - // private function. - if (AFD->getParameters()->hasInternalParameter("_")) - return true; - } - - if (auto SubscriptD = dyn_cast(D)) { - if (SubscriptD->getIndices()->hasInternalParameter("_")) - return true; - } - if (auto PD = dyn_cast(D)) { - if (PD->getAttrs().hasAttribute()) - return false; - StringRef NameStr = PD->getNameStr(); - if (NameStr.startswith("_Builtin")) - return true; - if (NameStr.startswith("_ExpressibleBy")) - return true; if (treatNonBuiltinProtocolsAsPublic) return false; } - if (auto ImportD = dyn_cast(D)) { - if (auto *Mod = ImportD->getModule()) { - if (Mod->isSwiftShimsModule()) - return true; - } - } - - auto VD = dyn_cast(D); - if (!VD || !VD->hasName()) - return false; - - // If the name has leading underscore then it's a private symbol. - if (!VD->getBaseName().isSpecial() && - VD->getBaseName().getIdentifier().str().startswith("_")) - return true; - - return false; + return hasUnderscoredNaming(); } AvailabilityContext Decl::getAvailabilityForLinkage() const { diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b06020ecb4f44..e3a21225fdb66 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -42,6 +42,7 @@ add_subdirectory(SwiftRemoteMirror) add_subdirectory(SIL) add_subdirectory(SILGen) add_subdirectory(SILOptimizer) +add_subdirectory(SymbolGraphGen) add_subdirectory(Syntax) add_subdirectory(SyntaxParse) add_subdirectory(TBDGen) diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index e3a6e2a3361cf..34db9ebb425d8 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -98,6 +98,7 @@ void Driver::parseDriverKind(ArrayRef Args) { .Case("swiftc", DriverKind::Batch) .Case("swift-autolink-extract", DriverKind::AutolinkExtract) .Case("swift-indent", DriverKind::SwiftIndent) + .Case("swift-symbolgraph-extract", DriverKind::SymbolGraph) .Default(None); if (Kind.hasValue()) @@ -3252,6 +3253,7 @@ void Driver::printHelp(bool ShowHidden) const { case DriverKind::Batch: case DriverKind::AutolinkExtract: case DriverKind::SwiftIndent: + case DriverKind::SymbolGraph: ExcludedFlagsBitmask |= options::NoBatchOption; break; } diff --git a/lib/Markup/LineList.cpp b/lib/Markup/LineList.cpp index 0afeb5eec995d..a89488b8c4213 100644 --- a/lib/Markup/LineList.cpp +++ b/lib/Markup/LineList.cpp @@ -115,8 +115,6 @@ LineList MarkupContext::getLineList(swift::RawComment RC) { // Determine if we have leading decorations in this block comment. bool HasASCIIArt = false; if (swift::startsWithNewline(Cleaned)) { - Builder.addLine(Cleaned.substr(0, 0), { C.Range.getStart(), - C.Range.getStart() }); unsigned NewlineBytes = swift::measureNewline(Cleaned); Cleaned = Cleaned.drop_front(NewlineBytes); CleanedStartLoc = CleanedStartLoc.getAdvancedLocOrInvalid(NewlineBytes); diff --git a/lib/SymbolGraphGen/CMakeLists.txt b/lib/SymbolGraphGen/CMakeLists.txt new file mode 100644 index 0000000000000..2780330587fb2 --- /dev/null +++ b/lib/SymbolGraphGen/CMakeLists.txt @@ -0,0 +1,13 @@ +add_swift_host_library(swiftSymbolGraphGen STATIC + DeclarationFragmentPrinter.cpp + Edge.cpp + JSON.cpp + Symbol.cpp + SymbolGraph.cpp + SymbolGraphGen.cpp + SymbolGraphASTWalker.cpp) + +target_link_libraries(swiftSymbolGraphGen + swiftAST + swiftFrontend + swiftMarkup) diff --git a/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp b/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp new file mode 100644 index 0000000000000..2e8db85e2d3b2 --- /dev/null +++ b/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp @@ -0,0 +1,140 @@ +//===--- DeclarationFragmentPrinter.cpp - Declaration Fragment Printer ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "DeclarationFragmentPrinter.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +void DeclarationFragmentPrinter::openFragment(FragmentKind Kind) { + assert(Kind != FragmentKind::None); + if (this->Kind != Kind) { + closeFragment(); + this->Kind = Kind, + Spelling.clear(); + USR.clear(); + } +} + +StringRef +DeclarationFragmentPrinter::getKindSpelling(FragmentKind Kind) const { + switch (Kind) { + case FragmentKind::Keyword: + return "keyword"; + case FragmentKind::Attribute: + return "attribute"; + case FragmentKind::NumberLiteral: + return "number"; + case FragmentKind::StringLiteral: + return "string"; + case FragmentKind::Identifier: + return "identifier"; + case FragmentKind::TypeIdentifier: + return "typeIdentifier"; + case FragmentKind::GenericParameter: + return "genericParameter"; + case FragmentKind::Text: + return "text"; + case FragmentKind::None: + llvm_unreachable("Fragment kind of 'None' has no spelling"); + } +} + +void DeclarationFragmentPrinter::closeFragment() { + if (Kind == FragmentKind::None) { + return; + } + + if (!Spelling.empty()) { + OS.object([&](){ + OS.attribute("kind", getKindSpelling(Kind)); + OS.attribute("spelling", Spelling.str()); + if (!USR.empty()) { + OS.attribute("preciseIdentifier", USR.str()); + } + }); + } + + Spelling.clear(); + USR.clear(); + Kind = FragmentKind::None; +} + +void DeclarationFragmentPrinter::printDeclLoc(const Decl *D) { + switch (D->getKind()) { + case DeclKind::Constructor: + case DeclKind::Destructor: + case DeclKind::Subscript: + openFragment(FragmentKind::Keyword); + break; + default: + openFragment(FragmentKind::Identifier); + break; + } +} + +void +DeclarationFragmentPrinter::printNamePre(PrintNameContext Context) { + switch (Context) { + case PrintNameContext::Keyword: + openFragment(FragmentKind::Keyword); + break; + case PrintNameContext::GenericParameter: + openFragment(FragmentKind::GenericParameter); + break; + case PrintNameContext::Attribute: + openFragment(FragmentKind::Attribute); + break; + case PrintNameContext::ClassDynamicSelf: + case PrintNameContext::FunctionParameterExternal: + openFragment(FragmentKind::Identifier); + break; + case PrintNameContext::FunctionParameterLocal: + openFragment(FragmentKind::Identifier); + break; + case PrintNameContext::TupleElement: + case PrintNameContext::TypeMember: + case PrintNameContext::Normal: + break; + } +} + +void DeclarationFragmentPrinter::printStructurePre(PrintStructureKind Kind, + const Decl *D) { + switch (Kind) { + case PrintStructureKind::NumberLiteral: + openFragment(FragmentKind::NumberLiteral); + break; + case PrintStructureKind::StringLiteral: + openFragment(FragmentKind::StringLiteral); + break; + default: + break; + } +} + +void DeclarationFragmentPrinter::printTypeRef(Type T, const TypeDecl *RefTo, + Identifier Name, + PrintNameContext NameContext) { + openFragment(FragmentKind::TypeIdentifier); + printText(Name.str()); + USR = Walker.getUSR(RefTo); + closeFragment(); +} + +void DeclarationFragmentPrinter::printText(StringRef Text) { + if (Kind == FragmentKind::None) { + openFragment(FragmentKind::Text); + } + Spelling.append(Text); +} diff --git a/lib/SymbolGraphGen/DeclarationFragmentPrinter.h b/lib/SymbolGraphGen/DeclarationFragmentPrinter.h new file mode 100644 index 0000000000000..2e5ec449bd34f --- /dev/null +++ b/lib/SymbolGraphGen/DeclarationFragmentPrinter.h @@ -0,0 +1,123 @@ +//===--- DeclarationFragmentPrinter.h - Declaration Fragment Printer ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_DECLARATIONFRAGMENTPRINTER_H +#define SWIFT_SYMBOLGRAPHGEN_DECLARATIONFRAGMENTPRINTER_H + +#include "llvm/Support/JSON.h" +#include "swift/AST/ASTPrinter.h" +#include "swift/Basic/LLVM.h" + +namespace swift { + +class Decl; +class Type; +class TypeDecl; + +namespace symbolgraphgen { + +struct SymbolGraphASTWalker; + +/// Prints AST nodes as a stream of tagged fragments for syntax highlighting. +/// +/// These fragments are meant to display a somewhat abbreviated part of the +/// declaration for display in documentation, ignoring things like member and +/// function bodies. +/// +/// For example, a function: +/// +/// ```swift +/// func foo() { +/// print("Hello, world!") +/// } +/// ``` +/// +/// Will have fragments representing the `func foo()` part. +class DeclarationFragmentPrinter : public ASTPrinter { + enum class FragmentKind { + None, + Keyword, + Attribute, + NumberLiteral, + StringLiteral, + Identifier, + TypeIdentifier, + GenericParameter, + Text, + }; + + SymbolGraphASTWalker &Walker; + + /// The output stream to print fragment objects to. + llvm::json::OStream &OS; + + /// The current fragment being considered. + FragmentKind Kind; + + /// The actual source text of the fragment. + SmallString<256> Spelling; + + SmallString<256> USR; + + StringRef getKindSpelling(FragmentKind Kind) const; + + /// Open a new kind of fragment without committing its spelling. + void openFragment(FragmentKind Kind); + + /// Close the current fragment if there is one, and commit it for display. + void closeFragment(); + +public: + DeclarationFragmentPrinter(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS, + Optional Key = None) + : Walker(Walker), + OS(OS), + Kind(FragmentKind::None) { + if (Key) { + OS.attributeBegin(*Key); + OS.arrayBegin(); + } else { + OS.arrayBegin(); + } + } + + void printDeclLoc(const Decl *D) override; + + void printDeclNameEndLoc(const Decl *D) override { + closeFragment(); + } + + void printNamePre(PrintNameContext Context) override; + + void printStructurePre(PrintStructureKind Kind, const Decl *D) override; + + void printNamePost(PrintNameContext Context) override { + closeFragment(); + } + + void printTypeRef(Type T, const TypeDecl *RefTo, Identifier Name, + PrintNameContext NameContext) override; + + void printText(StringRef Text) override; + + ~DeclarationFragmentPrinter() { + closeFragment(); + OS.arrayEnd(); + OS.attributeEnd(); + } +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_DECLARATIONFRAGMENTPRINTER_H diff --git a/lib/SymbolGraphGen/Edge.cpp b/lib/SymbolGraphGen/Edge.cpp new file mode 100644 index 0000000000000..296cc0960fee2 --- /dev/null +++ b/lib/SymbolGraphGen/Edge.cpp @@ -0,0 +1,40 @@ +//===--- Edge.cpp - Symbol Graph Edge -------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Module.h" +#include "Edge.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +void Edge::serialize(llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("kind", Kind.Name); + OS.attribute("source", Walker->getUSR(Source)); + OS.attribute("target", Walker->getUSR(Target)); + + // In case a dependent module isn't available, serialize a fallback name. + auto TargetModuleName = Target->getModuleContext()->getName().str(); + if (TargetModuleName != Walker->M.getName().str()) { + auto TargetSymbolIdentifier = Walker->getSymbolIdentifier(Target); + auto TargetComponents = TargetSymbolIdentifier.SimpleComponents; + SmallString<128> Scratch(TargetModuleName); + for (auto it = TargetComponents.begin(); + it != TargetComponents.end(); ++it) { + Scratch.push_back('.'); + Scratch.append(*it); + } + OS.attribute("targetFallback", Scratch.str()); + } + }); +} diff --git a/lib/SymbolGraphGen/Edge.h b/lib/SymbolGraphGen/Edge.h new file mode 100644 index 0000000000000..e22d2880151a7 --- /dev/null +++ b/lib/SymbolGraphGen/Edge.h @@ -0,0 +1,167 @@ +//===--- Edge.h - Symbol Graph Edge ---------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_EDGE_H +#define SWIFT_SYMBOLGRAPHGEN_EDGE_H + +#include "llvm/Support/JSON.h" +#include "swift/AST/Decl.h" +#include "swift/Basic/LLVM.h" + +#include "JSON.h" +#include "Symbol.h" + +namespace swift { +namespace symbolgraphgen { + +/// The kind of relationship, tagging an edge in the graph. +struct RelationshipKind { + StringRef Name; + + RelationshipKind(llvm::StringRef Name) : Name(Name) {} + + /** + A symbol A is a member of another symbol B. + + For example, a method or field of a class would be a member of that class. + + The implied inverse of this relationship is a symbol B is the owner + of a member symbol A. + */ + static inline RelationshipKind MemberOf() { + return RelationshipKind { "memberOf" }; + } + + /** + A symbol A conforms to an interface/protocol symbol B. + + For example, a class `C` that conforms to protocol `P` in Swift would use + this relationship. + + The implied inverse of this relationship is a symbol B that has + a conformer A. + */ + static inline RelationshipKind ConformsTo() { + return RelationshipKind { "conformsTo" }; + } + /** + A symbol A inherits from another symbol B. + + For example, a derived class inherits from a base class, or a protocol + that refines another protocol would use this relationship. + + The implied inverse of this relationship is a symbol B is a base + of another symbol A. + */ + static inline RelationshipKind InheritsFrom() { + return RelationshipKind { "inheritsFrom" }; + } + /** + A symbol A serves as a default implementation of an interface requirement B. + + The implied inverse of this relationship is an interface requirement B + has a default implementation of A. + */ + static inline RelationshipKind DefaultImplementationOf() { + return RelationshipKind { "defaultImplementationOf" }; + } + /** + A symbol A overrides another symbol B, such as through inheritance. + + The implied inverse of this relationship is a symbol A is the base + of symbol B. + */ + static inline RelationshipKind Overrides() { + return RelationshipKind { "overrides" }; + } + /** + A symbol A is a requirement of interface B. + + The implied inverse of this relationship is an interface B + has a requirement of A. + */ + static inline RelationshipKind RequirementOf() { + return RelationshipKind { "requirementOf" }; + } + /** + A symbol A is an optional requirement of interface B. + + The implied inverse of this relationship is an interface B + has an optional requirement of A. + */ + static inline RelationshipKind OptionalRequirementOf() { + return RelationshipKind { "optionalRequirementOf" }; + } + + bool operator==(const RelationshipKind &Other) const { + return Name == Other.Name; + } + + bool operator<(const RelationshipKind &Other) const { + return Name < Other.Name; + } +}; + +/// A relationship between two symbols: an edge in a directed graph. +struct Edge { + SymbolGraphASTWalker *Walker; + + /// The kind of relationship this edge represents. + RelationshipKind Kind; + + /// The precise identifier of the source symbol node. + const ValueDecl *Source; + + /// The precise identifier of the target symbol node. + const ValueDecl *Target; + + void serialize(llvm::json::OStream &OS) const; +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +namespace llvm { +using Edge = swift::symbolgraphgen::Edge; +template <> struct DenseMapInfo { + static inline Edge getEmptyKey() { + return { + nullptr, + { "Empty" }, + nullptr, + nullptr, + }; + } + static inline Edge getTombstoneKey() { + return { + nullptr, + { "Tombstone" }, + nullptr, + nullptr, + }; + } + static unsigned getHashValue(const Edge E) { + unsigned H = 0; + H ^= DenseMapInfo::getHashValue(E.Kind.Name); + H ^= DenseMapInfo::getHashValue(reinterpret_cast(E.Source)); + H ^= DenseMapInfo::getHashValue(reinterpret_cast(E.Target)); + return H; + } + static bool isEqual(const Edge LHS, const Edge RHS) { + return LHS.Kind == RHS.Kind && + LHS.Source == RHS.Source && + LHS.Target == RHS.Target; + } +}; +} // end namespace llvm + +#endif // SWIFT_SYMBOLGRAPHGEN_EDGE_H diff --git a/lib/SymbolGraphGen/FormatVersion.h b/lib/SymbolGraphGen/FormatVersion.h new file mode 100644 index 0000000000000..74ac1c196c5b4 --- /dev/null +++ b/lib/SymbolGraphGen/FormatVersion.h @@ -0,0 +1,20 @@ +//===--- FormatVersion.h - Symbol Graph Format Version 00------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H +#define SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H + +#define SWIFT_SYMBOLGRAPH_FORMAT_MAJOR 0 +#define SWIFT_SYMBOLGRAPH_FORMAT_MINOR 1 +#define SWIFT_SYMBOLGRAPH_FORMAT_PATCH 0 + +#endif // SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H diff --git a/lib/SymbolGraphGen/JSON.cpp b/lib/SymbolGraphGen/JSON.cpp new file mode 100644 index 0000000000000..6bb117daaeeb4 --- /dev/null +++ b/lib/SymbolGraphGen/JSON.cpp @@ -0,0 +1,57 @@ +//===--- JSON.cpp - Symbol Graph JSON Helpers -----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// Adds Symbol Graph JSON serialization to other types. +//===----------------------------------------------------------------------===// + +#include "JSON.h" + +void swift::symbolgraphgen::serialize(const llvm::VersionTuple &VT, + llvm::json::OStream &OS) { + OS.object([&](){ + OS.attribute("major", VT.getMajor()); + if (VT.getMinor()) { + OS.attribute("minor", *VT.getMinor()); + } + if (VT.getSubminor()) { + OS.attribute("patch", *VT.getSubminor()); + } + // Despite the name, + // this is not Semantic Versioning "build metadata" + if (VT.getBuild()) { + OS.attribute("prerelease", *VT.getBuild()); + } + }); +} + +void swift::symbolgraphgen::serialize(const llvm::Triple &T, + llvm::json::OStream &OS) { + OS.object([&](){ + OS.attribute("architecture", T.getArchName()); + if (!T.getEnvironmentName().empty()) { + OS.attribute("environment", T.getEnvironmentName()); + } + OS.attribute("vendor", T.getVendorName()); + OS.attributeObject("operatingSystem", [&](){ + OS.attribute("name", T.getOSTypeName(T.getOS())); + + unsigned Major; + unsigned Minor; + unsigned Patch; + T.getOSVersion(Major, Minor, Patch); + llvm::VersionTuple OSVersion(Major, Minor, Patch); + + OS.attributeBegin("minimumVersion"); + serialize(OSVersion, OS); + OS.attributeEnd(); + }); + }); +} diff --git a/lib/SymbolGraphGen/JSON.h b/lib/SymbolGraphGen/JSON.h new file mode 100644 index 0000000000000..0c0018da724cb --- /dev/null +++ b/lib/SymbolGraphGen/JSON.h @@ -0,0 +1,45 @@ +//===--- JSON.h - Symbol Graph JSON Helpers -------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// Adds Symbol Graph JSON serialization to other types. +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_JSON_H +#define SWIFT_SYMBOLGRAPHGEN_JSON_H + +#include "llvm/ADT/Triple.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/VersionTuple.h" +#include "swift/AST/GenericSignature.h" + +namespace swift { +namespace symbolgraphgen { + +struct AttributeRAII { + StringRef Key; + llvm::json::OStream &OS; + AttributeRAII(StringRef Key, llvm::json::OStream &OS) + : Key(Key), OS(OS) { + OS.attributeBegin(Key); + } + + ~AttributeRAII() { + OS.attributeEnd(); + } +}; + +void serialize(const llvm::VersionTuple &VT, llvm::json::OStream &OS); +void serialize(const llvm::Triple &T, llvm::json::OStream &OS); + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_JSON_H diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp new file mode 100644 index 0000000000000..441a636454051 --- /dev/null +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -0,0 +1,380 @@ +//===--- Symbol.cpp - Symbol Graph Node -----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/ASTContext.h" +#include "swift/AST/Module.h" +#include "swift/AST/ParameterList.h" +#include "swift/Basic/SourceManager.h" +#include "JSON.h" +#include "Symbol.h" +#include "SymbolGraph.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +void Symbol::serializeKind(StringRef Identifier, StringRef DisplayName, + llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("identifier", Identifier); + OS.attribute("displayName", DisplayName); + }); +} + +void Symbol::serializeKind(llvm::json::OStream &OS) const { + AttributeRAII A("kind", OS); + switch (VD->getKind()) { + case swift::DeclKind::Class: + serializeKind("swift.class", "Class", OS); + break; + case swift::DeclKind::Struct: + serializeKind("swift.struct", "Structure", OS); + break; + case swift::DeclKind::Enum: + serializeKind("swift.enum", "Enumeration", OS); + break; + case swift::DeclKind::EnumElement: + serializeKind("swift.enum.case", "Case", OS); + break; + case swift::DeclKind::Protocol: + serializeKind("swift.protocol", "Protocol", OS); + break; + case swift::DeclKind::Constructor: + serializeKind("swift.initializer", "Initializer", OS); + break; + case swift::DeclKind::Func: + serializeKind("swift.function", "Function", OS); + break; + case swift::DeclKind::Var: + serializeKind("swift.variable", "Variable", OS); + break; + case swift::DeclKind::TypeAlias: + serializeKind("swift.typealias", "Type Alias", OS); + break; + case swift::DeclKind::InfixOperator: + serializeKind("swift.infixOperator", "Infix Operator", OS); + break; + case swift::DeclKind::PrefixOperator: + serializeKind("swift.prefixOperator", "Prefix Operator", OS); + break; + case swift::DeclKind::PostfixOperator: + serializeKind("swift.postfixOperator", "Postfix Operator", OS); + break; + default: + llvm_unreachable("Unsupported declaration kind for symbol graph"); + } +} + +void Symbol::serializeIdentifier(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + AttributeRAII A("identifier", OS); + Walker.getSymbolIdentifier(VD).serialize(OS); +} + +void Symbol::serializeNames(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.attributeObject("names", [&](){ + auto Identifier = Walker.getSymbolIdentifier(VD); + OS.attribute("title", Identifier.SimpleComponents.back()); + // "navigator": null + Walker.serializeSubheadingDeclarationFragments("subheading", VD, OS); + // "prose": null + }); +} + +void Symbol::serializePosition(StringRef Key, + unsigned Line, unsigned ByteOffset, + llvm::json::OStream &OS) const { + OS.attributeObject(Key, [&](){ + OS.attribute("line", Line); + OS.attribute("character", ByteOffset); + }); +} + +void Symbol::serializeRange(size_t InitialIndentation, + SourceRange Range, SourceManager &SourceMgr, + llvm::json::OStream &OS) const { + OS.attributeObject("range", [&](){ + auto StartLineAndColumn = SourceMgr.getLineAndColumn(Range.Start); + auto StartLine = StartLineAndColumn.first; + auto StartColumn = StartLineAndColumn.second + InitialIndentation; + serializePosition("start", StartLine, StartColumn, OS); + + auto EndLineAndColumn = SourceMgr.getLineAndColumn(Range.End); + auto EndLine = EndLineAndColumn.first; + auto EndColumn = EndLineAndColumn.second + InitialIndentation; + serializePosition("end", EndLine, EndColumn, OS); + }); +} + +void Symbol::serializeDocComment(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.attributeObject("docComment", [&](){ + auto LL = Walker.Ctx.getLineList(VD->getRawComment()); + size_t InitialIndentation = LL.getLines().empty() + ? 0 + : markup::measureIndentation(LL.getLines().front().Text); + OS.attributeArray("lines", [&](){ + for (const auto &Line : LL.getLines()) { + // Line object + OS.object([&](){ + // Trim off any initial indentation from the line's + // text and start of its source range, if it has one. + if (Line.Range.isValid()) { + serializeRange(InitialIndentation, + Line.Range, Walker.M.getASTContext().SourceMgr, OS); + } + auto TrimmedLine = Line.Text.drop_front(std::min(InitialIndentation, + Line.FirstNonspaceOffset)); + OS.attribute("text", TrimmedLine); + }); + } + }); // end lines: [] + }); // end docComment: +} + +void Symbol::serializeFunctionSignature(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + if (const auto *FD = dyn_cast_or_null(VD)) { + OS.attributeObject("functionSignature", [&](){ + + // Parameters + if (const auto *ParamList = FD->getParameters()) { + if (ParamList->size()) { + OS.attributeArray("parameters", [&](){ + for (const auto *Param : *ParamList) { + auto ExternalName = Param->getArgumentName().str(); + auto InternalName = Param->getParameterName().str(); + + OS.object([&](){ + if (ExternalName.empty()) { + OS.attribute("name", InternalName); + } else { + OS.attribute("name", ExternalName); + if (ExternalName != InternalName && + !InternalName.empty()) { + OS.attribute("internalName", InternalName); + } + } + Walker.serializeDeclarationFragments("declarationFragments", + Param, OS); + }); // end parameter object + } + }); // end parameters: + } + } + + // Returns + if (const auto ReturnType = FD->getResultInterfaceType()) { + Walker.serializeDeclarationFragments("returns", ReturnType, OS); + } + }); + } +} + +void Symbol::serializeGenericParam(const swift::GenericTypeParamType &Param, + llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("name", Param.getName().str()); + OS.attribute("index", Param.getIndex()); + OS.attribute("depth", Param.getDepth()); + }); +} + +void Symbol::serializeGenericRequirement(const swift::Requirement &Req, + llvm::json::OStream &OS) const { + OS.object([&](){ + switch (Req.getKind()) { + case swift::RequirementKind::Conformance: + OS.attribute("kind", "conformance"); + break; + case swift::RequirementKind::Superclass: + OS.attribute("kind", "superclass"); + break; + case swift::RequirementKind::SameType: + OS.attribute("kind", "sameType"); + break; + case swift::RequirementKind::Layout: + return; + } + OS.attribute("lhs", Req.getFirstType()->getString()); + OS.attribute("rhs", Req.getSecondType()->getString()); + }); +} + +void Symbol::serializeSwiftGenericMixin(llvm::json::OStream &OS) const { + if (const auto *GC = VD->getAsGenericContext()) { + if (const auto Generics = GC->getGenericSignature()) { + + OS.attributeObject("swiftGenerics", [&](){ + if (!Generics->getGenericParams().empty()) { + OS.attributeArray("parameters", [&](){ + for (const auto Param : Generics->getGenericParams()) { + if (const auto *D = Param->getDecl()) { + if (D->isImplicit()) { + continue; + } + } + serializeGenericParam(*Param, OS); + } + }); // end parameters: + } + + if (!Generics->getRequirements().empty()) { + OS.attributeArray("constraints", [&](){ + for (const auto &Requirement : Generics->getRequirements()) { + serializeGenericRequirement(Requirement, OS); + } + }); // end constraints: + } + + }); // end swiftGenerics: + } + } +} + +void Symbol::serializeSwiftExtensionMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + if (const auto *Extension + = dyn_cast_or_null(VD->getInnermostDeclContext())) { + OS.attributeObject("swiftExtension", [&](){ + OS.attribute("definedInModule", Walker.M.getNameStr()); + auto Generics = Extension->getGenericSignature(); + if (Generics && !Generics->getRequirements().empty()) { + OS.attributeArray("constraints", [&](){ + for (const auto &Requirement : Generics->getRequirements()) { + serializeGenericRequirement(Requirement, OS); + } + }); // end constraints: + } + }); // end swiftExtension: + } +} + +void Symbol::serializeDeclarationFragmentMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + Walker.serializeDeclarationFragments("declarationFragments", VD, OS); +} + +void Symbol::serializeAccessLevelMixin(llvm::json::OStream &OS) const { + OS.attribute("accessLevel", getAccessLevelSpelling(VD->getFormalAccess())); +} + +llvm::Optional +Symbol::getDomain(PlatformAgnosticAvailabilityKind AgnosticKind, + PlatformKind Kind) const { + switch (AgnosticKind) { + // SPM- and Swift-specific availability. + case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific: + return { "SwiftPM" }; + case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific: + case PlatformAgnosticAvailabilityKind::UnavailableInSwift: + return { "Swift" }; + // Although these are in the agnostic kinds, they are actually a signal + // that there is either platform-specific or completely platform-agnostic. + // They'll be handled below. + case PlatformAgnosticAvailabilityKind::Deprecated: + case PlatformAgnosticAvailabilityKind::Unavailable: + case PlatformAgnosticAvailabilityKind::None: + break; + } + + // Platform-specific availability. + switch (Kind) { + case swift::PlatformKind::iOS: + return { "iOS" }; + case swift::PlatformKind::OSX: + return { "macOS" }; + case swift::PlatformKind::tvOS: + return { "tvOS" }; + case swift::PlatformKind::watchOS: + return { "watchOS" }; + case swift::PlatformKind::iOSApplicationExtension: + return { "iOSAppExtension" }; + case swift::PlatformKind::OSXApplicationExtension: + return { "macOSAppExtension" }; + case swift::PlatformKind::tvOSApplicationExtension: + return { "tvOSAppExtension" }; + case swift::PlatformKind::watchOSApplicationExtension: + return { "watchOSAppExtension" }; + // Platform-agnostic availability, such as "unconditionally deprecated" + // or "unconditionally obsoleted". + case swift::PlatformKind::none: + return None; + } +} + +void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const { + SmallVector Availabilities; + for (const auto *Attr : VD->getAttrs()) { + if (const auto *AvAttr = dyn_cast(Attr)) { + Availabilities.push_back(AvAttr); + } + } + if (Availabilities.empty()) { + return; + } + + OS.attributeArray("availability", [&](){ + for (const auto *AvAttr : Availabilities) { + OS.object([&](){ + auto Domain = getDomain(AvAttr->getPlatformAgnosticAvailability(), + AvAttr->Platform); + if (Domain) { + OS.attribute("domain", *Domain); + } + if (AvAttr->Introduced) { + AttributeRAII Introduced("introduced", OS); + symbolgraphgen::serialize(*AvAttr->Introduced, OS); + } + if (AvAttr->Deprecated) { + AttributeRAII Deprecated("deprecated", OS); + symbolgraphgen::serialize(*AvAttr->Deprecated, OS); + } + if (AvAttr->Obsoleted) { + AttributeRAII Obsoleted("obsoleted", OS); + symbolgraphgen::serialize(*AvAttr->Obsoleted, OS); + } + if (!AvAttr->Message.empty()) { + OS.attribute("message", AvAttr->Message); + } + if (!AvAttr->Rename.empty()) { + OS.attribute("renamed", AvAttr->Rename); + } + if (AvAttr->isUnconditionallyDeprecated()) { + OS.attribute("isUnconditionallyDeprecated", true); + } + if (AvAttr->isUnconditionallyUnavailable()) { + OS.attribute("isUnconditionallyUnavailable", true); + } + }); // end availability object + } + }); // end availability: [] +} + +void Symbol::serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.object([&](){ + serializeKind(OS); + serializeIdentifier(Walker, OS); + serializeNames(Walker, OS); + serializeDocComment(Walker, OS); + + // "Mixins" + serializeFunctionSignature(Walker, OS); + serializeSwiftGenericMixin(OS); + serializeSwiftExtensionMixin(Walker, OS); + serializeDeclarationFragmentMixin(Walker, OS); + serializeAccessLevelMixin(OS); + serializeAvailabilityMixin(OS); + }); +} diff --git a/lib/SymbolGraphGen/Symbol.h b/lib/SymbolGraphGen/Symbol.h new file mode 100644 index 0000000000000..9cdbca5757164 --- /dev/null +++ b/lib/SymbolGraphGen/Symbol.h @@ -0,0 +1,128 @@ +//===--- Symbol.h- Symbol Graph Node --------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOL_H +#define SWIFT_SYMBOLGRAPHGEN_SYMBOL_H + +#include "llvm/Support/JSON.h" +#include "swift/AST/Attr.h" +#include "swift/Basic/LLVM.h" +#include "swift/Markup/Markup.h" + +namespace swift { +namespace symbolgraphgen { + +struct AvailabilityDomain; +struct SymbolGraphASTWalker; + +/** + An identifier for a symbol that provides a globally unique identifier suitable for + internal lookups and a locally unique path for human use, such as a URL. + */ +struct SymbolIdentifier { + /** + A string that uniquely identifies a symbol within a module in the event of + ambiguities. A precise identifier need not be human readable. + */ + StringRef PreciseIdentifier; + + /** + The components for a "fully qualified" identifier. + */ + ArrayRef SimpleComponents; + + SymbolIdentifier(llvm::StringRef PreciseIdentifier, + ArrayRef SimpleComponents) + : PreciseIdentifier(PreciseIdentifier), + SimpleComponents(SimpleComponents) { + assert(!PreciseIdentifier.empty()); + } + + void serialize(llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("precise", PreciseIdentifier); + OS.attributeArray("simpleComponents", [&](){ + for (auto Component : SimpleComponents) { + OS.value(Component); + } + }); + }); + } + + bool operator==(const SymbolIdentifier &Other) const { + return PreciseIdentifier == Other.PreciseIdentifier && + SimpleComponents == Other.SimpleComponents; + } +}; + +/// A symbol from a module: a node in a graph. +struct Symbol { + const ValueDecl *VD; + + void serializeKind(StringRef Identifier, StringRef DisplayName, + llvm::json::OStream &OS) const; + + void serializeKind(llvm::json::OStream &OS) const; + + void serializeIdentifier(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeNames(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializePosition(StringRef Key, unsigned Line, unsigned ByteOffset, + llvm::json::OStream &OS) const; + + void serializeRange(size_t InitialIdentation, + SourceRange Range, SourceManager &SourceMgr, + llvm::json::OStream &OS) const; + + void serializeDocComment(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeFunctionSignature(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeGenericParam(const swift::GenericTypeParamType &Param, + llvm::json::OStream &OS) const; + + void serializeGenericRequirement(const swift::Requirement &Req, + llvm::json::OStream &OS) const; + + void serializeSwiftGenericMixin(llvm::json::OStream &OS) const; + + void serializeSwiftExtensionMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeDeclarationFragmentMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeAccessLevelMixin(llvm::json::OStream &OS) const; + + llvm::Optional + getDomain(PlatformAgnosticAvailabilityKind AgnosticKind, + PlatformKind Kind) const; + + void serializeAvailabilityMixin(llvm::json::OStream &OS) const; + + void serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + bool operator==(const Symbol &Other) const { + return VD == Other.VD; + } +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_SYMBOL_H diff --git a/lib/SymbolGraphGen/SymbolGraph.cpp b/lib/SymbolGraphGen/SymbolGraph.cpp new file mode 100644 index 0000000000000..0d41bdcf4a638 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraph.cpp @@ -0,0 +1,68 @@ +//===--- SymbolGraph.cpp - Symbol Graph Data Structure -------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Module.h" +#include "swift/Basic/Version.h" + +#include "FormatVersion.h" +#include "SymbolGraph.h" + +using namespace swift; +using namespace symbolgraphgen; + +SymbolGraph::SymbolGraph(ModuleDecl &M, llvm::Triple Target, + Optional ModuleVersion) +: M(M), Target(Target), ModuleVersion(ModuleVersion) {} + +void SymbolGraph::serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attributeObject("metadata", [&](){ + { + AttributeRAII FV("formatVersion", OS); + llvm::VersionTuple FormatVersion(SWIFT_SYMBOLGRAPH_FORMAT_MAJOR, + SWIFT_SYMBOLGRAPH_FORMAT_MINOR, + SWIFT_SYMBOLGRAPH_FORMAT_PATCH); + symbolgraphgen::serialize(FormatVersion, OS); + } // end formatVersion: + + auto VersionString = version::getSwiftFullVersion(); + StringRef VersionStringRef(VersionString.c_str(), VersionString.size()); + OS.attribute("generator", VersionStringRef); + }); // end metadata: + + OS.attributeObject("module", [&](){ + OS.attribute("name", M.getNameStr()); + AttributeRAII Platform("platform", OS); + symbolgraphgen::serialize(Target, OS); + }); + + if (ModuleVersion) { + AttributeRAII MV("moduleVersion", OS); + symbolgraphgen::serialize(*ModuleVersion, OS); + } + + OS.attributeArray("symbols", [&](){ + for (const auto *VD: Nodes) { + Symbol S { VD }; + S.serialize(Walker, OS); + } + }); + + OS.attributeArray("relationships", [&](){ + for (const auto Relationship : Edges) { + Relationship.serialize(OS); + } + }); + + }); +} diff --git a/lib/SymbolGraphGen/SymbolGraph.h b/lib/SymbolGraphGen/SymbolGraph.h new file mode 100644 index 0000000000000..1e893780e35f3 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraph.h @@ -0,0 +1,64 @@ +//===--- SymbolGraph.h - Symbol Graph Data Structure ----------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPH_H +#define SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPH_H + +#include "llvm/ADT/SmallSet.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/VersionTuple.h" +#include "swift/Basic/LLVM.h" +#include "Edge.h" +#include "JSON.h" + +namespace swift { +namespace symbolgraphgen { + +/// A graph of symbols and the relationships between them. +struct SymbolGraph { + /** + The module this symbol graph represents. + */ + ModuleDecl &M; + + /** + The module's target triple. + */ + llvm::Triple Target; + + /** + The semantic version of the module that this symbol graph describes, + if known. + */ + Optional ModuleVersion; + + /** + The symbols in a module: the nodes in the graph. + */ + llvm::SmallPtrSet Nodes; + + /** + The relationships between symbols: the edges in the graph. + */ + llvm::DenseSet Edges; + + SymbolGraph(ModuleDecl &M, llvm::Triple Target, + Optional ModuleVersion = None); + + void serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPH_H diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp new file mode 100644 index 0000000000000..2ce1337d65468 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp @@ -0,0 +1,364 @@ +//===--- SymbolGraphASTWalker.cpp - Symbol Graph AST Walker ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclObjC.h" +#include "llvm/ADT/StringSwitch.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/ASTPrinter.h" +#include "swift/AST/Decl.h" +#include "swift/AST/GenericSignature.h" +#include "swift/AST/Module.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/USRGeneration.h" +#include "swift/Basic/PrimitiveParsing.h" +#include "swift/Markup/Markup.h" +#include "swift/SymbolGraphGen/SymbolGraphGen.h" + +#include "DeclarationFragmentPrinter.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +SymbolGraphASTWalker::SymbolGraphASTWalker(ModuleDecl &M, + const SymbolGraphOptions &Options) + : Options(Options), + M(M), + Graph(M, Options.Target) {} + +/// Returns `true` if the symbol should be included as a node in the graph. +bool SymbolGraphASTWalker::shouldIncludeNode(const Decl *D) const { + // If this decl isn't in this module, don't record it, + // as it will appear elsewhere in its module's symbol graph. + if (D->getModuleContext()->getName() != M.getName()) { + return false; + } + + // Implicit declarations are probably not going to have documentation, + // so don't record it in the symbol graph. + if (D->isImplicit()) { + return false; + } + + // At this point, the declaration must be a ValueDecl. + auto VD = cast(D); + + // Don't record unconditionally private declarations + if (VD->isPrivateStdlibDecl(/*treatNonBuiltinProtocolsAsPublic=*/false)) { + return false; + } + + // Don't record effectively internal declarations if specified + if (Options.MinimumAccessLevel > AccessLevel::Internal && + VD->hasUnderscoredNaming()) { + return false; + } + + // Symbols must meet the minimum access level to be included in the graph. + if (VD->getFormalAccess() < Options.MinimumAccessLevel) { + return false; + } + + // Special cases + + auto BaseName = VD->getBaseName().userFacingName(); + + // ${MODULE}Version{Number,String} in ${Module}.h + SmallString<32> VersionNameIdentPrefix { M.getName().str() }; + VersionNameIdentPrefix.append("Version"); + + if (BaseName.startswith(VersionNameIdentPrefix.str())) { + return false; + } + + // Automatically mapped SIMD types + bool ShouldInclude = llvm::StringSwitch(BaseName) +#define MAP_SIMD_TYPE(C_TYPE, _, __) \ + .Case("swift_" #C_TYPE "2", false) \ + .Case("swift_" #C_TYPE "3", false) \ + .Case("swift_" #C_TYPE "4", false) +#include "swift/ClangImporter/SIMDMappedTypes.def" + .Case("SWIFT_TYPEDEFS", false) + .Case("char16_t", false) + .Case("char32_t", false) + .Default(true); + + return ShouldInclude; +} + +bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) { + + switch (D->getKind()) { + // We'll record nodes for the following kinds of declarations. + case swift::DeclKind::Class: + case swift::DeclKind::Struct: + case swift::DeclKind::Enum: + case swift::DeclKind::EnumElement: + case swift::DeclKind::Protocol: + case swift::DeclKind::Constructor: + case swift::DeclKind::Func: + case swift::DeclKind::Var: + case swift::DeclKind::TypeAlias: + break; + + // We'll descend into everything else. + default: + return true; + } + + if (!shouldIncludeNode(D)) { + return false; + } + + auto *VD = cast(D); + Graph.Nodes.insert(VD); + + // Record all of the possible relationships (edges) originating + // with this declaration. + recordMemberRelationship(VD); + recordConformanceRelationships(VD); + recordInheritanceRelationships(VD); + recordDefaultImplementationRelationships(VD); + recordOverrideRelationship(VD); + recordRequirementRelationships(VD); + recordOptionalRequirementRelationships(VD); + + return true; +} + +StringRef SymbolGraphASTWalker::getUSR(const ValueDecl *VD) { + auto Found = USRCache.find(VD); + if (Found != USRCache.end()) { + return Found->second; + } + llvm::SmallString<32> Scratch; + llvm::raw_svector_ostream OS(Scratch); + ide::printDeclUSR(VD, OS); + auto USR = Ctx.allocateCopy(Scratch.str()); + USRCache.insert({VD, USR}); + return USR; +} + +SymbolIdentifier +SymbolGraphASTWalker::getSymbolIdentifier(const ValueDecl *VD) { + // Look in the symbol identifier cache for this declartion. + auto Found = SymbolIdentifierCache.find(VD); + if (Found != SymbolIdentifierCache.end()) { + return Found->getSecond(); + } + + // Not found; need to build a symbol identifier and add it to the cache. + auto PreciseIdentifier = getUSR(VD); + llvm::SmallVector SimpleIdentifierChain; + + // Collect the spellings of the fully qualified identifier components. + auto Decl = VD; + while (Decl && !isa(Decl)) { + SmallString<32> Scratch; + Decl->getFullName().getString(Scratch); + SimpleIdentifierChain.push_back(Ctx.allocateCopy(Scratch.str())); + if (const auto *DC = Decl->getDeclContext()) { + if (const auto *Proto = DC->getExtendedProtocolDecl()) { + Decl = Proto; + } else if (const auto *Ext = dyn_cast_or_null(DC->getAsDecl())) { + Decl = Ext->getExtendedNominal(); + } else { + Decl = dyn_cast_or_null(DC->getAsDecl()); + } + } else { + Decl = nullptr; + } + } + + // The list is leaf-to-root, but our list is root-to-leaf, so reverse it. + std::reverse(SimpleIdentifierChain.begin(), SimpleIdentifierChain.end()); + + SymbolIdentifier Identifier { + PreciseIdentifier, + Ctx.allocateCopy(llvm::makeArrayRef(SimpleIdentifierChain)) + }; + + SymbolIdentifierCache.insert({VD, Identifier}); + return Identifier; +} + +PrintOptions SymbolGraphASTWalker::getDeclarationFragmentsPrintOptions() const { + PrintOptions Opts; + Opts.FunctionDefinitions = false; + Opts.ArgAndParamPrinting = + PrintOptions::ArgAndParamPrintingMode::ArgumentOnly; + Opts.PrintGetSetOnRWProperties = false; + Opts.PrintPropertyAccessors = false; + Opts.PrintSubscriptAccessors = false; + Opts.SkipUnderscoredKeywords = true; + Opts.SkipAttributes = true; + Opts.PrintOverrideKeyword = true; + Opts.PrintImplicitAttrs = false; + Opts.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; + Opts.PrintUserInaccessibleAttrs = false; + Opts.SkipPrivateStdlibDecls = true; + Opts.SkipUnderscoredStdlibProtocols = true; + + Opts.ExclusiveAttrList.clear(); + +#define DECL_ATTR(SPELLING, CLASS, OPTIONS, CODE) Opts.ExcludeAttrList.push_back(DAK_##CLASS); +#define TYPE_ATTR(X) Opts.ExcludeAttrList.push_back(TAK_##X); +#include "swift/AST/Attr.def" + + return Opts; +} + +void +SymbolGraphASTWalker::serializeDeclarationFragments(StringRef Key, + const ValueDecl *VD, + llvm::json::OStream &OS) { + DeclarationFragmentPrinter Printer(*this, OS, Key); + VD->print(Printer, getDeclarationFragmentsPrintOptions()); +} + +void +SymbolGraphASTWalker::serializeSubheadingDeclarationFragments(StringRef Key, + const ValueDecl *VD, + llvm::json::OStream &OS) { + DeclarationFragmentPrinter Printer(*this, OS, Key); + auto Options = getDeclarationFragmentsPrintOptions(); + Options.VarInitializers = false; + Options.PrintDefaultArgumentValue = false; + Options.PrintEmptyArgumentNames = false; + Options.PrintOverrideKeyword = false; + VD->print(Printer, Options); +} + +void +SymbolGraphASTWalker::serializeDeclarationFragments(StringRef Key, Type T, + llvm::json::OStream &OS) { + DeclarationFragmentPrinter Printer(*this, OS, Key); + T->print(Printer, getDeclarationFragmentsPrintOptions()); +} + +void SymbolGraphASTWalker::recordEdge(const ValueDecl *Source, + const ValueDecl *Target, + RelationshipKind Kind) { + if (Target->isPrivateStdlibDecl( + /*treatNonBuiltinProtocolsAsPublic = */false)) { + return; + } + + // There might be relationships on implicit declarations, + // such as overriding implicit @objc init(). + if (Target->isImplicit()) { + return; + } + + Graph.Nodes.insert(Source); + Graph.Nodes.insert(Target); + + Graph.Edges.insert({this, Kind, Source, Target}); +} + +void SymbolGraphASTWalker::recordMemberRelationship(const ValueDecl *VD) { + auto *DC = VD->getDeclContext(); + switch (DC->getContextKind()) { + case DeclContextKind::GenericTypeDecl: + case DeclContextKind::ExtensionDecl: + case swift::DeclContextKind::EnumElementDecl: + return recordEdge(VD, VD->getDeclContext()->getSelfNominalTypeDecl(), + RelationshipKind::MemberOf()); + case swift::DeclContextKind::AbstractClosureExpr: + case swift::DeclContextKind::Initializer: + case swift::DeclContextKind::TopLevelCodeDecl: + case swift::DeclContextKind::SubscriptDecl: + case swift::DeclContextKind::AbstractFunctionDecl: + case swift::DeclContextKind::SerializedLocal: + case swift::DeclContextKind::Module: + case swift::DeclContextKind::FileUnit: + break; + } +} + +void +SymbolGraphASTWalker::recordInheritanceRelationships(const ValueDecl *VD) { + if (const auto *NTD = dyn_cast(VD)) { + for (const auto &InheritanceLoc : NTD->getInherited()) { + auto Ty = InheritanceLoc.getType(); + if (!Ty) { + continue; + } + auto *InheritedTypeDecl = + dyn_cast_or_null(Ty->getAnyNominal()); + if (!InheritedTypeDecl) { + continue; + } + + recordEdge(VD, InheritedTypeDecl, RelationshipKind::InheritsFrom()); + } + } +} + +void SymbolGraphASTWalker::recordDefaultImplementationRelationships( + const ValueDecl *VD) { + if (const auto *Extension = dyn_cast(VD->getDeclContext())) { + if (const auto *Protocol = Extension->getExtendedProtocolDecl()) { + for (const auto *Member : Protocol->getMembers()) { + if (const auto *MemberVD = dyn_cast(Member)) { + if (MemberVD->getFullName().compare(VD->getFullName()) == 0) { + recordEdge(VD, MemberVD, + RelationshipKind::DefaultImplementationOf()); + } + } + } + } + } +} + +void +SymbolGraphASTWalker::recordRequirementRelationships(const ValueDecl *VD) { + if (const auto *Protocol = dyn_cast(VD->getDeclContext())) { + if (VD->isProtocolRequirement()) { + recordEdge(VD, Protocol, RelationshipKind::RequirementOf()); + } + } +} + +void SymbolGraphASTWalker::recordOptionalRequirementRelationships( + const ValueDecl *VD) { + if (const auto *Protocol = dyn_cast(VD->getDeclContext())) { + if (VD->isProtocolRequirement()) { + if (const auto *ClangDecl = VD->getClangDecl()) { + if (const auto *Method = dyn_cast(ClangDecl)) { + if (Method->isOptional()) { + recordEdge(VD, Protocol, + RelationshipKind::OptionalRequirementOf()); + } + } + } + } + } +} + +void +SymbolGraphASTWalker::recordConformanceRelationships(const ValueDecl *VD) { + if (const auto *NTD = dyn_cast(VD)) { + for (const auto *Conformance : NTD->getAllConformances()) { + recordEdge(VD, Conformance->getProtocol(), + RelationshipKind::ConformsTo()); + } + } +} + +void SymbolGraphASTWalker::recordOverrideRelationship(const ValueDecl *VD) { + if (const auto *Override = VD->getOverriddenDecl()) { + recordEdge(VD, Override, RelationshipKind::Overrides()); + } +} diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.h b/lib/SymbolGraphGen/SymbolGraphASTWalker.h new file mode 100644 index 0000000000000..fb3e3dc9b6d0d --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.h @@ -0,0 +1,161 @@ +//===--- SymbolGraphASTWalker.h - Symbol Graph AST Walker -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHASTWALKER_H +#define SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHASTWALKER_H + +#include "swift/Basic/LLVM.h" +#include "swift/IDE/SourceEntityWalker.h" +#include "swift/Markup/Markup.h" + +#include "SymbolGraph.h" + +namespace swift { + +class Decl; +class Type; +class ValueDecl; + +namespace symbolgraphgen { + +struct SymbolIdentifier; +struct SymbolGraph; +struct SymbolGraphOptions; + +/** + The `SymbolGraphASTWalker` is the core implementation that builds a + symbol graph. It walks a module for declarations, recording facts about + symbols and relationships between them. + */ +struct SymbolGraphASTWalker : public SourceEntityWalker { + /// Options for collecting and serialization. + const SymbolGraphOptions &Options; + + /// The module that this symbol graph will represent. + const ModuleDecl &M; + + /// The symbol graph. + SymbolGraph Graph; + + /// A context for allocations. + markup::MarkupContext Ctx; + + /// A cache of identifiers for declarations that may be seen more than once. + llvm::DenseMap SymbolIdentifierCache; + + /// A cache of USRs for declarations. + llvm::DenseMap USRCache; + + // MARK: - + + SymbolGraphASTWalker(ModuleDecl &M, const SymbolGraphOptions &Options); + virtual ~SymbolGraphASTWalker() {} + + // MARK: - + + /// Returns `true` if the symbol should be included as a node in the graph. + bool shouldIncludeNode(const Decl *D) const; + + virtual bool walkToDeclPre(Decl *D, CharSourceRange Range); + + // MARK: - Utilities and Conversions + + /// Get the USR of a declaration and add it to the local allocator. + StringRef getUSR(const ValueDecl *VD); + + /// Returns a `SymbolIdentifier` for a given declaration. + SymbolIdentifier getSymbolIdentifier(const ValueDecl *VD); + + // MARK: - Declaration Fragments + + /// Get the base print options for declaration fragments. + PrintOptions getDeclarationFragmentsPrintOptions() const; + + /// Serialize the overall declaration fragments for a `ValueDecl`. + void + serializeDeclarationFragments(StringRef Key, const ValueDecl *VD, + llvm::json::OStream &OS); + + /// Get the overall declaration fragments for a `ValueDecl` when it is viewed + /// as a subheading and/or part of a larger group of symbol listings. + void + serializeSubheadingDeclarationFragments(StringRef Key, const ValueDecl *VD, + llvm::json::OStream &OS); + + /// Get the overall declaration for a type declaration. + void + serializeDeclarationFragments(StringRef Key, Type T, + llvm::json::OStream &OS); + + // MARK: - Relationships (Edges) + + /** + Record a relationship between two declarations as an edge in the graph. + + \param Source The declaration serving as the source of the edge in the + directed graph. + \param Target The declaration serving as the target of the edge in the + directed graph. + \param Kind The kind of relationship the edge represents. + */ + void recordEdge(const ValueDecl *Source, const ValueDecl *Target, + RelationshipKind Kind); + + /** + Record a MemberOf relationship, if the given declaration is nested + in another. + */ + void recordMemberRelationship(const ValueDecl *VD); + + /** + Record InheritsFrom relationships for every class from which the + declaration inherits. + */ + void recordInheritanceRelationships(const ValueDecl *VD); + + /** + If the declaration is a default implementation in a protocol extension, + record a DefaultImplementationOf relationship between the declaration and + the requirement. + */ + void recordDefaultImplementationRelationships(const ValueDecl *VD); + + /** + Record a RequirementOf relationship if the declaration is a requirement + of a protocol. + */ + void recordRequirementRelationships(const ValueDecl *VD); + + /** + If the declaration is an Objective-C-based optional protocol requirement, + record an OptionalRequirementOf relationship between the declaration + and its containing protocol. + */ + void recordOptionalRequirementRelationships(const ValueDecl *VD); + + /** + Record ConformsTo relationships for each protocol conformance of + the declaration. + */ + void recordConformanceRelationships(const ValueDecl *VD); + + /** + Records an Overrides relationship if the given declaration + overrides another. + */ + void recordOverrideRelationship(const ValueDecl *VD); +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHASTWALKER_H diff --git a/lib/SymbolGraphGen/SymbolGraphGen.cpp b/lib/SymbolGraphGen/SymbolGraphGen.cpp new file mode 100644 index 0000000000000..667d7b3e328c4 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraphGen.cpp @@ -0,0 +1,56 @@ +//===--- SymbolGraphGen.cpp - Symbol Graph Generator Entry Point ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/JSON.h" +#include "llvm/Support/Path.h" +#include "swift/AST/Module.h" +#include "swift/SymbolGraphGen/SymbolGraphGen.h" + +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +// MARK: - Main Entry Point + +/// Emit a symbol graph JSON file for a `ModuleDecl`. +int +symbolgraphgen::emitSymbolGraphForModule(ModuleDecl *M, + const SymbolGraphOptions &Options) { + SymbolGraphASTWalker Walker(*M, Options); + SmallVector ModuleDecls; + M->getDisplayDecls(ModuleDecls); + + llvm::errs() << ModuleDecls.size() + << " top-level declarations in this module.\n"; + + for (auto *Decl : ModuleDecls) { + Walker.walk(Decl); + } + + llvm::errs() + << "Found " << Walker.Graph.Nodes.size() << " symbols and " + << Walker.Graph.Edges.size() << " relationships.\n"; + + std::error_code Error; + llvm::raw_fd_ostream OS(Options.OutputPath, Error, llvm::sys::fs::FA_Write); + if (Error) { + llvm::errs() << "Couldn't open output file for writing: " + << Error.message() << "\n"; + return EXIT_FAILURE; + } + + llvm::json::OStream J(OS, Options.PrettyPrint ? 2 : 0); + Walker.Graph.serialize(Walker, J); + + return EXIT_SUCCESS; +} diff --git a/test/IDE/comment_brief.swift b/test/IDE/comment_brief.swift index 1409f3e9fd36f..db447aaca1807 100644 --- a/test/IDE/comment_brief.swift +++ b/test/IDE/comment_brief.swift @@ -145,8 +145,8 @@ struct Indentation { // CHECK-NEXT: Func/briefBlockWithASCIIArt5 {{.*}} BriefComment=[Aaa. Bbb. *Ccc.] // CHECK-NEXT: Func/briefBlockWithASCIIArt6 {{.*}} BriefComment=[Aaa.] // CHECK-NEXT: Func/briefMixed1 {{.*}} BriefComment=[Aaa. Bbb.] -// CHECK-NEXT: Func/briefMixed2 {{.*}} BriefComment=[Aaa.] -// CHECK-NEXT: Func/briefMixed3 {{.*}} BriefComment=[Aaa.] +// CHECK-NEXT: Func/briefMixed2 {{.*}} BriefComment=[Aaa. Bbb.] +// CHECK-NEXT: Func/briefMixed3 {{.*}} BriefComment=[Aaa. Bbb.] // CHECK-NEXT: Struct/Indentation RawComment=none // CHECK-NEXT: Func/Indentation.briefBlockWithASCIIArt1 {{.*}} BriefComment=[Aaa.] diff --git a/test/SymbolGraph/Module.swift b/test/SymbolGraph/Module.swift new file mode 100644 index 0000000000000..dcfba01e9a43b --- /dev/null +++ b/test/SymbolGraph/Module.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name SymbolGraphModule -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name SymbolGraphModule -I %t -pretty-print -o %t/SymbolGraphModule.symbols.json +// RUN: %FileCheck %s --input-file %t/SymbolGraphModule.symbols.json + +public struct S { + public var x: Int +} + +// CHECK: module +// CHECK-NEXT: "name": "SymbolGraphModule" +// CHECK-NEXT: platform +// CHECK-NEXT: architecture +// CHECK: vendor +// CHECK-NEXT: operatingSystem +// CHECK-NEXT: name +// CHECK-NEXT: minimumVersion +// CHECK-NEXT: major +// CHECK-NEXT: minor +// CHECK-NEXT: patch diff --git a/test/SymbolGraph/Relationships/ConformsTo.swift b/test/SymbolGraph/Relationships/ConformsTo.swift new file mode 100644 index 0000000000000..c895fd5f7bfc6 --- /dev/null +++ b/test/SymbolGraph/Relationships/ConformsTo.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ConformsTo -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ConformsTo -I %t -pretty-print -o %t/ConformsTo.symbols.json +// RUN: %FileCheck %s --input-file %t/ConformsTo.symbols.json + +public protocol P { + var x: Int { get } +} + +public struct S: P { + public var x: Int +} + +// CHECK: "kind": "conformsTo" +// CHECK-NEXT: "source": "s:10ConformsTo1SV" +// CHECK-NEXT: "target": "s:10ConformsTo1PP" diff --git a/test/SymbolGraph/Relationships/DefaultImplementationOf.swift b/test/SymbolGraph/Relationships/DefaultImplementationOf.swift new file mode 100644 index 0000000000000..3193cfdf15200 --- /dev/null +++ b/test/SymbolGraph/Relationships/DefaultImplementationOf.swift @@ -0,0 +1,18 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DefaultImplementationOf -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DefaultImplementationOf -I %t -pretty-print -o %t/DefaultImplementationOf.symbols.json +// RUN: %FileCheck %s --input-file %t/DefaultImplementationOf.symbols.json + +public protocol P { + var x: Int { get } +} + +extension P { + public var x: Int { + return 2 + } +} + +// CHECK: "kind": "defaultImplementationOf", +// CHECK-NEXT: "source": "s:23DefaultImplementationOf1PPAAE1xSivp", +// CHECK-NEXT: "target": "s:23DefaultImplementationOf1PP1xSivp" diff --git a/test/SymbolGraph/Relationships/InheritsFrom.swift b/test/SymbolGraph/Relationships/InheritsFrom.swift new file mode 100644 index 0000000000000..feb9c4f9ec61b --- /dev/null +++ b/test/SymbolGraph/Relationships/InheritsFrom.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name InheritsFrom -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name InheritsFrom -I %t -pretty-print -o %t/InheritsFrom.symbols.json +// RUN: %FileCheck %s --input-file %t/InheritsFrom.symbols.json + +public class Base {} +public class Derived: Base {} + +// CHECK: "kind": "inheritsFrom" +// CHECK-NEXT: "source": "s:12InheritsFrom7DerivedC" +// CHECK-NEXT: "target": "s:12InheritsFrom4BaseC" diff --git a/test/SymbolGraph/Relationships/MemberOf.swift b/test/SymbolGraph/Relationships/MemberOf.swift new file mode 100644 index 0000000000000..f338e74aff62f --- /dev/null +++ b/test/SymbolGraph/Relationships/MemberOf.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name MemberOf -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name MemberOf -I %t -pretty-print -o %t/MemberOf.symbols.json +// RUN: %FileCheck %s --input-file %t/MemberOf.symbols.json + +public struct S { + public var x: Int +} + +// CHECK: "kind": "memberOf" +// CHECK-NEXT: "source": "s:8MemberOf1SV1xSivp" +// CHECK-NEXT: "target": "s:8MemberOf1SV" diff --git a/test/SymbolGraph/Relationships/Overrides.swift b/test/SymbolGraph/Relationships/Overrides.swift new file mode 100644 index 0000000000000..85d2f7afb57f2 --- /dev/null +++ b/test/SymbolGraph/Relationships/Overrides.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Overrides -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Overrides -I %t -pretty-print -o %t/Overrides.symbols.json +// RUN: %FileCheck %s --input-file %t/Overrides.symbols.json + +public class Base { + public var x: Int { + return 1 + } +} + +public class Derived: Base { + public override var x: Int { + return 2 + } +} + +// CHECK: "kind": "overrides" +// CHECK-NEXT: "source": "s:9Overrides7DerivedC1xSivp" +// CHECK-NEXT: "target": "s:9Overrides4BaseC1xSivp" diff --git a/test/SymbolGraph/Relationships/RequirementOf.swift b/test/SymbolGraph/Relationships/RequirementOf.swift new file mode 100644 index 0000000000000..a06507b523b7e --- /dev/null +++ b/test/SymbolGraph/Relationships/RequirementOf.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ConformsTo -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ConformsTo -I %t -pretty-print -o %t/ConformsTo.symbols.json +// RUN: %FileCheck %s --input-file %t/ConformsTo.symbols.json + +public protocol P { + var x: Int { get } +} + +// CHECK: "kind": "requirementOf" +// CHECK-NEXT: "source": "s:10ConformsTo1PP1xSivp" +// CHECK-NEXT: "target": "s:10ConformsTo1PP" diff --git a/test/SymbolGraph/Relationships/TargetFallback.swift b/test/SymbolGraph/Relationships/TargetFallback.swift new file mode 100644 index 0000000000000..274c423d0f748 --- /dev/null +++ b/test/SymbolGraph/Relationships/TargetFallback.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name TargetFallback -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name TargetFallback -I %t -pretty-print -o %t/TargetFallback.symbols.json +// RUN: %FileCheck %s --input-file %t/TargetFallback.symbols.json + +public struct S: CustomStringConvertible { + public var x: Int + public var description: String { + return x.description + } +} + +// CHECK: "kind": "conformsTo", +// CHECK-NEXT: "source": "s:14TargetFallback1SV", +// CHECK-NEXT: "target": "s:s23CustomStringConvertibleP", +// CHECK-NEXT: "targetFallback": "Swift.CustomStringConvertible" diff --git a/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift b/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift new file mode 100644 index 0000000000000..eb6e3bb15633f --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name IncludeInternal -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name IncludeInternal -I %t -pretty-print -o %t/IncludeInternal.symbols.json -minimum-access-level internal +// RUN: %FileCheck %s --input-file %t/IncludeInternal.symbols.json + +public struct ShouldAppear { + public var x: Int +} + +internal struct ShouldAlsoAppear { + internal var x: Int +} + +private struct ShouldntAppear { + var x: Int +} + +// CHECK: ShouldAppear +// CHECK: ShouldAlsoAppear +// CHECK-NOT: ShouldntAppear diff --git a/test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift b/test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift new file mode 100644 index 0000000000000..258b29058f1c1 --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift @@ -0,0 +1,15 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name PublicDefault -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name PublicDefault -I %t -pretty-print -o %t/PublicDefault.symbols.json +// RUN: %FileCheck %s --input-file %t/PublicDefault.symbols.json + +public struct ShouldAppear { + public var x: Int +} + +internal struct ShouldntAppear { + internal var x: Int +} + +// CHECK: ShouldAppear +// CHECK-NOT: ShouldntAppear diff --git a/test/SymbolGraph/Symbols/AccessLevels.swift b/test/SymbolGraph/Symbols/AccessLevels.swift new file mode 100644 index 0000000000000..e7d3daec46132 --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevels.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name AccessLevels -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name AccessLevels -I %t -pretty-print -o %t/AccessLevels.symbols.json +// RUN: %FileCheck %s --input-file %t/AccessLevels.symbols.json + +// CHECK: "accessLevel": "public" + +public struct PublicStruct { + public var x: Int +} + +// CHECK-NOT: "accessLevel": "private" diff --git a/test/SymbolGraph/Symbols/DocComment.swift b/test/SymbolGraph/Symbols/DocComment.swift new file mode 100644 index 0000000000000..b9c07d2df1548 --- /dev/null +++ b/test/SymbolGraph/Symbols/DocComment.swift @@ -0,0 +1,54 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DocComment -emit-module-path %t/DocComment.swiftmodule +// RUN: %target-swift-symbolgraph-extract -module-name DocComment -I %t -pretty-print -o %t/DocComment.symbols.json +// RUN: %FileCheck %s --input-file %t/DocComment.symbols.json + +// CHECK: "text": "Single line." + +/// Single line. +public struct S1 {} + +// CHECK: "text": "Two" +// CHECK: "text": "lines." + +/// Two +/// lines. +public struct S2 {} + +// CHECK: "text": "There are" +// CHECK: "text": "three lines" +// CHECK: "text": "here." + +/// There are +/// three lines +/// here. +public struct S3 {} + +// CHECK: "text": "Comment" +// CHECK: "text": "block." + +/** + Comment + block. +*/ +public struct S4 {} + +// CHECK: "text": "Comment block" +// CHECK: "text": "with more indentation." + +/** + Comment block + with more indentation. + */ +public struct S5 {} + +// CHECK: "text": "With" +// CHECK: "text": "ASCII" +// CHECK: "text": "art." + +/** + * With + * ASCII + * art. + */ +public struct S6 {} diff --git a/test/SymbolGraph/Symbols/Kinds.swift b/test/SymbolGraph/Symbols/Kinds.swift new file mode 100644 index 0000000000000..ec79235016659 --- /dev/null +++ b/test/SymbolGraph/Symbols/Kinds.swift @@ -0,0 +1,42 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Kinds -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Kinds -I %t -pretty-print -o %t/Kinds.symbols.json +// RUN: %FileCheck %s --input-file %t/Kinds.symbols.json + +// CHECK: "identifier": "swift.class" +// CHECK-NEXT: "displayName": "Class" +public class C {} + +// CHECK: "identifier": "swift.struct" +// CHECK-NEXT: "displayName": "Structure" +public struct S { + // CHECK: "identifier": "swift.variable" + // CHECK-NEXT: "displayName": "Variable" + public var x: Int + + // CHECK: "identifier": "swift.initializer" + // CHECK-NEXT: "displayName": "Initializer" + public init(x: Int) { + self.x = x + } + + // CHECK: "identifier": "swift.function" + // CHECK-NEXT: "displayName": "Function" + public func foo() {} +} + +// CHECK: "identifier": "swift.enum" +// CHECK-NEXT: "displayName": "Enumeration" +public enum E { + // CHECK: "identifier": "swift.enum.case" + // CHECK-NEXT: "displayName": "Case" + case oneCase +} + +// CHECK: "identifier": "swift.protocol" +// CHECK-NEXT: "displayName": "Protocol" +public protocol P {} + +// CHECK: "identifier": "swift.typealias" +// CHECK-NEXT: "displayName": "Type Alias" +public typealias Alias = S diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift new file mode 100644 index 0000000000000..d380b84e5d766 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift @@ -0,0 +1,24 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Availability -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Availability -I %t -pretty-print -o %t/Availability.symbols.json +// RUN: %FileCheck %s --input-file %t/Availability.symbols.json + +@available(macOS, introduced: 10.9, deprecated: 10.10, obsoleted: 10.11, message: "Everyone makes mistakes", renamed: "S2") +public struct S {} + +// CHECK: "domain": "macOS" + +// CHECK: introduced +// CHECK-NEXT: "major": 10 +// CHECK-NEXT: "minor": 9 + +// CHECK: deprecated +// CHECK-NEXT: "major": 10 +// CHECK-NEXT: "minor": 10 + +// CHECK: obsoleted +// CHECK-NEXT: "major": 10 +// CHECK-NEXT: "minor": 11 + +// CHECK: "message": "Everyone makes mistakes" +// CHECK: "renamed": "S2" diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift new file mode 100644 index 0000000000000..96488cbc63d20 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name UnconditionallyDeprecated -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name UnconditionallyDeprecated -I %t -pretty-print -o %t/UnconditionallyDeprecated.symbols.json +// RUN: %FileCheck %s --input-file %t/UnconditionallyDeprecated.symbols.json + +@available(*, deprecated) +public struct UnconditionallyDeprecated {} + +// CHECK-NOT: domain +// CHECK: "isUnconditionallyDeprecated": true diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift new file mode 100644 index 0000000000000..9fdcb6fa779f9 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name UnconditionallyUnavailable -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name UnconditionallyUnavailable -I %t -pretty-print -o %t/UnconditionallyUnavailable.symbols.json +// RUN: %FileCheck %s --input-file %t/UnconditionallyUnavailable.symbols.json + +@available(*, unavailable) +public struct UnconditionallyUnavailable {} + +// CHECK-NOT: domain +// CHECK: "isUnconditionallyUnavailable": true diff --git a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift new file mode 100644 index 0000000000000..9f35f6fde8b5e --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift @@ -0,0 +1,54 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DeclarationFragments -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DeclarationFragments -I %t -pretty-print -o %t/DeclarationFragments.symbols.json +// RUN: %FileCheck %s --input-file %t/DeclarationFragments.symbols.json + +public func foo(f: @escaping () -> (), x: Int = 2, s: S) {} + +// CHECK: declarationFragments + +// CHECK: "kind": "keyword", +// CHECK-NEXT: "spelling": "func" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": " " + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "foo" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": "<" + +// CHECK: "kind": "genericParameter", +// CHECK-NEXT: "spelling": "S" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ">(" + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "f" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ": () -> (), " + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "x" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ": " + +// CHECK: "kind": "typeIdentifier", +// CHECK-NEXT: "spelling": "Int", +// CHECK-NEXT: "preciseIdentifier": "s:Si" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": " = 2, " + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "s" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ": S" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ")" diff --git a/test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift b/test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift new file mode 100644 index 0000000000000..ae2a4bd6c441a --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift @@ -0,0 +1,37 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name FunctionSignature -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name FunctionSignature -I %t -pretty-print -o %t/FunctionSignature.symbols.json +// RUN: %FileCheck %s --input-file %t/FunctionSignature.symbols.json + +public func foo(_ noext: Int, ext int: Int) -> String { + return "OK" +} + +// CHECK: "name": "noext" +// CHECK-NOT: "internalName": "noext" +// CHECK-NEXT: declarationFragments + +// CHECK: "kind": "identifier" +// CHECK-NEXT: "spelling": "noext" +// CHECK: "kind": "text" +// CHECK-NEXT: "spelling": ": " +// CHECK: "kind": "typeIdentifier" +// CHECK-NEXT: "spelling": "Int" +// CHECK-NEXT: "preciseIdentifier": "s:Si" + +// CHECK: "name": "ext" +// CHECK-NEXT: "internalName": "int" +// CHECK-NEXT: declarationFragments + +// CHECK: "kind": "identifier" +// CHECK-NEXT: "spelling": "int" +// CHECK: "kind": "text" +// CHECK-NEXT: "spelling": ": " +// CHECK: "kind": "typeIdentifier" +// CHECK-NEXT: "spelling": "Int" +// CHECK-NEXT: "preciseIdentifier": "s:Si" + +// CHECK: returns +// CHECK: "kind": "typeIdentifier" +// CHECK-NEXT: "spelling": "String" +// CHECK-NEXT: "preciseIdentifier": "s:SS" diff --git a/test/SymbolGraph/Symbols/Names.swift b/test/SymbolGraph/Symbols/Names.swift new file mode 100644 index 0000000000000..4ea658167d919 --- /dev/null +++ b/test/SymbolGraph/Symbols/Names.swift @@ -0,0 +1,9 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Names -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Names -I %t -pretty-print -o %t/Names.symbols.json +// RUN: %FileCheck %s --input-file %t/Names.symbols.json + +public struct MyStruct {} + +// CHECK: names +// CHECK-NEXT: "title": "MyStruct" diff --git a/test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift b/test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift new file mode 100644 index 0000000000000..e62567f069c4c --- /dev/null +++ b/test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name SkipsPublicUnderscore -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name SkipsPublicUnderscore -I %t -pretty-print -o %t/SymbolGraphModule.symbols.json +// RUN: %FileCheck %s --input-file %t/SymbolGraphModule.symbols.json + +public struct _ShouldntAppear { + public var shouldntAppear: Int +} + +// CHECK-NOT: _ShouldntAppear +// CHECK-NOT: shouldntAppear diff --git a/test/lit.cfg b/test/lit.cfg index 1b43328020d69..9ea2cb9851a07 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -275,6 +275,7 @@ if 'syntax_parser_lib' in config.available_features: config.swift_reflection_dump = inferSwiftBinary('swift-reflection-dump') config.swift_remoteast_test = inferSwiftBinary('swift-remoteast-test') config.swift_indent = inferSwiftBinary('swift-indent') +config.swift_symbolgraph_extract = inferSwiftBinary('swift-symbolgraph-extract') config.clang = inferSwiftBinary('clang') config.llvm_link = inferSwiftBinary('llvm-link') config.swift_llvm_opt = inferSwiftBinary('swift-llvm-opt') @@ -904,6 +905,9 @@ if run_vendor == 'apple': config.target_sil_opt = ( "%s %s %s %s" % (xcrun_prefix, config.sil_opt, target_options, config.sil_test_options)) + config.target_swift_symbolgraph_extract = ( + "%s %s %s" % + (xcrun_prefix, config.swift_symbolgraph_extract, target_options)) config.target_swift_ide_test = ( "%s %s %s %s" % (xcrun_prefix, config.swift_ide_test, target_options, ccp_opt)) @@ -975,6 +979,10 @@ elif run_os in ['windows-msvc']: ('%r -target %s %s %s %s' % (config.sil_opt, config.variant_triple, \ resource_dir_opt, mcp_opt, \ config.sil_test_options)) + config.target_swift_symbol_graph_extract = \ + ('%r -target %s %s' % (config.swift_symbolgraph_extract, \ + config.variant_triple, \ + mcp_opt)) config.target_swift_ide_test = \ ('%r -target %s %s %s %s' % (config.swift_ide_test, \ config.variant_triple, \ @@ -1068,6 +1076,9 @@ elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'w config.target_sil_opt = ( '%s -target %s %s %s %s' % (config.sil_opt, config.variant_triple, resource_dir_opt, mcp_opt, config.sil_test_options)) + config.target_swift_symbolgraph_extract = ( + '%s -target %s %s' % + (config.swift_symbolgraph_extract, config.variant_triple, mcp_opt)) config.target_swift_ide_test = ( '%s -target %s %s %s %s' % (config.swift_ide_test, config.variant_triple, resource_dir_opt, @@ -1185,6 +1196,10 @@ elif run_os == 'linux-androideabi' or run_os == 'linux-android': '-target', config.variant_triple, android_include_paths_opt, resource_dir_opt, mcp_opt, config.sil_test_options]) + config.target_swift_symbolgraph_extract = ' '.join([ + config.swift_symbolgraph_extract, + '-target', config.variant_triple, + mcp_opt]) config.target_swift_ide_test = ' '.join([ config.swift_ide_test, '-target', config.variant_triple, @@ -1613,6 +1628,8 @@ config.substitutions.append(('%target-swift-ide-test\(mock-sdk:([^)]+)\)', swift_version)))) config.substitutions.append(('%target-swift-ide-test', "%s -swift-version %s" % (config.target_swift_ide_test, swift_version))) +config.substitutions.append(('%target-swift-symbolgraph-extract', config.target_swift_symbolgraph_extract)) + if not hasattr(config, 'target_swift_reflection_test'): config.target_swift_reflection_test = inferSwiftBinary(swift_reflection_test_name) diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 39aeebb29bafd..2b5eb14dc0d7a 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -3,12 +3,14 @@ add_swift_host_tool(swift autolink_extract_main.cpp modulewrap_main.cpp swift_indent_main.cpp + swift_symbolgraph_extract_main.cpp SWIFT_COMPONENT compiler ) target_link_libraries(swift PRIVATE swiftDriver - swiftFrontendTool) + swiftFrontendTool + swiftSymbolGraphGen) swift_create_post_build_symlink(swift SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" @@ -20,6 +22,11 @@ swift_create_post_build_symlink(swift DESTINATION "swift-indent${CMAKE_EXECUTABLE_SUFFIX}" WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}") +swift_create_post_build_symlink(swift + SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" + DESTINATION "swift-symbolgraph-extract${CMAKE_EXECUTABLE_SUFFIX}" + WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}") + swift_create_post_build_symlink(swift SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION "swift-autolink-extract${CMAKE_EXECUTABLE_SUFFIX}" @@ -28,6 +35,7 @@ swift_create_post_build_symlink(swift add_swift_tool_symlink(swiftc swift compiler) add_swift_tool_symlink(swift-autolink-extract swift autolink-driver) add_swift_tool_symlink(swift-indent swift editor-integration) +add_swift_tool_symlink(swift-symbolgraph-extract swift toolchain-tools) # If building as part of clang, make sure the headers are installed. if(NOT SWIFT_BUILT_STANDALONE) @@ -46,3 +54,7 @@ add_dependencies(editor-integration swift) swift_install_in_component(FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swift-indent${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION "bin" COMPONENT editor-integration) +add_dependencies(toolchain-tools swift) +swift_install_in_component(FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swift-symbolgraph-extract${CMAKE_EXECUTABLE_SUFFIX}" + DESTINATION "bin" + COMPONENT toolchain-tools) diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 0424977e4012a..17a451c76a051 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -70,6 +70,10 @@ extern int modulewrap_main(ArrayRef Args, const char *Argv0, extern int swift_indent_main(ArrayRef Args, const char *Argv0, void *MainAddr); +/// Run 'swift-symbolgraph-extract' +extern int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv0, +void *MainAddr); + /// Determine if the given invocation should run as a subcommand. /// /// \param ExecName The name of the argv[0] we were invoked as. @@ -152,6 +156,8 @@ static int run_driver(StringRef ExecName, return swift_indent_main( TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], (void *)(intptr_t)getExecutablePath); + case Driver::DriverKind::SymbolGraph: + return swift_symbolgraph_extract_main(TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], (void *)(intptr_t)getExecutablePath); default: break; } diff --git a/tools/driver/swift_symbolgraph_extract_main.cpp b/tools/driver/swift_symbolgraph_extract_main.cpp new file mode 100644 index 0000000000000..ef01acf5c3e58 --- /dev/null +++ b/tools/driver/swift_symbolgraph_extract_main.cpp @@ -0,0 +1,177 @@ +//===--- swift_indent_main.cpp - Swift code formatting tool ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Extracts a Symbol Graph from a .swiftmodule file. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/LLVMInitialize.h" +#include "swift/Basic/Version.h" +#include "swift/Frontend/Frontend.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/SymbolGraphGen/SymbolGraphGen.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/raw_ostream.h" + +using namespace swift; +using namespace llvm::opt; + +namespace options { +static llvm::cl::OptionCategory Category("swift-symbolgraph-extract Options"); + +static llvm::cl::opt +ModuleName("module-name", llvm::cl::desc("Name of the module to extract"), llvm::cl::cat(Category)); + +static llvm::cl::list +FrameworkSearchPaths("F", llvm::cl::desc("add a directory to the framework search paths"), llvm::cl::ZeroOrMore, + llvm::cl::cat(Category)); + +static llvm::cl::list +LibrarySearchPaths("L", llvm::cl::desc("Add a directory to the library search paths"), llvm::cl::ZeroOrMore, + llvm::cl::cat(Category)); + +static llvm::cl::list +ImportSearchPaths("I", llvm::cl::desc("Add directory to the import search paths"), llvm::cl::ZeroOrMore, + llvm::cl::cat(Category)); + +static llvm::cl::opt +ModuleCachePath("module-cache-path", llvm::cl::desc("Specifies a path to cache modules"), llvm::cl::cat(Category)); + +static llvm::cl::opt +SDK("sdk", llvm::cl::desc("Path to the SDK"), + llvm::cl::cat(Category)); + +static llvm::cl::opt +Target("target", llvm::cl::desc("Target triple"), + llvm::cl::cat(Category)); + +static llvm::cl::opt +SwiftVersion("swift-version", llvm::cl::desc("Interpret input according to a specific Swift language version number"), llvm::cl::cat(Category)); + +static llvm::cl::opt +PrettyPrint("pretty-print", llvm::cl::desc("Pretty-print the resulting Symbol Graph JSON"), llvm::cl::cat(Category)); + +static llvm::cl::opt +MinimumAccessLevel("minimum-access-level", llvm::cl::desc("Include symbols with this access level or more"), llvm::cl::cat(Category)); + +static llvm::cl::opt +OutputPath("o", llvm::cl::desc("Symbol Graph JSON Output Path"), llvm::cl::cat(Category)); +} // end namespace options + +int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv0, void *MainAddr) { + INITIALIZE_LLVM(); + + llvm::cl::HideUnrelatedOptions(options::Category); + + // LLVM Command Line expects to trim off argv[0]. + SmallVector ArgsWithArgv0 { Argv0 }; + ArgsWithArgv0.append(Args.begin(), Args.end()); + + llvm::cl::ParseCommandLineOptions(ArgsWithArgv0.size(), + llvm::makeArrayRef(ArgsWithArgv0).data(), "Swift Symbol Graph Extractor\n"); + + CompilerInvocation Invocation; + + Invocation.setMainExecutablePath( + llvm::sys::fs::getMainExecutable(Argv0, MainAddr)); + Invocation.setModuleName("swift_symbolgraph_extract"); + Invocation.setSDKPath(options::SDK); + Invocation.setTargetTriple(options::Target); + + std::vector FrameworkSearchPaths; + for (const auto &Path : options::FrameworkSearchPaths) { + FrameworkSearchPaths.push_back({ Path, /*isSystem*/ false}); + } + Invocation.setFrameworkSearchPaths(FrameworkSearchPaths); + Invocation.getSearchPathOptions().LibrarySearchPaths = options::LibrarySearchPaths; + Invocation.setImportSearchPaths(options::ImportSearchPaths); + + Invocation.getLangOptions().EnableObjCInterop = llvm::Triple(options::Target).isOSDarwin(); + Invocation.getLangOptions().DebuggerSupport = true; + + Invocation.getFrontendOptions().EnableLibraryEvolution = true; + + Invocation.setClangModuleCachePath(options::ModuleCachePath); + Invocation.getClangImporterOptions().ModuleCachePath = options::ModuleCachePath; + + if (!options::SwiftVersion.empty()) { + using version::Version; + bool isValid = false; + if (auto Version = Version::parseVersionString(options::SwiftVersion, + SourceLoc(), nullptr)) { + if (auto Effective = Version.getValue().getEffectiveLanguageVersion()) { + Invocation.getLangOptions().EffectiveLanguageVersion = *Effective; + isValid = true; + } + } + if (!isValid) { + llvm::errs() << "Unsupported Swift Version.\n"; + return EXIT_FAILURE; + } + } + + symbolgraphgen::SymbolGraphOptions Options { + options::OutputPath, + llvm::Triple(options::Target), + options::PrettyPrint, + AccessLevel::Public, + }; + + if (!options::MinimumAccessLevel.empty()) { + Options.MinimumAccessLevel = + llvm::StringSwitch(options::MinimumAccessLevel) + .Case("open", AccessLevel::Open) + .Case("public", AccessLevel::Public) + .Case("internal", AccessLevel::Internal) + .Case("fileprivate", AccessLevel::FilePrivate) + .Case("private", AccessLevel::Private) + .Default(AccessLevel::Public); + } + + PrintingDiagnosticConsumer DiagPrinter; + + CompilerInstance CI; + CI.getDiags().addConsumer(DiagPrinter); + + if (CI.setup(Invocation)) { + llvm::outs() << "Failed to setup compiler instance\n"; + return EXIT_FAILURE; + } + + auto M = CI.getASTContext().getModuleByName(options::ModuleName); + if (!M) { + llvm::errs() + << "Couldn't load module '" << options::ModuleName << '\'' + << " in the current SDK and search paths.\n"; + SmallVector VisibleModuleNames; + CI.getASTContext().getVisibleTopLevelModuleNames(VisibleModuleNames); + + if (VisibleModuleNames.empty()) { + llvm::errs() << "Could not find any modules.\n"; + } else { + std::sort(VisibleModuleNames.begin(), VisibleModuleNames.end(), + [](const Identifier &A, const Identifier &B) -> bool { + return A.str() < B.str(); + }); + llvm::errs() << "Current visible modules:\n"; + for (const auto &ModuleName : VisibleModuleNames) { + llvm::errs() << ModuleName.str() << "\n"; + } + } + return EXIT_FAILURE; + } + + return symbolgraphgen::emitSymbolGraphForModule(M, + Options); +} From e1f6e846cae9714effa6cc28f985b01de31ca2d2 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Fri, 10 Jan 2020 10:18:57 -0800 Subject: [PATCH 396/478] Re-apply "ModuleInterface: lock .swiftinterface while generating module cache" --- include/swift/AST/DiagnosticsFrontend.def | 6 ++ lib/Frontend/ModuleInterfaceBuilder.cpp | 71 ++++++++++++++++++++++- lib/Frontend/ModuleInterfaceBuilder.h | 5 +- lib/Frontend/ModuleInterfaceLoader.cpp | 10 ++-- test/Driver/lock_interface.swift | 13 +++++ 5 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 test/Driver/lock_interface.swift diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index c3beb96e145fb..049527c6fbb7f 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -343,6 +343,12 @@ ERROR(unknown_forced_module_loading_mode,none, "unknown value for SWIFT_FORCE_MODULE_LOADING variable: '%0'", (StringRef)) +REMARK(interface_file_lock_failure,none, + "could not acquire lock file for module interface '%0'", (StringRef)) + +REMARK(interface_file_lock_timed_out,none, + "timed out waiting to acquire lock file for module interface '%0'", (StringRef)) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index 59af6d226681e..2f7a40f255c21 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -36,6 +36,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/Regex.h" #include "llvm/Support/StringSaver.h" +#include "llvm/Support/LockFileManager.h" using namespace swift; using FileDependency = SerializationOptions::FileDependency; @@ -240,7 +241,7 @@ bool ModuleInterfaceBuilder::collectDepsForSerialization( return false; } -bool ModuleInterfaceBuilder::buildSwiftModule( +bool ModuleInterfaceBuilder::buildSwiftModuleInternal( StringRef OutPath, bool ShouldSerializeDeps, std::unique_ptr *ModuleBuffer) { bool SubError = false; @@ -384,3 +385,71 @@ bool ModuleInterfaceBuilder::buildSwiftModule( }); return !RunSuccess || SubError; } + +bool ModuleInterfaceBuilder::buildSwiftModule(StringRef OutPath, + bool ShouldSerializeDeps, + std::unique_ptr *ModuleBuffer, + llvm::function_ref RemarkRebuild) { + + while (1) { + // Attempt to lock the interface file. Only one process is allowed to build + // module from the interface so we don't consume too much memory when multiple + // processes are doing the same. + // FIXME: We should surface the module building step to the build system so + // we don't need to synchronize here. + llvm::LockFileManager Locked(interfacePath); + switch (Locked) { + case llvm::LockFileManager::LFS_Error:{ + // ModuleInterfaceBuilder takes care of correctness and locks are only + // necessary for performance. Fallback to building the module in case of any lock + // related errors. + if (RemarkRebuild) { + diags.diagnose(SourceLoc(), diag::interface_file_lock_failure, + interfacePath); + } + // Clear out any potential leftover. + Locked.unsafeRemoveLockFile(); + LLVM_FALLTHROUGH; + } + case llvm::LockFileManager::LFS_Owned: { + if (RemarkRebuild) { + RemarkRebuild(); + } + return buildSwiftModuleInternal(OutPath, ShouldSerializeDeps, ModuleBuffer); + } + case llvm::LockFileManager::LFS_Shared: { + // Someone else is responsible for building the module. Wait for them to + // finish. + switch (Locked.waitForUnlock()) { + case llvm::LockFileManager::Res_Success: { + // This process may have a different module output path. If the other + // process doesn't build the interface to this output path, we should try + // building ourselves. + auto bufferOrError = llvm::MemoryBuffer::getFile(OutPath); + if (!bufferOrError) + continue; + if (ModuleBuffer) + *ModuleBuffer = std::move(bufferOrError.get()); + return false; + } + case llvm::LockFileManager::Res_OwnerDied: { + continue; // try again to get the lock. + } + case llvm::LockFileManager::Res_Timeout: { + // Since ModuleInterfaceBuilder takes care of correctness, we try waiting for + // another process to complete the build so swift does not do it done + // twice. If case of timeout, build it ourselves. + if (RemarkRebuild) { + diags.diagnose(SourceLoc(), diag::interface_file_lock_timed_out, + interfacePath); + } + // Clear the lock file so that future invocations can make progress. + Locked.unsafeRemoveLockFile(); + continue; + } + } + break; + } + } + } +} diff --git a/lib/Frontend/ModuleInterfaceBuilder.h b/lib/Frontend/ModuleInterfaceBuilder.h index f8f096a39538a..b5de3c64b8a8e 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.h +++ b/lib/Frontend/ModuleInterfaceBuilder.h @@ -67,6 +67,8 @@ class ModuleInterfaceBuilder { version::Version &Vers, llvm::StringSaver &SubArgSaver, SmallVectorImpl &SubArgs); + bool buildSwiftModuleInternal(StringRef OutPath, bool ShouldSerializeDeps, + std::unique_ptr *ModuleBuffer); public: ModuleInterfaceBuilder(SourceManager &sourceMgr, DiagnosticEngine &diags, const SearchPathOptions &searchPathOpts, @@ -102,7 +104,8 @@ class ModuleInterfaceBuilder { } bool buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps, - std::unique_ptr *ModuleBuffer); + std::unique_ptr *ModuleBuffer, + llvm::function_ref RemarkRebuild = nullptr); }; } // end namespace swift diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index ceb0cb7ae7ad8..87ecf51cb370d 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -950,13 +950,11 @@ class ModuleInterfaceLoaderImpl { std::unique_ptr moduleBuffer; // We didn't discover a module corresponding to this interface. - // Diagnose that we didn't find a loadable module, if we were asked to. - if (remarkOnRebuildFromInterface) { + auto remarkRebuild = [&]() { rebuildInfo.diagnose(ctx, diagnosticLoc, moduleName, interfacePath); - } - + }; // If we found an out-of-date .swiftmodule, we still want to add it as // a dependency of the .swiftinterface. That way if it's updated, but // the .swiftinterface remains the same, we invalidate the cache and @@ -966,7 +964,9 @@ class ModuleInterfaceLoaderImpl { builder.addExtraDependency(modulePath); if (builder.buildSwiftModule(cachedOutputPath, /*shouldSerializeDeps*/true, - &moduleBuffer)) + &moduleBuffer, + remarkOnRebuildFromInterface ? remarkRebuild: + llvm::function_ref())) return std::make_error_code(std::errc::invalid_argument); assert(moduleBuffer && diff --git a/test/Driver/lock_interface.swift b/test/Driver/lock_interface.swift new file mode 100644 index 0000000000000..5fb992c6d8f62 --- /dev/null +++ b/test/Driver/lock_interface.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) +// RUN: echo 'public func foo() {}' > %t/Foo.swift +// RUN: %target-swift-frontend-typecheck -emit-module-interface-path %t/Foo.swiftinterface %t/Foo.swift -enable-library-evolution +// RUN: touch %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift +// RUN: echo 'import Foo' > %t/file-01.swift +// RUN: echo 'import Foo' > %t/file-02.swift +// RUN: echo 'import Foo' > %t/file-03.swift +// RUN: %target-swiftc_driver -j20 %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift -I %t -Xfrontend -Rmodule-interface-rebuild &> %t/result.txt +// RUN: %FileCheck %s -check-prefix=CHECK-REBUILD < %t/result.txt + +// Ensure we only build Foo module once from the interface +// CHECK-REBUILD: rebuilding module 'Foo' from interface +// CHECK-REBUILD-NOT: rebuilding module 'Foo' from interface From ac7ba746f6e4bc9e8a382e136a94918d5d6597f0 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 10 Jan 2020 13:03:42 -0800 Subject: [PATCH 397/478] [CodeCompletion] Stop printing underlined keyword in override completion e.g. `__consuming`. Now, conformance completion for Sequence.makeIterator() is: struct MySequence: Sequence { func makeIterator() -> some IteratorProtocol { <#code#> } } rdar://problem/56963545 --- lib/IDE/CodeCompletion.cpp | 1 + test/IDE/complete_from_stdlib.swift | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 8834bafca281c..cd6b58e40d963 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -4265,6 +4265,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { PrintOptions Options; if (auto transformType = CurrDeclContext->getDeclaredTypeInContext()) Options.setBaseType(transformType); + Options.SkipUnderscoredKeywords = true; Options.PrintImplicitAttrs = false; Options.ExclusiveAttrList.push_back(TAK_escaping); Options.ExclusiveAttrList.push_back(TAK_autoclosure); diff --git a/test/IDE/complete_from_stdlib.swift b/test/IDE/complete_from_stdlib.swift index 71698cc0af650..3acd80ee120dc 100644 --- a/test/IDE/complete_from_stdlib.swift +++ b/test/IDE/complete_from_stdlib.swift @@ -66,6 +66,8 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INFIX_STRING_1 | %FileCheck %s -check-prefix=INFIX_STRING // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INFIX_EXT_STRING_1 | %FileCheck %s -check-prefix=INFIX_EXT_STRING +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONFORM_SEQUENCE | %FileCheck %s -check-prefix=CONFORM_SEQUENCE + // NO_STDLIB_PRIVATE: Begin completions // NO_STDLIB_PRIVATE: End completions @@ -286,3 +288,14 @@ func testInfixOperator4(_ x: String) { // INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: && {#Bool#}[#Bool#] // INFIX_EXT_STRING-NOT: == // INFIX_EXT_STRING: End completions + +class TestSequence : Sequence { +#^CONFORM_SEQUENCE^# +// CONFORM_SEQUENCE: Begin completions +// CONFORM_SEQUENCE-DAG: Decl[AssociatedType]/Super: typealias Element = {#(Type)#}; +// CONFORM_SEQUENCE-DAG: Decl[AssociatedType]/Super: typealias Iterator = {#(Type)#}; +// CONFORM_SEQUENCE-DAG: Decl[InstanceMethod]/Super: func makeIterator() -> some IteratorProtocol {|}; +// CONFORM_SEQUENCE-DAG: Decl[InstanceVar]/Super: var underestimatedCount: Int; +// CONFORM_SEQUENCE-DAG: Decl[InstanceMethod]/Super: func withContiguousStorageIfAvailable(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R? {|}; +// CONFORM_SEQUENCE: End completions +} From 70e6db81f194d2bb725c2859b91b7ad2429cf364 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 10 Jan 2020 13:28:03 -0800 Subject: [PATCH 398/478] Revert "[stdlib] Add withContiguousStorageIfAvailable to SubString.UTF8View" --- stdlib/public/core/Substring.swift | 7 ------- test/stdlib/subString.swift | 20 -------------------- 2 files changed, 27 deletions(-) diff --git a/stdlib/public/core/Substring.swift b/stdlib/public/core/Substring.swift index c187129c51b0c..9786f34dfb148 100644 --- a/stdlib/public/core/Substring.swift +++ b/stdlib/public/core/Substring.swift @@ -389,13 +389,6 @@ extension Substring.UTF8View: BidirectionalCollection { return _slice.distance(from: start, to: end) } - @_alwaysEmitIntoClient - public func withContiguousStorageIfAvailable( - _ body: (UnsafeBufferPointer) throws -> R - ) rethrows -> R? { - return try _slice.withContiguousStorageIfAvailable(body) - } - @inlinable public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { _slice._failEarlyRangeCheck(index, bounds: bounds) diff --git a/test/stdlib/subString.swift b/test/stdlib/subString.swift index 1fa936649baf7..17e0d41e98edc 100644 --- a/test/stdlib/subString.swift +++ b/test/stdlib/subString.swift @@ -12,19 +12,6 @@ func checkMatch(_ x: S, _ y: T, _ i: S.Index) expectEqual(x[i], y[i]) } -func checkMatchContiguousStorage(_ x: S, _ y: T) - where S.Element == T.Element, S.Element: Equatable -{ - let xElement = x.withContiguousStorageIfAvailable { $0.first } - let yElement = y.withContiguousStorageIfAvailable { $0.first } - expectEqual(xElement, yElement) -} - -func checkHasContiguousStorage(_ x: S) { - let hasStorage = x.withContiguousStorageIfAvailable { _ in true } ?? false - expectTrue(hasStorage) -} - SubstringTests.test("Equality") { let s = "abcdefg" let s1 = s[s.index(s.startIndex, offsetBy: 2) ..< @@ -241,13 +228,6 @@ SubstringTests.test("UTF8View") { expectEqual("", String(t.dropLast(100))!) expectEqual("", String(u.dropFirst(100))!) expectEqual("", String(u.dropLast(100))!) - - checkHasContiguousStorage(s.utf8) - checkHasContiguousStorage(t) - checkHasContiguousStorage(u) - checkMatchContiguousStorage(Array(s.utf8), s.utf8) - checkMatchContiguousStorage(Array(t), t) - checkMatchContiguousStorage(Array(u), u) } } From 308ea8aeb0d3932b8a07695e8152d69675d17521 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 10 Jan 2020 14:10:00 -0800 Subject: [PATCH 399/478] [TypeChecker] NFC: Add test-case for SR-4664 Resolves: rdar://problem/31764828 --- test/Constraints/sr4664.swift | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test/Constraints/sr4664.swift diff --git a/test/Constraints/sr4664.swift b/test/Constraints/sr4664.swift new file mode 100644 index 0000000000000..d3a10fd8f918a --- /dev/null +++ b/test/Constraints/sr4664.swift @@ -0,0 +1,22 @@ +// RUN: %target-typecheck-verify-swift + +struct M where T : Collection { // expected-note {{where 'T' = 'X.Y'}} + static func f(a: T, b: T) -> [E] { + } +} + +enum E {} + +struct S {} + +struct X { + struct Y { + let s: [S] + } + + let y: [Y] +} + +let x = X(y: []) +let a = M.f(a: x.y[0], b: x.y[1]) +// expected-error@-1 {{generic struct 'M' requires that 'X.Y' conform to 'Collection'}} From a83bfabc8bdbd0dbf527649145c8cd0c954987c1 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Fri, 10 Jan 2020 16:31:51 -0800 Subject: [PATCH 400/478] [DebugInfo] Stop relying on asm gadgets to extend variable ranges (#29029) The 'fake use' asm gadget does not always keep variables alive. E.g., in rdar://57754659, llvm was unable to preserve two local variables despite the use of these gadgets. These variables were backed by a LoadInst and an ExtractValueInst respectively. Instead of emitting shadow copies for just those kinds of instructions, use shadow copies exclusively. This may cause more variables to appear in the debugger window before they are initialized, but should result in fewer variables being dropped. rdar://57754659 --- lib/IRGen/IRGenSIL.cpp | 107 +----------------- .../liverange-extension-vector.swift | 15 --- test/DebugInfo/liverange-extension.swift | 63 ----------- test/DebugInfo/shadow_copies.swift | 33 +++++- test/IRGen/unmanaged_objc_throw_func.swift | 2 +- 5 files changed, 37 insertions(+), 183 deletions(-) delete mode 100644 test/DebugInfo/liverange-extension-vector.swift delete mode 100644 test/DebugInfo/liverange-extension.swift diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 2eea84a734e08..7292f9e0160ca 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -389,10 +389,6 @@ class IRGenSILFunction : /// Keeps track of the mapping of source variables to -O0 shadow copy allocas. llvm::SmallDenseMap ShadowStackSlots; llvm::SmallDenseMap, 8> AnonymousVariables; - /// To avoid inserting elements into ValueDomPoints twice. - llvm::SmallDenseSet ValueVariables; - /// Holds the DominancePoint of values that are storage for a source variable. - SmallVector, 8> ValueDomPoints; unsigned NumAnonVars = 0; /// Accumulative amount of allocated bytes on the stack. Used to limit the @@ -661,78 +657,6 @@ class IRGenSILFunction : return Name; } - /// Try to emit an inline assembly gadget which extends the lifetime of - /// \p Var. Returns whether or not this was successful. - bool emitLifetimeExtendingUse(llvm::Value *Var) { - llvm::Type *ArgTys; - auto *Ty = Var->getType(); - // Vectors, Pointers and Floats are expected to fit into a register. - if (Ty->isPointerTy() || Ty->isFloatingPointTy() || Ty->isVectorTy()) - ArgTys = {Ty}; - else { - // If this is not a scalar or vector type, we can't handle it. - if (isa(Ty)) - return false; - // The storage is guaranteed to be no larger than the register width. - // Extend the storage so it would fit into a register. - llvm::Type *IntTy; - switch (IGM.getClangASTContext().getTargetInfo().getRegisterWidth()) { - case 64: - IntTy = IGM.Int64Ty; - break; - case 32: - IntTy = IGM.Int32Ty; - break; - default: - llvm_unreachable("unsupported register width"); - } - ArgTys = {IntTy}; - Var = Builder.CreateZExtOrBitCast(Var, IntTy); - } - // Emit an empty inline assembler expression depending on the register. - auto *AsmFnTy = llvm::FunctionType::get(IGM.VoidTy, ArgTys, false); - auto *InlineAsm = llvm::InlineAsm::get(AsmFnTy, "", "r", true); - Builder.CreateAsmCall(InlineAsm, Var); - return true; - } - - /// At -Onone, forcibly keep all LLVM values that are tracked by - /// debug variables alive by inserting an empty inline assembler - /// expression depending on the value in the blocks dominated by the - /// value. - void emitDebugVariableRangeExtension(const SILBasicBlock *CurBB) { - if (IGM.IRGen.Opts.shouldOptimize()) - return; - for (auto &Variable : ValueDomPoints) { - llvm::Instruction *Var = Variable.first; - DominancePoint VarDominancePoint = Variable.second; - if (getActiveDominancePoint() == VarDominancePoint || - isActiveDominancePointDominatedBy(VarDominancePoint)) { - bool ExtendedLifetime = emitLifetimeExtendingUse(Var); - if (!ExtendedLifetime) - continue; - - // Propagate dbg.values for Var into the current basic block. Note - // that this shouldn't be necessary. LiveDebugValues should be doing - // this but can't in general because it currently only tracks register - // locations. - llvm::BasicBlock *BB = Var->getParent(); - llvm::BasicBlock *CurBB = Builder.GetInsertBlock(); - if (BB == CurBB) - // The current basic block must be a successor of the dbg.value(). - continue; - - llvm::SmallVector DbgValues; - llvm::findDbgValues(DbgValues, Var); - for (auto *DVI : DbgValues) - if (DVI->getParent() == BB) - IGM.DebugInfo->getBuilder().insertDbgValueIntrinsic( - DVI->getValue(), DVI->getVariable(), DVI->getExpression(), - DVI->getDebugLoc(), &*CurBB->getFirstInsertionPt()); - } - } - } - /// To make it unambiguous whether a `var` binding has been initialized, /// zero-initialize the shadow copy alloca. LLDB uses the first pointer-sized /// field to recognize to detect uninitizialized variables. This can be @@ -757,6 +681,9 @@ class IRGenSILFunction : /// Account for bugs in LLVM. /// + /// - When a variable is spilled into a stack slot, LiveDebugValues fails to + /// recognize a restore of that slot for a different variable. + /// /// - The LLVM type legalizer currently doesn't update debug /// intrinsics when a large value is split up into smaller /// pieces. Note that this heuristic as a bit too conservative @@ -764,9 +691,7 @@ class IRGenSILFunction : /// /// - CodeGen Prepare may drop dbg.values pointing to PHI instruction. bool needsShadowCopy(llvm::Value *Storage) { - return (IGM.DataLayout.getTypeSizeInBits(Storage->getType()) > - IGM.getClangASTContext().getTargetInfo().getRegisterWidth()) || - isa(Storage); + return !isa(Storage); } /// Unconditionally emit a stack shadow copy of an \c llvm::Value. @@ -800,27 +725,10 @@ class IRGenSILFunction : if (IGM.IRGen.Opts.DisableDebuggerShadowCopies || IGM.IRGen.Opts.shouldOptimize() || IsAnonymous || isa(Storage) || isa(Storage) || - Storage->getType() == IGM.RefCountedPtrTy) + Storage->getType() == IGM.RefCountedPtrTy || !needsShadowCopy(Storage)) return Storage; - // Always emit shadow copies for function arguments. - if (VarInfo.ArgNo == 0) - // Otherwise only if debug value range extension is not feasible. - if (!needsShadowCopy(Storage)) { - // Mark for debug value range extension unless this is a constant, or - // unless it's not possible to emit lifetime-extending uses for this. - if (auto *Value = dyn_cast(Storage)) { - // Emit a use at the start of the storage lifetime to force early - // materialization. This makes variables available for inspection as - // soon as they are defined. - bool ExtendedLifetime = emitLifetimeExtendingUse(Value); - if (ExtendedLifetime) - if (ValueVariables.insert(Value).second) - ValueDomPoints.push_back({Value, getActiveDominancePoint()}); - } - - return Storage; - } + // Emit a shadow copy. return emitShadowCopy(Storage, Scope, VarInfo, Align); } @@ -1900,9 +1808,6 @@ void IRGenSILFunction::visitSILBasicBlock(SILBasicBlock *BB) { IGM.DebugInfo->setCurrentLoc( Builder, DS, RegularLocation::getAutoGeneratedLocation()); } - - if (isa(&I)) - emitDebugVariableRangeExtension(BB); } visit(&I); } diff --git a/test/DebugInfo/liverange-extension-vector.swift b/test/DebugInfo/liverange-extension-vector.swift deleted file mode 100644 index 7320a1c4a4aa7..0000000000000 --- a/test/DebugInfo/liverange-extension-vector.swift +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %target-swift-frontend %s -g -emit-ir -o - | %FileCheck %s -// REQUIRES: objc_interop, CPU=x86_64 -import simd - -func use(_ x: T) {} - -func getInt32() -> Int32 { return -1 } - -public func rangeExtension(x: Int32, y: Int32) { - let p = int2(x, y) - // CHECK: define {{.*}}rangeExtension - // CHECK: llvm.dbg.value(metadata <2 x i32> %[[P:.*]], metadata {{.*}}, metadata - use(p) - // CHECK: asm sideeffect "", "r"{{.*}}[[P]] -} diff --git a/test/DebugInfo/liverange-extension.swift b/test/DebugInfo/liverange-extension.swift deleted file mode 100644 index a7ba868005393..0000000000000 --- a/test/DebugInfo/liverange-extension.swift +++ /dev/null @@ -1,63 +0,0 @@ -// RUN: %target-swift-frontend %s -g -emit-ir -o - | %FileCheck %s - -// REQUIRES: CPU=x86_64 -// -// We require x86_64 to make the test easier to read. Without this, -// writing check lines that ensure our asm gadgets match up with the -// right values is painfully hard to do. - -func use(_ x: T) {} - -func getInt32() -> Int32 { return -1 } - -// CHECK-LABEL: define {{.*}}rangeExtension -public func rangeExtension(_ b: Bool) { - let i = getInt32() - // CHECK: [[I:%.*]] = call swiftcc i32 @"{{.*}}getInt32 - // CHECK-NEXT: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[I_ZEXT]]) - // CHECK-NEXT: llvm.dbg.value(metadata i32 [[I]], metadata [[MD_I:!.*]], metadata - - use(i) - - // CHECK: br i1 - - if b { - // CHECK: llvm.dbg.value(metadata i32 [[I]], metadata [[MD_I]] - - let j = getInt32() - // CHECK: [[J:%.*]] = call swiftcc i32 @"{{.*}}getInt32 - // CHECK-NEXT: [[J_ZEXT:%.*]] = zext i32 [[J]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[J_ZEXT]]) - // CHECK: llvm.dbg.value(metadata i32 [[J]], metadata [[MD_J:!.*]], metadata - - use(j) - // CHECK: call swiftcc void @"{{.*}}use - - // CHECK: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[I_ZEXT]]) - // CHECK-NEXT: [[J_ZEXT:%.*]] = zext i32 [[J]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[J_ZEXT]]) - - // CHECK: br label - } - - // CHECK-NOT: llvm.dbg.value(metadata i32 [[J]] - // CHECK: llvm.dbg.value(metadata i32 [[I]] - - let z = getInt32() - // CHECK: [[Z:%.*]] = call swiftcc i32 @"{{.*}}getInt32 - // CHECK: llvm.dbg.value(metadata i32 [[Z]], metadata [[MD_Z:!.*]], metadata - - use(z) - // CHECK: call swiftcc void @"{{.*}}use - - // CHECK: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[I_ZEXT]]) - // CHECK-NEXT: [[Z_ZEXT:%.*]] = zext i32 [[Z]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[Z_ZEXT]]) -} - -// CHECK-DAG: [[MD_I]] = !DILocalVariable(name: "i" -// CHECK-DAG: [[MD_J]] = !DILocalVariable(name: "j" -// CHECK-DAG: [[MD_Z]] = !DILocalVariable(name: "z" diff --git a/test/DebugInfo/shadow_copies.swift b/test/DebugInfo/shadow_copies.swift index 7d348c8c5849f..69a04f7bd9d84 100644 --- a/test/DebugInfo/shadow_copies.swift +++ b/test/DebugInfo/shadow_copies.swift @@ -18,14 +18,14 @@ class ClassB : ClassA override init (_ input : Int64) { // CHECK: @"$s{{.*}}6ClassBCyACs5Int64Vcfc" - // NOPCOPY: @"$s{{.*}}6ClassBCyACs5Int64Vcfc" + // NOCOPY: @"$s{{.*}}6ClassBCyACs5Int64Vcfc" // CHECK: alloca {{.*}}ClassBC* - // NOPCOPY: alloca {{.*}}ClassBC* + // NOCOPY: alloca {{.*}}ClassBC* // CHECK: alloca i64 // CHECK-NOT: alloca - // NOPCOPY-NOT: alloca + // NOCOPY-NOT: alloca // CHECK: ret {{.*}}ClassBC // NOCOPY: ret {{.*}}ClassBC super.init (input) @@ -33,3 +33,30 @@ class ClassB : ClassA } let b = ClassB(1); + +func use(_ x: Int) {} + +class ClassC +{ + // CHECK: define {{.*}}@"$s13shadow_copies6ClassCCACycfc" + // NOCOPY: define {{.*}}@"$s13shadow_copies6ClassCCACycfc" + init () + { + // CHECK: alloca %T13shadow_copies6ClassCC* + // CHECK-NOT: alloca + // NOCOPY-NOT: alloca + + // CHECK: call void @llvm.dbg.value(metadata i64 10 + // NOCOPY: call void @llvm.dbg.value(metadata i64 10 + let x = 10 + + use(x) + + use(x) + + // CHECK: ret + // NOCOPY: ret + } +} + +let c = ClassC() diff --git a/test/IRGen/unmanaged_objc_throw_func.swift b/test/IRGen/unmanaged_objc_throw_func.swift index dcccf5174ba71..128b41b3999f7 100644 --- a/test/IRGen/unmanaged_objc_throw_func.swift +++ b/test/IRGen/unmanaged_objc_throw_func.swift @@ -18,7 +18,7 @@ import Foundation // CHECK-NEXT: store i{{32|64}} 1, i{{32|64}}* %._value, align {{[0-9]+}} // CHECK-NEXT: %[[T4:.+]] = call swiftcc %TSo7NSArrayC* @"$sSa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF"(%swift.bridge* %[[T1]], %swift.type* @"$sSiN") // CHECK-NEXT: %[[T5:.+]] = bitcast %TSo7NSArrayC* %[[T4]] to %TSo10CFArrayRefa* - // CHECK-NEXT: call void asm sideeffect "", "r"(%TSo10CFArrayRefa* %[[T5]]) + // CHECK-NEXT: store %TSo10CFArrayRefa* %[[T5]] // CHECK-NEXT: call void @swift_bridgeObjectRelease(%swift.bridge* %[[T1]]) #{{[0-9]+}} // CHECK-NEXT: %[[T6:.+]] = bitcast %TSo10CFArrayRefa* %[[T5]] to i8* // CHECK-NEXT: call void @llvm.objc.release(i8* %[[T6]]) From 1a275a45c4d9af7e589716fcfe58d97f68504edb Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Fri, 10 Jan 2020 16:56:33 -0800 Subject: [PATCH 401/478] =?UTF-8?q?[test]=20Accelerate:=20Don=E2=80=99t=20?= =?UTF-8?q?hide=20utility=20functions=20in=20an=20if=20#available=20block?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/stdlib/Accelerate.swift | 88 ++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/test/stdlib/Accelerate.swift b/test/stdlib/Accelerate.swift index 9f19817c9a7b7..7211442e50cec 100644 --- a/test/stdlib/Accelerate.swift +++ b/test/stdlib/Accelerate.swift @@ -529,51 +529,51 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { expectTrue(elementsAlmostEqual(result, legacyResult)) expectTrue(elementsAlmostEqual(result, returnedResult)) } - - //===----------------------------------------------------------------------===// - // - // Array almost equal. - // - //===----------------------------------------------------------------------===// - - func elementsAlmostEqual(_ lhs: [T], _ rhs: [T]) -> Bool { - var returnValue = true - zip(lhs, rhs).forEach { - if !isAlmostEqual($0.0, $0.1) { - returnValue = false - return - } - } - return returnValue - } - - func isAlmostEqual(_ lhs: T, - _ rhs: T, - tolerance: T = T.ulpOfOne.squareRoot()) -> Bool { - assert(tolerance >= .ulpOfOne && tolerance < 1, "tolerance should be in [.ulpOfOne, 1).") - guard lhs.isFinite && rhs.isFinite else { - return rescaledAlmostEqual(lhs, rhs, tolerance: tolerance) - } - let scale = max(abs(lhs), abs(rhs), .leastNormalMagnitude) - return abs(lhs - rhs) < scale*tolerance - } - - func rescaledAlmostEqual(_ lhs: T, - _ rhs: T, - tolerance: T) -> Bool { - if lhs.isNaN || rhs.isNaN { return false } - if lhs.isInfinite { - if rhs.isInfinite { return lhs == rhs } - let scaledLhs = T(sign: lhs.sign, - exponent: T.greatestFiniteMagnitude.exponent, - significand: 1) - let scaledRhs = T(sign: .plus, - exponent: -1, - significand: rhs) - return isAlmostEqual(scaledLhs, scaledRhs, tolerance: tolerance) - } - return rescaledAlmostEqual(rhs, lhs, tolerance: tolerance) +} + +//===----------------------------------------------------------------------===// +// +// Array almost equal. +// +//===----------------------------------------------------------------------===// + +func elementsAlmostEqual(_ lhs: [T], _ rhs: [T]) -> Bool { + var returnValue = true + zip(lhs, rhs).forEach { + if !isAlmostEqual($0.0, $0.1) { + returnValue = false + return } + } + return returnValue +} + +func isAlmostEqual(_ lhs: T, + _ rhs: T, + tolerance: T = T.ulpOfOne.squareRoot()) -> Bool { + assert(tolerance >= .ulpOfOne && tolerance < 1, "tolerance should be in [.ulpOfOne, 1).") + guard lhs.isFinite && rhs.isFinite else { + return rescaledAlmostEqual(lhs, rhs, tolerance: tolerance) + } + let scale = max(abs(lhs), abs(rhs), .leastNormalMagnitude) + return abs(lhs - rhs) < scale*tolerance +} + +func rescaledAlmostEqual(_ lhs: T, + _ rhs: T, + tolerance: T) -> Bool { + if lhs.isNaN || rhs.isNaN { return false } + if lhs.isInfinite { + if rhs.isInfinite { return lhs == rhs } + let scaledLhs = T(sign: lhs.sign, + exponent: T.greatestFiniteMagnitude.exponent, + significand: 1) + let scaledRhs = T(sign: .plus, + exponent: -1, + significand: rhs) + return isAlmostEqual(scaledLhs, scaledRhs, tolerance: tolerance) + } + return rescaledAlmostEqual(rhs, lhs, tolerance: tolerance) } runAllTests() From a4c7cad87223a3a6a20a2a65b45103d075786151 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 10 Jan 2020 22:39:17 -0800 Subject: [PATCH 402/478] test: attempt to fix windows builder --- test/lit.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lit.cfg b/test/lit.cfg index 26bc562f25cd9..4ee12fa1e9f31 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -991,7 +991,7 @@ elif run_os in ['windows-msvc']: ('%r -target %s %s %s %s' % (config.sil_opt, config.variant_triple, \ resource_dir_opt, mcp_opt, \ config.sil_test_options)) - config.target_swift_symbol_graph_extract = \ + config.target_swift_symbolgraph_extract = \ ('%r -target %s %s' % (config.swift_symbolgraph_extract, \ config.variant_triple, \ mcp_opt)) From be7d3d244f891ffb477b9f42a5b2cc369963b9e6 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Mon, 8 Apr 2019 22:30:48 -0700 Subject: [PATCH 403/478] Start adding wasm --- CMakeLists.txt | 4 +- cmake/modules/AddSwift.cmake | 9 +++++ cmake/modules/SwiftConfigureSDK.cmake | 5 +++ cmake/modules/SwiftSetIfArchBitness.cmake | 3 +- lib/Basic/LangOptions.cpp | 2 + utils/build-script-impl | 40 ++++++++++++++++++- utils/build_swift/driver_arguments.py | 27 +++++++++++++ .../swift_build_support/targets.py | 9 ++++- 8 files changed, 95 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc318be7bb31b..9511489c6fb54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,7 +259,7 @@ set(SWIFT_ANDROID_DEPLOY_DEVICE_PATH "" CACHE STRING # foreach(sdk ANDROID;FREEBSD;LINUX;WINDOWS;HAIKU) - foreach(arch aarch64;armv6;armv7;i686;powerpc64;powerpc64le;s390x;x86_64) + foreach(arch aarch64;armv6;armv7;i686;powerpc64;powerpc64le;s390x;wasm32;x86_64) set(SWIFT_${sdk}_${arch}_ICU_UC "" CACHE STRING "Path to a directory containing the icuuc library for ${sdk}") set(SWIFT_${sdk}_${arch}_ICU_UC_INCLUDE "" CACHE STRING @@ -633,6 +633,8 @@ else() set(SWIFT_HOST_VARIANT_ARCH_default "powerpc64le") elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "s390x") set(SWIFT_HOST_VARIANT_ARCH_default "s390x") + elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "wasm32") + set(SWIFT_HOST_VARIANT_ARCH_default "wasm32") # FIXME: Only matches v6l/v7l - by far the most common variants elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv6l") set(SWIFT_HOST_VARIANT_ARCH_default "armv6") diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 2be88f8b615be..4d675282cb23f 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -465,6 +465,8 @@ function(_add_variant_link_flags) list(APPEND link_libraries "pthread") elseif("${LFLAGS_SDK}" STREQUAL "CYGWIN") # No extra libraries required. + elseif("${LFLAGS_SDK}" STREQUAL "WASM") + # No extra libraries required. elseif("${LFLAGS_SDK}" STREQUAL "WINDOWS") # We don't need to add -nostdlib using MSVC or clang-cl, as MSVC and clang-cl rely on auto-linking entirely. if(NOT SWIFT_COMPILER_IS_MSVC_LIKE) @@ -1612,6 +1614,9 @@ endfunction() # SWIFT_MODULE_DEPENDS_HAIKU # Swift modules this library depends on when built for Haiku. # +# SWIFT_MODULE_DEPENDS_WASM +# Swift modules this library depends on when built for WebAssembly. +# # FRAMEWORK_DEPENDS # System frameworks this library depends on. # @@ -1720,6 +1725,7 @@ function(add_swift_target_library name) SWIFT_MODULE_DEPENDS_OSX SWIFT_MODULE_DEPENDS_TVOS SWIFT_MODULE_DEPENDS_WATCHOS + SWIFT_MODULE_DEPENDS_WASM SWIFT_MODULE_DEPENDS_WINDOWS SWIFT_MODULE_DEPENDS_FROM_SDK TARGET_SDKS) @@ -1830,6 +1836,9 @@ function(add_swift_target_library name) elseif(${sdk} STREQUAL HAIKU) list(APPEND swiftlib_module_depends_flattened ${SWIFTLIB_SWIFT_MODULE_DEPENDS_HAIKU}) + elseif(${sdk} STREQUAL WASM) + list(APPEND swiftlib_module_depends_flattened + ${SWIFTLIB_SWIFT_MODULE_DEPENDS_WASM}) elseif(${sdk} STREQUAL WINDOWS) list(APPEND swiftlib_module_depends_flattened ${SWIFTLIB_SWIFT_MODULE_DEPENDS_WINDOWS}) diff --git a/cmake/modules/SwiftConfigureSDK.cmake b/cmake/modules/SwiftConfigureSDK.cmake index 3a87bfcd80bb2..1fbf17e00ed85 100644 --- a/cmake/modules/SwiftConfigureSDK.cmake +++ b/cmake/modules/SwiftConfigureSDK.cmake @@ -319,6 +319,11 @@ macro(configure_sdk_unix name architectures) message(FATAL_ERROR "unsupported arch for Haiku: ${arch}") endif() set(SWIFT_SDK_HAIKU_ARCH_x86_64_TRIPLE "x86_64-unknown-haiku") + elseif("${prefix}" STREQUAL "WASM") + if(NOT arch STREQUAL wasm32) + message(FATAL_ERROR "unsupported arch for WebAssembly: ${arch}") + endif() + set(SWIFT_SDK_WASM_ARCH_wasm32_TRIPLE "wasm32-unknown-unknown-wasm") else() message(FATAL_ERROR "unknown Unix OS: ${prefix}") endif() diff --git a/cmake/modules/SwiftSetIfArchBitness.cmake b/cmake/modules/SwiftSetIfArchBitness.cmake index 5212cf3ccb854..d38a9689150ba 100644 --- a/cmake/modules/SwiftSetIfArchBitness.cmake +++ b/cmake/modules/SwiftSetIfArchBitness.cmake @@ -12,7 +12,8 @@ function(set_if_arch_bitness var_name) "${SIA_ARCH}" STREQUAL "armv6" OR "${SIA_ARCH}" STREQUAL "armv7" OR "${SIA_ARCH}" STREQUAL "armv7k" OR - "${SIA_ARCH}" STREQUAL "armv7s") + "${SIA_ARCH}" STREQUAL "armv7s" OR + "${SIA_ARCH}" STREQUAL "wasm32") set("${var_name}" "${SIA_CASE_32_BIT}" PARENT_SCOPE) elseif("${SIA_ARCH}" STREQUAL "x86_64" OR "${SIA_ARCH}" STREQUAL "arm64" OR diff --git a/lib/Basic/LangOptions.cpp b/lib/Basic/LangOptions.cpp index 95c937cbd2f43..9242a605eaa89 100644 --- a/lib/Basic/LangOptions.cpp +++ b/lib/Basic/LangOptions.cpp @@ -308,6 +308,8 @@ std::pair LangOptions::setTarget(llvm::Triple triple) { case llvm::Triple::ArchType::systemz: addPlatformConditionValue(PlatformConditionKind::Endianness, "big"); break; + default: + llvm_unreachable("undefined architecture endianness"); } // Set the "runtime" platform condition. diff --git a/utils/build-script-impl b/utils/build-script-impl index 4f28e5084133d..6c6c7f89230e2 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -115,6 +115,7 @@ KNOWN_SETTINGS=( skip-build-watchos-device "" "set to skip building Swift stdlibs for Apple watchOS devices (i.e. build simulators only)" skip-build-watchos-simulator "" "set to skip building Swift stdlibs for Apple watchOS simulators (i.e. build devices only)" skip-build-android "" "set to skip building Swift stdlibs for Android" + skip-build-wasm "" "set to skip building Swift stdlibs for WebAssembly" skip-build-lldb "" "set to skip building LLDB" skip-build-llbuild "" "set to skip building llbuild" skip-build-libcxx "" "set to skip building libcxx" @@ -150,6 +151,8 @@ KNOWN_SETTINGS=( skip-test-watchos-host "" "set to skip testing the host parts of the watchOS toolchain" skip-test-android "" "set to skip testing Swift stdlibs for Android" skip-test-android-host "" "set to skip testing the host parts of the Android toolchain" + skip-test-wasm "" "set to skip testing Swift stdlibs for WebAssembly" + skip-test-wasm-host "" "set to skip testing the host parts of the WebAssembly toolchain" validation-test "0" "set to run the validation test suite" long-test "0" "set to run the long test suite" stress-test "0" "set to run the stress test suite" @@ -412,7 +415,8 @@ function verify_host_is_supported() { | watchsimulator-i386 \ | watchos-armv7k \ | android-armv7 \ - | android-aarch64) + | android-aarch64 \ + | wasm-wasm32) ;; *) echo "Unknown host tools target: ${host}" @@ -455,6 +459,40 @@ function set_build_options_for_host() { SWIFT_HOST_TRIPLE="armv7-unknown-linux-gnueabihf" llvm_target_arch="ARM" ;; + linux-*) + SWIFT_HOST_VARIANT="linux" + SWIFT_HOST_VARIANT_SDK="LINUX" + case ${host} in + linux-x86_64) + SWIFT_HOST_VARIANT_ARCH="x86_64" + ;; + linux-i686) + SWIFT_HOST_VARIANT_ARCH="i686" + ;; + linux-armv6) + SWIFT_HOST_VARIANT_ARCH="armv6" + SWIFT_HOST_TRIPLE="armv6-unknown-linux-gnueabihf" + llvm_target_arch="ARM" + ;; + linux-armv7) + SWIFT_HOST_VARIANT_ARCH="armv7" + SWIFT_HOST_TRIPLE="armv7-unknown-linux-gnueabihf" + llvm_target_arch="ARM" + ;; + linux-aarch64) + SWIFT_HOST_VARIANT_ARCH="aarch64" + ;; + linux-powerpc64) + SWIFT_HOST_VARIANT_ARCH="powerpc64" + ;; + linux-powerpc64le) + SWIFT_HOST_VARIANT_ARCH="powerpc64le" + ;; + linux-s390x) + SWIFT_HOST_VARIANT_ARCH="s390x" + ;; + esac + ;; macosx-* | iphoneos-* | iphonesimulator-* | \ appletvos-* | appletvsimulator-* | \ watchos-* | watchsimulator-*) diff --git a/utils/build_swift/driver_arguments.py b/utils/build_swift/driver_arguments.py index 4308fb7b2fbff..406c451447ad3 100644 --- a/utils/build_swift/driver_arguments.py +++ b/utils/build_swift/driver_arguments.py @@ -139,6 +139,7 @@ def _apply_default_arguments(args): args.build_tvos = False args.build_watchos = False args.build_android = False + args.build_wasm = False args.build_benchmarks = False args.build_external_benchmarks = False args.build_lldb = False @@ -168,6 +169,9 @@ def _apply_default_arguments(args): if not args.android or not args.build_android: args.build_android = False + if not args.wasm or not args.build_wasm: + args.build_wasm = False + # --test-paths implies --test and/or --validation-test # depending on what directories/files have been specified. if args.test_paths: @@ -204,6 +208,7 @@ def _apply_default_arguments(args): args.test_tvos = False args.test_watchos = False args.test_android = False + args.test_wasm = False args.test_swiftpm = False args.test_swiftsyntax = False args.test_indexstoredb = False @@ -250,11 +255,19 @@ def _apply_default_arguments(args): if not args.test_android: args.test_android_host = False + if not args.build_wasm: + args.test_wasm = False + args.test_wasm_host = False + + if not args.test_android: + args.test_android_host = False + if not args.host_test: args.test_ios_host = False args.test_tvos_host = False args.test_watchos_host = False args.test_android_host = False + args.test_wasm_host = False def create_argument_parser(): @@ -336,6 +349,9 @@ def create_argument_parser(): option('--android', toggle_true, help='also build for Android') + option('--wasm', toggle_true, + help='also build for WebAssembly') + option('--swift-analyze-code-coverage', store, choices=['false', 'not-merged', 'merged'], # so CMake can see the inert mode as a false value @@ -924,6 +940,9 @@ def create_argument_parser(): option('--skip-build-android', toggle_false('build_android'), help='skip building Swift stdlibs for Android') + option('--skip-build-wasm', toggle_false('build_wasm'), + help='skip building Swift stdlibs for WebAssembly') + option('--skip-build-benchmarks', toggle_false('build_benchmarks'), help='skip building Swift Benchmark Suite') @@ -980,6 +999,14 @@ def create_argument_parser(): help='skip testing Android device targets on the host machine (the ' 'phone itself)') + option('--skip-test-wasm', + toggle_false('test_wasm'), + help='skip testing all WebAssembly targets.') + option('--skip-test-wasm-host', + toggle_false('test_wasm_host'), + help='skip testing WebAssembly device targets on the host machine (the ' + 'WebAssembly runtime)') + option('--skip-test-swiftpm', toggle_false('test_swiftpm'), help='skip testing swiftpm') option('--skip-test-swiftsyntax', toggle_false('test_swiftsyntax'), diff --git a/utils/swift_build_support/swift_build_support/targets.py b/utils/swift_build_support/swift_build_support/targets.py index 5e4f30ef1940d..f7a32c8293ee3 100644 --- a/utils/swift_build_support/swift_build_support/targets.py +++ b/utils/swift_build_support/swift_build_support/targets.py @@ -158,6 +158,8 @@ class StdlibDeploymentTarget(object): Haiku = Platform("haiku", archs=["x86_64"]) + Wasm = Platform("wasm", archs=["wasm32"]) + # The list of known platforms. known_platforms = [ OSX, @@ -169,7 +171,8 @@ class StdlibDeploymentTarget(object): Cygwin, Android, Windows, - Haiku] + Haiku, + Wasm] # Cache of targets by name. _targets_by_name = dict((target.name, target) @@ -233,6 +236,10 @@ def host_target(): if machine == 'x86_64': return StdlibDeploymentTarget.Haiku.x86_64 + elif system == 'Wasm': + if machine == 'wasm32': + return StdlibDeploymentTarget.Wasm.wasm32 + raise NotImplementedError('System "%s" with architecture "%s" is not ' 'supported' % (system, machine)) From 8c87347ecc0ec40bb847cdf48d2700ce67c80393 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 9 Apr 2019 15:40:01 -0700 Subject: [PATCH 404/478] Add wasm object emit support --- lib/Basic/Platform.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Basic/Platform.cpp b/lib/Basic/Platform.cpp index 85abc0ee97e43..36050f446b0c3 100644 --- a/lib/Basic/Platform.cpp +++ b/lib/Basic/Platform.cpp @@ -138,6 +138,8 @@ static StringRef getPlatformNameForDarwin(const DarwinPlatformKind platform) { StringRef swift::getPlatformNameForTriple(const llvm::Triple &triple) { switch (triple.getOS()) { case llvm::Triple::UnknownOS: + if (triple.isOSBinFormatWasm()) + return "wasm"; llvm_unreachable("unknown OS"); case llvm::Triple::Ananas: case llvm::Triple::CloudABI: From 854b630b51596cd395c131c339e8bada14c20aff Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 9 Apr 2019 15:54:47 -0700 Subject: [PATCH 405/478] build-script: parse skip wasm command line args --- utils/build-script | 6 ++++++ utils/build-script-impl | 3 +++ .../swift_build_support/host_specific_configuration.py | 2 ++ 3 files changed, 11 insertions(+) diff --git a/utils/build-script b/utils/build-script index b1d060328bc6d..3ba7c82f8c6fa 100755 --- a/utils/build-script +++ b/utils/build-script @@ -498,6 +498,8 @@ class BuildScriptInvocation(object): impl_args += ["--skip-build-watchos-simulator"] if not args.build_android: impl_args += ["--skip-build-android"] + if not args.build_wasm: + impl_args += ["--skip-build-wasm"] if not args.build_clang_tools_extra: impl_args += ["--skip-build-clang-tools-extra"] @@ -538,6 +540,10 @@ class BuildScriptInvocation(object): impl_args += ["--skip-test-android"] if not args.test_android_host: impl_args += ["--skip-test-android-host"] + if not args.test_wasm: + impl_args += ["--skip-test-wasm"] + if not args.test_wasm_host: + impl_args += ["--skip-test-wasm-host"] if args.build_runtime_with_host_compiler: impl_args += ["--build-runtime-with-host-compiler"] if args.validation_test: diff --git a/utils/build-script-impl b/utils/build-script-impl index 6c6c7f89230e2..d63268d7a0bab 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1256,6 +1256,9 @@ function common_cross_c_flags() { android-arm64) echo -n " -arch aarch64" ;; + wasm-wasm32) + echo -n " -arch wasm32" + ;; esac } diff --git a/utils/swift_build_support/swift_build_support/host_specific_configuration.py b/utils/swift_build_support/swift_build_support/host_specific_configuration.py index e5316b043eff8..557e1fed49660 100644 --- a/utils/swift_build_support/swift_build_support/host_specific_configuration.py +++ b/utils/swift_build_support/swift_build_support/host_specific_configuration.py @@ -191,6 +191,8 @@ def __platforms_to_skip_build(self, args): StdlibDeploymentTarget.AppleWatchSimulator) if not args.build_android: platforms_to_skip_build.add(StdlibDeploymentTarget.Android) + if not args.build_wasm: + platforms_to_skip_build.add(StdlibDeploymentTarget.Wasm) return platforms_to_skip_build def __platforms_to_skip_test(self, args): From 47dad583f55cab061752d7e5d4a3cf98b65b28bd Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 9 Apr 2019 16:35:13 -0700 Subject: [PATCH 406/478] Handle WASM sdk in cmake --- CMakeLists.txt | 20 +++++++++++++++++-- test/CMakeLists.txt | 3 ++- utils/build-script | 3 +++ .../host_specific_configuration.py | 4 ++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9511489c6fb54..cac97cdf09e3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,10 +255,10 @@ set(SWIFT_ANDROID_DEPLOY_DEVICE_PATH "" CACHE STRING "Path on an Android device where build products will be pushed. These are used when running the test suite against the device") # -# User-configurable ICU specific options for Android, FreeBSD, Linux and Haiku. +# User-configurable ICU specific options for Android, FreeBSD, Linux, Haiku, and WebAssembly. # -foreach(sdk ANDROID;FREEBSD;LINUX;WINDOWS;HAIKU) +foreach(sdk ANDROID;FREEBSD;LINUX;WINDOWS;HAIKU;WASM) foreach(arch aarch64;armv6;armv7;i686;powerpc64;powerpc64le;s390x;wasm32;x86_64) set(SWIFT_${sdk}_${arch}_ICU_UC "" CACHE STRING "Path to a directory containing the icuuc library for ${sdk}") @@ -819,6 +819,22 @@ if(swift_build_windows AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") configure_sdk_windows("Windows" "msvc" "${SWIFT_SDK_WINDOWS_ARCHITECTURES}") endif() +# Should we cross-compile the standard library for WebAssembly (Emscripten)? +is_sdk_requested(WASM swift_build_wasm) +if(swift_build_wasm AND NOT "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "WASM") + #if ("${SWIFT_ANDROID_NDK_PATH}" STREQUAL "") + # message(FATAL_ERROR "You must set SWIFT_ANDROID_NDK_PATH to cross-compile the Swift runtime for Android") + #endif() + #if (NOT ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Darwin" OR "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux")) + # message(FATAL_ERROR "A Darwin or Linux host is required to build the Swift runtime for Android") + #endif() + + if("${SWIFT_SDK_WASM_ARCHITECTURES}" STREQUAL "") + set(SWIFT_SDK_WASM_ARCHITECTURES wasm32) + endif() + configure_sdk_unix("Wasm" "${SWIFT_SDK_WASM_ARCHITECTURES}") +endif() + if("${SWIFT_SDKS}" STREQUAL "") set(SWIFT_SDKS "${SWIFT_CONFIGURED_SDKS}") endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a6a06dfea4e24..9ffc66df937d8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -75,7 +75,8 @@ function(get_test_dependencies SDK result_var_name) ("${SDK}" STREQUAL "FREEBSD") OR ("${SDK}" STREQUAL "ANDROID") OR ("${SDK}" STREQUAL "WINDOWS") OR - ("${SDK}" STREQUAL "HAIKU")) + ("${SDK}" STREQUAL "HAIKU") OR + ("${SDK}" STREQUAL "WASM")) # No extra dependencies. else() message(FATAL_ERROR "Unknown SDK: ${SDK}") diff --git a/utils/build-script b/utils/build-script index 3ba7c82f8c6fa..366a47f5e758a 100755 --- a/utils/build-script +++ b/utils/build-script @@ -213,6 +213,9 @@ class BuildScriptInvocation(object): elif args.android_arch == "aarch64": args.stdlib_deployment_targets.append( StdlibDeploymentTarget.Android.aarch64.name) + if args.wasm: + args.stdlib_deployment_targets.append( + StdlibDeploymentTarget.Wasm.wasm32.name) # Infer platform flags from manually-specified configure targets. # This doesn't apply to Darwin platforms, as they are diff --git a/utils/swift_build_support/swift_build_support/host_specific_configuration.py b/utils/swift_build_support/swift_build_support/host_specific_configuration.py index 557e1fed49660..ba3b5def3231e 100644 --- a/utils/swift_build_support/swift_build_support/host_specific_configuration.py +++ b/utils/swift_build_support/swift_build_support/host_specific_configuration.py @@ -232,6 +232,8 @@ def __platforms_to_skip_test(self, args): StdlibDeploymentTarget.AppleWatchSimulator) if not args.test_android: platforms_to_skip_test.add(StdlibDeploymentTarget.Android) + if not args.test_wasm: + platforms_to_skip_test.add(StdlibDeploymentTarget.Wasm) return platforms_to_skip_test @@ -252,4 +254,6 @@ def __platforms_to_skip_test_host(self, args): platforms_to_skip_test_host.add(StdlibDeploymentTarget.AppleTV) if not args.test_watchos_host: platforms_to_skip_test_host.add(StdlibDeploymentTarget.AppleWatch) + if not args.test_wasm_host: + platforms_to_skip_test_host.add(StdlibDeploymentTarget.Wasm) return platforms_to_skip_test_host From 7a7f11a157bff995e278108d0d2ce4b219e08ccc Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 9 Apr 2019 19:46:11 -0700 Subject: [PATCH 407/478] Integrate Emscripten sdk path --- cmake/modules/AddSwift.cmake | 19 +++++++++++++++++-- cmake/modules/SwiftConfigureSDK.cmake | 13 ++++++++++++- cmake/modules/SwiftWasmSupport.cmake | 16 ++++++++++++++++ include/swift/Runtime/Mutex.h | 2 +- include/swift/Runtime/MutexPThread.h | 2 +- stdlib/public/SwiftShims/Visibility.h | 2 +- utils/build-script | 24 ++++++++++++++++++++++++ utils/build-script-impl | 18 ++++++++++++++++++ utils/build_swift/driver_arguments.py | 17 +++++++++++++++++ 9 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 cmake/modules/SwiftWasmSupport.cmake diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 4d675282cb23f..1623e8ab07236 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -2,6 +2,7 @@ include(SwiftList) include(SwiftXcodeSupport) include(SwiftWindowsSupport) include(SwiftAndroidSupport) +include(SwiftWasmSupport) # SWIFTLIB_DIR is the directory in the build tree where Swift resource files # should be placed. Note that $CMAKE_CFG_INTDIR expands to "." for @@ -337,6 +338,12 @@ function(_add_variant_c_compile_flags) foreach(path ${${CFLAGS_ARCH}_INCLUDE}) list(APPEND result "\"${CMAKE_INCLUDE_FLAG_C}${path}\"") endforeach() + elseif(CFLAGS_SDK STREQUAL WASM) + list(APPEND result "-D__EMSCRIPTEN__=1") + swift_wasm_include_for_arch(${CFLAGS_ARCH} ${CFLAGS_ARCH}_INCLUDE) + foreach(path ${${CFLAGS_ARCH}_INCLUDE}) + list(APPEND result "\"${CMAKE_INCLUDE_FLAG_C}${path}\"") + endforeach() endif() set(ICU_UC_INCLUDE_DIR ${SWIFT_${CFLAGS_SDK}_${CFLAGS_ARCH}_ICU_UC_INCLUDE}) @@ -391,6 +398,11 @@ function(_add_variant_swift_compile_flags foreach(path IN LISTS ${arch}_swift_include) list(APPEND result "\"${CMAKE_INCLUDE_FLAG_C}${path}\"") endforeach() + elseif("${sdk}" STREQUAL "WASM") + swift_wasm_include_for_arch(${arch} ${arch}_swift_include) + foreach(path IN LISTS ${arch}_swift_include) + list(APPEND result "\"${CMAKE_INCLUDE_FLAG_C}${path}\"") + endforeach() endif() if(NOT BUILD_STANDALONE) @@ -465,8 +477,6 @@ function(_add_variant_link_flags) list(APPEND link_libraries "pthread") elseif("${LFLAGS_SDK}" STREQUAL "CYGWIN") # No extra libraries required. - elseif("${LFLAGS_SDK}" STREQUAL "WASM") - # No extra libraries required. elseif("${LFLAGS_SDK}" STREQUAL "WINDOWS") # We don't need to add -nostdlib using MSVC or clang-cl, as MSVC and clang-cl rely on auto-linking entirely. if(NOT SWIFT_COMPILER_IS_MSVC_LIKE) @@ -502,6 +512,11 @@ function(_add_variant_link_flags) foreach(path IN LISTS ${LFLAGS_ARCH}_LIB) list(APPEND library_search_directories ${path}) endforeach() + elseif("${LFLAGS_SDK}" STREQUAL "WASM") + swift_wasm_lib_for_arch(${LFLAGS_ARCH} ${LFLAGS_ARCH}_LIB) + foreach(path IN LISTS ${LFLAGS_ARCH}_LIB) + list(APPEND library_search_directories ${path}) + endforeach() else() # If lto is enabled, we need to add the object path flag so that the LTO code # generator leaves the intermediate object file in a place where it will not diff --git a/cmake/modules/SwiftConfigureSDK.cmake b/cmake/modules/SwiftConfigureSDK.cmake index 1fbf17e00ed85..e80f60332a983 100644 --- a/cmake/modules/SwiftConfigureSDK.cmake +++ b/cmake/modules/SwiftConfigureSDK.cmake @@ -7,6 +7,7 @@ set(SWIFT_CONFIGURED_SDKS) include(SwiftWindowsSupport) include(SwiftAndroidSupport) +include(SwiftWasmSupport) # Report the given SDK to the user. function(_report_sdk prefix) @@ -59,6 +60,14 @@ function(_report_sdk prefix) message(STATUS " ${arch} INCLUDE: ${${arch}_INCLUDE}") message(STATUS " ${arch} LIB: ${${arch}_LIB}") endforeach() + elseif("${prefix}" STREQUAL "WASM") + message(STATUS " Emscripten Dir: $ENV{SWIFT_WASM_EMSCRIPTEN_PATH}") + foreach(arch ${SWIFT_SDK_${prefix}_ARCHITECTURES}) + swift_wasm_include_for_arch(${arch} ${arch}_INCLUDE) + swift_wasm_lib_for_arch(${arch} ${arch}_LIB) + message(STATUS " ${arch} INCLUDE: ${${arch}_INCLUDE}") + message(STATUS " ${arch} LIB: ${${arch}_LIB}") + endforeach() else() foreach(arch ${SWIFT_SDK_${prefix}_ARCHITECTURES}) message(STATUS " ${arch} Path: ${SWIFT_SDK_${prefix}_ARCH_${arch}_PATH}") @@ -290,7 +299,7 @@ macro(configure_sdk_unix name architectures) if("${prefix}" STREQUAL "LINUX") if(arch MATCHES "(armv6|armv7)") - set(SWIFT_SDK_LINUX_ARCH_${arch}_TRIPLE "${arch}-unknown-linux-gnueabihf") + set(SWIFT_SDK_LINUX_ARCH_${arch}i_TRIPLE "${arch}-unknown-linux-gnueabihf") elseif(arch MATCHES "(aarch64|i686|powerpc64|powerpc64le|s390x|x86_64)") set(SWIFT_SDK_LINUX_ARCH_${arch}_TRIPLE "${arch}-unknown-linux-gnu") else() @@ -323,6 +332,8 @@ macro(configure_sdk_unix name architectures) if(NOT arch STREQUAL wasm32) message(FATAL_ERROR "unsupported arch for WebAssembly: ${arch}") endif() + # FIXME: this is actually wrong: emscripten doesn't use sysroot. + set(SWIFT_SDK_WASM_ARCH_wasm32_PATH "${SWIFT_WASM_EMSCRIPTEN_PATH}/system") set(SWIFT_SDK_WASM_ARCH_wasm32_TRIPLE "wasm32-unknown-unknown-wasm") else() message(FATAL_ERROR "unknown Unix OS: ${prefix}") diff --git a/cmake/modules/SwiftWasmSupport.cmake b/cmake/modules/SwiftWasmSupport.cmake new file mode 100644 index 0000000000000..1d14263fbb49a --- /dev/null +++ b/cmake/modules/SwiftWasmSupport.cmake @@ -0,0 +1,16 @@ +function(swift_wasm_include_for_arch arch var) + set(paths) + list(APPEND paths + "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/include/libcxx" + "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/lib/libcxxabi/include" + "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/include/compat" + "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/include" + "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/include/libc" + "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/lib/libc/musl/arch/emscripten" + "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/local/include" + "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/include/SDL") + set(${var} ${paths} PARENT_SCOPE) +endfunction() + +function(swift_wasm_lib_for_arch arch var) +endfunction() diff --git a/include/swift/Runtime/Mutex.h b/include/swift/Runtime/Mutex.h index 1b320e9d22a6e..bfaa8a397e9f3 100644 --- a/include/swift/Runtime/Mutex.h +++ b/include/swift/Runtime/Mutex.h @@ -20,7 +20,7 @@ #include -#if (defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__HAIKU__)) +#if (defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__EMSCRIPTEN__)) #include "swift/Runtime/MutexPThread.h" #elif defined(_WIN32) #include "swift/Runtime/MutexWin32.h" diff --git a/include/swift/Runtime/MutexPThread.h b/include/swift/Runtime/MutexPThread.h index 1b06a2977bd06..fe976147ab3de 100644 --- a/include/swift/Runtime/MutexPThread.h +++ b/include/swift/Runtime/MutexPThread.h @@ -26,7 +26,7 @@ typedef pthread_cond_t ConditionHandle; typedef pthread_mutex_t MutexHandle; typedef pthread_rwlock_t ReadWriteLockHandle; -#if defined(__CYGWIN__) || defined(__ANDROID__) || defined(__HAIKU__) +#if defined(__CYGWIN__) || defined(__ANDROID__) || defined(__HAIKU__) || defined(__EMSCRIPTEN__) // At the moment CYGWIN pthreads implementation doesn't support the use of // constexpr for static allocation versions. The way they define things // results in a reinterpret_cast which violates constexpr. Similarly, Android's diff --git a/stdlib/public/SwiftShims/Visibility.h b/stdlib/public/SwiftShims/Visibility.h index 8577fad1653b9..6ebea03367541 100644 --- a/stdlib/public/SwiftShims/Visibility.h +++ b/stdlib/public/SwiftShims/Visibility.h @@ -76,7 +76,7 @@ // SWIFT_RUNTIME_EXPORT on the library it's exported from. /// Attribute used to export symbols from the runtime. -#if defined(__MACH__) +#if defined(__MACH__) || defined(__EMSCRIPTEN__) # define SWIFT_EXPORT_ATTRIBUTE __attribute__((__visibility__("default"))) diff --git a/utils/build-script b/utils/build-script index 366a47f5e758a..43b0bc5baf22a 100755 --- a/utils/build-script +++ b/utils/build-script @@ -119,6 +119,20 @@ class BuildScriptInvocation(object): "--android-icu-i18n-include, and --android-icu-data " "must be specified") + if args.wasm: + if args.wasm_emscripten is None or \ + args.wasm_icu_uc is None or \ + args.wasm_icu_uc_include is None or \ + args.wasm_icu_i18n is None or \ + args.wasm_icu_i18n_include is None or \ + args.wasm_icu_data is None: + diagnostics.fatal( + "when building for WebAssembly, --wasm-emscripten, " + "--wasm-icu-uc, " + "--wasm-icu-uc-include, --wasm-icu-i18n, " + "--wasm-icu-i18n-include, and --wasm-icu-data " + "must be specified") + targets_needing_toolchain = [ 'build_indexstoredb', 'build_sourcekitlsp', @@ -587,6 +601,16 @@ class BuildScriptInvocation(object): args.android_deploy_device_path, ] + if args.wasm: + impl_args += [ + "--wasm-emscripten", args.wasm_emscripten, + "--wasm-icu-uc", args.wasm_icu_uc, + "--wasm-icu-uc-include", args.wasm_icu_uc_include, + "--wasm-icu-i18n", args.wasm_icu_i18n, + "--wasm-icu-i18n-include", args.wasm_icu_i18n_include, + "--wasm-icu-data", args.wasm_icu_data, + ] + if platform.system() == 'Darwin': impl_args += [ "--toolchain-prefix", diff --git a/utils/build-script-impl b/utils/build-script-impl index d63268d7a0bab..cf199b0b5dedf 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -236,6 +236,12 @@ KNOWN_SETTINGS=( android-icu-data "" "Path to libicudata.so" android-deploy-device-path "" "Path on an Android device to which built Swift stdlib products will be deployed" android-arch "armv7" "The Android target architecture when building for Android" + wasm-emscripten "" "An absolute path to the Emscripten that will be used as a libc implementation for Wasm builds" + wasm-icu-uc "" "Path to libicuuc.so" + wasm-icu-uc-include "" "Path to a directory containing headers for libicuuc" + wasm-icu-i18n "" "Path to libicui18n.so" + wasm-icu-i18n-include "" "Path to a directory containing headers libicui18n" + wasm-icu-data "" "Path to libicudata.so" check-args-only "" "set to check all arguments are known. Exit with status 0 if success, non zero otherwise" common-cmake-options "" "CMake options used for all targets, including LLVM/Clang" cmark-cmake-options "" "CMake options used for all cmark targets" @@ -1658,6 +1664,18 @@ for host in "${ALL_HOSTS[@]}"; do ) fi + if [[ ! "${SKIP_BUILD_WASM}" ]]; then + cmake_options=( + "${cmake_options[@]}" + -DSWIFT_WASM_EMSCRIPTEN_PATH:STRING="${WASM_EMSCRIPTEN}" + -DSWIFT_WASM_ICU_UC:STRING="${WASM_ICU_UC}" + -DSWIFT_WASM_ICU_UC_INCLUDE:STRING="${WASM_ICU_UC_INCLUDE}" + -DSWIFT_WASM_ICU_I18N:STRING="${WASM_ICU_I18N}" + -DSWIFT_WASM_ICU_I18N_INCLUDE:STRING="${WASM_ICU_I18N_INCLUDE}" + -DSWIFT_WASM_ICU_DATA:STRING="${WASM_ICU_DATA}" + ) + fi + if [[ "${DARWIN_OVERLAY_TARGET}" != "" ]]; then # Split LOCAL_HOST into a pair ``arch-sdk`` # Example LOCAL_HOST: macosx-x86_64 diff --git a/utils/build_swift/driver_arguments.py b/utils/build_swift/driver_arguments.py index 406c451447ad3..cff07151a870d 100644 --- a/utils/build_swift/driver_arguments.py +++ b/utils/build_swift/driver_arguments.py @@ -1075,6 +1075,23 @@ def create_argument_parser(): 'Currently only armv7 and aarch64 are supported. ' '%(default)s is the default.') + in_group('Build settings for Android') + + option('--wasm-emscripten', store_path, + help='An absolute path to Emscripten that will be used as a libc ' + 'implementation for Wasm builds') + + option('--wasm-icu-uc', store_path, + help='Path to libicuuc.so') + option('--wasm-icu-uc-include', store_path, + help='Path to a directory containing headers for libicuuc') + option('--wasm-icu-i18n', store_path, + help='Path to libicui18n.so') + option('--wasm-icu-i18n-include', store_path, + help='Path to a directory containing headers libicui18n') + option('--wasm-icu-data', store_path, + help='Path to libicudata.so') + # ------------------------------------------------------------------------- in_group('Experimental language features') From 7d4bc9f1e9cd405eaa3cff8a1af8e148983f7865 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 9 Apr 2019 22:59:56 -0700 Subject: [PATCH 408/478] start adding Emscripten defines in stdlib --- stdlib/public/SwiftShims/LibcShims.h | 4 +++- stdlib/public/runtime/Errors.cpp | 2 +- stdlib/public/runtime/Exclusivity.cpp | 8 +++++++- stdlib/public/runtime/Heap.cpp | 5 +++++ stdlib/public/runtime/ImageInspectionCOFF.cpp | 2 +- stdlib/public/runtime/ImageInspectionCOFF.h | 2 +- stdlib/public/runtime/ImageInspectionELF.h | 2 +- stdlib/public/runtime/ThreadLocalStorage.h | 2 ++ stdlib/public/stubs/LibcShims.cpp | 2 +- 9 files changed, 22 insertions(+), 7 deletions(-) diff --git a/stdlib/public/SwiftShims/LibcShims.h b/stdlib/public/SwiftShims/LibcShims.h index 5725f294885f9..e8ac251ed0f5d 100644 --- a/stdlib/public/SwiftShims/LibcShims.h +++ b/stdlib/public/SwiftShims/LibcShims.h @@ -43,6 +43,8 @@ typedef __swift_uint32_t __swift_mode_t; typedef __swift_uint16_t __swift_mode_t; #elif defined(_WIN32) typedef __swift_int32_t __swift_mode_t; +#elif defined(__EMSCRIPTEN__) +typedef __swift_uint32_t __swift_mode_t; #else // just guessing typedef __swift_uint16_t __swift_mode_t; #endif @@ -105,7 +107,7 @@ static inline __swift_size_t _swift_stdlib_malloc_size(const void *ptr) { return malloc_size(ptr); } #elif defined(__linux__) || defined(__CYGWIN__) || defined(__ANDROID__) \ - || defined(__HAIKU__) || defined(__FreeBSD__) + || defined(__HAIKU__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__) static inline __swift_size_t _swift_stdlib_malloc_size(const void *ptr) { #if defined(__ANDROID__) #if !defined(__ANDROID_API__) || __ANDROID_API__ >= 17 diff --git a/stdlib/public/runtime/Errors.cpp b/stdlib/public/runtime/Errors.cpp index 6e202434750f0..e2c0dd4a7487d 100644 --- a/stdlib/public/runtime/Errors.cpp +++ b/stdlib/public/runtime/Errors.cpp @@ -14,7 +14,7 @@ // //===----------------------------------------------------------------------===// -#if defined(__CYGWIN__) || defined(__HAIKU__) +#if defined(__CYGWIN__) || defined(__HAIKU__) || defined(__EMSCRIPTEN__) #define SWIFT_SUPPORTS_BACKTRACE_REPORTING 0 #else #define SWIFT_SUPPORTS_BACKTRACE_REPORTING 1 diff --git a/stdlib/public/runtime/Exclusivity.cpp b/stdlib/public/runtime/Exclusivity.cpp index dfff68f105f0f..09c33a4b2e9e9 100644 --- a/stdlib/public/runtime/Exclusivity.cpp +++ b/stdlib/public/runtime/Exclusivity.cpp @@ -24,7 +24,9 @@ #include // Pick a return-address strategy -#if __GNUC__ +#if defined(__EMSCRIPTEN__) +#define get_return_address() ((void*) 0) +#elif __GNUC__ #define get_return_address() __builtin_return_address(0) #elif _MSC_VER #include @@ -36,7 +38,11 @@ using namespace swift; +#ifdef __EMSCRIPTEN__ +bool swift::_swift_disableExclusivityChecking = true; +#else bool swift::_swift_disableExclusivityChecking = false; +#endif static const char *getAccessName(ExclusivityFlags flags) { switch (flags) { diff --git a/stdlib/public/runtime/Heap.cpp b/stdlib/public/runtime/Heap.cpp index 46e133a7b1d17..c32fbafdf25ef 100644 --- a/stdlib/public/runtime/Heap.cpp +++ b/stdlib/public/runtime/Heap.cpp @@ -42,6 +42,11 @@ using namespace swift; #elif defined(_WIN32) # define MALLOC_ALIGN_MASK 7 +#elif defined(__EMSCRIPTEN__) +// Musl malloc is 4*sizeof(size_t), so 16 bytes on 32-bit? +// For some reason the unknown alignment code fails because std::max isn't constexpr? +# define MALLOC_ALIGN_MASK 15 + #else // Unknown alignment, but the standard requires alignment suitable for the largest // standard types. diff --git a/stdlib/public/runtime/ImageInspectionCOFF.cpp b/stdlib/public/runtime/ImageInspectionCOFF.cpp index a6f6d906f98e6..4c534b8ef82e1 100644 --- a/stdlib/public/runtime/ImageInspectionCOFF.cpp +++ b/stdlib/public/runtime/ImageInspectionCOFF.cpp @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if !defined(__ELF__) && !defined(__MACH__) +#if !defined(__ELF__) && !defined(__MACH__) && !defined(__EMSCRIPTEN__) #include "ImageInspection.h" #include "ImageInspectionCOFF.h" diff --git a/stdlib/public/runtime/ImageInspectionCOFF.h b/stdlib/public/runtime/ImageInspectionCOFF.h index c76a33e25d0c4..5f9d2eb7a836c 100644 --- a/stdlib/public/runtime/ImageInspectionCOFF.h +++ b/stdlib/public/runtime/ImageInspectionCOFF.h @@ -19,7 +19,7 @@ #ifndef SWIFT_RUNTIME_IMAGEINSPECTIONCOFF_H #define SWIFT_RUNTIME_IMAGEINSPECTIONCOFF_H -#if !defined(__ELF__) && !defined(__MACH__) +#if !defined(__ELF__) && !defined(__MACH__) && !defined(__EMSCRIPTEN__) #include "../SwiftShims/Visibility.h" #include diff --git a/stdlib/public/runtime/ImageInspectionELF.h b/stdlib/public/runtime/ImageInspectionELF.h index afa2ee7150603..67d2c758418dc 100644 --- a/stdlib/public/runtime/ImageInspectionELF.h +++ b/stdlib/public/runtime/ImageInspectionELF.h @@ -21,7 +21,7 @@ #define SWIFT_REFLECTION_METADATA_ELF_NOTE_MAGIC_STRING "swift_reflection_metadata_magic_string" -#if defined(__ELF__) +#if defined(__ELF__) || defined(__EMSCRIPTEN__) #include "../SwiftShims/Visibility.h" #include diff --git a/stdlib/public/runtime/ThreadLocalStorage.h b/stdlib/public/runtime/ThreadLocalStorage.h index 82e2457b24f8a..243b12da5b30a 100644 --- a/stdlib/public/runtime/ThreadLocalStorage.h +++ b/stdlib/public/runtime/ThreadLocalStorage.h @@ -81,6 +81,8 @@ typedef int __swift_thread_key_t; typedef unsigned long __swift_thread_key_t; # elif defined(__HAIKU__) typedef int __swift_thread_key_t; +# elif defined(__EMSCRIPTEN__) +typedef unsigned int __swift_thread_key_t; # else typedef unsigned long __swift_thread_key_t; # endif diff --git a/stdlib/public/stubs/LibcShims.cpp b/stdlib/public/stubs/LibcShims.cpp index 083deaa4747a1..8368698067bd7 100644 --- a/stdlib/public/stubs/LibcShims.cpp +++ b/stdlib/public/stubs/LibcShims.cpp @@ -23,7 +23,7 @@ #include #include -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__EMSCRIPTEN__) #include #endif From 744f3b0830f6fe9571daa5556cb5deddc64e31cc Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 9 Apr 2019 23:27:33 -0700 Subject: [PATCH 409/478] actually pass the icu paths into CMake --- utils/build-script-impl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index cf199b0b5dedf..5c827eb520a0b 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1668,11 +1668,11 @@ for host in "${ALL_HOSTS[@]}"; do cmake_options=( "${cmake_options[@]}" -DSWIFT_WASM_EMSCRIPTEN_PATH:STRING="${WASM_EMSCRIPTEN}" - -DSWIFT_WASM_ICU_UC:STRING="${WASM_ICU_UC}" - -DSWIFT_WASM_ICU_UC_INCLUDE:STRING="${WASM_ICU_UC_INCLUDE}" - -DSWIFT_WASM_ICU_I18N:STRING="${WASM_ICU_I18N}" - -DSWIFT_WASM_ICU_I18N_INCLUDE:STRING="${WASM_ICU_I18N_INCLUDE}" - -DSWIFT_WASM_ICU_DATA:STRING="${WASM_ICU_DATA}" + -DSWIFT_WASM_wasm32_ICU_UC:STRING="${WASM_ICU_UC}" + -DSWIFT_WASM_wasm32_ICU_UC_INCLUDE:STRING="${WASM_ICU_UC_INCLUDE}" + -DSWIFT_WASM_wasm32_ICU_I18N:STRING="${WASM_ICU_I18N}" + -DSWIFT_WASM_wasm32_ICU_I18N_INCLUDE:STRING="${WASM_ICU_I18N_INCLUDE}" + -DSWIFT_WASM_wasm32_ICU_DATA:STRING="${WASM_ICU_DATA}" ) fi From fa81b696b6696369d48865bd5094a10ff153c629 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 9 Apr 2019 23:51:43 -0700 Subject: [PATCH 410/478] do not link WebAssembly shared libraries for now We have no working linker. --- cmake/modules/AddSwift.cmake | 4 +++- fakeld | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100755 fakeld diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 1623e8ab07236..0d7c52675b7d7 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -546,7 +546,9 @@ function(_add_variant_link_flags) if(NOT SWIFT_COMPILER_IS_MSVC_LIKE) # FIXME: On Apple platforms, find_program needs to look for "ld64.lld" find_program(LDLLD_PATH "ld.lld") - if((SWIFT_ENABLE_LLD_LINKER AND LDLLD_PATH AND NOT APPLE) OR + if("${LFLAGS_SDK}" STREQUAL "WASM") + list(APPEND result "-fuse-ld=/home/zhuowei/swift-source/swift/fakeld") + elseif((SWIFT_ENABLE_LLD_LINKER AND LDLLD_PATH AND NOT APPLE) OR ("${LFLAGS_SDK}" STREQUAL "WINDOWS" AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "WINDOWS")) list(APPEND result "-fuse-ld=lld") diff --git a/fakeld b/fakeld new file mode 100755 index 0000000000000..1df157004de91 --- /dev/null +++ b/fakeld @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +import sys +def outputname(): + for i in range(len(sys.argv)): + if sys.argv[i] == "-o": + return sys.argv[i + 1] + return "a.out" +with open(outputname(), "wb") as outfile: + pass From 0e499c0ae5e2b7cd464e5ed887ee43a6fb5accb2 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Wed, 10 Apr 2019 16:50:21 -0700 Subject: [PATCH 411/478] add script to launch build --- vvv.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100755 vvv.sh diff --git a/vvv.sh b/vvv.sh new file mode 100755 index 0000000000000..bcb75036bf6b1 --- /dev/null +++ b/vvv.sh @@ -0,0 +1,10 @@ +utils/build-script --release-debuginfo --wasm \ + --llvm-targets-to-build "X86;ARM;AArch64;PowerPC;SystemZ;WebAssembly" \ + --llvm-max-parallel-lto-link-jobs 1 --swift-tools-max-parallel-lto-link-jobs 1 \ + --wasm-emscripten "/home/zhuowei/Documents/emsdk/emscripten/1.38.30" \ + --wasm-icu-uc "todo" \ + --wasm-icu-uc-include "$PWD/NO" \ + --wasm-icu-i18n "todo" \ + --wasm-icu-i18n-include "todo" \ + --wasm-icu-data "todo" \ + "$@" From 81ee2d6c7f7c57c50e8b8ff559d8305f9e77add9 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Wed, 10 Apr 2019 17:46:07 -0700 Subject: [PATCH 412/478] start adding wasm32 to 32-bit checks in stdlib --- stdlib/public/core/AtomicInt.swift.gyb | 8 +++---- stdlib/public/core/Builtin.swift | 2 +- stdlib/public/core/SmallString.swift | 2 +- stdlib/public/core/StringGuts.swift | 2 +- stdlib/public/core/StringObject.swift | 32 +++++++++++++------------- stdlib/public/core/StringStorage.swift | 12 +++++----- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/stdlib/public/core/AtomicInt.swift.gyb b/stdlib/public/core/AtomicInt.swift.gyb index 62217f282ccc5..80514f42c147c 100644 --- a/stdlib/public/core/AtomicInt.swift.gyb +++ b/stdlib/public/core/AtomicInt.swift.gyb @@ -65,7 +65,7 @@ internal func _swift_stdlib_atomicCompareExchangeStrongInt( object target: UnsafeMutablePointer, expected: UnsafeMutablePointer, desired: Int) -> Bool { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) let (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_Int32( target._rawValue, expected.pointee._value, desired._value) #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) @@ -82,7 +82,7 @@ internal func _swift_stdlib_atomicCompareExchangeStrongInt( public // Existing uses outside stdlib func _swift_stdlib_atomicLoadInt( object target: UnsafeMutablePointer) -> Int { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) let value = Builtin.atomicload_seqcst_Int32(target._rawValue) return Int(value) #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) @@ -95,7 +95,7 @@ func _swift_stdlib_atomicLoadInt( internal func _swift_stdlib_atomicStoreInt( object target: UnsafeMutablePointer, desired: Int) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) Builtin.atomicstore_seqcst_Int32(target._rawValue, desired._value) #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) Builtin.atomicstore_seqcst_Int64(target._rawValue, desired._value) @@ -111,7 +111,7 @@ func _swift_stdlib_atomicFetch${operation}Int( object target: UnsafeMutablePointer, operand: Int) -> Int { let rawTarget = UnsafeMutableRawPointer(target) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) let value = _swift_stdlib_atomicFetch${operation}Int32( object: rawTarget.assumingMemoryBound(to: Int32.self), operand: Int32(operand)) diff --git a/stdlib/public/core/Builtin.swift b/stdlib/public/core/Builtin.swift index 07c9ce14af6a7..b29bcc0d6aa59 100644 --- a/stdlib/public/core/Builtin.swift +++ b/stdlib/public/core/Builtin.swift @@ -379,7 +379,7 @@ internal var _objectPointerLowSpareBitShift: UInt { } #if arch(i386) || arch(arm) || arch(powerpc64) || arch(powerpc64le) || arch( - s390x) + s390x) || arch(wasm32) @inlinable internal var _objectPointerIsObjCBit: UInt { @inline(__always) get { return 0x0000_0002 } diff --git a/stdlib/public/core/SmallString.swift b/stdlib/public/core/SmallString.swift index 1f53f8e7ff4fb..9792ed1cf485f 100644 --- a/stdlib/public/core/SmallString.swift +++ b/stdlib/public/core/SmallString.swift @@ -76,7 +76,7 @@ internal struct _SmallString { extension _SmallString { @inlinable @inline(__always) internal static var capacity: Int { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) return 10 #else return 15 diff --git a/stdlib/public/core/StringGuts.swift b/stdlib/public/core/StringGuts.swift index 2b7765cd4093e..cc6ed08226e67 100644 --- a/stdlib/public/core/StringGuts.swift +++ b/stdlib/public/core/StringGuts.swift @@ -178,7 +178,7 @@ extension _StringGuts { #else @usableFromInline @inline(never) @_effects(releasenone) internal func _invariantCheck() { - #if arch(i386) || arch(arm) + #if arch(i386) || arch(arm) || arch(wasm32) _internalInvariant(MemoryLayout.size == 12, """ the runtime is depending on this, update Reflection.mm and \ this if you change it diff --git a/stdlib/public/core/StringObject.swift b/stdlib/public/core/StringObject.swift index 501d81b66ff53..72cf15c5200c7 100644 --- a/stdlib/public/core/StringObject.swift +++ b/stdlib/public/core/StringObject.swift @@ -77,7 +77,7 @@ internal struct _StringObject { internal init(zero: ()) { self._storage = 0 } } -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) @usableFromInline @frozen internal enum Variant { case immortal(UInt) @@ -169,7 +169,7 @@ extension _StringObject { @usableFromInline internal typealias RawBitPattern = (UInt64, UInt64) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) // On 32-bit platforms, raw bit conversion is one-way only and uses the same // layout as on 64-bit platforms. @usableFromInline @@ -245,7 +245,7 @@ extension _StringObject { @inlinable @_transparent internal var discriminatedObjectRawBits: UInt64 { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) let low32: UInt switch _variant { case .immortal(let bitPattern): @@ -387,7 +387,7 @@ extension _StringObject.Nibbles { extension _StringObject { @inlinable @inline(__always) internal static var nativeBias: UInt { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) return 20 #else return 32 @@ -512,7 +512,7 @@ extension _StringObject { // spare bits (the most significant nibble) in a pointer. let word1 = small.rawBits.0.littleEndian let word2 = small.rawBits.1.littleEndian -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) // On 32-bit, we need to unpack the small string. let smallStringDiscriminatorAndCount: UInt64 = 0xFF00_0000_0000_0000 @@ -556,7 +556,7 @@ extension _StringObject { @inlinable @inline(__always) internal init(empty:()) { // Canonical empty pattern: small zero-length string -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( count: 0, variant: .immortal(0), @@ -819,7 +819,7 @@ extension _StringObject { @inline(__always) internal var nativeStorage: __StringStorage { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) guard case .native(let storage) = _variant else { _internalInvariantFailure() } @@ -832,7 +832,7 @@ extension _StringObject { @inline(__always) internal var sharedStorage: __SharedStringStorage { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) guard case .native(let storage) = _variant else { _internalInvariantFailure() } @@ -846,7 +846,7 @@ extension _StringObject { @inline(__always) internal var cocoaObject: AnyObject { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) guard case .bridged(let object) = _variant else { _internalInvariantFailure() } @@ -935,7 +935,7 @@ extension _StringObject { internal init(immortal bufPtr: UnsafeBufferPointer, isASCII: Bool) { let countAndFlags = CountAndFlags( immortalCount: bufPtr.count, isASCII: isASCII) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( variant: .immortal(start: bufPtr.baseAddress._unsafelyUnwrappedUnchecked), discriminator: Nibbles.largeImmortal(), @@ -955,7 +955,7 @@ extension _StringObject { @inline(__always) internal init(_ storage: __StringStorage) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( variant: .native(storage), discriminator: Nibbles.largeMortal(), @@ -969,7 +969,7 @@ extension _StringObject { } internal init(_ storage: __SharedStringStorage) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( variant: .native(storage), discriminator: Nibbles.largeMortal(), @@ -987,7 +987,7 @@ extension _StringObject { ) { let countAndFlags = CountAndFlags(sharedCount: length, isASCII: isASCII) let discriminator = Nibbles.largeCocoa(providesFastUTF8: providesFastUTF8) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( variant: .bridged(cocoa), discriminator: discriminator, @@ -1009,7 +1009,7 @@ extension _StringObject { #else @usableFromInline @inline(never) @_effects(releasenone) internal func _invariantCheck() { - #if arch(i386) || arch(arm) + #if arch(i386) || arch(arm) || arch(wasm32) _internalInvariant(MemoryLayout<_StringObject>.size == 12) _internalInvariant(MemoryLayout<_StringObject>.stride == 12) _internalInvariant(MemoryLayout<_StringObject>.alignment == 4) @@ -1079,7 +1079,7 @@ extension _StringObject { } } - #if arch(i386) || arch(arm) + #if arch(i386) || arch(arm) || arch(wasm32) switch _variant { case .immortal: _internalInvariant(isImmortal) @@ -1099,7 +1099,7 @@ extension _StringObject { let raw = self.rawBits let word0 = ("0000000000000000" + String(raw.0, radix: 16)).suffix(16) let word1 = ("0000000000000000" + String(raw.1, radix: 16)).suffix(16) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) print(""" StringObject(\ <\(word0) \(word1)> \ diff --git a/stdlib/public/core/StringStorage.swift b/stdlib/public/core/StringStorage.swift index 8e8a3d29d2133..a80937d1f7141 100644 --- a/stdlib/public/core/StringStorage.swift +++ b/stdlib/public/core/StringStorage.swift @@ -46,7 +46,7 @@ private typealias CountAndFlags = _StringObject.CountAndFlags // renamed. The old name must not be used in the new runtime. final internal class __StringStorage : __SwiftNativeNSString, _AbstractStringStorage { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) // The total allocated storage capacity. Note that this includes the required // nul-terminator. internal var _realCapacity: Int @@ -106,7 +106,7 @@ final internal class __StringStorage // for Strings ~1KB or larger, though at this point we're well into our growth // curve. private func determineCodeUnitCapacity(_ desiredCapacity: Int) -> Int { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) // FIXME: Adapt to actual 32-bit allocator. For now, let's arrange things so // that the instance size will be a multiple of 4. let bias = Int(bitPattern: _StringObject.nativeBias) @@ -139,7 +139,7 @@ extension __StringStorage { __StringStorage.self, realCodeUnitCapacity._builtinWordValue, UInt8.self, 1._builtinWordValue, Optional<_StringBreadcrumbs>.self) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) storage._realCapacity = realCodeUnitCapacity storage._count = countAndFlags.count storage._flags = countAndFlags.flags @@ -319,7 +319,7 @@ extension __StringStorage { internal func _updateCountAndFlags(newCount: Int, newIsASCII: Bool) { let countAndFlags = CountAndFlags( mortalCount: newCount, isASCII: newIsASCII) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self._count = countAndFlags.count self._flags = countAndFlags.flags #else @@ -463,7 +463,7 @@ final internal class __SharedStringStorage internal var _owner: AnyObject? internal var start: UnsafePointer -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) internal var _count: Int internal var _flags: UInt16 @@ -485,7 +485,7 @@ final internal class __SharedStringStorage ) { self._owner = nil self.start = ptr -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self._count = countAndFlags.count self._flags = countAndFlags.flags #else From f8211352e9ac1e58072386d4e469b65f44af40ed Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Wed, 10 Apr 2019 18:31:05 -0700 Subject: [PATCH 413/478] WebAssembly: more conditional compile defines --- stdlib/public/Platform/Platform.swift | 4 ++-- stdlib/public/core/BridgeStorage.swift | 2 +- stdlib/public/core/DictionaryVariant.swift | 2 +- stdlib/public/core/Hasher.swift | 4 ++-- stdlib/public/core/SetVariant.swift | 2 +- stdlib/public/core/StringBridge.swift | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/stdlib/public/Platform/Platform.swift b/stdlib/public/Platform/Platform.swift index f5e54c7da1283..347931eb3b2fe 100644 --- a/stdlib/public/Platform/Platform.swift +++ b/stdlib/public/Platform/Platform.swift @@ -332,7 +332,7 @@ public var SIG_DFL: sig_t? { return nil } public var SIG_IGN: sig_t { return unsafeBitCast(1, to: sig_t.self) } public var SIG_ERR: sig_t { return unsafeBitCast(-1, to: sig_t.self) } public var SIG_HOLD: sig_t { return unsafeBitCast(5, to: sig_t.self) } -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Haiku) || os(Wasm) public typealias sighandler_t = __sighandler_t public var SIG_DFL: sighandler_t? { return nil } @@ -380,7 +380,7 @@ public var SEM_FAILED: UnsafeMutablePointer? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) // The value is ABI. Value verified to be correct for OS X, iOS, watchOS, tvOS. return UnsafeMutablePointer(bitPattern: -1) -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) // The value is ABI. Value verified to be correct on Glibc. return UnsafeMutablePointer(bitPattern: 0) #else diff --git a/stdlib/public/core/BridgeStorage.swift b/stdlib/public/core/BridgeStorage.swift index 4b100f28f7cfe..fcc971f8b6f76 100644 --- a/stdlib/public/core/BridgeStorage.swift +++ b/stdlib/public/core/BridgeStorage.swift @@ -61,7 +61,7 @@ internal struct _BridgeStorage { rawValue = Builtin.reinterpretCast(native) } -#if !(arch(i386) || arch(arm)) +#if !(arch(i386) || arch(arm) || arch(wasm32)) @inlinable @inline(__always) internal init(taggedPayload: UInt) { diff --git a/stdlib/public/core/DictionaryVariant.swift b/stdlib/public/core/DictionaryVariant.swift index 988ef9f7d0679..5ffaacc45ef32 100644 --- a/stdlib/public/core/DictionaryVariant.swift +++ b/stdlib/public/core/DictionaryVariant.swift @@ -46,7 +46,7 @@ extension Dictionary { @inlinable @inline(__always) init(dummy: Void) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init(native: _NativeDictionary()) #else self.object = _BridgeStorage(taggedPayload: 0) diff --git a/stdlib/public/core/Hasher.swift b/stdlib/public/core/Hasher.swift index f7098db934aa3..202c2830610ab 100644 --- a/stdlib/public/core/Hasher.swift +++ b/stdlib/public/core/Hasher.swift @@ -160,7 +160,7 @@ extension Hasher { @inline(__always) internal mutating func combine(_ value: UInt) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) combine(UInt32(truncatingIfNeeded: value)) #else combine(UInt64(truncatingIfNeeded: value)) @@ -423,7 +423,7 @@ public struct Hasher { @usableFromInline internal static func _hash(seed: Int, _ value: UInt) -> Int { var state = _State(seed: seed) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) _internalInvariant(UInt.bitWidth < UInt64.bitWidth) let tbc = _TailBuffer( tail: UInt64(truncatingIfNeeded: value), diff --git a/stdlib/public/core/SetVariant.swift b/stdlib/public/core/SetVariant.swift index 0092fc74ca45e..ae3149002c7c4 100644 --- a/stdlib/public/core/SetVariant.swift +++ b/stdlib/public/core/SetVariant.swift @@ -36,7 +36,7 @@ extension Set { @inlinable @inline(__always) init(dummy: ()) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init(native: _NativeSet()) #else self.object = _BridgeStorage(taggedPayload: 0) diff --git a/stdlib/public/core/StringBridge.swift b/stdlib/public/core/StringBridge.swift index 1896d974204b6..2b892c2d52867 100644 --- a/stdlib/public/core/StringBridge.swift +++ b/stdlib/public/core/StringBridge.swift @@ -285,7 +285,7 @@ internal enum _KnownCocoaString { case storage case shared case cocoa -#if !(arch(i386) || arch(arm)) +#if !(arch(i386) || arch(arm) || arch(wasm32)) case tagged #endif From c47380ecffa4a95030e6369c676f726202f51d32 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Wed, 10 Apr 2019 21:11:53 -0700 Subject: [PATCH 414/478] Hack: Remove returnaddress calls emitted by exclusivity checks WebAssembly doesn't have __builtin_return_address. --- lib/IRGen/IRGenSIL.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 7292f9e0160ca..d3394346328bd 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -4343,7 +4343,8 @@ void IRGenSILFunction::visitBeginUnpairedAccessInst( // in which case we should use the caller, which is generally ok because // materializeForSet can't usually be thunked. llvm::Value *pc; - if (hasBeenInlined(access)) { + // hack: wasm doesn't have returnaddress + if (true || hasBeenInlined(access)) { pc = llvm::ConstantPointerNull::get(IGM.Int8PtrTy); } else { auto retAddrFn = From bf2fbf05acbd17a42c4c34d488fe116735d30281 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Wed, 10 Apr 2019 21:58:07 -0700 Subject: [PATCH 415/478] WebAssembly: Hack: disable all atomic instructions This matches what Clang does. This doesn't check that the target is Wasm; will need to do that. Patcheng's port seems to have working? atomics, so it's probably possible to get atomics working, but this is fine for now. This still doesn't get the stdlib to compile, but at least the error now matches what I get when I run clang on the -emit-ir LLVM IR output. --- lib/IRGen/IRGen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 4938a22b029fc..342baae37c7c0 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -162,6 +162,8 @@ swift::getIRTargetOptions(IRGenOptions &Opts, ASTContext &Ctx) { // Explicitly request debugger tuning for LLDB which is the default // on Darwin platforms but not on others. TargetOpts.DebuggerTuning = llvm::DebuggerKind::LLDB; + // WebAssembly HACK: disable atomics + TargetOpts.ThreadModel = llvm::ThreadModel::Single; auto *Clang = static_cast(Ctx.getClangModuleLoader()); clang::TargetOptions &ClangOpts = Clang->getTargetInfo().getTargetOpts(); From f9426068651308f010524c8f91b38f42068face3 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Fri, 12 Apr 2019 21:47:00 -0700 Subject: [PATCH 416/478] change non-Emscripten specific ifdefs to check for __wasm__ instead --- stdlib/public/SwiftShims/Visibility.h | 2 +- stdlib/public/runtime/Exclusivity.cpp | 4 ++-- stdlib/public/runtime/ImageInspectionCOFF.cpp | 2 +- stdlib/public/runtime/ImageInspectionCOFF.h | 2 +- stdlib/public/runtime/ImageInspectionELF.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stdlib/public/SwiftShims/Visibility.h b/stdlib/public/SwiftShims/Visibility.h index 6ebea03367541..8245a3b9be7e7 100644 --- a/stdlib/public/SwiftShims/Visibility.h +++ b/stdlib/public/SwiftShims/Visibility.h @@ -76,7 +76,7 @@ // SWIFT_RUNTIME_EXPORT on the library it's exported from. /// Attribute used to export symbols from the runtime. -#if defined(__MACH__) || defined(__EMSCRIPTEN__) +#if defined(__MACH__) || defined(__wasm__) # define SWIFT_EXPORT_ATTRIBUTE __attribute__((__visibility__("default"))) diff --git a/stdlib/public/runtime/Exclusivity.cpp b/stdlib/public/runtime/Exclusivity.cpp index 09c33a4b2e9e9..59acc280dfadf 100644 --- a/stdlib/public/runtime/Exclusivity.cpp +++ b/stdlib/public/runtime/Exclusivity.cpp @@ -24,7 +24,7 @@ #include // Pick a return-address strategy -#if defined(__EMSCRIPTEN__) +#if defined(__wasm__) #define get_return_address() ((void*) 0) #elif __GNUC__ #define get_return_address() __builtin_return_address(0) @@ -38,7 +38,7 @@ using namespace swift; -#ifdef __EMSCRIPTEN__ +#ifdef __wasm__ bool swift::_swift_disableExclusivityChecking = true; #else bool swift::_swift_disableExclusivityChecking = false; diff --git a/stdlib/public/runtime/ImageInspectionCOFF.cpp b/stdlib/public/runtime/ImageInspectionCOFF.cpp index 4c534b8ef82e1..09f0251f12bb6 100644 --- a/stdlib/public/runtime/ImageInspectionCOFF.cpp +++ b/stdlib/public/runtime/ImageInspectionCOFF.cpp @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if !defined(__ELF__) && !defined(__MACH__) && !defined(__EMSCRIPTEN__) +#if !defined(__ELF__) && !defined(__MACH__) && !defined(__wasm__) #include "ImageInspection.h" #include "ImageInspectionCOFF.h" diff --git a/stdlib/public/runtime/ImageInspectionCOFF.h b/stdlib/public/runtime/ImageInspectionCOFF.h index 5f9d2eb7a836c..b5e54c46687db 100644 --- a/stdlib/public/runtime/ImageInspectionCOFF.h +++ b/stdlib/public/runtime/ImageInspectionCOFF.h @@ -19,7 +19,7 @@ #ifndef SWIFT_RUNTIME_IMAGEINSPECTIONCOFF_H #define SWIFT_RUNTIME_IMAGEINSPECTIONCOFF_H -#if !defined(__ELF__) && !defined(__MACH__) && !defined(__EMSCRIPTEN__) +#if !defined(__ELF__) && !defined(__MACH__) && !defined(__wasm__) #include "../SwiftShims/Visibility.h" #include diff --git a/stdlib/public/runtime/ImageInspectionELF.h b/stdlib/public/runtime/ImageInspectionELF.h index 67d2c758418dc..6b120897fc926 100644 --- a/stdlib/public/runtime/ImageInspectionELF.h +++ b/stdlib/public/runtime/ImageInspectionELF.h @@ -21,7 +21,7 @@ #define SWIFT_REFLECTION_METADATA_ELF_NOTE_MAGIC_STRING "swift_reflection_metadata_magic_string" -#if defined(__ELF__) || defined(__EMSCRIPTEN__) +#if defined(__ELF__) || defined(__wasm__) #include "../SwiftShims/Visibility.h" #include From 9d5bea9bdf54c5df4e1ecfad320ca7dc46736993 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Fri, 12 Apr 2019 22:42:06 -0700 Subject: [PATCH 417/478] Switch to WASI --- CMakeLists.txt | 2 +- cmake/modules/AddSwift.cmake | 17 +---------------- cmake/modules/SwiftConfigureSDK.cmake | 13 ++----------- cmake/modules/SwiftWasmSupport.cmake | 16 ---------------- include/swift/Runtime/Mutex.h | 2 +- include/swift/Runtime/MutexPThread.h | 2 +- stdlib/public/SwiftShims/LibcShims.h | 4 ++-- stdlib/public/runtime/Errors.cpp | 2 +- stdlib/public/runtime/Heap.cpp | 2 +- stdlib/public/runtime/ThreadLocalStorage.h | 2 +- stdlib/public/stubs/LibcShims.cpp | 2 +- utils/build-script | 6 +++--- utils/build-script-impl | 4 ++-- utils/build_swift/driver_arguments.py | 4 ++-- vvv.sh | 2 +- 15 files changed, 20 insertions(+), 60 deletions(-) delete mode 100644 cmake/modules/SwiftWasmSupport.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index cac97cdf09e3e..18b26885dccdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -819,7 +819,7 @@ if(swift_build_windows AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") configure_sdk_windows("Windows" "msvc" "${SWIFT_SDK_WINDOWS_ARCHITECTURES}") endif() -# Should we cross-compile the standard library for WebAssembly (Emscripten)? +# Should we cross-compile the standard library for WebAssembly (WASI)? is_sdk_requested(WASM swift_build_wasm) if(swift_build_wasm AND NOT "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "WASM") #if ("${SWIFT_ANDROID_NDK_PATH}" STREQUAL "") diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 0d7c52675b7d7..fd5b06588427e 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -2,7 +2,6 @@ include(SwiftList) include(SwiftXcodeSupport) include(SwiftWindowsSupport) include(SwiftAndroidSupport) -include(SwiftWasmSupport) # SWIFTLIB_DIR is the directory in the build tree where Swift resource files # should be placed. Note that $CMAKE_CFG_INTDIR expands to "." for @@ -338,12 +337,6 @@ function(_add_variant_c_compile_flags) foreach(path ${${CFLAGS_ARCH}_INCLUDE}) list(APPEND result "\"${CMAKE_INCLUDE_FLAG_C}${path}\"") endforeach() - elseif(CFLAGS_SDK STREQUAL WASM) - list(APPEND result "-D__EMSCRIPTEN__=1") - swift_wasm_include_for_arch(${CFLAGS_ARCH} ${CFLAGS_ARCH}_INCLUDE) - foreach(path ${${CFLAGS_ARCH}_INCLUDE}) - list(APPEND result "\"${CMAKE_INCLUDE_FLAG_C}${path}\"") - endforeach() endif() set(ICU_UC_INCLUDE_DIR ${SWIFT_${CFLAGS_SDK}_${CFLAGS_ARCH}_ICU_UC_INCLUDE}) @@ -398,11 +391,6 @@ function(_add_variant_swift_compile_flags foreach(path IN LISTS ${arch}_swift_include) list(APPEND result "\"${CMAKE_INCLUDE_FLAG_C}${path}\"") endforeach() - elseif("${sdk}" STREQUAL "WASM") - swift_wasm_include_for_arch(${arch} ${arch}_swift_include) - foreach(path IN LISTS ${arch}_swift_include) - list(APPEND result "\"${CMAKE_INCLUDE_FLAG_C}${path}\"") - endforeach() endif() if(NOT BUILD_STANDALONE) @@ -513,10 +501,7 @@ function(_add_variant_link_flags) list(APPEND library_search_directories ${path}) endforeach() elseif("${LFLAGS_SDK}" STREQUAL "WASM") - swift_wasm_lib_for_arch(${LFLAGS_ARCH} ${LFLAGS_ARCH}_LIB) - foreach(path IN LISTS ${LFLAGS_ARCH}_LIB) - list(APPEND library_search_directories ${path}) - endforeach() + # No extra libraries needed. else() # If lto is enabled, we need to add the object path flag so that the LTO code # generator leaves the intermediate object file in a place where it will not diff --git a/cmake/modules/SwiftConfigureSDK.cmake b/cmake/modules/SwiftConfigureSDK.cmake index e80f60332a983..307abc0a798e7 100644 --- a/cmake/modules/SwiftConfigureSDK.cmake +++ b/cmake/modules/SwiftConfigureSDK.cmake @@ -7,7 +7,6 @@ set(SWIFT_CONFIGURED_SDKS) include(SwiftWindowsSupport) include(SwiftAndroidSupport) -include(SwiftWasmSupport) # Report the given SDK to the user. function(_report_sdk prefix) @@ -60,14 +59,6 @@ function(_report_sdk prefix) message(STATUS " ${arch} INCLUDE: ${${arch}_INCLUDE}") message(STATUS " ${arch} LIB: ${${arch}_LIB}") endforeach() - elseif("${prefix}" STREQUAL "WASM") - message(STATUS " Emscripten Dir: $ENV{SWIFT_WASM_EMSCRIPTEN_PATH}") - foreach(arch ${SWIFT_SDK_${prefix}_ARCHITECTURES}) - swift_wasm_include_for_arch(${arch} ${arch}_INCLUDE) - swift_wasm_lib_for_arch(${arch} ${arch}_LIB) - message(STATUS " ${arch} INCLUDE: ${${arch}_INCLUDE}") - message(STATUS " ${arch} LIB: ${${arch}_LIB}") - endforeach() else() foreach(arch ${SWIFT_SDK_${prefix}_ARCHITECTURES}) message(STATUS " ${arch} Path: ${SWIFT_SDK_${prefix}_ARCH_${arch}_PATH}") @@ -332,8 +323,8 @@ macro(configure_sdk_unix name architectures) if(NOT arch STREQUAL wasm32) message(FATAL_ERROR "unsupported arch for WebAssembly: ${arch}") endif() - # FIXME: this is actually wrong: emscripten doesn't use sysroot. - set(SWIFT_SDK_WASM_ARCH_wasm32_PATH "${SWIFT_WASM_EMSCRIPTEN_PATH}/system") + set(SWIFT_SDK_WASM_ARCH_wasm32_PATH "${SWIFT_WASM_WASI_SDK_PATH}/share/sysroot") + # fixme: Wasi is wasm32-unknown-wasi-musl. This LLVM doesn't have it yet. set(SWIFT_SDK_WASM_ARCH_wasm32_TRIPLE "wasm32-unknown-unknown-wasm") else() message(FATAL_ERROR "unknown Unix OS: ${prefix}") diff --git a/cmake/modules/SwiftWasmSupport.cmake b/cmake/modules/SwiftWasmSupport.cmake deleted file mode 100644 index 1d14263fbb49a..0000000000000 --- a/cmake/modules/SwiftWasmSupport.cmake +++ /dev/null @@ -1,16 +0,0 @@ -function(swift_wasm_include_for_arch arch var) - set(paths) - list(APPEND paths - "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/include/libcxx" - "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/lib/libcxxabi/include" - "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/include/compat" - "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/include" - "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/include/libc" - "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/lib/libc/musl/arch/emscripten" - "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/local/include" - "${SWIFT_WASM_EMSCRIPTEN_PATH}/system/include/SDL") - set(${var} ${paths} PARENT_SCOPE) -endfunction() - -function(swift_wasm_lib_for_arch arch var) -endfunction() diff --git a/include/swift/Runtime/Mutex.h b/include/swift/Runtime/Mutex.h index bfaa8a397e9f3..3bf61177f9790 100644 --- a/include/swift/Runtime/Mutex.h +++ b/include/swift/Runtime/Mutex.h @@ -20,7 +20,7 @@ #include -#if (defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__EMSCRIPTEN__)) +#if (defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__wasi__)) #include "swift/Runtime/MutexPThread.h" #elif defined(_WIN32) #include "swift/Runtime/MutexWin32.h" diff --git a/include/swift/Runtime/MutexPThread.h b/include/swift/Runtime/MutexPThread.h index fe976147ab3de..50eef549cc0be 100644 --- a/include/swift/Runtime/MutexPThread.h +++ b/include/swift/Runtime/MutexPThread.h @@ -26,7 +26,7 @@ typedef pthread_cond_t ConditionHandle; typedef pthread_mutex_t MutexHandle; typedef pthread_rwlock_t ReadWriteLockHandle; -#if defined(__CYGWIN__) || defined(__ANDROID__) || defined(__HAIKU__) || defined(__EMSCRIPTEN__) +#if defined(__CYGWIN__) || defined(__ANDROID__) || defined(__HAIKU__) || defined(__wasi__) // At the moment CYGWIN pthreads implementation doesn't support the use of // constexpr for static allocation versions. The way they define things // results in a reinterpret_cast which violates constexpr. Similarly, Android's diff --git a/stdlib/public/SwiftShims/LibcShims.h b/stdlib/public/SwiftShims/LibcShims.h index e8ac251ed0f5d..1c60a7561c5b8 100644 --- a/stdlib/public/SwiftShims/LibcShims.h +++ b/stdlib/public/SwiftShims/LibcShims.h @@ -43,7 +43,7 @@ typedef __swift_uint32_t __swift_mode_t; typedef __swift_uint16_t __swift_mode_t; #elif defined(_WIN32) typedef __swift_int32_t __swift_mode_t; -#elif defined(__EMSCRIPTEN__) +#elif defined(__wasi__) typedef __swift_uint32_t __swift_mode_t; #else // just guessing typedef __swift_uint16_t __swift_mode_t; @@ -107,7 +107,7 @@ static inline __swift_size_t _swift_stdlib_malloc_size(const void *ptr) { return malloc_size(ptr); } #elif defined(__linux__) || defined(__CYGWIN__) || defined(__ANDROID__) \ - || defined(__HAIKU__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__) + || defined(__HAIKU__) || defined(__FreeBSD__) || defined(__wasi__) static inline __swift_size_t _swift_stdlib_malloc_size(const void *ptr) { #if defined(__ANDROID__) #if !defined(__ANDROID_API__) || __ANDROID_API__ >= 17 diff --git a/stdlib/public/runtime/Errors.cpp b/stdlib/public/runtime/Errors.cpp index e2c0dd4a7487d..48201d54d6a2a 100644 --- a/stdlib/public/runtime/Errors.cpp +++ b/stdlib/public/runtime/Errors.cpp @@ -14,7 +14,7 @@ // //===----------------------------------------------------------------------===// -#if defined(__CYGWIN__) || defined(__HAIKU__) || defined(__EMSCRIPTEN__) +#if defined(__CYGWIN__) || defined(__HAIKU__) || defined(__wasi__) #define SWIFT_SUPPORTS_BACKTRACE_REPORTING 0 #else #define SWIFT_SUPPORTS_BACKTRACE_REPORTING 1 diff --git a/stdlib/public/runtime/Heap.cpp b/stdlib/public/runtime/Heap.cpp index c32fbafdf25ef..a241d153299e3 100644 --- a/stdlib/public/runtime/Heap.cpp +++ b/stdlib/public/runtime/Heap.cpp @@ -42,7 +42,7 @@ using namespace swift; #elif defined(_WIN32) # define MALLOC_ALIGN_MASK 7 -#elif defined(__EMSCRIPTEN__) +#elif defined(__wasi__) // Musl malloc is 4*sizeof(size_t), so 16 bytes on 32-bit? // For some reason the unknown alignment code fails because std::max isn't constexpr? # define MALLOC_ALIGN_MASK 15 diff --git a/stdlib/public/runtime/ThreadLocalStorage.h b/stdlib/public/runtime/ThreadLocalStorage.h index 243b12da5b30a..ebf57f6ceb69a 100644 --- a/stdlib/public/runtime/ThreadLocalStorage.h +++ b/stdlib/public/runtime/ThreadLocalStorage.h @@ -81,7 +81,7 @@ typedef int __swift_thread_key_t; typedef unsigned long __swift_thread_key_t; # elif defined(__HAIKU__) typedef int __swift_thread_key_t; -# elif defined(__EMSCRIPTEN__) +# elif defined(__wasi__) typedef unsigned int __swift_thread_key_t; # else typedef unsigned long __swift_thread_key_t; diff --git a/stdlib/public/stubs/LibcShims.cpp b/stdlib/public/stubs/LibcShims.cpp index 8368698067bd7..b54ede8c61f66 100644 --- a/stdlib/public/stubs/LibcShims.cpp +++ b/stdlib/public/stubs/LibcShims.cpp @@ -23,7 +23,7 @@ #include #include -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__EMSCRIPTEN__) +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__wasi__) #include #endif diff --git a/utils/build-script b/utils/build-script index 43b0bc5baf22a..7de82769a9b43 100755 --- a/utils/build-script +++ b/utils/build-script @@ -120,14 +120,14 @@ class BuildScriptInvocation(object): "must be specified") if args.wasm: - if args.wasm_emscripten is None or \ + if args.wasm_wasi_sdk is None or \ args.wasm_icu_uc is None or \ args.wasm_icu_uc_include is None or \ args.wasm_icu_i18n is None or \ args.wasm_icu_i18n_include is None or \ args.wasm_icu_data is None: diagnostics.fatal( - "when building for WebAssembly, --wasm-emscripten, " + "when building for WebAssembly, --wasm-wasi-sdk, " "--wasm-icu-uc, " "--wasm-icu-uc-include, --wasm-icu-i18n, " "--wasm-icu-i18n-include, and --wasm-icu-data " @@ -603,7 +603,7 @@ class BuildScriptInvocation(object): if args.wasm: impl_args += [ - "--wasm-emscripten", args.wasm_emscripten, + "--wasm-wasi-sdk", args.wasm_wasi_sdk, "--wasm-icu-uc", args.wasm_icu_uc, "--wasm-icu-uc-include", args.wasm_icu_uc_include, "--wasm-icu-i18n", args.wasm_icu_i18n, diff --git a/utils/build-script-impl b/utils/build-script-impl index 5c827eb520a0b..5fd027d0ca502 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -236,7 +236,7 @@ KNOWN_SETTINGS=( android-icu-data "" "Path to libicudata.so" android-deploy-device-path "" "Path on an Android device to which built Swift stdlib products will be deployed" android-arch "armv7" "The Android target architecture when building for Android" - wasm-emscripten "" "An absolute path to the Emscripten that will be used as a libc implementation for Wasm builds" + wasm-wasi-sdk "" "An absolute path to the WASI SDK that will be used as a libc implementation for Wasm builds" wasm-icu-uc "" "Path to libicuuc.so" wasm-icu-uc-include "" "Path to a directory containing headers for libicuuc" wasm-icu-i18n "" "Path to libicui18n.so" @@ -1667,7 +1667,7 @@ for host in "${ALL_HOSTS[@]}"; do if [[ ! "${SKIP_BUILD_WASM}" ]]; then cmake_options=( "${cmake_options[@]}" - -DSWIFT_WASM_EMSCRIPTEN_PATH:STRING="${WASM_EMSCRIPTEN}" + -DSWIFT_WASM_WASI_SDK_PATH:STRING="${WASM_WASI_SDK}" -DSWIFT_WASM_wasm32_ICU_UC:STRING="${WASM_ICU_UC}" -DSWIFT_WASM_wasm32_ICU_UC_INCLUDE:STRING="${WASM_ICU_UC_INCLUDE}" -DSWIFT_WASM_wasm32_ICU_I18N:STRING="${WASM_ICU_I18N}" diff --git a/utils/build_swift/driver_arguments.py b/utils/build_swift/driver_arguments.py index cff07151a870d..e7e169d5dd70a 100644 --- a/utils/build_swift/driver_arguments.py +++ b/utils/build_swift/driver_arguments.py @@ -1077,8 +1077,8 @@ def create_argument_parser(): in_group('Build settings for Android') - option('--wasm-emscripten', store_path, - help='An absolute path to Emscripten that will be used as a libc ' + option('--wasm-wasi-sdk', store_path, + help='An absolute path to WASI SDK that will be used as a libc ' 'implementation for Wasm builds') option('--wasm-icu-uc', store_path, diff --git a/vvv.sh b/vvv.sh index bcb75036bf6b1..7fb2c02157ec3 100755 --- a/vvv.sh +++ b/vvv.sh @@ -1,7 +1,7 @@ utils/build-script --release-debuginfo --wasm \ --llvm-targets-to-build "X86;ARM;AArch64;PowerPC;SystemZ;WebAssembly" \ --llvm-max-parallel-lto-link-jobs 1 --swift-tools-max-parallel-lto-link-jobs 1 \ - --wasm-emscripten "/home/zhuowei/Documents/emsdk/emscripten/1.38.30" \ + --wasm-wasi-sdk "/home/zhuowei/Downloads/wasi-sdk-3.0-linux/wasi-sdk-3.0/opt/wasi-sdk" \ --wasm-icu-uc "todo" \ --wasm-icu-uc-include "$PWD/NO" \ --wasm-icu-i18n "todo" \ From 5411508e24239527b37412ee2bc349cbe33b2b8d Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Fri, 12 Apr 2019 22:56:43 -0700 Subject: [PATCH 418/478] fix WASI header includes --- stdlib/public/runtime/Metadata.cpp | 3 +++ stdlib/public/stubs/Stubs.cpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 2546d90dec5cd..23b4ecce1e370 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -44,7 +44,10 @@ #else #include #include +// WASI doesn't have dynamic linking yet +#ifndef __wasi__ #include +#endif //__wasi #endif #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" diff --git a/stdlib/public/stubs/Stubs.cpp b/stdlib/public/stubs/Stubs.cpp index 55bada6a1db92..e2551564f31f9 100644 --- a/stdlib/public/stubs/Stubs.cpp +++ b/stdlib/public/stubs/Stubs.cpp @@ -26,7 +26,7 @@ #define NOMINMAX #include #else -#if !defined(__HAIKU__) +#if !defined(__HAIKU__) && !defined(__wasi__) #include #else #include @@ -67,7 +67,7 @@ static float swift_strtof_l(const char *nptr, char **endptr, locale_t loc) { #define strtod_l swift_strtod_l #define strtof_l swift_strtof_l #endif -#elif defined(__linux__) +#elif defined(__linux__) || defined(__wasi__) #include #else #include From 146f91b510e9d7dae8938fc13cb780c876b17c37 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Sat, 13 Apr 2019 17:41:32 -0700 Subject: [PATCH 419/478] WebAssembly: Disable Comdat for reflection metadata --- include/swift/IRGen/Linking.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index 56b76c07be474..eeaabd8bbcbd9 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -1157,6 +1157,11 @@ class ApplyIRLinkage { // TODO: BFD and gold do not handle COMDATs properly if (Triple.isOSBinFormatELF()) return; + // WebAssembly: hack: comdat + custom section = explosion + // the comdat code assumes section name would be unique for each comdat + // this doesn't happen for metadata. + if (Triple.isOSBinFormatWasm()) + return; if (IRL.Linkage == llvm::GlobalValue::LinkOnceODRLinkage || IRL.Linkage == llvm::GlobalValue::WeakODRLinkage) From 1963a3254755539f422674566da0cd8c0e89ec6f Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Sun, 14 Apr 2019 11:52:02 -0700 Subject: [PATCH 420/478] Disable generating PC-relative relocations in constant builder This doesn't take care of all the PC relative relocations in constant builder yet. This still breaks on `((.Lgot.$sSTMp.656-($ss18EnumeratedSequenceVMn))-56)+1` Which sits in rodata. Not sure where that's generated yet --- lib/IRGen/ConstantBuilder.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/IRGen/ConstantBuilder.h b/lib/IRGen/ConstantBuilder.h index 3bb9e7d1fb338..aca1fda1be3d0 100644 --- a/lib/IRGen/ConstantBuilder.h +++ b/lib/IRGen/ConstantBuilder.h @@ -82,6 +82,11 @@ class ConstantAggregateBuilderBase void addRelativeAddress(llvm::Constant *target) { assert(!isa(target)); + if (IGM().TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + // WebAssembly: hack: doesn't support PCrel data relocations + add(llvm::ConstantExpr::getPtrToInt(target, IGM().RelativeAddressTy, false)); + return; + } addRelativeOffset(IGM().RelativeAddressTy, target); } @@ -90,6 +95,13 @@ class ConstantAggregateBuilderBase /// a "GOT-equivalent", i.e. a pointer to an external object; if so, /// set the low bit of the offset to indicate that this is true. void addRelativeAddress(ConstantReference reference) { + if (IGM().TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + // WebAssembly: hack: doesn't support PCrel data relocations + // also, we should set the lowest bit, but I don't know how to do that + // there's no GOT on WebAssembly anyways though + add(llvm::ConstantExpr::getPtrToInt(reference.getValue(), IGM().RelativeAddressTy, false)); + return; + } addTaggedRelativeOffset(IGM().RelativeAddressTy, reference.getValue(), unsigned(reference.isIndirect())); @@ -99,6 +111,11 @@ class ConstantAggregateBuilderBase /// The target must be a "GOT-equivalent", i.e. a pointer to an /// external object. void addIndirectRelativeAddress(ConstantReference reference) { + if (IGM().TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + // WebAssembly: hack: doesn't support PCrel data relocations + add(llvm::ConstantExpr::getPtrToInt(reference.getValue(), IGM().RelativeAddressTy, false)); + return; + } assert(reference.isIndirect()); addRelativeOffset(IGM().RelativeAddressTy, reference.getValue()); From d697ef71a26aecdcd12bcae856e04df39e466805 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Sun, 14 Apr 2019 15:52:09 -0700 Subject: [PATCH 421/478] WebAssembly: remove even more PCRel relocations This removes the PCRel reloc in \01l_type_metadata_table but nowhere else --- lib/IRGen/GenDecl.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 51fb697e355f6..799cb7022b2e8 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -3082,6 +3082,11 @@ IRGenModule::emitDirectRelativeReference(llvm::Constant *target, // Convert the target to an integer. auto targetAddr = llvm::ConstantExpr::getPtrToInt(target, SizeTy); + // WebAssembly hack: WebAssembly doesn't support PC-relative references + if (TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + return targetAddr; + } + SmallVector indices; indices.push_back(llvm::ConstantInt::get(Int32Ty, 0)); for (unsigned baseIndex : baseIndices) { From 081d348482c5a176fba92835b8c1d0b535de45af Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Sun, 14 Apr 2019 17:05:57 -0700 Subject: [PATCH 422/478] WebAssembly: remove more PCRel relocations This at least seems to get rid of all the PCRel relocations emitted. Now it crashes in: (($sSTMp)+48)-8 The expr type is 0 expression kind not supported yay?! --- lib/IRGen/GenMeta.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 182b329efb513..29427288b92a8 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -4518,6 +4518,12 @@ GenericRequirementsMetadata irgen::addGenericRequirements( unsigned tag = unsigned(descriptorRef.isIndirect()); if (protocol->isObjC()) tag |= 0x02; + // WebAssembly: hack: Wasm doesn't support PC-relative offsets. + // also doesn't handle tag yet + if (IGM.TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + B.add(llvm::ConstantExpr::getPtrToInt(descriptorRef.getValue(), IGM.RelativeAddressTy, false)); + return; + } B.addTaggedRelativeOffset(IGM.RelativeAddressTy, descriptorRef.getValue(), From f028ccaf2cc93789fe092d57ba37d90e6908411b Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Sun, 14 Apr 2019 23:38:32 -0700 Subject: [PATCH 423/478] WebAssembly: disable relative pointers in the runtime This is completely untested. --- include/swift/Basic/RelativePointer.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/swift/Basic/RelativePointer.h b/include/swift/Basic/RelativePointer.h index f5cffd1c33c25..31cfd2971ece2 100644 --- a/include/swift/Basic/RelativePointer.h +++ b/include/swift/Basic/RelativePointer.h @@ -146,6 +146,11 @@ static inline uintptr_t applyRelativeOffset(BasePtrTy *basePtr, Offset offset) { std::is_signed::value, "offset type should be signed integer"); +#ifdef __wasm__ + // WebAssembly: hack: disable relative pointers + return (uintptr_t)(intptr_t)offset; +#endif + auto base = reinterpret_cast(basePtr); // We want to do wrapping arithmetic, but with a sign-extended // offset. To do this in C, we need to do signed promotion to get @@ -164,6 +169,13 @@ static inline Offset measureRelativeOffset(A *referent, B *base) { static_assert(std::is_integral::value && std::is_signed::value, "offset type should be signed integer"); +#ifdef __wasm__ + // WebAssembly: hack: disable relative pointers + auto offset = (Offset)(uintptr_t)referent; + assert((intptr_t)offset == (intptr_t)(uintptr_t)referent + && "pointer too large to fit in offset type"); + return offset; +#endif auto distance = (uintptr_t)referent - (uintptr_t)base; // Truncate as unsigned, then wrap around to signed. From a345d203826efa99c575c93c2a622c0aff7b534e Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Sun, 14 Apr 2019 23:38:57 -0700 Subject: [PATCH 424/478] WebAssembly: HACK: disable generating lldb-optimized debug info WebAssembly doesn't have support for the DWARF5 features yet. It's probably not too hard to add (just add the sections), but I decided to just disable it out of caution since I have no idea if other changes are needed. This doesn't test for the WebAssembly platform yet; will do that later --- lib/IRGen/IRGen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 342baae37c7c0..c0f626e92ec12 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -161,7 +161,8 @@ swift::getIRTargetOptions(IRGenOptions &Opts, ASTContext &Ctx) { // Explicitly request debugger tuning for LLDB which is the default // on Darwin platforms but not on others. - TargetOpts.DebuggerTuning = llvm::DebuggerKind::LLDB; + // WebAssembly HACK: -glldb on WebAssembly crashes LLVM because it doesn't create a debug_names section + // TargetOpts.DebuggerTuning = llvm::DebuggerKind::LLDB; // WebAssembly HACK: disable atomics TargetOpts.ThreadModel = llvm::ThreadModel::Single; From cf047854af6f8e837e2f7051450d2b79aca14ea9 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Mon, 15 Apr 2019 20:23:25 -0700 Subject: [PATCH 425/478] Start exporting WASI musl in Glibc modulemap --- .../SwiftPrivateLibcExtras/Subprocess.swift | 2 +- stdlib/public/Platform/CMakeLists.txt | 5 +++-- stdlib/public/Platform/Platform.swift | 4 +++- stdlib/public/Platform/glibc.modulemap.gyb | 18 +++++++++++++++++- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift index e95e07e142c3c..510ac1e9d8dc3 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift @@ -13,7 +13,7 @@ import SwiftPrivate #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) import Glibc #elseif os(Windows) import MSVCRT diff --git a/stdlib/public/Platform/CMakeLists.txt b/stdlib/public/Platform/CMakeLists.txt index 90fba95ec1b4e..6e0c46477e558 100644 --- a/stdlib/public/Platform/CMakeLists.txt +++ b/stdlib/public/Platform/CMakeLists.txt @@ -39,7 +39,7 @@ add_swift_target_library(swiftGlibc ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_O SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - TARGET_SDKS ANDROID CYGWIN FREEBSD LINUX HAIKU + TARGET_SDKS ANDROID CYGWIN FREEBSD LINUX HAIKU WASM INSTALL_IN_COMPONENT sdk-overlay DEPENDS glibc_modulemap) @@ -62,7 +62,8 @@ foreach(sdk ${SWIFT_SDKS}) NOT "${sdk}" STREQUAL "FREEBSD" AND NOT "${sdk}" STREQUAL "ANDROID" AND NOT "${sdk}" STREQUAL "CYGWIN" AND - NOT "${sdk}" STREQUAL "HAIKU") + NOT "${sdk}" STREQUAL "HAIKU" AND + NOT "${sdk}" STREQUAL "WASM") continue() endif() diff --git a/stdlib/public/Platform/Platform.swift b/stdlib/public/Platform/Platform.swift index 347931eb3b2fe..17c60c276036b 100644 --- a/stdlib/public/Platform/Platform.swift +++ b/stdlib/public/Platform/Platform.swift @@ -332,7 +332,7 @@ public var SIG_DFL: sig_t? { return nil } public var SIG_IGN: sig_t { return unsafeBitCast(1, to: sig_t.self) } public var SIG_ERR: sig_t { return unsafeBitCast(-1, to: sig_t.self) } public var SIG_HOLD: sig_t { return unsafeBitCast(5, to: sig_t.self) } -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Haiku) || os(Wasm) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Haiku) public typealias sighandler_t = __sighandler_t public var SIG_DFL: sighandler_t? { return nil } @@ -366,6 +366,8 @@ public var SIG_IGN: _crt_signal_t { public var SIG_ERR: _crt_signal_t { return unsafeBitCast(-1, to: _crt_signal_t.self) } +#elseif os(Wasm) +// WebAssembly/WASI doesn't have signals. #else internal var _ignore = _UnsupportedPlatformError() #endif diff --git a/stdlib/public/Platform/glibc.modulemap.gyb b/stdlib/public/Platform/glibc.modulemap.gyb index 12de1073b6317..97c52fe1ce4fa 100644 --- a/stdlib/public/Platform/glibc.modulemap.gyb +++ b/stdlib/public/Platform/glibc.modulemap.gyb @@ -126,10 +126,12 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/math.h" export * } +% if CMAKE_SDK != "WASM": module setjmp { header "${GLIBC_INCLUDE_PATH}/setjmp.h" export * } +% end module signal { header "${GLIBC_INCLUDE_PATH}/signal.h" export * @@ -319,6 +321,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/dirent.h" export * } +% if CMAKE_SDK != "WASM": module dl { header "${GLIBC_INCLUDE_PATH}/link.h" export * @@ -327,6 +330,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/dlfcn.h" export * } +% end module fcntl { header "${GLIBC_INCLUDE_PATH}/fcntl.h" export * @@ -335,10 +339,12 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/fnmatch.h" export * } +% if CMAKE_SDK != "WASM": module grp { header "${GLIBC_INCLUDE_PATH}/grp.h" export * } +% end module ioctl { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/ioctl.h" export * @@ -373,10 +379,12 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/pthread.h" export * } +% if CMAKE_SDK != "WASM": module pwd { header "${GLIBC_INCLUDE_PATH}/pwd.h" export * } +% end module regex { header "${GLIBC_INCLUDE_PATH}/regex.h" export * @@ -422,18 +430,22 @@ module SwiftGlibc [system] { } % end +% if CMAKE_SDK != "WASM": module ipc { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/ipc.h" export * } +% end module mman { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/mman.h" export * } +% if CMAKE_SDK != "WASM": module msg { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/msg.h" export * } +% end module resource { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/resource.h" export * @@ -442,7 +454,7 @@ module SwiftGlibc [system] { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/select.h" export * } -% if CMAKE_SDK != "FREEBSD" and CMAKE_SDK != "HAIKU": +% if CMAKE_SDK != "FREEBSD" and CMAKE_SDK != "HAIKU" and CMAKE_SDK != "WASM": module sendfile { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/sendfile.h" export * @@ -492,10 +504,12 @@ module SwiftGlibc [system] { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/utsname.h" export * } +% if CMAKE_SDK != "WASM": module wait { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/wait.h" export * } +% end } % if CMAKE_SDK in ["LINUX", "FREEBSD"]: module sysexits { @@ -518,8 +532,10 @@ module SwiftGlibc [system] { } } +% if CMAKE_SDK != "WASM": module CUUID [system] { header "${GLIBC_INCLUDE_PATH}/uuid/uuid.h" link "uuid" export * } +% end From 3f8ccd0bfc01adae8fcaf4b0cf44c61c8fe07d57 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 16 Apr 2019 00:06:37 -0700 Subject: [PATCH 426/478] WebAssembly: disable subprocesses --- .../private/SwiftPrivateLibcExtras/CMakeLists.txt | 1 + stdlib/private/SwiftPrivateLibcExtras/Subprocess.c | 2 +- .../private/SwiftPrivateLibcExtras/Subprocess.swift | 13 +++++++++++++ .../SwiftPrivateLibcExtras.swift | 4 +++- .../private/SwiftPrivateThreadExtras/CMakeLists.txt | 1 + .../SwiftPrivateThreadExtras.swift | 2 +- .../SwiftPrivateThreadExtras/ThreadBarriers.swift | 2 +- 7 files changed, 21 insertions(+), 4 deletions(-) diff --git a/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt index 22979ebc7ce8b..86ad055eb4f0b 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt @@ -17,6 +17,7 @@ add_swift_target_library(swiftSwiftPrivateLibcExtras ${SWIFT_STDLIB_LIBRARY_BUIL SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc + SWIFT_MODULE_DEPENDS_WASM Glibc SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.c b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.c index 4ef6fdbff8537..26d2402db4626 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.c +++ b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.c @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// // posix_spawn is not available on Android or Windows (MSVC). -#if !defined(__ANDROID__) && !defined(__HAIKU__) && (!defined(_WIN32) || defined(__CYGWIN__)) +#if !defined(__ANDROID__) && !defined(__HAIKU__) && (!defined(_WIN32) || defined(__CYGWIN__)) && !defined(__wasi__) #include "swift/Runtime/Config.h" diff --git a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift index 510ac1e9d8dc3..f8ec3333de45d 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift @@ -21,6 +21,9 @@ import WinSDK #endif internal func _signalToString(_ signal: Int) -> String { +#if os(Wasm) + return "unsupported" +#else switch CInt(signal) { case SIGILL: return "SIGILL" case SIGABRT: return "SIGABRT" @@ -33,6 +36,7 @@ internal func _signalToString(_ signal: Int) -> String { #endif default: return "SIG???? (\(signal))" } +#endif // os(Wasm) } public enum ProcessTerminationStatus : CustomStringConvertible { @@ -141,6 +145,15 @@ public func waitProcess(_ process: HANDLE) -> ProcessTerminationStatus { } return .exit(Int(status)) } +#elseif os(Wasm) +// Oops, we can't launch tests in subprocesses yet! +public func spawnChild(_ args: [String]) + -> (pid: pid_t, stdinFD: CInt, stdoutFD: CInt, stderrFD: CInt) { + fatalError("Not supported on WebAssembly!") +} +public func posixWaitpid(_ pid: pid_t) -> ProcessTerminationStatus { + fatalError("Not supported on WebAssembly!") +} #else // posix_spawn is not available on Android. // posix_spawn is not available on Haiku. diff --git a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift index 6fa2c06b6f6ee..8c09674b4faae 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift @@ -13,7 +13,7 @@ import SwiftPrivate #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) import Glibc #elseif os(Windows) import MSVCRT @@ -125,6 +125,8 @@ public func _stdlib_pipe() -> (readEnd: CInt, writeEnd: CInt, error: CInt) { let ret = fds.withUnsafeMutableBufferPointer { unsafeFds -> CInt in #if os(Windows) return _pipe(unsafeFds.baseAddress, 0, 0) +#elseif os(Wasm) + fatalError("no pipes on WebAssembly") #else return pipe(unsafeFds.baseAddress) #endif diff --git a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt index 97e2cc0d2af5b..b6d794dfc051d 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt @@ -14,6 +14,7 @@ add_swift_target_library(swiftSwiftPrivateThreadExtras ${SWIFT_STDLIB_LIBRARY_BU SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc + SWIFT_MODULE_DEPENDS_WASM Glibc SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental diff --git a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift index 002aef8e74145..feff077024252 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift @@ -17,7 +17,7 @@ #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) import Glibc #elseif os(Windows) import MSVCRT diff --git a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift index 9f5e183447021..68a7880d18a07 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift @@ -12,7 +12,7 @@ #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) import Glibc #elseif os(Windows) import MSVCRT From 816997689a8bcf813597e899f0ba8554776c654d Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 16 Apr 2019 22:42:40 -0700 Subject: [PATCH 427/478] WebAssembly: add EINVAL constant manually Swift's importer won't import error constants from WASI, because the constants are wrapped in UINT16_C(), which Swift doesn't support. --- stdlib/public/Platform/Glibc.swift.gyb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stdlib/public/Platform/Glibc.swift.gyb b/stdlib/public/Platform/Glibc.swift.gyb index 2de990adfffaf..b8376b159138e 100644 --- a/stdlib/public/Platform/Glibc.swift.gyb +++ b/stdlib/public/Platform/Glibc.swift.gyb @@ -69,3 +69,8 @@ public let ${prefix}_TRUE_MIN = ${type}.leastNonzeroMagnitude #endif % end %end + +#if os(Wasm) +// WebAssembly's error.h uses a macro that Swift can't import. +public let EINVAL:Int32 = 28 +#endif From a308700e64f134c81c44ff43d256bf992fbee979 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 16 Apr 2019 23:39:43 -0700 Subject: [PATCH 428/478] WebAssembly: add more wasm Glibc target dependencies to cmakefiles --- stdlib/private/StdlibUnittest/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/private/StdlibUnittest/CMakeLists.txt b/stdlib/private/StdlibUnittest/CMakeLists.txt index 710ccede83171..5f6cc350fbe3a 100644 --- a/stdlib/private/StdlibUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnittest/CMakeLists.txt @@ -40,6 +40,7 @@ add_swift_target_library(swiftStdlibUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc + SWIFT_MODULE_DEPENDS_WASM Glibc SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental From 599e9e1233f98970b81eeae050cc914870791ce6 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 16 Apr 2019 23:41:39 -0700 Subject: [PATCH 429/478] WebAssembly: hack: Disable tests for now --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18b26885dccdb..f3fe9bda9f530 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1156,8 +1156,8 @@ if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") endif() if(SWIFT_INCLUDE_TESTS) - add_subdirectory(test) - add_subdirectory(unittests) + #add_subdirectory(test) + #add_subdirectory(unittests) endif() if(SWIFT_INCLUDE_DOCS) add_subdirectory(docs) From 9da659cb0594c6896c49f3f7431b83da19d4deb9 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Wed, 17 Apr 2019 00:14:16 -0700 Subject: [PATCH 430/478] WebAssembly: add more ifdefs for WebAssembly --- include/swift/SwiftRemoteMirror/Platform.h | 4 ++-- stdlib/private/StdlibUnittest/InterceptTraps.cpp | 11 ++++++++++- stdlib/private/StdlibUnittest/RaceTest.swift | 7 ++++++- stdlib/private/StdlibUnittest/StdlibCoreExtras.swift | 2 +- stdlib/private/StdlibUnittest/StdlibUnittest.swift | 9 ++++++++- stdlib/public/Platform/Glibc.swift.gyb | 1 + .../swift-reflection-test/swift-reflection-test.c | 2 +- 7 files changed, 29 insertions(+), 7 deletions(-) diff --git a/include/swift/SwiftRemoteMirror/Platform.h b/include/swift/SwiftRemoteMirror/Platform.h index cefdea2c4de6f..1ec61dde9cad9 100644 --- a/include/swift/SwiftRemoteMirror/Platform.h +++ b/include/swift/SwiftRemoteMirror/Platform.h @@ -18,7 +18,7 @@ extern "C" { #endif #if defined(swiftRemoteMirror_EXPORTS) -# if defined(__ELF__) +# if defined(__ELF__) || defined(__wasm__) # define SWIFT_REMOTE_MIRROR_LINKAGE __attribute__((__visibility__("protected"))) # elif defined(__MACH__) # define SWIFT_REMOTE_MIRROR_LINKAGE __attribute__((__visibility__("default"))) @@ -30,7 +30,7 @@ extern "C" { # endif # endif #else -# if defined(__ELF__) +# if defined(__ELF__) || defined(__wasm__) # define SWIFT_REMOTE_MIRROR_LINKAGE __attribute__((__visibility__("default"))) # elif defined(__MACH__) # define SWIFT_REMOTE_MIRROR_LINKAGE __attribute__((__visibility__("default"))) diff --git a/stdlib/private/StdlibUnittest/InterceptTraps.cpp b/stdlib/private/StdlibUnittest/InterceptTraps.cpp index 377397bebb9bf..2e76ac6e84aea 100644 --- a/stdlib/private/StdlibUnittest/InterceptTraps.cpp +++ b/stdlib/private/StdlibUnittest/InterceptTraps.cpp @@ -26,6 +26,9 @@ #include "swift/Runtime/Config.h" +// WebAssembly: no signals on WASI yet +#ifndef __wasi__ + static void CrashCatcher(int Sig) { const char *Msg; switch (Sig) { @@ -65,11 +68,16 @@ VectoredCrashHandler(PEXCEPTION_POINTERS ExceptionInfo) { } #endif +#endif // __wasi__ + SWIFT_CC(swift) SWIFT_RUNTIME_LIBRARY_VISIBILITY extern "C" void installTrapInterceptor() { // Disable buffering on stdout so that everything is printed before crashing. setbuf(stdout, 0); +// WebAssembly: no signals on WASI +#ifndef __wasi__ + #if defined(_WIN32) _set_abort_behavior(0, _WRITE_ABORT_MSG); #endif @@ -85,5 +93,6 @@ void installTrapInterceptor() { signal(SIGBUS, CrashCatcher); signal(SIGSYS, CrashCatcher); #endif -} +#endif // __wasi__ +} diff --git a/stdlib/private/StdlibUnittest/RaceTest.swift b/stdlib/private/StdlibUnittest/RaceTest.swift index 3bdee4f03d32f..60ba5e368c083 100644 --- a/stdlib/private/StdlibUnittest/RaceTest.swift +++ b/stdlib/private/StdlibUnittest/RaceTest.swift @@ -41,7 +41,7 @@ import SwiftPrivateLibcExtras import SwiftPrivateThreadExtras #if os(macOS) || os(iOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) import Glibc #elseif os(Windows) import MSVCRT @@ -562,7 +562,12 @@ class _InterruptibleSleep { return } +#if os(Wasm) +// WebAssembly/WASI on wasm32 is the only 32-bit platform with Int64 time_t + var timeout = timeval(tv_sec: time_t(duration), tv_usec: 0) +#else var timeout = timeval(tv_sec: duration, tv_usec: 0) +#endif var readFDs = _stdlib_fd_set() var writeFDs = _stdlib_fd_set() diff --git a/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift b/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift index 20bb2b2c9376f..8b356c2acff2a 100644 --- a/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift +++ b/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift @@ -14,7 +14,7 @@ import SwiftPrivate import SwiftPrivateLibcExtras #if os(macOS) || os(iOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) import Glibc #elseif os(Windows) import MSVCRT diff --git a/stdlib/private/StdlibUnittest/StdlibUnittest.swift b/stdlib/private/StdlibUnittest/StdlibUnittest.swift index c67f1778d8a3b..03e0ca2c9cbec 100644 --- a/stdlib/private/StdlibUnittest/StdlibUnittest.swift +++ b/stdlib/private/StdlibUnittest/StdlibUnittest.swift @@ -18,7 +18,7 @@ import SwiftPrivateLibcExtras #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Foundation import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) import Glibc #elseif os(Windows) import MSVCRT @@ -748,6 +748,8 @@ extension ProcessTerminationStatus { case .signal(let signal): #if os(Windows) return CInt(signal) == SIGILL +#elseif os(Wasm) + return false #else return CInt(signal) == SIGILL || CInt(signal) == SIGTRAP #endif @@ -1746,6 +1748,7 @@ public enum OSVersion : CustomStringConvertible { case windowsCygnus case windows case haiku + case wasm public var description: String { switch self { @@ -1777,6 +1780,8 @@ public enum OSVersion : CustomStringConvertible { return "Windows" case .haiku: return "Haiku" + case .wasm: + return "Wasm" } } } @@ -1821,6 +1826,8 @@ func _getOSVersion() -> OSVersion { return .windows #elseif os(Haiku) return .haiku +#elseif os(Wasm) + return .wasm #else let productVersion = _getSystemVersionPlistProperty("ProductVersion")! let (major, minor, bugFix) = _parseDottedVersionTriple(productVersion) diff --git a/stdlib/public/Platform/Glibc.swift.gyb b/stdlib/public/Platform/Glibc.swift.gyb index b8376b159138e..3258bcd28b10c 100644 --- a/stdlib/public/Platform/Glibc.swift.gyb +++ b/stdlib/public/Platform/Glibc.swift.gyb @@ -72,5 +72,6 @@ public let ${prefix}_TRUE_MIN = ${type}.leastNonzeroMagnitude #if os(Wasm) // WebAssembly's error.h uses a macro that Swift can't import. +public let EINTR:Int32 = 27 public let EINVAL:Int32 = 28 #endif diff --git a/stdlib/tools/swift-reflection-test/swift-reflection-test.c b/stdlib/tools/swift-reflection-test/swift-reflection-test.c index dfcfe544c1078..e454e0dcd493b 100644 --- a/stdlib/tools/swift-reflection-test/swift-reflection-test.c +++ b/stdlib/tools/swift-reflection-test/swift-reflection-test.c @@ -27,7 +27,7 @@ #include #include #include -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__wasi__) #include #elif defined(_WIN32) #include From 4df104b93ee6bc5fc58925874e6549bd502565d8 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Thu, 18 Apr 2019 00:06:09 -0700 Subject: [PATCH 431/478] disable DWARF5 and atomics on WebAssembly LLVM doesn't support generating DWARF5 debugging data for WebAssembly, and support for atomics is currently very limited. Set LLVM target options to avoid generating DWARF5 debug data and lower atomics to regular load/stores when building for WebAssembly. This shouldn't affect existing platforms. --- lib/IRGen/IRGen.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index c0f626e92ec12..62c08892a8a18 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -168,6 +168,13 @@ swift::getIRTargetOptions(IRGenOptions &Opts, ASTContext &Ctx) { auto *Clang = static_cast(Ctx.getClangModuleLoader()); clang::TargetOptions &ClangOpts = Clang->getTargetInfo().getTargetOpts(); + + // WebAssembly doesn't support atomics or DWARF5 yet. + if (Clang->getTargetInfo().getTriple().isOSBinFormatWasm()) { + TargetOpts.DebuggerTuning = llvm::DebuggerKind::Default; + TargetOpts.ThreadModel = llvm::ThreadModel::Single; + } + return std::make_tuple(TargetOpts, ClangOpts.CPU, ClangOpts.Features, ClangOpts.Triple); } From 685335de6281370c78062557f8d4be9b83abcc1a Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Thu, 18 Apr 2019 00:10:36 -0700 Subject: [PATCH 432/478] remove the older dwarf5 and atomics patch --- lib/IRGen/IRGen.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 62c08892a8a18..c554415dd6f27 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -161,10 +161,7 @@ swift::getIRTargetOptions(IRGenOptions &Opts, ASTContext &Ctx) { // Explicitly request debugger tuning for LLDB which is the default // on Darwin platforms but not on others. - // WebAssembly HACK: -glldb on WebAssembly crashes LLVM because it doesn't create a debug_names section - // TargetOpts.DebuggerTuning = llvm::DebuggerKind::LLDB; - // WebAssembly HACK: disable atomics - TargetOpts.ThreadModel = llvm::ThreadModel::Single; + TargetOpts.DebuggerTuning = llvm::DebuggerKind::LLDB; auto *Clang = static_cast(Ctx.getClangModuleLoader()); clang::TargetOptions &ClangOpts = Clang->getTargetInfo().getTargetOpts(); From 57c47af35058af9436aa5f5c6b95fcbb4524e877 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Fri, 19 Apr 2019 14:33:11 -0700 Subject: [PATCH 433/478] fix wasi sdk path --- vvv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vvv.sh b/vvv.sh index 7fb2c02157ec3..e53a1d5071727 100755 --- a/vvv.sh +++ b/vvv.sh @@ -1,7 +1,7 @@ utils/build-script --release-debuginfo --wasm \ --llvm-targets-to-build "X86;ARM;AArch64;PowerPC;SystemZ;WebAssembly" \ --llvm-max-parallel-lto-link-jobs 1 --swift-tools-max-parallel-lto-link-jobs 1 \ - --wasm-wasi-sdk "/home/zhuowei/Downloads/wasi-sdk-3.0-linux/wasi-sdk-3.0/opt/wasi-sdk" \ + --wasm-wasi-sdk "/home/zhuowei/wasi-sdk" \ --wasm-icu-uc "todo" \ --wasm-icu-uc-include "$PWD/NO" \ --wasm-icu-i18n "todo" \ From d54affbe6dc2b88da98c1475cb8f961cba35268c Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Fri, 19 Apr 2019 16:23:15 -0700 Subject: [PATCH 434/478] WebAssembly: HACK: use llvm-ar from the WASI sdk Ubuntu's GNU ar doesn't work with wasm-ld (doesn't see any files inside the archive) --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f3fe9bda9f530..b6e7480df0631 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,9 @@ ENABLE_LANGUAGE(C) include(SwiftUtils) include(CheckSymbolExists) +# WebAssembly: hack: use llvm-ar for creating static libraries; Ubuntu's GNU ar doesn't work with wasm-ld +set(CMAKE_AR "${SWIFT_WASM_WASI_SDK_PATH}/bin/llvm-ar") + # # User-configurable options that control the inclusion and default build # behavior for components which may not strictly be necessary (tools, examples, From 813a441c093a9a7eb73dabc93add87ac32be9397 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Fri, 19 Apr 2019 18:24:32 -0700 Subject: [PATCH 435/478] WebAssembly: remove file locking on stdin; WASI doesn't support file locking --- stdlib/public/stubs/Stubs.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stdlib/public/stubs/Stubs.cpp b/stdlib/public/stubs/Stubs.cpp index e2551564f31f9..d8416c62f1733 100644 --- a/stdlib/public/stubs/Stubs.cpp +++ b/stdlib/public/stubs/Stubs.cpp @@ -503,6 +503,8 @@ const char *swift::_swift_stdlib_strtof_clocale( void swift::_swift_stdlib_flockfile_stdout() { #if defined(_WIN32) _lock_file(stdout); +#elif defined(__wasi__) + // WebAssembly/WASI doesn't support file locking yet #else flockfile(stdout); #endif @@ -511,6 +513,8 @@ void swift::_swift_stdlib_flockfile_stdout() { void swift::_swift_stdlib_funlockfile_stdout() { #if defined(_WIN32) _unlock_file(stdout); +#elif defined(__wasi__) + // WebAssembly/WASI doesn't support file locking yet #else funlockfile(stdout); #endif From 79cb5943a71423709e9c4dbcaf4e869e46f08b03 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Fri, 19 Apr 2019 18:25:04 -0700 Subject: [PATCH 436/478] WebAssembly: compile ImageInspectionELF for WebAssembly for now --- stdlib/public/runtime/ImageInspectionELF.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/stdlib/public/runtime/ImageInspectionELF.cpp b/stdlib/public/runtime/ImageInspectionELF.cpp index 331ee3614f54f..9c0fe4d949dcf 100644 --- a/stdlib/public/runtime/ImageInspectionELF.cpp +++ b/stdlib/public/runtime/ImageInspectionELF.cpp @@ -18,11 +18,13 @@ /// //===----------------------------------------------------------------------===// -#if defined(__ELF__) +#if defined(__ELF__) || defined(__wasm__) #include "ImageInspection.h" #include "ImageInspectionELF.h" +#ifndef __wasm__ #include +#endif using namespace swift; @@ -132,6 +134,7 @@ void swift_addNewDSOImage(const void *addr) { } int swift::lookupSymbol(const void *address, SymbolInfo *info) { +#ifndef __wasm__ Dl_info dlinfo; if (dladdr(address, &dlinfo) == 0) { return 0; @@ -142,6 +145,9 @@ int swift::lookupSymbol(const void *address, SymbolInfo *info) { info->symbolName.reset(dlinfo.dli_sname); info->symbolAddress = dlinfo.dli_saddr; return 1; +#else + return 0; +#endif } // This is only used for backward deployment hooks, which we currently only support for From fb0f306fc14ddd65d7880edfd6bdb387442b930a Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Fri, 19 Apr 2019 23:29:51 -0700 Subject: [PATCH 437/478] update icu include path --- vvv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vvv.sh b/vvv.sh index e53a1d5071727..db86f386cd5fa 100755 --- a/vvv.sh +++ b/vvv.sh @@ -3,7 +3,7 @@ utils/build-script --release-debuginfo --wasm \ --llvm-max-parallel-lto-link-jobs 1 --swift-tools-max-parallel-lto-link-jobs 1 \ --wasm-wasi-sdk "/home/zhuowei/wasi-sdk" \ --wasm-icu-uc "todo" \ - --wasm-icu-uc-include "$PWD/NO" \ + --wasm-icu-uc-include "/home/zhuowei/Documents/BuildICU/icu_out/include" \ --wasm-icu-i18n "todo" \ --wasm-icu-i18n-include "todo" \ --wasm-icu-data "todo" \ From 1deb168bab6a7bba5d0b8f9c35176d9db2a1f111 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Sat, 20 Apr 2019 01:03:04 -0700 Subject: [PATCH 438/478] WebAssembly: add a sample script for linking a wasm file --- linkPlease.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100755 linkPlease.sh diff --git a/linkPlease.sh b/linkPlease.sh new file mode 100755 index 0000000000000..7316eecbbdb5f --- /dev/null +++ b/linkPlease.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# Copy to ../build/Ninja-RelWithDebInfoAssert/swift-linux-x86_64/bin +exec /home/zhuowei/wasi-sdk/bin/wasm-ld --error-limit=0 -o hello.wasm \ + /home/zhuowei/wasi-sdk/share/sysroot/lib/wasm32-wasi/crt1.o \ + hello.o \ + -L../lib/swift_static/wasm/wasm32 \ + -L../lib/swift/wasm/wasm32 \ + -L/home/zhuowei/wasi-sdk/share/sysroot/lib/wasm32-wasi \ + -L/home/zhuowei/Documents/BuildICU/icu_out/lib \ + -lswiftCore \ + -lc -lc++ -lc++abi -lswiftImageInspectionShared \ + -licuuc -licudata \ + /home/zhuowei/wasi-sdk/lib/clang/8.0.0/lib/wasi/libclang_rt.builtins-wasm32.a /home/zhuowei/Documents/FakePthread/*.o --verbose From 7b068f7cd37443ac018a30481160cacb27159c19 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Sat, 20 Apr 2019 11:56:10 -0700 Subject: [PATCH 439/478] WebAssembly: use getentropy on Wasi --- stdlib/public/stubs/Random.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stdlib/public/stubs/Random.cpp b/stdlib/public/stubs/Random.cpp index bb69f7e7793d1..e099d9c5e43ee 100644 --- a/stdlib/public/stubs/Random.cpp +++ b/stdlib/public/stubs/Random.cpp @@ -42,6 +42,10 @@ #include "swift/Runtime/Mutex.h" #include "../SwiftShims/Random.h" +#ifdef __wasi__ +#include // std::min +#endif + #if defined(__APPLE__) SWIFT_RUNTIME_STDLIB_API @@ -88,7 +92,7 @@ void swift::swift_stdlib_random(void *buf, __swift_size_t nbytes) { if (getrandom_available) { actual_nbytes = WHILE_EINTR(syscall(__NR_getrandom, buf, nbytes, 0)); } -#elif __has_include() && (defined(__CYGWIN__) || defined(__Fuchsia__)) +#elif __has_include() && (defined(__CYGWIN__) || defined(__Fuchsia__) || defined(__wasi__)) __swift_size_t getentropy_nbytes = std::min(nbytes, __swift_size_t{256}); if (0 == getentropy(buf, getentropy_nbytes)) { From d7f643c2321c66aa76bbaa600f7c2db955749703 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Sun, 21 Apr 2019 16:30:03 -0700 Subject: [PATCH 440/478] fix path to fakeld --- cmake/modules/AddSwift.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index fd5b06588427e..6552239d8145b 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -532,7 +532,7 @@ function(_add_variant_link_flags) # FIXME: On Apple platforms, find_program needs to look for "ld64.lld" find_program(LDLLD_PATH "ld.lld") if("${LFLAGS_SDK}" STREQUAL "WASM") - list(APPEND result "-fuse-ld=/home/zhuowei/swift-source/swift/fakeld") + list(APPEND result "-fuse-ld=${CMAKE_SOURCE_DIR}/fakeld") elseif((SWIFT_ENABLE_LLD_LINKER AND LDLLD_PATH AND NOT APPLE) OR ("${LFLAGS_SDK}" STREQUAL "WINDOWS" AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "WINDOWS")) From f03d51c43262acbdbe679c3e7f7b4499cd1db7b1 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Sun, 21 Apr 2019 17:44:05 -0700 Subject: [PATCH 441/478] WebAssembly: add start/end objects for metadata The WebAssembly linker, unlike ELF linkers, doesn't define __start and __stop symbols for sections. So we have to make our own. If you put an object first in the command line, its symbols are placed first; same for last in command line. So we make two files. This is the same method that Swift used to use on ELF platforms. run ./buildstartend.sh; these two objects must be linked at the start and the end. see the updated linkPlease.sh file. --- buildstartend.sh | 4 ++++ linkPlease.sh | 6 +++++- stdlib/public/runtime/SwiftRT-ELF.cpp | 7 +++++++ swift_end.cpp | 29 +++++++++++++++++++++++++++ swift_start.cpp | 29 +++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 1 deletion(-) create mode 100755 buildstartend.sh create mode 100644 swift_end.cpp create mode 100644 swift_start.cpp diff --git a/buildstartend.sh b/buildstartend.sh new file mode 100755 index 0000000000000..4739c828f17f9 --- /dev/null +++ b/buildstartend.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +/home/zhuowei/wasi-sdk/bin/clang++ -c swift_start.cpp +/home/zhuowei/wasi-sdk/bin/clang++ -c swift_end.cpp diff --git a/linkPlease.sh b/linkPlease.sh index 7316eecbbdb5f..d3b160ed6bd72 100755 --- a/linkPlease.sh +++ b/linkPlease.sh @@ -2,6 +2,8 @@ # Copy to ../build/Ninja-RelWithDebInfoAssert/swift-linux-x86_64/bin exec /home/zhuowei/wasi-sdk/bin/wasm-ld --error-limit=0 -o hello.wasm \ /home/zhuowei/wasi-sdk/share/sysroot/lib/wasm32-wasi/crt1.o \ + /home/zhuowei/swift-source/swift/swift_start.o \ + ../lib/swift_static/wasm/wasm32/swiftrt.o \ hello.o \ -L../lib/swift_static/wasm/wasm32 \ -L../lib/swift/wasm/wasm32 \ @@ -10,4 +12,6 @@ exec /home/zhuowei/wasi-sdk/bin/wasm-ld --error-limit=0 -o hello.wasm \ -lswiftCore \ -lc -lc++ -lc++abi -lswiftImageInspectionShared \ -licuuc -licudata \ - /home/zhuowei/wasi-sdk/lib/clang/8.0.0/lib/wasi/libclang_rt.builtins-wasm32.a /home/zhuowei/Documents/FakePthread/*.o --verbose + /home/zhuowei/wasi-sdk/lib/clang/8.0.0/lib/wasi/libclang_rt.builtins-wasm32.a /home/zhuowei/Documents/FakePthread/*.o \ + /home/zhuowei/swift-source/swift/swift_end.o \ + --verbose --no-gc-sections diff --git a/stdlib/public/runtime/SwiftRT-ELF.cpp b/stdlib/public/runtime/SwiftRT-ELF.cpp index fa585a8a68241..9b4b8e8dbe2fb 100644 --- a/stdlib/public/runtime/SwiftRT-ELF.cpp +++ b/stdlib/public/runtime/SwiftRT-ELF.cpp @@ -46,9 +46,16 @@ static swift::MetadataSections sections{}; __attribute__((__constructor__)) static void swift_image_constructor() { +#ifndef __wasm__ #define SWIFT_SECTION_RANGE(name) \ { reinterpret_cast(&__start_##name), \ static_cast(&__stop_##name - &__start_##name) } +#else +// WebAssembly hack: ok this should really go in its own file +#define SWIFT_SECTION_RANGE(name) \ + { reinterpret_cast(&__start_##name) + sizeof(void*), \ + static_cast(&__stop_##name - &__start_##name) } +#endif sections = { swift::CurrentSectionMetadataVersion, diff --git a/swift_end.cpp b/swift_end.cpp new file mode 100644 index 0000000000000..714e0d35d7442 --- /dev/null +++ b/swift_end.cpp @@ -0,0 +1,29 @@ +//===--- SwiftRT-ELF.cpp --------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include + +// We synthesize the start/stop symbols ourselves. +#define DECLARE_SWIFT_SECTION(name) \ + __attribute__((__section__(#name),__visibility__("hidden"),__aligned__(1))) const void* __stop_##name = (void*)0xfacefeed; \ + +extern "C" { +DECLARE_SWIFT_SECTION(swift5_protocols) +DECLARE_SWIFT_SECTION(swift5_protocol_conformances) +DECLARE_SWIFT_SECTION(swift5_type_metadata) + +DECLARE_SWIFT_SECTION(swift5_typeref) +DECLARE_SWIFT_SECTION(swift5_reflstr) +DECLARE_SWIFT_SECTION(swift5_fieldmd) +DECLARE_SWIFT_SECTION(swift5_assocty) +DECLARE_SWIFT_SECTION(swift5_replace) +} diff --git a/swift_start.cpp b/swift_start.cpp new file mode 100644 index 0000000000000..5a77ea3c0f527 --- /dev/null +++ b/swift_start.cpp @@ -0,0 +1,29 @@ +//===--- SwiftRT-ELF.cpp --------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include + +// We synthesize the start/stop symbols ourselves. +#define DECLARE_SWIFT_SECTION(name) \ + __attribute__((__section__(#name),__visibility__("hidden"),__aligned__(1))) const void* __start_##name = (void*)0xdeadbeef; \ + +extern "C" { +DECLARE_SWIFT_SECTION(swift5_protocols) +DECLARE_SWIFT_SECTION(swift5_protocol_conformances) +DECLARE_SWIFT_SECTION(swift5_type_metadata) + +DECLARE_SWIFT_SECTION(swift5_typeref) +DECLARE_SWIFT_SECTION(swift5_reflstr) +DECLARE_SWIFT_SECTION(swift5_fieldmd) +DECLARE_SWIFT_SECTION(swift5_assocty) +DECLARE_SWIFT_SECTION(swift5_replace) +} From e4cec6eb2f0328514553d525a8a2c091873d4396 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 23 Apr 2019 22:33:43 -0700 Subject: [PATCH 442/478] attempt to fix swift_once; doesn't work --- include/swift/Runtime/Once.h | 4 ++++ .../public/runtime/CompatibilityOverride.cpp | 5 +++++ stdlib/public/runtime/HeapObject.cpp | 6 ++++++ stdlib/public/runtime/Once.cpp | 21 +++++++++++++++++++ 4 files changed, 36 insertions(+) diff --git a/include/swift/Runtime/Once.h b/include/swift/Runtime/Once.h index 8a78cddc23c56..95265cfcda794 100644 --- a/include/swift/Runtime/Once.h +++ b/include/swift/Runtime/Once.h @@ -44,6 +44,10 @@ typedef std::once_flag swift_once_t; /// extent of type swift_once_t. SWIFT_RUNTIME_EXPORT void swift_once(swift_once_t *predicate, void (*fn)(void *), void *context); +#ifdef __wasm__ +// WebAssembly: hack +void swift_once_real(swift_once_t *predicate, void (*fn)(void *), void *context); +#endif } diff --git a/stdlib/public/runtime/CompatibilityOverride.cpp b/stdlib/public/runtime/CompatibilityOverride.cpp index 3eebb8581ff24..7723eaad3a439 100644 --- a/stdlib/public/runtime/CompatibilityOverride.cpp +++ b/stdlib/public/runtime/CompatibilityOverride.cpp @@ -49,7 +49,12 @@ static_assert(std::is_pod::value, static OverrideSection *getOverrideSectionPtr() { static OverrideSection *OverrideSectionPtr; static swift_once_t Predicate; + // WebAssembly: hack +#ifdef __wasm__ + swift_once_real(&Predicate, [](void *) { +#else swift_once(&Predicate, [](void *) { +#endif size_t Size; OverrideSectionPtr = static_cast( lookupSection("__DATA", "__swift52_hooks", &Size)); diff --git a/stdlib/public/runtime/HeapObject.cpp b/stdlib/public/runtime/HeapObject.cpp index 712c0afca7fc7..103e3aee87175 100644 --- a/stdlib/public/runtime/HeapObject.cpp +++ b/stdlib/public/runtime/HeapObject.cpp @@ -151,7 +151,13 @@ swift::swift_initStaticObject(HeapMetadata const *metadata, // refcount to 1 while another thread already incremented it - and would // decrement it to 0 afterwards. InitStaticObjectContext Ctx = { object, metadata }; +#ifdef __wasm__ + // WebAssembly: hack: swift_once has been modified to take a function pointer without a parameter. + // so use the _real version that does have a parameter + swift_once_real(token, initStaticObjectWithContext, &Ctx); +#else swift_once(token, initStaticObjectWithContext, &Ctx); +#endif return object; } diff --git a/stdlib/public/runtime/Once.cpp b/stdlib/public/runtime/Once.cpp index 57282590cbd06..7554d5d4ae751 100644 --- a/stdlib/public/runtime/Once.cpp +++ b/stdlib/public/runtime/Once.cpp @@ -52,7 +52,28 @@ void swift::swift_once(swift_once_t *predicate, void (*fn)(void *), dispatch_once_f(predicate, context, fn); #elif defined(__CYGWIN__) _swift_once_f(predicate, context, fn); +#elif defined(__wasm__) + // WebAssembly: hack: Swift compiler passes in a fn that doesn't take a parameter, + // which is invalid in WebAssembly. So swift_once casts the function. + // The correct way to fix this is to change + // SILGenModule::emitLazyGlobalInitializer + // but this is OK as a proof of concept. + // Keep a copy of the unmodified swift_once function below. + std::call_once(*predicate, [fn, context]() { ((void (*)())fn)(); }); #else std::call_once(*predicate, [fn, context]() { fn(context); }); #endif } + +#ifdef __wasm__ +void swift::swift_once_real(swift_once_t *predicate, void (*fn)(void *), + void *context) { +#if defined(__APPLE__) + dispatch_once_f(predicate, context, fn); +#elif defined(__CYGWIN__) + _swift_once_f(predicate, context, fn); +#else + std::call_once(*predicate, [fn, context]() { fn(context); }); +#endif +} +#endif From 1117e190d81f0d11d2474a632b9bd6f87cb0dbd8 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Wed, 24 Apr 2019 01:46:13 -0700 Subject: [PATCH 443/478] whoops, missed a swift_once --- stdlib/public/runtime/CompatibilityOverride.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/stdlib/public/runtime/CompatibilityOverride.h b/stdlib/public/runtime/CompatibilityOverride.h index abc2eea227424..ad79a27273d51 100644 --- a/stdlib/public/runtime/CompatibilityOverride.h +++ b/stdlib/public/runtime/CompatibilityOverride.h @@ -39,7 +39,7 @@ namespace swift { Override_ ## name getOverride_ ## name(); #include "CompatibilityOverride.def" - +#ifndef __wasm__ /// Used to define an override point. The override point #defines the appropriate /// OVERRIDE macro from CompatibilityOverride.def to this macro, then includes /// the file to generate the override points. The original implementation of the @@ -55,6 +55,20 @@ namespace swift { return Override(COMPATIBILITY_UNPAREN namedArgs, swift_ ## name ## Impl); \ return swift_ ## name ## Impl namedArgs; \ } +#else +// WebAssembly: hack: change to swift_once_real +#define COMPATIBILITY_OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + attrs ccAttrs ret namespace swift_ ## name typedArgs { \ + static Override_ ## name Override; \ + static swift_once_t Predicate; \ + swift_once_real(&Predicate, [](void *) { \ + Override = getOverride_ ## name(); \ + }, nullptr); \ + if (Override != nullptr) \ + return Override(COMPATIBILITY_UNPAREN namedArgs, swift_ ## name ## Impl); \ + return swift_ ## name ## Impl namedArgs; \ + } +#endif } /* end namespace swift */ From 6c730f3868fcf605cee267d6a742104e39ca289f Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 29 Apr 2019 21:03:23 +0100 Subject: [PATCH 444/478] Add wasm branch scheme to update-checkout-config This allows cloning all of the repositories directly with ./swift/utils/update-checkout --clone --scheme wasm without relying on paths hardcoded in swiftwasm-sdk. It also clones icu that way as well, as that one is used when building on Linux. Later we could add "WebAssembly" platform to update-checkout-config.json to clone it only for WebAssembly and Linux platforms, but for now it's pulled for all platforms to make things easy. https://github.com/swiftwasm/swift/pull/1 --- .../update-checkout-config.json | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index 969612f899647..923dd356f6707 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -3,7 +3,7 @@ "https-clone-pattern": "https://github.com/%s.git", "repos" : { "swift": { - "remote": { "id": "apple/swift" } }, + "remote": { "id": "swiftwasm/swift" } }, "cmark": { "remote": { "id": "apple/swift-cmark" } }, "llbuild": { @@ -29,8 +29,7 @@ "ninja": { "remote": { "id": "ninja-build/ninja" } }, "icu": { - "remote": { "id": "unicode-org/icu" }, - "platforms": [ "Linux" ] + "remote": { "id": "unicode-org/icu" } }, "cmake": { "remote": { "id": "KitWare/CMake" }, @@ -43,11 +42,34 @@ "swift-format": { "remote": { "id": "apple/swift-format" } }, "llvm-project": { - "remote": { "id": "apple/llvm-project" } } + "remote": { "id": "swiftwasm/llvm-project" } } }, "default-branch-scheme": "master", "branch-schemes": { - "master": { + "wasm": { + "aliases": ["wasm"], + "repos": { + "llvm-project": "swiftwasm", + "swift": "swiftwasm", + "cmark": "master", + "llbuild": "master", + "swiftpm": "master", + "swift-syntax": "master", + "swift-stress-tester": "master", + "swift-corelibs-xctest": "master", + "swift-corelibs-foundation": "master", + "swift-corelibs-libdispatch": "master", + "swift-integration-tests": "master", + "swift-xcode-playground-support": "master", + "ninja": "release", + "icu": "release-61-1", + "cmake": "v3.15.1", + "indexstore-db": "master", + "sourcekit-lsp": "master", + "swift-format": "master" + } + }, + "master": { "aliases": ["master", "swift/master"], "repos": { "llvm-project": "swift/master", From e3f3640d40d7863473c739423bf5d82d40ac2b4b Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 30 Apr 2019 00:40:32 -0700 Subject: [PATCH 445/478] WebAssembly: fix metadata table sizes Whoops. --- stdlib/public/runtime/SwiftRT-ELF.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/runtime/SwiftRT-ELF.cpp b/stdlib/public/runtime/SwiftRT-ELF.cpp index 9b4b8e8dbe2fb..65ae9f2aba91b 100644 --- a/stdlib/public/runtime/SwiftRT-ELF.cpp +++ b/stdlib/public/runtime/SwiftRT-ELF.cpp @@ -54,7 +54,7 @@ static void swift_image_constructor() { // WebAssembly hack: ok this should really go in its own file #define SWIFT_SECTION_RANGE(name) \ { reinterpret_cast(&__start_##name) + sizeof(void*), \ - static_cast(&__stop_##name - &__start_##name) } + static_cast(&__stop_##name - &__start_##name - sizeof(void*)) } #endif sections = { From 8c46117965d7b715ceeefec2d7a549b4f5626f4f Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 30 Apr 2019 00:40:52 -0700 Subject: [PATCH 446/478] WebAssembly: add a ton of logging when looking up metadata Not sure what's going on here: https://gist.github.com/zhuowei/beff646ebc09bed4458421cf7aba210b --- stdlib/public/runtime/MetadataLookup.cpp | 28 +++++++++++++++++++ stdlib/public/runtime/ProtocolConformance.cpp | 22 +++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index 73c76a03b813a..bb7909fc89291 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -333,19 +333,47 @@ _findExtendedTypeContextDescriptor(const ContextDescriptor *maybeExtension, /// buildContextDescriptorMangling in MetadataReader. bool swift::_isCImportedTagType(const TypeContextDescriptor *type, const ParsedTypeIdentity &identity) { + fprintf(stderr, "trying to dump type %p\n", type); + fprintf(stderr, "name: %s\n", type->Name.get()); + fprintf(stderr, "trying to dump identity %p\n", &identity); + fprintf(stderr, "User facing name: %s\n", identity.UserFacingName.str().c_str()); + fprintf(stderr, "ok, let's go\n"); // Tag types are always imported as structs or enums. if (type->getKind() != ContextDescriptorKind::Enum && type->getKind() != ContextDescriptorKind::Struct) return false; + fprintf(stderr, "is it a c typedef\n"); + // Not a typedef imported as a nominal type. if (identity.isCTypedef()) return false; + fprintf(stderr, "is related entity\n"); + // Not a related entity. if (identity.isAnyRelatedEntity()) return false; + fprintf(stderr, "is c imported context\n"); + fprintf(stderr, "type's parent, raw: %x\n", *((unsigned int*)&type->Parent)); + fprintf(stderr, "type's parent: %p\n", type->Parent.get()); +// fprintf(stderr, "type's parent name: %s\n", type->Parent->Name.get()); + fprintf(stderr, "trying to get module context\n"); + + for (auto cur = type->Parent.get(); true; cur = cur->Parent.get()) { + fprintf(stderr, "cur %p\n", cur); + fprintf(stderr, "cur %x\n", (unsigned int)cur->getKind()); + if (auto module = dyn_cast(cur)) { + fprintf(stderr, "found\n"); + break; + } + } + + fprintf(stderr, "type's parent module context: %p\n", type->Parent->getModuleContext()); + fprintf(stderr, "trying to get name\n"); + fprintf(stderr, "type's parent module context name: %s\n", type->Parent->getModuleContext()->Name.get()); + // Imported from C. return type->Parent->isCImportedContext(); } diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 5cc50b09051c0..d38f944773c81 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -553,6 +553,7 @@ static const ProtocolConformanceDescriptor * swift_conformsToSwiftProtocolImpl(const Metadata * const type, const ProtocolDescriptor *protocol, StringRef module) { + fprintf(stderr, "in impl2\n"); auto &C = Conformances.get(); // See if we have a cached conformance. The ConcurrentMap data structure @@ -579,17 +580,25 @@ swift_conformsToSwiftProtocolImpl(const Metadata * const type, C.cacheFailure(type, protocol, snapshot.count()); return nullptr; } - + fprintf(stderr, "got to really scan\n"); // Really scan conformance records. for (size_t i = startIndex; i < endIndex; i++) { + fprintf(stderr, "index = %lx\n", (unsigned long)i); auto §ion = snapshot.Start[i]; // Eagerly pull records for nondependent witnesses into our cache. for (const auto &record : section) { - auto &descriptor = *record.get(); + fprintf(stderr, "got a record\n"); + auto descriptorPtr = record.get(); + auto &descriptor = *descriptorPtr; + fprintf(stderr, "got the descriptor: %p\n", descriptorPtr); + fprintf(stderr, "got the protocol: %p\n", descriptor.getProtocol()); + descriptor.getProtocol()->dump(); + fprintf(stderr, "got it\n"); // We only care about conformances for this protocol. if (descriptor.getProtocol() != protocol) continue; + fprintf(stderr, "about to get matching type\n"); // If there's a matching type, record the positive result. ConformanceCandidate candidate(descriptor); @@ -604,6 +613,7 @@ swift_conformsToSwiftProtocolImpl(const Metadata * const type, } // Conformance scan is complete. + fprintf(stderr, "about to update cache\n"); // Search the cache once more, and this time update the cache if necessary. FoundConformance = searchInConformanceCache(type, protocol); @@ -618,10 +628,18 @@ swift_conformsToSwiftProtocolImpl(const Metadata * const type, static const WitnessTable * swift_conformsToProtocolImpl(const Metadata * const type, const ProtocolDescriptor *protocol) { + // WebAssembly: logging pls + fprintf(stderr, "swift_conformsToProtocolImpl: %p %p\n", type, protocol); + protocol->dump(); + fprintf(stderr, "trying to dump the type now\n"); + type->dump(); + fprintf(stderr, "dumped the type\n"); + // end WebAssembly auto description = swift_conformsToSwiftProtocol(type, protocol, StringRef()); if (!description) return nullptr; + description->getProtocol()->dump(); return description->getWitnessTable( findConformingSuperclass(type, description)); From 1f7c6a1557f63cf102e693f741da274e245460dd Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 30 Apr 2019 00:44:08 -0700 Subject: [PATCH 447/478] WebAssembly: fix a typo accidentally introduced into CMake file Thanks to maxdesiatov. --- cmake/modules/SwiftConfigureSDK.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/SwiftConfigureSDK.cmake b/cmake/modules/SwiftConfigureSDK.cmake index 307abc0a798e7..279fd16e3c5a0 100644 --- a/cmake/modules/SwiftConfigureSDK.cmake +++ b/cmake/modules/SwiftConfigureSDK.cmake @@ -290,7 +290,7 @@ macro(configure_sdk_unix name architectures) if("${prefix}" STREQUAL "LINUX") if(arch MATCHES "(armv6|armv7)") - set(SWIFT_SDK_LINUX_ARCH_${arch}i_TRIPLE "${arch}-unknown-linux-gnueabihf") + set(SWIFT_SDK_LINUX_ARCH_${arch}_TRIPLE "${arch}-unknown-linux-gnueabihf") elseif(arch MATCHES "(aarch64|i686|powerpc64|powerpc64le|s390x|x86_64)") set(SWIFT_SDK_LINUX_ARCH_${arch}_TRIPLE "${arch}-unknown-linux-gnu") else() From 0918ea1856f2ce4aa951dfa74be1880eb40aa88e Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 30 Apr 2019 12:01:58 -0700 Subject: [PATCH 448/478] WebAssembly: fix GOT-relative pointers without PCrel Previous commits changed a bunch of relative pointers to absolute in the metadata; at the time I didn't add support for adding tag bits, which signifies if the pointers point to the .got section (and thus needs an extra layer of indirection.) This broke _isCImportedTagType on $ss7UnicodeO5ASCIIOMn, so this commit adds back tag support for metadata pointers. --- lib/IRGen/ConstantBuilder.h | 10 +++++++++- lib/IRGen/GenMeta.cpp | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/IRGen/ConstantBuilder.h b/lib/IRGen/ConstantBuilder.h index aca1fda1be3d0..8c85f8d3c2a33 100644 --- a/lib/IRGen/ConstantBuilder.h +++ b/lib/IRGen/ConstantBuilder.h @@ -99,7 +99,15 @@ class ConstantAggregateBuilderBase // WebAssembly: hack: doesn't support PCrel data relocations // also, we should set the lowest bit, but I don't know how to do that // there's no GOT on WebAssembly anyways though - add(llvm::ConstantExpr::getPtrToInt(reference.getValue(), IGM().RelativeAddressTy, false)); + + + llvm::Constant *offset = llvm::ConstantExpr::getPtrToInt(reference.getValue(), IGM().RelativeAddressTy, false); + // borrowed from addTaggedRelativeOffset + unsigned tag = unsigned(reference.isIndirect()); + if (tag) { + offset = llvm::ConstantExpr::getAdd(offset, llvm::ConstantInt::get(IGM().RelativeAddressTy, tag)); + } + add(offset); return; } addTaggedRelativeOffset(IGM().RelativeAddressTy, diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 29427288b92a8..12675741c3c9c 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -4521,7 +4521,12 @@ GenericRequirementsMetadata irgen::addGenericRequirements( // WebAssembly: hack: Wasm doesn't support PC-relative offsets. // also doesn't handle tag yet if (IGM.TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { - B.add(llvm::ConstantExpr::getPtrToInt(descriptorRef.getValue(), IGM.RelativeAddressTy, false)); + llvm::Constant *offset = llvm::ConstantExpr::getPtrToInt(descriptorRef.getValue(), IGM.RelativeAddressTy, false); + // borrowed from addTaggedRelativeOffset + if (tag) { + offset = llvm::ConstantExpr::getAdd(offset, llvm::ConstantInt::get(IGM.RelativeAddressTy, tag)); + } + B.add(offset); return; } From de91e3cc53e276d91df506c46ef0a8721b299480 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 30 Apr 2019 15:07:55 -0700 Subject: [PATCH 449/478] WebAssembly: add logging in swift_getAssociatedTypeWitness --- stdlib/public/runtime/Metadata.cpp | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 23b4ecce1e370..bab9b02eb82af 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -4403,6 +4403,7 @@ swift_getAssociatedTypeWitnessSlowImpl( const Metadata *conformingType, const ProtocolRequirement *reqBase, const ProtocolRequirement *assocType) { + fprintf(stderr, "Entering slow path: %p %p %p %p\n", wtable, conformingType, reqBase, assocType); #ifndef NDEBUG { const ProtocolConformanceDescriptor *conformance = wtable->Description; @@ -4429,6 +4430,8 @@ swift_getAssociatedTypeWitnessSlowImpl( const char *mangledNameBase = (const char *)(uintptr_t(witness) & ~ProtocolRequirementFlags::AssociatedTypeMangledNameBit); + fprintf(stderr, "Mangled name base: %p\n", mangledNameBase); + fprintf(stderr, "name: %s\n", mangledNameBase); // Check whether the mangled name has the prefix byte indicating that // the mangled name is relative to the protocol itself. @@ -4443,13 +4446,18 @@ swift_getAssociatedTypeWitnessSlowImpl( const ProtocolConformanceDescriptor *conformance = wtable->Description; const ProtocolDescriptor *protocol = conformance->getProtocol(); + fprintf(stderr, "conformance %p protocol %p\n", conformance, protocol); + // Extract the mangled name itself. StringRef mangledName = Demangle::makeSymbolicMangledNameStringRef(mangledNameBase); + fprintf(stderr, "mangledName: %s\n", mangledName.str().c_str()); + // Demangle the associated type. MetadataResponse response; if (inProtocolContext) { + fprintf(stderr, "in protocol context\n"); // The protocol's Self is the only generic parameter that can occur in the // type. response = @@ -4472,6 +4480,7 @@ swift_getAssociatedTypeWitnessSlowImpl( dependentDescriptor); }).getResponse(); } else { + fprintf(stderr, "getting original conforming type\n"); // The generic parameters in the associated type name are those of the // conforming type. @@ -4490,6 +4499,18 @@ swift_getAssociatedTypeWitnessSlowImpl( }).getResponse(); } auto assocTypeMetadata = response.Value; + fprintf(stderr, "assocTypeMetadata: %p\n", assocTypeMetadata); + + if (true) { + auto conformingTypeNameInfo = swift_getTypeName(conformingType, true); + StringRef conformingTypeName(conformingTypeNameInfo.data, + conformingTypeNameInfo.length); + StringRef assocTypeName = findAssociatedTypeName(protocol, assocType); + fprintf(stderr, "fin: %s %s %s %s\n", assocTypeName.str().c_str(), + conformingTypeName.str().c_str(), + protocol->Name.get(), + mangledName.str().c_str()); + } if (!assocTypeMetadata) { auto conformingTypeNameInfo = swift_getTypeName(conformingType, true); @@ -4525,9 +4546,20 @@ swift::swift_getAssociatedTypeWitness(MetadataRequest request, // If the low bit of the witness is clear, it's already a metadata pointer. unsigned witnessIndex = assocType - reqBase; auto witness = ((const void* const *)wtable)[witnessIndex]; + fprintf(stderr, "getAssociatedTypeWitness fastpath: %x %p\n", witnessIndex, witness); if (LLVM_LIKELY((uintptr_t(witness) & ProtocolRequirementFlags::AssociatedTypeMangledNameBit) == 0)) { // Cached metadata pointers are always complete. + fprintf(stderr, "fastpath: %p\n", witness); + auto witnessPtr = (const Metadata *)witness; + witnessPtr->dump(); + if (witnessPtr->getKind() == MetadataKind::Class) { + fprintf(stderr, "class description:\n"); + auto witnessClass = witnessPtr->getClassObject(); + fprintf(stderr, "%lx\n", *(unsigned long*)&witnessClass->Data); + if (witnessClass->isTypeMetadata()) + fprintf(stderr, "name: %s\n", witnessClass->getDescription()->Name.get()); + } return MetadataResponse{(const Metadata *)witness, MetadataState::Complete}; } From f55626417a7b1368dcacd2d9a57288eea3b631e2 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Thu, 2 May 2019 11:55:24 -0700 Subject: [PATCH 450/478] Revert "WebAssembly: add logging in swift_getAssociatedTypeWitness" Don't need the logging anymore. This reverts commit 7d35644ca7bfa027505f09b9232d34b8191bd42d. --- stdlib/public/runtime/Metadata.cpp | 32 ------------------------------ 1 file changed, 32 deletions(-) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index bab9b02eb82af..23b4ecce1e370 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -4403,7 +4403,6 @@ swift_getAssociatedTypeWitnessSlowImpl( const Metadata *conformingType, const ProtocolRequirement *reqBase, const ProtocolRequirement *assocType) { - fprintf(stderr, "Entering slow path: %p %p %p %p\n", wtable, conformingType, reqBase, assocType); #ifndef NDEBUG { const ProtocolConformanceDescriptor *conformance = wtable->Description; @@ -4430,8 +4429,6 @@ swift_getAssociatedTypeWitnessSlowImpl( const char *mangledNameBase = (const char *)(uintptr_t(witness) & ~ProtocolRequirementFlags::AssociatedTypeMangledNameBit); - fprintf(stderr, "Mangled name base: %p\n", mangledNameBase); - fprintf(stderr, "name: %s\n", mangledNameBase); // Check whether the mangled name has the prefix byte indicating that // the mangled name is relative to the protocol itself. @@ -4446,18 +4443,13 @@ swift_getAssociatedTypeWitnessSlowImpl( const ProtocolConformanceDescriptor *conformance = wtable->Description; const ProtocolDescriptor *protocol = conformance->getProtocol(); - fprintf(stderr, "conformance %p protocol %p\n", conformance, protocol); - // Extract the mangled name itself. StringRef mangledName = Demangle::makeSymbolicMangledNameStringRef(mangledNameBase); - fprintf(stderr, "mangledName: %s\n", mangledName.str().c_str()); - // Demangle the associated type. MetadataResponse response; if (inProtocolContext) { - fprintf(stderr, "in protocol context\n"); // The protocol's Self is the only generic parameter that can occur in the // type. response = @@ -4480,7 +4472,6 @@ swift_getAssociatedTypeWitnessSlowImpl( dependentDescriptor); }).getResponse(); } else { - fprintf(stderr, "getting original conforming type\n"); // The generic parameters in the associated type name are those of the // conforming type. @@ -4499,18 +4490,6 @@ swift_getAssociatedTypeWitnessSlowImpl( }).getResponse(); } auto assocTypeMetadata = response.Value; - fprintf(stderr, "assocTypeMetadata: %p\n", assocTypeMetadata); - - if (true) { - auto conformingTypeNameInfo = swift_getTypeName(conformingType, true); - StringRef conformingTypeName(conformingTypeNameInfo.data, - conformingTypeNameInfo.length); - StringRef assocTypeName = findAssociatedTypeName(protocol, assocType); - fprintf(stderr, "fin: %s %s %s %s\n", assocTypeName.str().c_str(), - conformingTypeName.str().c_str(), - protocol->Name.get(), - mangledName.str().c_str()); - } if (!assocTypeMetadata) { auto conformingTypeNameInfo = swift_getTypeName(conformingType, true); @@ -4546,20 +4525,9 @@ swift::swift_getAssociatedTypeWitness(MetadataRequest request, // If the low bit of the witness is clear, it's already a metadata pointer. unsigned witnessIndex = assocType - reqBase; auto witness = ((const void* const *)wtable)[witnessIndex]; - fprintf(stderr, "getAssociatedTypeWitness fastpath: %x %p\n", witnessIndex, witness); if (LLVM_LIKELY((uintptr_t(witness) & ProtocolRequirementFlags::AssociatedTypeMangledNameBit) == 0)) { // Cached metadata pointers are always complete. - fprintf(stderr, "fastpath: %p\n", witness); - auto witnessPtr = (const Metadata *)witness; - witnessPtr->dump(); - if (witnessPtr->getKind() == MetadataKind::Class) { - fprintf(stderr, "class description:\n"); - auto witnessClass = witnessPtr->getClassObject(); - fprintf(stderr, "%lx\n", *(unsigned long*)&witnessClass->Data); - if (witnessClass->isTypeMetadata()) - fprintf(stderr, "name: %s\n", witnessClass->getDescription()->Name.get()); - } return MetadataResponse{(const Metadata *)witness, MetadataState::Complete}; } From bab29e3d22c9f0515115ecd240209652b3b35925 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Thu, 2 May 2019 11:56:27 -0700 Subject: [PATCH 451/478] Revert "WebAssembly: add a ton of logging when looking up metadata" Don't need logging anymore. This reverts commit 065ab6f61c80c2573d72e1572c751914d42f7a61. --- stdlib/public/runtime/MetadataLookup.cpp | 28 ------------------- stdlib/public/runtime/ProtocolConformance.cpp | 22 ++------------- 2 files changed, 2 insertions(+), 48 deletions(-) diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index bb7909fc89291..73c76a03b813a 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -333,47 +333,19 @@ _findExtendedTypeContextDescriptor(const ContextDescriptor *maybeExtension, /// buildContextDescriptorMangling in MetadataReader. bool swift::_isCImportedTagType(const TypeContextDescriptor *type, const ParsedTypeIdentity &identity) { - fprintf(stderr, "trying to dump type %p\n", type); - fprintf(stderr, "name: %s\n", type->Name.get()); - fprintf(stderr, "trying to dump identity %p\n", &identity); - fprintf(stderr, "User facing name: %s\n", identity.UserFacingName.str().c_str()); - fprintf(stderr, "ok, let's go\n"); // Tag types are always imported as structs or enums. if (type->getKind() != ContextDescriptorKind::Enum && type->getKind() != ContextDescriptorKind::Struct) return false; - fprintf(stderr, "is it a c typedef\n"); - // Not a typedef imported as a nominal type. if (identity.isCTypedef()) return false; - fprintf(stderr, "is related entity\n"); - // Not a related entity. if (identity.isAnyRelatedEntity()) return false; - fprintf(stderr, "is c imported context\n"); - fprintf(stderr, "type's parent, raw: %x\n", *((unsigned int*)&type->Parent)); - fprintf(stderr, "type's parent: %p\n", type->Parent.get()); -// fprintf(stderr, "type's parent name: %s\n", type->Parent->Name.get()); - fprintf(stderr, "trying to get module context\n"); - - for (auto cur = type->Parent.get(); true; cur = cur->Parent.get()) { - fprintf(stderr, "cur %p\n", cur); - fprintf(stderr, "cur %x\n", (unsigned int)cur->getKind()); - if (auto module = dyn_cast(cur)) { - fprintf(stderr, "found\n"); - break; - } - } - - fprintf(stderr, "type's parent module context: %p\n", type->Parent->getModuleContext()); - fprintf(stderr, "trying to get name\n"); - fprintf(stderr, "type's parent module context name: %s\n", type->Parent->getModuleContext()->Name.get()); - // Imported from C. return type->Parent->isCImportedContext(); } diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index d38f944773c81..5cc50b09051c0 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -553,7 +553,6 @@ static const ProtocolConformanceDescriptor * swift_conformsToSwiftProtocolImpl(const Metadata * const type, const ProtocolDescriptor *protocol, StringRef module) { - fprintf(stderr, "in impl2\n"); auto &C = Conformances.get(); // See if we have a cached conformance. The ConcurrentMap data structure @@ -580,25 +579,17 @@ swift_conformsToSwiftProtocolImpl(const Metadata * const type, C.cacheFailure(type, protocol, snapshot.count()); return nullptr; } - fprintf(stderr, "got to really scan\n"); + // Really scan conformance records. for (size_t i = startIndex; i < endIndex; i++) { - fprintf(stderr, "index = %lx\n", (unsigned long)i); auto §ion = snapshot.Start[i]; // Eagerly pull records for nondependent witnesses into our cache. for (const auto &record : section) { - fprintf(stderr, "got a record\n"); - auto descriptorPtr = record.get(); - auto &descriptor = *descriptorPtr; - fprintf(stderr, "got the descriptor: %p\n", descriptorPtr); - fprintf(stderr, "got the protocol: %p\n", descriptor.getProtocol()); - descriptor.getProtocol()->dump(); - fprintf(stderr, "got it\n"); + auto &descriptor = *record.get(); // We only care about conformances for this protocol. if (descriptor.getProtocol() != protocol) continue; - fprintf(stderr, "about to get matching type\n"); // If there's a matching type, record the positive result. ConformanceCandidate candidate(descriptor); @@ -613,7 +604,6 @@ swift_conformsToSwiftProtocolImpl(const Metadata * const type, } // Conformance scan is complete. - fprintf(stderr, "about to update cache\n"); // Search the cache once more, and this time update the cache if necessary. FoundConformance = searchInConformanceCache(type, protocol); @@ -628,18 +618,10 @@ swift_conformsToSwiftProtocolImpl(const Metadata * const type, static const WitnessTable * swift_conformsToProtocolImpl(const Metadata * const type, const ProtocolDescriptor *protocol) { - // WebAssembly: logging pls - fprintf(stderr, "swift_conformsToProtocolImpl: %p %p\n", type, protocol); - protocol->dump(); - fprintf(stderr, "trying to dump the type now\n"); - type->dump(); - fprintf(stderr, "dumped the type\n"); - // end WebAssembly auto description = swift_conformsToSwiftProtocol(type, protocol, StringRef()); if (!description) return nullptr; - description->getProtocol()->dump(); return description->getWitnessTable( findConformingSuperclass(type, description)); From fccc69b6d38207bf08dca16957c46ff82bf13ed4 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Sat, 8 Jun 2019 17:35:09 -0700 Subject: [PATCH 452/478] WebAssembly: add replac2 section to start/end objects --- swift_end.cpp | 1 + swift_start.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/swift_end.cpp b/swift_end.cpp index 714e0d35d7442..b3d476ac41305 100644 --- a/swift_end.cpp +++ b/swift_end.cpp @@ -26,4 +26,5 @@ DECLARE_SWIFT_SECTION(swift5_reflstr) DECLARE_SWIFT_SECTION(swift5_fieldmd) DECLARE_SWIFT_SECTION(swift5_assocty) DECLARE_SWIFT_SECTION(swift5_replace) +DECLARE_SWIFT_SECTION(swift5_replac2) } diff --git a/swift_start.cpp b/swift_start.cpp index 5a77ea3c0f527..a9cf1faa390b3 100644 --- a/swift_start.cpp +++ b/swift_start.cpp @@ -26,4 +26,5 @@ DECLARE_SWIFT_SECTION(swift5_reflstr) DECLARE_SWIFT_SECTION(swift5_fieldmd) DECLARE_SWIFT_SECTION(swift5_assocty) DECLARE_SWIFT_SECTION(swift5_replace) +DECLARE_SWIFT_SECTION(swift5_replac2) } From 90b4dd08aaacc55417510d9e1f6748b44ff9cdd9 Mon Sep 17 00:00:00 2001 From: Kyle Verrier Date: Sat, 15 Jun 2019 15:12:59 -0400 Subject: [PATCH 453/478] Update default checkout scheme to "wasm". --- utils/update_checkout/update-checkout-config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index 923dd356f6707..9c10c92c79b34 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -44,7 +44,7 @@ "llvm-project": { "remote": { "id": "swiftwasm/llvm-project" } } }, - "default-branch-scheme": "master", + "default-branch-scheme": "wasm", "branch-schemes": { "wasm": { "aliases": ["wasm"], From 3037fb703517c46fcb66c2580e60d7b41038c62d Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 22 Oct 2019 13:13:57 +0100 Subject: [PATCH 454/478] Add --build-swift-static-stdlib to build script --- vvv.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/vvv.sh b/vvv.sh index db86f386cd5fa..6f202440842fd 100755 --- a/vvv.sh +++ b/vvv.sh @@ -7,4 +7,5 @@ utils/build-script --release-debuginfo --wasm \ --wasm-icu-i18n "todo" \ --wasm-icu-i18n-include "todo" \ --wasm-icu-data "todo" \ + --build-swift-static-stdlib \ "$@" From 7bba6af1cecfa322beb297d3261bd10e79125190 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 27 Oct 2019 17:52:26 +0900 Subject: [PATCH 455/478] [WASM] Set LIBC_INCLUDE_DIRECTORY for WASM to build SwiftGlibc properly (#9) `LIBC_INCLUDE_DIRECTORY` is used to generate `glibc.modulemap`. Currently the directory is set `/usr/include` but it's system header path and we should use `wasi-sdk` version. --- cmake/modules/SwiftConfigureSDK.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/modules/SwiftConfigureSDK.cmake b/cmake/modules/SwiftConfigureSDK.cmake index 279fd16e3c5a0..75cebc60578c9 100644 --- a/cmake/modules/SwiftConfigureSDK.cmake +++ b/cmake/modules/SwiftConfigureSDK.cmake @@ -326,6 +326,8 @@ macro(configure_sdk_unix name architectures) set(SWIFT_SDK_WASM_ARCH_wasm32_PATH "${SWIFT_WASM_WASI_SDK_PATH}/share/sysroot") # fixme: Wasi is wasm32-unknown-wasi-musl. This LLVM doesn't have it yet. set(SWIFT_SDK_WASM_ARCH_wasm32_TRIPLE "wasm32-unknown-unknown-wasm") + set(SWIFT_SDK_WASM_ARCH_wasm32_LIBC_INCLUDE_DIRECTORY "${SWIFT_WASM_WASI_SDK_PATH}/share/sysroot/include" CACHE STRING "Path to C library headers") + set(SWIFT_SDK_WASM_ARCH_wasm32_LIBC_ARCHITECTURE_INCLUDE_DIRECTORY "${SWIFT_WASM_WASI_SDK_PATH}/sysroot/include" CACHE STRING "Path to C library architecture headers") else() message(FATAL_ERROR "unknown Unix OS: ${prefix}") endif() From 0a1f4fc13aff86c3db406a772ea64f9a576f8a95 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 3 Nov 2019 00:30:13 +0900 Subject: [PATCH 456/478] Support building on macOS to develop this project (#8) * Add simple GitHub Actions config for macOS * Add brew install to the macOS GH action * Add update-checkout call to macOS GH action * Export sourcedir shell variable in macOS GH action * [WASM] Use prebuilt llvm-ar only on Linux * [WASM] Added new Object format WASM to distinguish from ELF * [WASM] Separate SwiftRT from ELF * [WASM] Add preprocessor condition for wasm32 to track latest upstream * Add wasi-sdk and icu install steps to the script * [WASM] Set LIBC_INCLUDE_DIRECTORY without CACHE attribute to force new value * [WASM] Adding share path to point correct directory * [WASM] Add Linux CI job * [WASM] Checkout branch after checking out Swift repos * [WASM] Use release build to reduce artifact size * [WASM] Use SWIFT_PRIMARY_VARIANT_SDK instead of HOST_SDK In this case, SWIFT_PRIMARY_VARIANT_ARCH is used but host SDK was not PRIMARY_SDK. * [WASM] Remove ICU_STATICLIB on Linux * [WASM] Output build log verbose * [WASM] Set ICU lib directory instead of archive * [WASM] Sort build options * Revert "[WASM] Use SWIFT_PRIMARY_VARIANT_SDK instead of HOST_SDK" --- .github/workflows/main.yml | 111 +++++++++++++++++++++++++ CMakeLists.txt | 8 +- cmake/modules/AddSwift.cmake | 3 +- cmake/modules/SwiftConfigureSDK.cmake | 6 +- lib/Driver/CMakeLists.txt | 3 +- stdlib/public/core/StringStorage.swift | 2 +- stdlib/public/runtime/CMakeLists.txt | 17 +++- stdlib/public/runtime/SwiftRT-ELF.cpp | 7 -- stdlib/public/runtime/SwiftRT-WASM.cpp | 11 +++ 9 files changed, 152 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/main.yml create mode 100644 stdlib/public/runtime/SwiftRT-WASM.cpp diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000000..ba665370d2b70 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,111 @@ +name: CI + +on: + pull_request: + branches: + - swiftwasm + +jobs: + linux_build: + timeout-minutes: 0 + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v1 + - name: Run a multi-line script + run: | + sudo apt update + sudo apt install \ + git ninja-build clang python \ + uuid-dev libicu-dev icu-devtools libbsd-dev \ + libedit-dev libxml2-dev libsqlite3-dev swig \ + libpython-dev libncurses5-dev pkg-config \ + libblocksruntime-dev libcurl4-openssl-dev \ + systemtap-sdt-dev tzdata rsync + + ./utils/update-checkout --clone --scheme wasm + git checkout $GITHUB_SHA + export sourcedir=$PWD/.. + cd $sourcedir + + wget -O install_cmake.sh "https://github.com/Kitware/CMake/releases/download/v3.15.3/cmake-3.15.3-Linux-x86_64.sh" + chmod +x install_cmake.sh + sudo mkdir -p /opt/cmake + sudo ./install_cmake.sh --skip-license --prefix=/opt/cmake + sudo ln -sf /opt/cmake/bin/* /usr/local/bin + cmake --version + + wget -O wasi-sdk.tar.gz https://github.com/swiftwasm/wasi-sdk/releases/download/20190421.6/wasi-sdk-3.19gefb17cb478f9.m-linux.tar.gz + tar xfz wasi-sdk.tar.gz + mv wasi-sdk-3.19gefb17cb478f9+m/opt/wasi-sdk ./wasi-sdk + + wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" + tar xf icu.tar.xz + + cd swift + utils/build-script --release --wasm --verbose \ + --skip-build-benchmarks \ + --extra-cmake-options=" \ + -DSWIFT_SDKS='WASM;LINUX' \ + -DSWIFT_BUILD_SOURCEKIT=FALSE \ + -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ + " \ + --build-stdlib-deployment-targets "wasm-wasm32" \ + --build-swift-static-stdlib \ + --install-destdir="$sourcedir/install" \ + --install-prefix="/opt/swiftwasm-sdk" \ + --install-swift \ + --installable-package="$sourcedir/swiftwasm.tar.gz" \ + --llvm-targets-to-build "X86;WebAssembly" \ + --stdlib-deployment-targets "wasm-wasm32" \ + --wasm-icu-data "todo-icu-data" \ + --wasm-icu-i18n "$sourcedir/icu_out/lib" \ + --wasm-icu-i18n-include "$sourcedir/icu_out/include" \ + --wasm-icu-uc "$sourcedir/icu_out/lib" \ + --wasm-icu-uc-include "$sourcedir/icu_out/include" \ + --wasm-wasi-sdk "$sourcedir/wasi-sdk" + + macos_build: + timeout-minutes: 0 + runs-on: macOS-10.14 + + steps: + - uses: actions/checkout@v1 + - name: Run a multi-line script + run: | + brew install cmake ninja + ./utils/update-checkout --clone --scheme wasm + git checkout $GITHUB_SHA + export sourcedir=$PWD/.. + cd $sourcedir + wget -O wasi-sdk.tar.gz https://github.com/swiftwasm/wasi-sdk/releases/download/20190421.6/wasi-sdk-3.19gefb17cb478f9.m-linux.tar.gz + tar xfz wasi-sdk.tar.gz + mv wasi-sdk-3.19gefb17cb478f9+m/opt/wasi-sdk ./wasi-sdk + # Link sysroot/usr/include to sysroot/include because Darwin sysroot doesn't + # find header files in sysroot/include but sysroot/usr/include + mkdir wasi-sdk/share/sysroot/usr/ + ln -s ../include wasi-sdk/share/sysroot/usr/include + wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" + tar xf icu.tar.xz + cd swift + ./utils/build-script --release --wasm --verbose \ + --skip-build-benchmarks \ + --extra-cmake-options=" \ + -DSWIFT_PRIMARY_VARIANT_SDK:STRING=WASM \ + -DSWIFT_PRIMARY_VARIANT_ARCH:STRING=wasm32 \ + -DSWIFT_OSX_x86_64_ICU_STATICLIB=TRUE \ + -DSWIFT_BUILD_SOURCEKIT=FALSE \ + -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ + " \ + --build-stdlib-deployment-targets "wasm-wasm32" \ + --build-swift-dynamic-sdk-overlay false \ + --build-swift-static-sdk-overlay false \ + --build-swift-static-stdlib \ + --llvm-targets-to-build "X86;WebAssembly" \ + --stdlib-deployment-targets "wasm-wasm32" \ + --wasm-icu-data "todo-icu-data" \ + --wasm-icu-i18n "$sourcedir/icu_out/lib" \ + --wasm-icu-i18n-include "$sourcedir/icu_out/include" \ + --wasm-icu-uc "$sourcedir/icu_out/lib" \ + --wasm-icu-uc-include "$sourcedir/icu_out/include" \ + --wasm-wasi-sdk "$sourcedir/wasi-sdk" diff --git a/CMakeLists.txt b/CMakeLists.txt index b6e7480df0631..7a28865055c46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,9 +31,6 @@ ENABLE_LANGUAGE(C) include(SwiftUtils) include(CheckSymbolExists) -# WebAssembly: hack: use llvm-ar for creating static libraries; Ubuntu's GNU ar doesn't work with wasm-ld -set(CMAKE_AR "${SWIFT_WASM_WASI_SDK_PATH}/bin/llvm-ar") - # # User-configurable options that control the inclusion and default build # behavior for components which may not strictly be necessary (tools, examples, @@ -832,6 +829,11 @@ if(swift_build_wasm AND NOT "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "WASM") # message(FATAL_ERROR "A Darwin or Linux host is required to build the Swift runtime for Android") #endif() + if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux") + # WebAssembly: hack: use llvm-ar for creating static libraries; Ubuntu's GNU ar doesn't work with wasm-ld + set(CMAKE_AR "${SWIFT_WASM_WASI_SDK_PATH}/bin/llvm-ar") + endif() + if("${SWIFT_SDK_WASM_ARCHITECTURES}" STREQUAL "") set(SWIFT_SDK_WASM_ARCHITECTURES wasm32) endif() diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 6552239d8145b..788e08f1d25a2 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -1000,7 +1000,8 @@ function(_add_swift_library_single target name) ${INCORPORATED_OBJECT_LIBRARIES_EXPRESSIONS} ${SWIFTLIB_SINGLE_XCODE_WORKAROUND_SOURCES}) if(("${SWIFT_SDK_${SWIFTLIB_SINGLE_SDK}_OBJECT_FORMAT}" STREQUAL "ELF" OR - "${SWIFT_SDK_${SWIFTLIB_SINGLE_SDK}_OBJECT_FORMAT}" STREQUAL "COFF") AND + "${SWIFT_SDK_${SWIFTLIB_SINGLE_SDK}_OBJECT_FORMAT}" STREQUAL "COFF" OR + "${SWIFT_SDK_${SWIFTLIB_SINGLE_SDK}_OBJECT_FORMAT}" STREQUAL "WASM") AND SWIFTLIB_SINGLE_TARGET_LIBRARY) if("${libkind}" STREQUAL "SHARED" AND NOT SWIFTLIB_SINGLE_NOSWIFTRT) # TODO(compnerd) switch to the generator expression when cmake is upgraded diff --git a/cmake/modules/SwiftConfigureSDK.cmake b/cmake/modules/SwiftConfigureSDK.cmake index 75cebc60578c9..0749eaea22082 100644 --- a/cmake/modules/SwiftConfigureSDK.cmake +++ b/cmake/modules/SwiftConfigureSDK.cmake @@ -203,6 +203,8 @@ macro(configure_sdk_unix name architectures) set(SWIFT_SDK_${prefix}_ARCHITECTURES "${architectures}") if("${prefix}" STREQUAL "CYGWIN") set(SWIFT_SDK_${prefix}_OBJECT_FORMAT "COFF") + elseif("${prefix}" STREQUAL "WASM") + set(SWIFT_SDK_${prefix}_OBJECT_FORMAT "WASM") else() set(SWIFT_SDK_${prefix}_OBJECT_FORMAT "ELF") endif() @@ -326,8 +328,8 @@ macro(configure_sdk_unix name architectures) set(SWIFT_SDK_WASM_ARCH_wasm32_PATH "${SWIFT_WASM_WASI_SDK_PATH}/share/sysroot") # fixme: Wasi is wasm32-unknown-wasi-musl. This LLVM doesn't have it yet. set(SWIFT_SDK_WASM_ARCH_wasm32_TRIPLE "wasm32-unknown-unknown-wasm") - set(SWIFT_SDK_WASM_ARCH_wasm32_LIBC_INCLUDE_DIRECTORY "${SWIFT_WASM_WASI_SDK_PATH}/share/sysroot/include" CACHE STRING "Path to C library headers") - set(SWIFT_SDK_WASM_ARCH_wasm32_LIBC_ARCHITECTURE_INCLUDE_DIRECTORY "${SWIFT_WASM_WASI_SDK_PATH}/sysroot/include" CACHE STRING "Path to C library architecture headers") + set(SWIFT_SDK_WASM_ARCH_wasm32_LIBC_INCLUDE_DIRECTORY "${SWIFT_WASM_WASI_SDK_PATH}/share/sysroot/include") + set(SWIFT_SDK_WASM_ARCH_wasm32_LIBC_ARCHITECTURE_INCLUDE_DIRECTORY "${SWIFT_WASM_WASI_SDK_PATH}/share/sysroot/include") else() message(FATAL_ERROR "unknown Unix OS: ${prefix}") endif() diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt index 448f5044b7064..83d6afa8ca55c 100644 --- a/lib/Driver/CMakeLists.txt +++ b/lib/Driver/CMakeLists.txt @@ -33,7 +33,8 @@ target_link_libraries(swiftDriver PRIVATE if(SWIFT_BUILD_STATIC_STDLIB) set(static_stdlib_lnk_file_list) foreach(sdk ${SWIFT_CONFIGURED_SDKS}) - if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF") + if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF" OR + "${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "WASM") string(TOLOWER "${sdk}" lowercase_sdk) if(SWIFT_${SWIFT_HOST_VARIANT_SDK}_${SWIFT_HOST_VARIANT_ARCH}_ICU_STATICLIB) set(ICU_STATICLIB "TRUE") diff --git a/stdlib/public/core/StringStorage.swift b/stdlib/public/core/StringStorage.swift index a80937d1f7141..3e47a43cc3f30 100644 --- a/stdlib/public/core/StringStorage.swift +++ b/stdlib/public/core/StringStorage.swift @@ -186,7 +186,7 @@ extension __StringStorage { let count = try initializer(buffer) let countAndFlags = CountAndFlags(mortalCount: count, isASCII: false) - #if arch(i386) || arch(arm) + #if arch(i386) || arch(arm) || arch(wasm32) storage._count = countAndFlags.count storage._flags = countAndFlags.flags #else diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 001d97d517405..afeec4dd9a26a 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -80,6 +80,7 @@ endif(LLVM_ENABLE_ASSERTIONS) set(LLVM_OPTIONAL_SOURCES SwiftRT-COFF.cpp SwiftRT-ELF.cpp + SwiftRT-WASM.cpp ${swift_runtime_sources} ${swift_runtime_objc_sources} ${swift_runtime_leaks_sources}) @@ -174,11 +175,14 @@ add_swift_target_library(swiftRuntime OBJECT_LIBRARY set(ELFISH_SDKS) set(COFF_SDKS) +set(WASM_SDKS) foreach(sdk ${SWIFT_CONFIGURED_SDKS}) if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF") list(APPEND ELFISH_SDKS ${sdk}) elseif("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "COFF") list(APPEND COFF_SDKS ${sdk}) + elseif("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "WASM") + list(APPEND WASM_SDKS ${sdk}) endif() endforeach() @@ -190,6 +194,16 @@ add_swift_target_library(swiftImageRegistrationObjectELF TARGET_SDKS ${ELFISH_SDKS} SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT none) + +add_swift_target_library(swiftImageRegistrationObjectWASM + OBJECT_LIBRARY IS_STDLIB IS_STDLIB_CORE + SwiftRT-WASM.cpp + C_COMPILE_FLAGS ${SWIFT_RUNTIME_CORE_CXX_FLAGS} + LINK_FLAGS ${SWIFT_RUNTIME_CORE_LINK_FLAGS} + TARGET_SDKS ${WASM_SDKS} + SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + INSTALL_IN_COMPONENT none) + # FIXME(compnerd) this should be compiled twice, once for static and once for # shared. The static version should be used for building the standard library. add_swift_target_library(swiftImageRegistrationObjectCOFF @@ -207,7 +221,8 @@ foreach(sdk ${SWIFT_CONFIGURED_SDKS}) set(arch_suffix "${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}") if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF" OR - "${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "COFF") + "${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "COFF" OR + "${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "WASM") # TODO(compnerd) switch to the generator expression when cmake is upgraded # to a version which supports it. # set(swiftrtObject "$") diff --git a/stdlib/public/runtime/SwiftRT-ELF.cpp b/stdlib/public/runtime/SwiftRT-ELF.cpp index 65ae9f2aba91b..fa585a8a68241 100644 --- a/stdlib/public/runtime/SwiftRT-ELF.cpp +++ b/stdlib/public/runtime/SwiftRT-ELF.cpp @@ -46,16 +46,9 @@ static swift::MetadataSections sections{}; __attribute__((__constructor__)) static void swift_image_constructor() { -#ifndef __wasm__ #define SWIFT_SECTION_RANGE(name) \ { reinterpret_cast(&__start_##name), \ static_cast(&__stop_##name - &__start_##name) } -#else -// WebAssembly hack: ok this should really go in its own file -#define SWIFT_SECTION_RANGE(name) \ - { reinterpret_cast(&__start_##name) + sizeof(void*), \ - static_cast(&__stop_##name - &__start_##name - sizeof(void*)) } -#endif sections = { swift::CurrentSectionMetadataVersion, diff --git a/stdlib/public/runtime/SwiftRT-WASM.cpp b/stdlib/public/runtime/SwiftRT-WASM.cpp new file mode 100644 index 0000000000000..6812763e9b1e7 --- /dev/null +++ b/stdlib/public/runtime/SwiftRT-WASM.cpp @@ -0,0 +1,11 @@ +//===--- SwiftRT-WASM.cpp --------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// From 45080a473a6e6cac3d31c734923f424e03af8ea9 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 9 Nov 2019 02:08:11 +0900 Subject: [PATCH 457/478] Swift Runtime on WebAssembly (#11) * [WASM] Build ImageInspectionShared even on macOS ImageInspectionShared was built only if host OS is Linux. But it's necessary when primary sdk is WASM. * [WASM] Use llvm-ar instead of ranlib on macOS Specify ar binary through extra-cmake-options * [WASM] Install llvm through HomeBrew to use llvm-ar * [WASM] Use llvm-ranlib instead of ranlib of Xcode * [WASM] Read offset as pointer when target is wasm Because WASM doesn't support relative pointer, compiler emit direct address instead of offset from current address. * [WASM] Copy SwiftRT-ELF.cpp into SwiftRT-WASM.cpp * [WASM] Emit empty swift5 sections if there aren't * [WASM] Remove swift_end and swift_start files --- .github/workflows/main.yml | 6 ++- CMakeLists.txt | 5 -- lib/IRGen/MetadataRequest.cpp | 16 ++++-- stdlib/public/runtime/CMakeLists.txt | 11 +++- stdlib/public/runtime/SwiftRT-WASM.cpp | 75 ++++++++++++++++++++++++++ swift_end.cpp | 30 ----------- swift_start.cpp | 30 ----------- 7 files changed, 101 insertions(+), 72 deletions(-) delete mode 100644 swift_end.cpp delete mode 100644 swift_start.cpp diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ba665370d2b70..0950dff14adfa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -49,6 +49,8 @@ jobs: -DSWIFT_SDKS='WASM;LINUX' \ -DSWIFT_BUILD_SOURCEKIT=FALSE \ -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ + -DCMAKE_AR='$sourcedir/wasi-sdk/bin/llvm-ar' \ + -DCMAKE_RANLIB='$sourcedir/wasi-sdk/bin/llvm-ranlib' \ " \ --build-stdlib-deployment-targets "wasm-wasm32" \ --build-swift-static-stdlib \ @@ -73,7 +75,7 @@ jobs: - uses: actions/checkout@v1 - name: Run a multi-line script run: | - brew install cmake ninja + brew install cmake ninja llvm ./utils/update-checkout --clone --scheme wasm git checkout $GITHUB_SHA export sourcedir=$PWD/.. @@ -96,6 +98,8 @@ jobs: -DSWIFT_OSX_x86_64_ICU_STATICLIB=TRUE \ -DSWIFT_BUILD_SOURCEKIT=FALSE \ -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ + -DCMAKE_AR='/usr/local/opt/llvm/bin/llvm-ar' \ + -DCMAKE_RANLIB='/usr/local/opt/llvm/bin/llvm-ranlib' \ " \ --build-stdlib-deployment-targets "wasm-wasm32" \ --build-swift-dynamic-sdk-overlay false \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a28865055c46..f3fe9bda9f530 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -829,11 +829,6 @@ if(swift_build_wasm AND NOT "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "WASM") # message(FATAL_ERROR "A Darwin or Linux host is required to build the Swift runtime for Android") #endif() - if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux") - # WebAssembly: hack: use llvm-ar for creating static libraries; Ubuntu's GNU ar doesn't work with wasm-ld - set(CMAKE_AR "${SWIFT_WASM_WASI_SDK_PATH}/bin/llvm-ar") - endif() - if("${SWIFT_SDK_WASM_ARCHITECTURES}" STREQUAL "") set(SWIFT_SDK_WASM_ARCHITECTURES wasm32) endif() diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 75926952f8a4d..47158041becc0 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -2417,10 +2417,18 @@ emitMetadataAccessByMangledName(IRGenFunction &IGF, CanType type, IGM.Int32Ty); stringAddrOffset = subIGF.Builder.CreateSExtOrBitCast(stringAddrOffset, IGM.SizeTy); - auto stringAddrBase = subIGF.Builder.CreatePtrToInt(cache, IGM.SizeTy); - if (IGM.getModule()->getDataLayout().isBigEndian()) { - stringAddrBase = subIGF.Builder.CreateAdd(stringAddrBase, - llvm::ConstantInt::get(IGM.SizeTy, 4)); + llvm::Value *stringAddr; + if (IGM.TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + stringAddr = subIGF.Builder.CreateIntToPtr(stringAddrOffset, IGM.Int8PtrTy); + } else { + auto stringAddrBase = subIGF.Builder.CreatePtrToInt(cache, IGM.SizeTy); + if (IGM.getModule()->getDataLayout().isBigEndian()) { + stringAddrBase = subIGF.Builder.CreateAdd(stringAddrBase, + llvm::ConstantInt::get(IGM.SizeTy, 4)); + } + stringAddr = subIGF.Builder.CreateAdd(stringAddrBase, + stringAddrOffset); + stringAddr = subIGF.Builder.CreateIntToPtr(stringAddr, IGM.Int8PtrTy); } auto stringAddr = subIGF.Builder.CreateAdd(stringAddrBase, stringAddrOffset); diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index afeec4dd9a26a..1e869fa0690a4 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -89,8 +89,15 @@ set(swift_runtime_library_compile_flags ${swift_runtime_compile_flags}) list(APPEND swift_runtime_library_compile_flags -DswiftCore_EXPORTS) list(APPEND swift_runtime_library_compile_flags -I${SWIFT_SOURCE_DIR}/include) -set(sdk "${SWIFT_HOST_VARIANT_SDK}") -if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") +set(image_inspection_shared_sdk) +if(SWIFT_BUILD_STATIC_STDLIB AND "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "LINUX") + set(image_inspection_shared_sdk "${SWIFT_HOST_VARIANT_SDK}") +elseif("${SWIFT_PRIMARY_VARIANT_SDK}" STREQUAL "WASM") + set(image_inspection_shared_sdk "${SWIFT_PRIMARY_VARIANT_SDK}") +endif() + +if(NOT "${image_inspection_shared_sdk}" STREQUAL "") + set(sdk "${image_inspection_shared_sdk}") list(REMOVE_ITEM swift_runtime_sources ImageInspectionELF.cpp) set(static_binary_lnk_file_list) string(TOLOWER "${sdk}" lowercase_sdk) diff --git a/stdlib/public/runtime/SwiftRT-WASM.cpp b/stdlib/public/runtime/SwiftRT-WASM.cpp index 6812763e9b1e7..1b4c84f3f2ae9 100644 --- a/stdlib/public/runtime/SwiftRT-WASM.cpp +++ b/stdlib/public/runtime/SwiftRT-WASM.cpp @@ -9,3 +9,78 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// + +#include "ImageInspectionELF.h" + +#include + +// Create empty sections to ensure that the start/stop symbols are synthesized +// by the linker. Otherwise, we may end up with undefined symbol references as +// the linker table section was never constructed. + +#define DECLARE_SWIFT_SECTION(name) \ + __attribute__((__used__,__section__(#name),__aligned__(1))) const char __dummy_##name = 0x00; \ + __attribute__((__visibility__("hidden"),__aligned__(1))) extern const char __start_##name; \ + __attribute__((__visibility__("hidden"),__aligned__(1))) extern const char __stop_##name; + +extern "C" { +DECLARE_SWIFT_SECTION(swift5_protocols) +DECLARE_SWIFT_SECTION(swift5_protocol_conformances) +DECLARE_SWIFT_SECTION(swift5_type_metadata) + +DECLARE_SWIFT_SECTION(swift5_typeref) +DECLARE_SWIFT_SECTION(swift5_reflstr) +DECLARE_SWIFT_SECTION(swift5_fieldmd) +DECLARE_SWIFT_SECTION(swift5_assocty) +DECLARE_SWIFT_SECTION(swift5_replace) +DECLARE_SWIFT_SECTION(swift5_replac2) +DECLARE_SWIFT_SECTION(swift5_builtin) +DECLARE_SWIFT_SECTION(swift5_capture) +} + +#undef DECLARE_SWIFT_SECTION + +namespace { +static swift::MetadataSections sections{}; +} + +__attribute__((__constructor__)) +static void swift_image_constructor() { +#define SWIFT_SECTION_RANGE(name) \ + { reinterpret_cast(&__start_##name), \ + static_cast(&__stop_##name - &__start_##name) } + + sections = { + swift::CurrentSectionMetadataVersion, + 0, + + nullptr, + nullptr, + + SWIFT_SECTION_RANGE(swift5_protocols), + SWIFT_SECTION_RANGE(swift5_protocol_conformances), + SWIFT_SECTION_RANGE(swift5_type_metadata), + + SWIFT_SECTION_RANGE(swift5_typeref), + SWIFT_SECTION_RANGE(swift5_reflstr), + SWIFT_SECTION_RANGE(swift5_fieldmd), + SWIFT_SECTION_RANGE(swift5_assocty), + SWIFT_SECTION_RANGE(swift5_replace), + SWIFT_SECTION_RANGE(swift5_replac2), + SWIFT_SECTION_RANGE(swift5_builtin), + SWIFT_SECTION_RANGE(swift5_capture), + }; + +#undef SWIFT_SECTION_RANGE + + swift_addNewDSOImage(§ions); +} + +static __attribute__((__used__)) +__attribute__((__section__(".note.swift_reflection_metadata"))) +__attribute__((__aligned__(1))) +struct { + const char MagicString[sizeof(SWIFT_REFLECTION_METADATA_ELF_NOTE_MAGIC_STRING)]; + const swift::MetadataSections *Sections; +} __attribute__((__packed__)) +Note = {SWIFT_REFLECTION_METADATA_ELF_NOTE_MAGIC_STRING, §ions}; diff --git a/swift_end.cpp b/swift_end.cpp deleted file mode 100644 index b3d476ac41305..0000000000000 --- a/swift_end.cpp +++ /dev/null @@ -1,30 +0,0 @@ -//===--- SwiftRT-ELF.cpp --------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#include - -// We synthesize the start/stop symbols ourselves. -#define DECLARE_SWIFT_SECTION(name) \ - __attribute__((__section__(#name),__visibility__("hidden"),__aligned__(1))) const void* __stop_##name = (void*)0xfacefeed; \ - -extern "C" { -DECLARE_SWIFT_SECTION(swift5_protocols) -DECLARE_SWIFT_SECTION(swift5_protocol_conformances) -DECLARE_SWIFT_SECTION(swift5_type_metadata) - -DECLARE_SWIFT_SECTION(swift5_typeref) -DECLARE_SWIFT_SECTION(swift5_reflstr) -DECLARE_SWIFT_SECTION(swift5_fieldmd) -DECLARE_SWIFT_SECTION(swift5_assocty) -DECLARE_SWIFT_SECTION(swift5_replace) -DECLARE_SWIFT_SECTION(swift5_replac2) -} diff --git a/swift_start.cpp b/swift_start.cpp deleted file mode 100644 index a9cf1faa390b3..0000000000000 --- a/swift_start.cpp +++ /dev/null @@ -1,30 +0,0 @@ -//===--- SwiftRT-ELF.cpp --------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#include - -// We synthesize the start/stop symbols ourselves. -#define DECLARE_SWIFT_SECTION(name) \ - __attribute__((__section__(#name),__visibility__("hidden"),__aligned__(1))) const void* __start_##name = (void*)0xdeadbeef; \ - -extern "C" { -DECLARE_SWIFT_SECTION(swift5_protocols) -DECLARE_SWIFT_SECTION(swift5_protocol_conformances) -DECLARE_SWIFT_SECTION(swift5_type_metadata) - -DECLARE_SWIFT_SECTION(swift5_typeref) -DECLARE_SWIFT_SECTION(swift5_reflstr) -DECLARE_SWIFT_SECTION(swift5_fieldmd) -DECLARE_SWIFT_SECTION(swift5_assocty) -DECLARE_SWIFT_SECTION(swift5_replace) -DECLARE_SWIFT_SECTION(swift5_replac2) -} From 92ff3cf99594ba2f58e6dc0d5926ab1467f04fc7 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 12 Nov 2019 14:55:50 +0000 Subject: [PATCH 458/478] Move CI build commands to separate scripts (#13) This allows running build commands in a reproducible way either locally or with any other CI. * Move CI build commands to separate scripts * Run CI on pushes to `swiftwasm` branch * Split build and setup steps into separate scrtipts * Fix execution directories in the build scripts --- .github/workflows/main.yml | 97 ++------------------------------------ build-linux.sh | 25 ++++++++++ build-mac.sh | 25 ++++++++++ ci-linux.sh | 33 +++++++++++++ ci-mac.sh | 20 ++++++++ 5 files changed, 108 insertions(+), 92 deletions(-) create mode 100755 build-linux.sh create mode 100755 build-mac.sh create mode 100755 ci-linux.sh create mode 100755 ci-mac.sh diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0950dff14adfa..9ca46da5c1f08 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,9 @@ name: CI on: + push: + branches: + - swiftwasm pull_request: branches: - swiftwasm @@ -13,59 +16,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Run a multi-line script - run: | - sudo apt update - sudo apt install \ - git ninja-build clang python \ - uuid-dev libicu-dev icu-devtools libbsd-dev \ - libedit-dev libxml2-dev libsqlite3-dev swig \ - libpython-dev libncurses5-dev pkg-config \ - libblocksruntime-dev libcurl4-openssl-dev \ - systemtap-sdt-dev tzdata rsync - - ./utils/update-checkout --clone --scheme wasm - git checkout $GITHUB_SHA - export sourcedir=$PWD/.. - cd $sourcedir - - wget -O install_cmake.sh "https://github.com/Kitware/CMake/releases/download/v3.15.3/cmake-3.15.3-Linux-x86_64.sh" - chmod +x install_cmake.sh - sudo mkdir -p /opt/cmake - sudo ./install_cmake.sh --skip-license --prefix=/opt/cmake - sudo ln -sf /opt/cmake/bin/* /usr/local/bin - cmake --version - - wget -O wasi-sdk.tar.gz https://github.com/swiftwasm/wasi-sdk/releases/download/20190421.6/wasi-sdk-3.19gefb17cb478f9.m-linux.tar.gz - tar xfz wasi-sdk.tar.gz - mv wasi-sdk-3.19gefb17cb478f9+m/opt/wasi-sdk ./wasi-sdk - - wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" - tar xf icu.tar.xz - - cd swift - utils/build-script --release --wasm --verbose \ - --skip-build-benchmarks \ - --extra-cmake-options=" \ - -DSWIFT_SDKS='WASM;LINUX' \ - -DSWIFT_BUILD_SOURCEKIT=FALSE \ - -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ - -DCMAKE_AR='$sourcedir/wasi-sdk/bin/llvm-ar' \ - -DCMAKE_RANLIB='$sourcedir/wasi-sdk/bin/llvm-ranlib' \ - " \ - --build-stdlib-deployment-targets "wasm-wasm32" \ - --build-swift-static-stdlib \ - --install-destdir="$sourcedir/install" \ - --install-prefix="/opt/swiftwasm-sdk" \ - --install-swift \ - --installable-package="$sourcedir/swiftwasm.tar.gz" \ - --llvm-targets-to-build "X86;WebAssembly" \ - --stdlib-deployment-targets "wasm-wasm32" \ - --wasm-icu-data "todo-icu-data" \ - --wasm-icu-i18n "$sourcedir/icu_out/lib" \ - --wasm-icu-i18n-include "$sourcedir/icu_out/include" \ - --wasm-icu-uc "$sourcedir/icu_out/lib" \ - --wasm-icu-uc-include "$sourcedir/icu_out/include" \ - --wasm-wasi-sdk "$sourcedir/wasi-sdk" + run: ./ci-linux.sh macos_build: timeout-minutes: 0 @@ -74,42 +25,4 @@ jobs: steps: - uses: actions/checkout@v1 - name: Run a multi-line script - run: | - brew install cmake ninja llvm - ./utils/update-checkout --clone --scheme wasm - git checkout $GITHUB_SHA - export sourcedir=$PWD/.. - cd $sourcedir - wget -O wasi-sdk.tar.gz https://github.com/swiftwasm/wasi-sdk/releases/download/20190421.6/wasi-sdk-3.19gefb17cb478f9.m-linux.tar.gz - tar xfz wasi-sdk.tar.gz - mv wasi-sdk-3.19gefb17cb478f9+m/opt/wasi-sdk ./wasi-sdk - # Link sysroot/usr/include to sysroot/include because Darwin sysroot doesn't - # find header files in sysroot/include but sysroot/usr/include - mkdir wasi-sdk/share/sysroot/usr/ - ln -s ../include wasi-sdk/share/sysroot/usr/include - wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" - tar xf icu.tar.xz - cd swift - ./utils/build-script --release --wasm --verbose \ - --skip-build-benchmarks \ - --extra-cmake-options=" \ - -DSWIFT_PRIMARY_VARIANT_SDK:STRING=WASM \ - -DSWIFT_PRIMARY_VARIANT_ARCH:STRING=wasm32 \ - -DSWIFT_OSX_x86_64_ICU_STATICLIB=TRUE \ - -DSWIFT_BUILD_SOURCEKIT=FALSE \ - -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ - -DCMAKE_AR='/usr/local/opt/llvm/bin/llvm-ar' \ - -DCMAKE_RANLIB='/usr/local/opt/llvm/bin/llvm-ranlib' \ - " \ - --build-stdlib-deployment-targets "wasm-wasm32" \ - --build-swift-dynamic-sdk-overlay false \ - --build-swift-static-sdk-overlay false \ - --build-swift-static-stdlib \ - --llvm-targets-to-build "X86;WebAssembly" \ - --stdlib-deployment-targets "wasm-wasm32" \ - --wasm-icu-data "todo-icu-data" \ - --wasm-icu-i18n "$sourcedir/icu_out/lib" \ - --wasm-icu-i18n-include "$sourcedir/icu_out/include" \ - --wasm-icu-uc "$sourcedir/icu_out/lib" \ - --wasm-icu-uc-include "$sourcedir/icu_out/include" \ - --wasm-wasi-sdk "$sourcedir/wasi-sdk" + run: ./ci-mac.sh diff --git a/build-linux.sh b/build-linux.sh new file mode 100755 index 0000000000000..7d91a1abd9b57 --- /dev/null +++ b/build-linux.sh @@ -0,0 +1,25 @@ +#/bin/bash + +utils/build-script --release --wasm --verbose \ + --skip-build-benchmarks \ + --extra-cmake-options=" \ + -DSWIFT_SDKS='WASM;LINUX' \ + -DSWIFT_BUILD_SOURCEKIT=FALSE \ + -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ + -DCMAKE_AR='$sourcedir/wasi-sdk/bin/llvm-ar' \ + -DCMAKE_RANLIB='$sourcedir/wasi-sdk/bin/llvm-ranlib' \ + " \ + --build-stdlib-deployment-targets "wasm-wasm32" \ + --build-swift-static-stdlib \ + --install-destdir="$sourcedir/install" \ + --install-prefix="/opt/swiftwasm-sdk" \ + --install-swift \ + --installable-package="$sourcedir/swiftwasm.tar.gz" \ + --llvm-targets-to-build "X86;WebAssembly" \ + --stdlib-deployment-targets "wasm-wasm32" \ + --wasm-icu-data "todo-icu-data" \ + --wasm-icu-i18n "$sourcedir/icu_out/lib" \ + --wasm-icu-i18n-include "$sourcedir/icu_out/include" \ + --wasm-icu-uc "$sourcedir/icu_out/lib" \ + --wasm-icu-uc-include "$sourcedir/icu_out/include" \ + --wasm-wasi-sdk "$sourcedir/wasi-sdk" diff --git a/build-mac.sh b/build-mac.sh new file mode 100755 index 0000000000000..741b394981492 --- /dev/null +++ b/build-mac.sh @@ -0,0 +1,25 @@ +#/bin/bash + +./utils/build-script --release --wasm --verbose \ + --skip-build-benchmarks \ + --extra-cmake-options=" \ + -DSWIFT_PRIMARY_VARIANT_SDK:STRING=WASM \ + -DSWIFT_PRIMARY_VARIANT_ARCH:STRING=wasm32 \ + -DSWIFT_OSX_x86_64_ICU_STATICLIB=TRUE \ + -DSWIFT_BUILD_SOURCEKIT=FALSE \ + -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ + -DCMAKE_AR='/usr/local/opt/llvm/bin/llvm-ar' \ + -DCMAKE_RANLIB='/usr/local/opt/llvm/bin/llvm-ranlib' \ + " \ + --build-stdlib-deployment-targets "wasm-wasm32" \ + --build-swift-dynamic-sdk-overlay false \ + --build-swift-static-sdk-overlay false \ + --build-swift-static-stdlib \ + --llvm-targets-to-build "X86;WebAssembly" \ + --stdlib-deployment-targets "wasm-wasm32" \ + --wasm-icu-data "todo-icu-data" \ + --wasm-icu-i18n "$sourcedir/icu_out/lib" \ + --wasm-icu-i18n-include "$sourcedir/icu_out/include" \ + --wasm-icu-uc "$sourcedir/icu_out/lib" \ + --wasm-icu-uc-include "$sourcedir/icu_out/include" \ + --wasm-wasi-sdk "$sourcedir/wasi-sdk" diff --git a/ci-linux.sh b/ci-linux.sh new file mode 100755 index 0000000000000..18953fb7ce1f5 --- /dev/null +++ b/ci-linux.sh @@ -0,0 +1,33 @@ +#/bin/bash + +sudo apt update +sudo apt install \ + git ninja-build clang python \ + uuid-dev libicu-dev icu-devtools libbsd-dev \ + libedit-dev libxml2-dev libsqlite3-dev swig \ + libpython-dev libncurses5-dev pkg-config \ + libblocksruntime-dev libcurl4-openssl-dev \ + systemtap-sdt-dev tzdata rsync + +export current_sha=`git rev-parse HEAD` +./utils/update-checkout --clone --scheme wasm +git checkout $current_sha +export sourcedir=$PWD/.. +cd $sourcedir + +wget -O install_cmake.sh "https://github.com/Kitware/CMake/releases/download/v3.15.3/cmake-3.15.3-Linux-x86_64.sh" +chmod +x install_cmake.sh +sudo mkdir -p /opt/cmake +sudo ./install_cmake.sh --skip-license --prefix=/opt/cmake +sudo ln -sf /opt/cmake/bin/* /usr/local/bin +cmake --version + +wget -O wasi-sdk.tar.gz https://github.com/swiftwasm/wasi-sdk/releases/download/20190421.6/wasi-sdk-3.19gefb17cb478f9.m-linux.tar.gz +tar xfz wasi-sdk.tar.gz +mv wasi-sdk-3.19gefb17cb478f9+m/opt/wasi-sdk ./wasi-sdk + +wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" +tar xf icu.tar.xz + +cd swift +./build-linux.sh diff --git a/ci-mac.sh b/ci-mac.sh new file mode 100755 index 0000000000000..dc8bab5527073 --- /dev/null +++ b/ci-mac.sh @@ -0,0 +1,20 @@ +#/bin/bash + +brew install cmake ninja llvm +export current_sha=`git rev-parse HEAD` +./utils/update-checkout --clone --scheme wasm +git checkout $current_sha +export sourcedir=$PWD/.. +cd $sourcedir +wget -O wasi-sdk.tar.gz https://github.com/swiftwasm/wasi-sdk/releases/download/20190421.6/wasi-sdk-3.19gefb17cb478f9.m-linux.tar.gz +tar xfz wasi-sdk.tar.gz +mv wasi-sdk-3.19gefb17cb478f9+m/opt/wasi-sdk ./wasi-sdk +# Link sysroot/usr/include to sysroot/include because Darwin sysroot doesn't +# find header files in sysroot/include but sysroot/usr/include +mkdir wasi-sdk/share/sysroot/usr/ +ln -s ../include wasi-sdk/share/sysroot/usr/include +wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" +tar xf icu.tar.xz + +cd swift +./build-mac.sh From 72845a9008714ddc6f83b6689a0674994678ba32 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 12 Nov 2019 14:59:29 +0000 Subject: [PATCH 459/478] Install wget in ci-linux.sh, make path explicit in build-linux.sh --- build-linux.sh | 2 +- ci-linux.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-linux.sh b/build-linux.sh index 7d91a1abd9b57..720a67971c21e 100755 --- a/build-linux.sh +++ b/build-linux.sh @@ -1,6 +1,6 @@ #/bin/bash -utils/build-script --release --wasm --verbose \ +./utils/build-script --release --wasm --verbose \ --skip-build-benchmarks \ --extra-cmake-options=" \ -DSWIFT_SDKS='WASM;LINUX' \ diff --git a/ci-linux.sh b/ci-linux.sh index 18953fb7ce1f5..1c8da320bc53f 100755 --- a/ci-linux.sh +++ b/ci-linux.sh @@ -7,7 +7,7 @@ sudo apt install \ libedit-dev libxml2-dev libsqlite3-dev swig \ libpython-dev libncurses5-dev pkg-config \ libblocksruntime-dev libcurl4-openssl-dev \ - systemtap-sdt-dev tzdata rsync + systemtap-sdt-dev tzdata rsync wget export current_sha=`git rev-parse HEAD` ./utils/update-checkout --clone --scheme wasm From 78679791a56d00a1cf85eb9fa459ec26eebcca9a Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 14 Nov 2019 08:17:57 +0000 Subject: [PATCH 460/478] Update WASI SDK, create installable package on macOS (#14) New WASI SDK package should contain a new version of the linker. Also, the script should now create an installable package on macOS so that one could create a full SwiftWasm package later after the build. * Update WASI SDK, create installable package on macOS * Fix sysroot path, update wasi-sdk on Linux * Exclude module net for wasm SDK in glibc.modulemap * Remove module termios from glic.modulemap for wasm * Disable _stdlib_mkstemps for wasm --- build-linux.sh | 2 ++ build-mac.sh | 8 +++++++- ci-linux.sh | 5 +++-- ci-mac.sh | 5 +++-- .../SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift | 2 +- stdlib/public/Platform/glibc.modulemap.gyb | 4 ++++ 6 files changed, 20 insertions(+), 6 deletions(-) diff --git a/build-linux.sh b/build-linux.sh index 720a67971c21e..285a5b05a3c9c 100755 --- a/build-linux.sh +++ b/build-linux.sh @@ -1,5 +1,7 @@ #/bin/bash +export sourcedir=$PWD/.. + ./utils/build-script --release --wasm --verbose \ --skip-build-benchmarks \ --extra-cmake-options=" \ diff --git a/build-mac.sh b/build-mac.sh index 741b394981492..2751f98df442c 100755 --- a/build-mac.sh +++ b/build-mac.sh @@ -1,5 +1,7 @@ #/bin/bash +export sourcedir=$PWD/.. + ./utils/build-script --release --wasm --verbose \ --skip-build-benchmarks \ --extra-cmake-options=" \ @@ -22,4 +24,8 @@ --wasm-icu-i18n-include "$sourcedir/icu_out/include" \ --wasm-icu-uc "$sourcedir/icu_out/lib" \ --wasm-icu-uc-include "$sourcedir/icu_out/include" \ - --wasm-wasi-sdk "$sourcedir/wasi-sdk" + --wasm-wasi-sdk "$sourcedir/wasi-sdk" \ + --install-swift \ + --install-prefix="/opt/swiftwasm-sdk" \ + --install-destdir="$sourcedir/install" \ + --installable-package="$sourcedir/swiftwasm-mac.tar.gz" diff --git a/ci-linux.sh b/ci-linux.sh index 1c8da320bc53f..467e38b1f1403 100755 --- a/ci-linux.sh +++ b/ci-linux.sh @@ -22,9 +22,10 @@ sudo ./install_cmake.sh --skip-license --prefix=/opt/cmake sudo ln -sf /opt/cmake/bin/* /usr/local/bin cmake --version -wget -O wasi-sdk.tar.gz https://github.com/swiftwasm/wasi-sdk/releases/download/20190421.6/wasi-sdk-3.19gefb17cb478f9.m-linux.tar.gz +wget -O wasi-sdk.tar.gz https://github.com/swiftwasm/wasi-sdk/releases/download/20191022.1/wasi-sdk-4.39g3025a5f47c04-linux.tar.gz tar xfz wasi-sdk.tar.gz -mv wasi-sdk-3.19gefb17cb478f9+m/opt/wasi-sdk ./wasi-sdk +mv wasi-sdk-4.39g3025a5f47c04 ./wasi-sdk +mv wasi-sdk/share/wasi-sysroot wasi-sdk/share/sysroot wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" tar xf icu.tar.xz diff --git a/ci-mac.sh b/ci-mac.sh index dc8bab5527073..d3a08d7164af9 100755 --- a/ci-mac.sh +++ b/ci-mac.sh @@ -6,9 +6,10 @@ export current_sha=`git rev-parse HEAD` git checkout $current_sha export sourcedir=$PWD/.. cd $sourcedir -wget -O wasi-sdk.tar.gz https://github.com/swiftwasm/wasi-sdk/releases/download/20190421.6/wasi-sdk-3.19gefb17cb478f9.m-linux.tar.gz +wget -O wasi-sdk.tar.gz https://github.com/swiftwasm/wasi-sdk/releases/download/20191022.1/wasi-sdk-4.39g3025a5f47c04-linux.tar.gz tar xfz wasi-sdk.tar.gz -mv wasi-sdk-3.19gefb17cb478f9+m/opt/wasi-sdk ./wasi-sdk +mv wasi-sdk-4.39g3025a5f47c04 ./wasi-sdk +mv wasi-sdk/share/wasi-sysroot wasi-sdk/share/sysroot # Link sysroot/usr/include to sysroot/include because Darwin sysroot doesn't # find header files in sysroot/include but sysroot/usr/include mkdir wasi-sdk/share/sysroot/usr/ diff --git a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift index 8c09674b4faae..6042ba6550ad8 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift @@ -20,7 +20,7 @@ import MSVCRT #endif public func _stdlib_mkstemps(_ template: inout String, _ suffixlen: CInt) -> CInt { -#if os(Android) || os(Haiku) || os(Windows) +#if os(Android) || os(Haiku) || os(Windows) || os(Wasm) preconditionFailure("mkstemps doesn't work on your platform") #else var utf8CStr = template.utf8CString diff --git a/stdlib/public/Platform/glibc.modulemap.gyb b/stdlib/public/Platform/glibc.modulemap.gyb index 97c52fe1ce4fa..eba7fde86588a 100644 --- a/stdlib/public/Platform/glibc.modulemap.gyb +++ b/stdlib/public/Platform/glibc.modulemap.gyb @@ -353,12 +353,14 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/libgen.h" export * } +% if CMAKE_SDK != "WASM": module net { module if { header "${GLIBC_INCLUDE_PATH}/net/if.h" export * } } +% end module netinet { module in { header "${GLIBC_INCLUDE_PATH}/netinet/in.h" @@ -517,10 +519,12 @@ module SwiftGlibc [system] { export * } % end +% if CMAKE_SDK != "WASM": module termios { header "${GLIBC_INCLUDE_PATH}/termios.h" export * } +% end module unistd { header "${GLIBC_INCLUDE_PATH}/unistd.h" export * From 4faa5d429d543195047776104fdb073fdbd825c5 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 15 Nov 2019 12:36:29 +0000 Subject: [PATCH 461/478] Make SymbolLookup.swift compilable for wasm (#16) `SymbolLookup.swift` was added recently, which broke our builds after rebasing on top of Apple's `master`. Using anything from this file wouldn't make sense on WebAssembly. It looks there are no codepaths that would call it on that platform, but there are still references to it around, so it has to be at least compilable. --- stdlib/private/StdlibUnittest/SymbolLookup.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stdlib/private/StdlibUnittest/SymbolLookup.swift b/stdlib/private/StdlibUnittest/SymbolLookup.swift index 2e9c627487a4f..6967c11327e0e 100644 --- a/stdlib/private/StdlibUnittest/SymbolLookup.swift +++ b/stdlib/private/StdlibUnittest/SymbolLookup.swift @@ -12,7 +12,7 @@ #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) import Glibc #elseif os(Windows) import MSVCRT @@ -23,7 +23,7 @@ #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2) -#elseif os(Linux) +#elseif os(Linux) || os(Wasm) let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: 0) #elseif os(Android) #if arch(arm) || arch(i386) @@ -43,6 +43,8 @@ public func pointerToSwiftCoreSymbol(name: String) -> UnsafeMutableRawPointer? { #if os(Windows) return unsafeBitCast(GetProcAddress(hStdlibCore, name), to: UnsafeMutableRawPointer?.self) +#elseif os(Wasm) + fatalError("\(#function) is not supported on WebAssembly") #else return dlsym(RTLD_DEFAULT, name) #endif From 0a6058de667c21c20eb6f48df19cea62299fe374 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sat, 16 Nov 2019 09:52:08 +0000 Subject: [PATCH 462/478] Run packaging scripts and smoke test on CI (#15) Any changes in this repository should be tested with the packaging script and a basic smoke test that compiles `hello.swift` on CI. In the future packaging and linking scripts should be moved to this repository, `swiftwasm-sdk` and `swiftwasm-package-sdk` projects probably would be archived when that happens. For now we're pulling the scripts from `swiftwasm-package-sdk` as it is. * Run packaging scripts and smoke test on CI * Make prepare-package.sh executable * Make SymbolLookup.swift compilable for wasm * Use GitHub Actions upload/download steps * Remove packaging steps from ci-*.sh * Move prepare-package.sh to main.yml to avoid clone * Refine formatting in .github/workflows/main.yml --- .github/workflows/main.yml | 46 ++++++++++++++++++++++++++++++++++++-- build-linux.sh | 2 +- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9ca46da5c1f08..6ece20e7139ad 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,8 +15,13 @@ jobs: steps: - uses: actions/checkout@v1 - - name: Run a multi-line script + - name: Build Linux installable archive run: ./ci-linux.sh + - name: Upload Linux installable archive + uses: actions/upload-artifact@v1 + with: + name: linux-installable + path: ../swiftwasm-linux.tar.gz macos_build: timeout-minutes: 0 @@ -24,5 +29,42 @@ jobs: steps: - uses: actions/checkout@v1 - - name: Run a multi-line script + - name: Build macOS installable archive run: ./ci-mac.sh + - name: Upload macOS installable archive + uses: actions/upload-artifact@v1 + with: + name: macos-installable + path: ../swiftwasm-mac.tar.gz + + package: + name: Build SwiftWasm packages + needs: + - linux_build + - macos_build + runs-on: ubuntu-18.04 + steps: + - name: Download installable Linux archive + uses: actions/download-artifact@v1 + with: + name: linux-installable + - name: Download installable macOS archive + uses: actions/download-artifact@v1 + with: + name: macos-installable + - name: Build the packages + shell: bash + run: | + git clone https://github.com/swiftwasm/swiftwasm-package-sdk.git + cd swiftwasm-package-sdk + ./download-prebuilts.sh + + cp ../linux-installable/swiftwasm-linux.tar.gz prebuilt/swiftwasm.tar.gz + cp ../macos-installable/swiftwasm-mac.tar.gz prebuilt/swiftwasm-mac.tar.gz + ./build-packages.sh + + cd output + tar xf swiftwasm-sdk-linux.tar.xz + + cd swiftwasm-sdk + ./swiftwasm example/hello.swift hello.wasm diff --git a/build-linux.sh b/build-linux.sh index 285a5b05a3c9c..30285c85a2dc9 100755 --- a/build-linux.sh +++ b/build-linux.sh @@ -16,7 +16,7 @@ export sourcedir=$PWD/.. --install-destdir="$sourcedir/install" \ --install-prefix="/opt/swiftwasm-sdk" \ --install-swift \ - --installable-package="$sourcedir/swiftwasm.tar.gz" \ + --installable-package="$sourcedir/swiftwasm-linux.tar.gz" \ --llvm-targets-to-build "X86;WebAssembly" \ --stdlib-deployment-targets "wasm-wasm32" \ --wasm-icu-data "todo-icu-data" \ From d9c832c530290f2d4467b3956dccf2e187261a10 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sun, 17 Nov 2019 09:52:55 +0000 Subject: [PATCH 463/478] Upload SwiftWasm artifacts for later inspection (#17) It would be convenient if packaged SDK and result of the smoke test were uploaded as artifacts of the CI run and become downloadable for later use. * Add more logging to the smoke test * Add a newline to main.yml * Upload packages and hello.wasm as CI artifacts * Normalize filenames for swiftwasm-package-sdk * Add macos_smoke_test job --- .github/workflows/main.yml | 54 ++++++++++++++++++++++++++++++++++---- build-mac.sh | 2 +- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6ece20e7139ad..8c81e1643c433 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,7 +35,7 @@ jobs: uses: actions/upload-artifact@v1 with: name: macos-installable - path: ../swiftwasm-mac.tar.gz + path: ../swiftwasm-macos.tar.gz package: name: Build SwiftWasm packages @@ -59,12 +59,56 @@ jobs: cd swiftwasm-package-sdk ./download-prebuilts.sh - cp ../linux-installable/swiftwasm-linux.tar.gz prebuilt/swiftwasm.tar.gz - cp ../macos-installable/swiftwasm-mac.tar.gz prebuilt/swiftwasm-mac.tar.gz + cp ../linux-installable/swiftwasm-linux.tar.gz \ + ../macos-installable/swiftwasm-macos.tar.gz \ + prebuilt ./build-packages.sh cd output - tar xf swiftwasm-sdk-linux.tar.xz + tar xf swiftwasm-sdk-linux.tar.xz && echo "Successfully unpacked Linux SDK" cd swiftwasm-sdk - ./swiftwasm example/hello.swift hello.wasm + ./swiftwasm example/hello.swift hello.wasm && echo "Successfully linked hello.wasm" + + - name: Upload macOS package + uses: actions/upload-artifact@v1 + with: + name: macos-package + path: swiftwasm-package-sdk/output/swiftwasm-sdk-macos.tar.xz + + - name: Upload Linux package + uses: actions/upload-artifact@v1 + with: + name: linux-package + path: swiftwasm-package-sdk/output/swiftwasm-sdk-linux.tar.xz + + - name: Upload hello.wasm compiled with Linux package + uses: actions/upload-artifact@v1 + with: + name: linux-hello.wasm + path: swiftwasm-package-sdk/output/swiftwasm-sdk/hello.wasm + + macos_smoke_test: + name: Compile hello.swift on macOS + runs-on: macOS-10.14 + needs: package + steps: + - name: Download SwiftWasm macOS package + uses: actions/download-artifact@v1 + with: + name: macos-package + + - name: Build hello.wasm + shell: bash + run: | + cd macos-package + tar xf swiftwasm-sdk-macos.tar.xz && echo "Successfully unpacked macOS SDK" + + cd swiftwasm-sdk + ./swiftwasm example/hello.swift hello.wasm && echo "Successfully linked hello.wasm" + + - name: Upload hello.wasm compiled with macOS package + uses: actions/upload-artifact@v1 + with: + name: macos-hello.wasm + path: macos-package/swiftwasm-sdk/hello.wasm diff --git a/build-mac.sh b/build-mac.sh index 2751f98df442c..aee098f9703cc 100755 --- a/build-mac.sh +++ b/build-mac.sh @@ -28,4 +28,4 @@ export sourcedir=$PWD/.. --install-swift \ --install-prefix="/opt/swiftwasm-sdk" \ --install-destdir="$sourcedir/install" \ - --installable-package="$sourcedir/swiftwasm-mac.tar.gz" + --installable-package="$sourcedir/swiftwasm-macos.tar.gz" From 9e868c8725c62d70a41831231e22804859cec261 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 19 Nov 2019 13:16:40 +0000 Subject: [PATCH 464/478] Move packaging scripts from swiftwasm-package-sdk (#18) Currently our CI scripts rely on validity of scripts in the [`swiftwasm-package-sdk`](https://github.com/swiftwasm/swiftwasm-package-sdk) repository. That can't always be the case, especialy as we don't checkout a specific commit in that repository. And even if we did, managing this isn't convenient. Adding a submodule has its own set of issues and I personally think that a monorepo approach is much simpler for a small set of scripts, at least at an early stage. In fact, it would make sense to have these scripts in the upstream [`swift`](https://github.com/apple/swift) repository at some point, similarly to [what Android people have previously done](https://github.com/apple/swift/tree/master/utils/android). Thus, these scripts and a few small helper files are copied to `utils/webassembly` directory and are executed directly on CI. After this PR is merged, I don't see a particular need for our [`swiftwasm-package-sdk`](https://github.com/swiftwasm/swiftwasm-package-sdk) and [`swiftwasm-sdk`](https://github.com/swiftwasm/swiftwasm-sdk) repos, those probably can be archived. As a small cleanup addition, `.github/workflows/main.yml` file now has consistent indentation. * Move packaging scripts from swiftwasm-package-sdk * Rename `wasm` directory to `webassembly` * Make all .sh scripts executable after download * Make sdkroot/swiftwasm script executable * Add newline to build-mac-package.sh * Add newline to build-packages.sh * Remove swift_start.o and swift_end.o --- .github/workflows/main.yml | 60 +++++++----- utils/webassembly/.gitignore | 5 + utils/webassembly/README.md | 23 +++++ utils/webassembly/build-linux-package.sh | 9 ++ utils/webassembly/build-mac-package.sh | 9 ++ utils/webassembly/build-packages.sh | 5 + utils/webassembly/copy-shared-files.sh | 4 + .../download-installable-prebuilts.sh | 7 ++ utils/webassembly/download-prebuilts.sh | 8 ++ utils/webassembly/linux/unpack-prebuilts.sh | 20 ++++ utils/webassembly/macos/unpack-prebuilts.sh | 30 ++++++ utils/webassembly/remove-swift-extra-files.sh | 26 ++++++ utils/webassembly/remove-wasi-extra-files.sh | 10 ++ utils/webassembly/sdkroot/README.md | 87 ++++++++++++++++++ utils/webassembly/sdkroot/example/hello.swift | 1 + .../sdkroot/extra_objs/fakelocaltime.o | Bin 0 -> 423 bytes .../sdkroot/extra_objs/fakepthread.o | Bin 0 -> 4061 bytes .../sdkroot/extra_utils/generateModulemap.sh | 2 + utils/webassembly/sdkroot/swiftwasm | 40 ++++++++ 19 files changed, 321 insertions(+), 25 deletions(-) create mode 100644 utils/webassembly/.gitignore create mode 100644 utils/webassembly/README.md create mode 100755 utils/webassembly/build-linux-package.sh create mode 100755 utils/webassembly/build-mac-package.sh create mode 100755 utils/webassembly/build-packages.sh create mode 100755 utils/webassembly/copy-shared-files.sh create mode 100755 utils/webassembly/download-installable-prebuilts.sh create mode 100755 utils/webassembly/download-prebuilts.sh create mode 100755 utils/webassembly/linux/unpack-prebuilts.sh create mode 100755 utils/webassembly/macos/unpack-prebuilts.sh create mode 100755 utils/webassembly/remove-swift-extra-files.sh create mode 100755 utils/webassembly/remove-wasi-extra-files.sh create mode 100644 utils/webassembly/sdkroot/README.md create mode 100644 utils/webassembly/sdkroot/example/hello.swift create mode 100644 utils/webassembly/sdkroot/extra_objs/fakelocaltime.o create mode 100644 utils/webassembly/sdkroot/extra_objs/fakepthread.o create mode 100755 utils/webassembly/sdkroot/extra_utils/generateModulemap.sh create mode 100755 utils/webassembly/sdkroot/swiftwasm diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8c81e1643c433..46d9785712789 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,10 +3,10 @@ name: CI on: push: branches: - - swiftwasm + - swiftwasm pull_request: branches: - - swiftwasm + - swiftwasm jobs: linux_build: @@ -14,34 +14,39 @@ jobs: runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v1 - - name: Build Linux installable archive - run: ./ci-linux.sh - - name: Upload Linux installable archive - uses: actions/upload-artifact@v1 - with: - name: linux-installable - path: ../swiftwasm-linux.tar.gz + - uses: actions/checkout@v1 + - name: Build Linux installable archive + run: ./ci-linux.sh + - name: Upload Linux installable archive + uses: actions/upload-artifact@v1 + with: + name: linux-installable + path: ../swiftwasm-linux.tar.gz macos_build: timeout-minutes: 0 runs-on: macOS-10.14 steps: - - uses: actions/checkout@v1 - - name: Build macOS installable archive - run: ./ci-mac.sh - - name: Upload macOS installable archive - uses: actions/upload-artifact@v1 - with: - name: macos-installable - path: ../swiftwasm-macos.tar.gz + - uses: actions/checkout@v1 + - name: Build macOS installable archive + run: ./ci-mac.sh + - name: Upload macOS installable archive + uses: actions/upload-artifact@v1 + with: + name: macos-installable + path: ../swiftwasm-macos.tar.gz + - name: Upload packaging scripts + uses: actions/upload-artifact@v1 + with: + name: packaging-scripts + path: utils/webassembly package: name: Build SwiftWasm packages needs: - - linux_build - - macos_build + - linux_build + - macos_build runs-on: ubuntu-18.04 steps: - name: Download installable Linux archive @@ -52,11 +57,16 @@ jobs: uses: actions/download-artifact@v1 with: name: macos-installable + - name: Download packaging scripts + uses: actions/download-artifact@v1 + with: + name: packaging-scripts - name: Build the packages shell: bash run: | - git clone https://github.com/swiftwasm/swiftwasm-package-sdk.git - cd swiftwasm-package-sdk + cd packaging-scripts + find . -name '*.sh' -exec chmod +x {} \; + chmod +x sdkroot/swiftwasm ./download-prebuilts.sh cp ../linux-installable/swiftwasm-linux.tar.gz \ @@ -74,19 +84,19 @@ jobs: uses: actions/upload-artifact@v1 with: name: macos-package - path: swiftwasm-package-sdk/output/swiftwasm-sdk-macos.tar.xz + path: packaging-scripts/output/swiftwasm-sdk-macos.tar.xz - name: Upload Linux package uses: actions/upload-artifact@v1 with: name: linux-package - path: swiftwasm-package-sdk/output/swiftwasm-sdk-linux.tar.xz + path: packaging-scripts/output/swiftwasm-sdk-linux.tar.xz - name: Upload hello.wasm compiled with Linux package uses: actions/upload-artifact@v1 with: name: linux-hello.wasm - path: swiftwasm-package-sdk/output/swiftwasm-sdk/hello.wasm + path: packaging-scripts/output/swiftwasm-sdk/hello.wasm macos_smoke_test: name: Compile hello.swift on macOS diff --git a/utils/webassembly/.gitignore b/utils/webassembly/.gitignore new file mode 100644 index 0000000000000..411f44532e7bc --- /dev/null +++ b/utils/webassembly/.gitignore @@ -0,0 +1,5 @@ +compiler +swiftwasm-sdk +prebuilt +output +tmpdir diff --git a/utils/webassembly/README.md b/utils/webassembly/README.md new file mode 100644 index 0000000000000..106f4fc35d84d --- /dev/null +++ b/utils/webassembly/README.md @@ -0,0 +1,23 @@ +Creates packages containing everything needed to build WebAssembly programs with Swift. + +# Building + +``` +./download-prebuilts.sh +./download-installable-prebuilts.sh +./build-packages.sh +``` + +# Contents of package + +- Swift toolchain from [swiftwasm-sdk](https://github.com/swiftwasm/swiftwasm-sdk) +- WASI modified sysroot from [wasi-sdk](https://github.com/swiftwasm/wasi-sdk) +- libicu from [icu4c-wasi](https://github.com/swiftwasm/icu4c-wasi) +- linking helpers from [swiftwasm-wasi-stubs](https://github.com/swiftwasm/swiftwasm-wasi-stubs) +- wasi-ld, either from wasi-sdk (on Linux) or upstream LLVM 9.0 (on Mac) +- build script for compiling a Swift file to a .wasm +- a Getting Started guide + +# Notes + +This shares a lot with the [swiftwasm-compile-service](https://github.com/swiftwasm/swiftwasm-compile-service). diff --git a/utils/webassembly/build-linux-package.sh b/utils/webassembly/build-linux-package.sh new file mode 100755 index 0000000000000..685d5c247a18e --- /dev/null +++ b/utils/webassembly/build-linux-package.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +echo "Unpacking Linux prebuilts" +mkdir -p output +cd linux +./unpack-prebuilts.sh +echo "Compressing" +tar cJf ../output/swiftwasm-sdk-linux.tar.xz swiftwasm-sdk +cd .. diff --git a/utils/webassembly/build-mac-package.sh b/utils/webassembly/build-mac-package.sh new file mode 100755 index 0000000000000..cd3c2602e8b5d --- /dev/null +++ b/utils/webassembly/build-mac-package.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +echo "Unpacking macOS prebuilts" +mkdir -p output +cd macos +./unpack-prebuilts.sh +echo "Compressing macOS package" +tar cJf ../output/swiftwasm-sdk-macos.tar.xz swiftwasm-sdk +cd .. diff --git a/utils/webassembly/build-packages.sh b/utils/webassembly/build-packages.sh new file mode 100755 index 0000000000000..e83fc38beb5d6 --- /dev/null +++ b/utils/webassembly/build-packages.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e +rm -rf output || true +./build-linux-package.sh +./build-mac-package.sh diff --git a/utils/webassembly/copy-shared-files.sh b/utils/webassembly/copy-shared-files.sh new file mode 100755 index 0000000000000..7e1ce8a81832d --- /dev/null +++ b/utils/webassembly/copy-shared-files.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +cp -r ../sdkroot/* compiler/ +cp ../linux/compiler/opt/swiftwasm-sdk/lib/swift/wasm/wasm32/glibc.modulemap compiler/extra_utils diff --git a/utils/webassembly/download-installable-prebuilts.sh b/utils/webassembly/download-installable-prebuilts.sh new file mode 100755 index 0000000000000..c407580547fc4 --- /dev/null +++ b/utils/webassembly/download-installable-prebuilts.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e +mkdir -p prebuilt +cd prebuilt +wget -O swiftwasm-linux.tar.gz https://github.com/swiftwasm/swiftwasm-sdk/releases/download/20191112.1.linux/swiftwasm.tar.gz +# Mac specific +wget -O swiftwasm-macos.tar.gz https://github.com/swiftwasm/swiftwasm-sdk/releases/download/20191112.1.mac/swiftwasm-mac.tar.gz diff --git a/utils/webassembly/download-prebuilts.sh b/utils/webassembly/download-prebuilts.sh new file mode 100755 index 0000000000000..5ee1c8b3bf756 --- /dev/null +++ b/utils/webassembly/download-prebuilts.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e +mkdir -p prebuilt +cd prebuilt +wget https://github.com/swiftwasm/wasi-sdk/releases/download/20191022.1/wasi-sdk-4.39g3025a5f47c04-linux.tar.gz +wget https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz +# Mac specific +wget http://releases.llvm.org/9.0.0/clang+llvm-9.0.0-x86_64-darwin-apple.tar.xz diff --git a/utils/webassembly/linux/unpack-prebuilts.sh b/utils/webassembly/linux/unpack-prebuilts.sh new file mode 100755 index 0000000000000..a5d5f669aa39f --- /dev/null +++ b/utils/webassembly/linux/unpack-prebuilts.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e +rm -rf swiftwasm-sdk compiler +mkdir swiftwasm-sdk +ln -s swiftwasm-sdk compiler +cd compiler +untar="../../prebuilt/wasi-sdk-"*"-linux.tar.gz +../../prebuilt/swiftwasm-linux.tar.gz +../../prebuilt/icu4c-wasi.tar.xz" +for i in $untar +do + echo $i + tar xf $i +done +cd .. +mv "compiler/wasi-sdk-"* "compiler/wasi-sdk" +mv compiler/wasi-sdk/share/wasi-sysroot compiler/wasi-sdk/share/sysroot +../remove-swift-extra-files.sh || true +../remove-wasi-extra-files.sh || true +../copy-shared-files.sh || true diff --git a/utils/webassembly/macos/unpack-prebuilts.sh b/utils/webassembly/macos/unpack-prebuilts.sh new file mode 100755 index 0000000000000..169dd8cde7ce5 --- /dev/null +++ b/utils/webassembly/macos/unpack-prebuilts.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -e +rm -rf swiftwasm-sdk compiler tmpdir +mkdir swiftwasm-sdk tmpdir +ln -s swiftwasm-sdk compiler +cd compiler +untar="../../prebuilt/wasi-sdk-"*"-linux.tar.gz +../../prebuilt/swiftwasm-macos.tar.gz +../../prebuilt/icu4c-wasi.tar.xz" +for i in $untar +do + echo $i + tar xf $i +done +# Mac: unpack the Linux one and copy stdlibs +cd .. +cd tmpdir +tar xf ../../prebuilt/clang+llvm-*-x86_64-darwin-apple.tar.xz +tar xf ../../prebuilt/swiftwasm-linux.tar.gz +cd .. +mv "compiler/wasi-sdk-"* "compiler/wasi-sdk" +mv compiler/wasi-sdk/share/wasi-sysroot compiler/wasi-sdk/share/sysroot +../remove-swift-extra-files.sh || true +rm -r compiler/wasi-sdk/bin +mkdir compiler/wasi-sdk/bin +cp tmpdir/clang+llvm-*-x86_64-darwin-apple/bin/wasm-ld compiler/wasi-sdk/bin +cp -a tmpdir/opt/swiftwasm-sdk/lib/swift/wasm compiler/opt/swiftwasm-sdk/lib/swift/wasm +cp -a tmpdir/opt/swiftwasm-sdk/lib/swift_static compiler/opt/swiftwasm-sdk/lib/swift_static +# ok, finally copy over the shared files +../copy-shared-files.sh || true diff --git a/utils/webassembly/remove-swift-extra-files.sh b/utils/webassembly/remove-swift-extra-files.sh new file mode 100755 index 0000000000000..2e4e2d541a45f --- /dev/null +++ b/utils/webassembly/remove-swift-extra-files.sh @@ -0,0 +1,26 @@ +#!/bin/bash +basepath="compiler/opt/swiftwasm-sdk" +filestoremove="bin/sil-* +bin/lldb* +bin/sourcekitd-* +bin/swift-api-digester +bin/swift-autolink-extract +bin/swift-demangle +bin/swift-demangle-yamldump +bin/swift-format +bin/swift-llvm-opt +bin/swift-refactor +bin/swift-reflection-dump +bin/swift-*-test +lib/libsourcekitdInProc.so +lib/swift/clang/lib/linux/* +lib/swift_static/linux/* +lib/swift/linux/x86_64/* +lib/swift/linux/*" +for i in $filestoremove +do + echo $basepath/$i + rm $basepath/$i +done +# Mac only +rm -r $basepath/lib/swift/macosx $basepath/lib/sourcekitd.framework diff --git a/utils/webassembly/remove-wasi-extra-files.sh b/utils/webassembly/remove-wasi-extra-files.sh new file mode 100755 index 0000000000000..19ac34009874f --- /dev/null +++ b/utils/webassembly/remove-wasi-extra-files.sh @@ -0,0 +1,10 @@ +#!/bin/bash +basepath="compiler/wasi-sdk" +filestoremove="bin/clang* +bin/llvm* +bin/llc" +for i in $filestoremove +do + echo $basepath/$i + rm $basepath/$i +done diff --git a/utils/webassembly/sdkroot/README.md b/utils/webassembly/sdkroot/README.md new file mode 100644 index 0000000000000..db8048d2a504d --- /dev/null +++ b/utils/webassembly/sdkroot/README.md @@ -0,0 +1,87 @@ +SwiftWasm: Getting started +========================== + +Thank you for trying SwiftWasm! Here's how to get started. + +Please visit our website at https://swiftwasm.org for the latest updates. + + +Install dependencies +==================== + +Before running SwiftWasm, you will need to install some dependencies. + +Ubuntu: + +``` +sudo apt-get install libatomic1 +``` + +macOS: + +(No dependencies needed.) + +Windows: + +Install Windows Subsystem for Linux, then follow the Ubuntu instructions. + + + + +Compile SwiftWasm +================= + +Run + +``` +./swiftwasm example/hello.swift hello.wasm +``` + +To compile example/hello.swift to hello.wasm. + + + + +Running Wasm files +================== + +To run the resulting hello.wasm file: + +- Visit https://swiftwasm.org/polyfill/ +- select "Browse", and choose the hello.wasm file +- you should get output in the textbox. + +This polyfill should work in Firefox 66, Chrome 74, and Safari 12.1. + +You can also run the file outside a browser with: + +- Wasmtime https://github.com/CraneStation/wasmtime +- Lucet https://github.com/swiftwasm/lucet/tree/swiftwasm +- or any other WASI-compatible WebAssembly runtime. + + + + +Questions and support +===================== + +If you have any questions, please open an issue on + +https://github.com/swiftwasm/swift + + + +Third-party licenses +==================== + +This package contains components with their own license requirements. + +Swift compiler: https://github.com/apple/swift/blob/master/LICENSE.txt + +LLVM/Clang: https://github.com/llvm/llvm-project/blob/master/lld/LICENSE.TXT + +WASI sysroot: https://github.com/CraneStation/wasi-sysroot/blob/master/LICENSE + +ICU: https://github.com/unicode-org/icu/blob/master/icu4c/LICENSE + +WASI polyfill: https://github.com/CraneStation/wasmtime/blob/master/wasmtime-wasi/LICENSE diff --git a/utils/webassembly/sdkroot/example/hello.swift b/utils/webassembly/sdkroot/example/hello.swift new file mode 100644 index 0000000000000..e61506705879c --- /dev/null +++ b/utils/webassembly/sdkroot/example/hello.swift @@ -0,0 +1 @@ +print("Hello, 🌐!") diff --git a/utils/webassembly/sdkroot/extra_objs/fakelocaltime.o b/utils/webassembly/sdkroot/extra_objs/fakelocaltime.o new file mode 100644 index 0000000000000000000000000000000000000000..904d3a6dedc6108030be87a4d3012e63e153ed9a GIT binary patch literal 423 zcmb7Au};G<5WTaVCN!0rg#ocRf`O^(#KKeo8xn|x(M^M?OcSSaTqLGM%=|zHet-|) zTVUo#uoGZl!^7Er@7_DTgRCwQ0PwkOTOf06u$hy_Rr2W(Mx}lj3sER7Gb2h_R_2ia zdU#I=rEisyxfORCom*9DVY5XcaSiY-tZkOB#JW=2N<*7Y7#CHZ6;_o}7-#k52O$hU zy88yB=D@vLNO6{=gd-=blyZ95fwP`soN_oOBTkRVC=GbPS<*8DCkh=K-lq|ddJ!Lt zcBFrG3y1@TC?_L|aC9ITQlIYcc88F=WO+petmMkxRl3h!?F&bbB2?X12^zLL) h*OR|31mMNfLnEEp5leBaE@>~2}=BA9S^yPfa% zd++<+d%y4P6ujml0RZyO%E}5Tv$WMBtyv;JSS$V;U=~P=UWsw$2hZnSccB{iUg$3R zi}i4s0+GuTu3HT%)zB|T?%YyPj;i&*jl6veK4}279h;F?uDOkRHHiF>wPHsx*9h@> z4yYt4-oAPm0b{4+hEqTY9#4J{pz$Zi$9M%7sd3_{WbGonwH^5@;LwugFq_(}=I|0Y zip_19p+opZW~gQ>d2_l$CP=0>v1!d=Nai+clkYFUSjkY zSkGv{gofn2*hw|)x#9?0bu>&t+wJHj;uzRs+wOPvij~YJ*+E`^oB35W^oK`O98ka<;d4Jd&*x!bGx zd!zlK?^PbRmxAU}qk%uQe#M?{8UUq9bEJ`=Zn+**+=E^OfaIgZ%%@8gAKxAJ zm5d-T?c;x>qTCO-#bdIHlOsTBlV1 zww4Y9LPxu5k*?~XD?h&|=i+cNtXI6qE2bje(OI~&?g1YDH(DlWfa!I(9XSF#v<|OJ zeCrdtqy{jxPQ+xwI_ogffxBLmWd3gH6D6%R{%?6LE_LW*M~hE9{p3^Z8KDyK7!l9n z+a?jG2@x03ZlRr~L|j1o1MMUu;w!XEjIe7ArFfGK13$v^0B>^(PVs^`%Zd1c6ZRuV z_zK4heM7>B6$_3j1#v<_80{Cdn+joXsYrWQ#R+pxMcSvT1z)NK@hzU_CzY_jCA=ag zfFBbGuL=v^69w^+AmVF5*bf5X%VG=g>k@uRv*0zYAWmyU{DpQzC+vM4Dd+Sl;GgM8 zxu9EcQ7?$^bt3*m_!R@;V}=9#q=E2R!-9{Eg81AZ;yZ(|-z0q9*be+<6XD~g1t-jc u_`pOM?FQPZAtKJB{fhQxj)+gtzR3}GIft&l=XL?Vl|$Fphb=fhT=)lRhUi}a literal 0 HcmV?d00001 diff --git a/utils/webassembly/sdkroot/extra_utils/generateModulemap.sh b/utils/webassembly/sdkroot/extra_utils/generateModulemap.sh new file mode 100755 index 0000000000000..940e5a413339b --- /dev/null +++ b/utils/webassembly/sdkroot/extra_utils/generateModulemap.sh @@ -0,0 +1,2 @@ +#!/bin/sh +exec sed -e "s@\"/include@\"$1/include@g" "$(dirname $0)/glibc.modulemap" diff --git a/utils/webassembly/sdkroot/swiftwasm b/utils/webassembly/sdkroot/swiftwasm new file mode 100755 index 0000000000000..d87894df816cd --- /dev/null +++ b/utils/webassembly/sdkroot/swiftwasm @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +if [ "$#" -lt 2 ] +then + echo "usage: swiftwasm file1.swift file2.swift.... output.wasm" + exit 1 +fi +sdk="$(dirname $0)" +tmpobj="$(mktemp -t swiftwasm-XXXXXXXX)" +outputfile="${@: -1}" +if [[ "$outputfile" != *.wasm ]] +then + echo "output should end in .wasm" + exit 1 +fi +sysroot="$(dirname $0)/wasi-sdk/share/sysroot" +abssysroot="$(cd "$(dirname "$sysroot")" && pwd)/$(basename "$sysroot")" + +"$sdk/extra_utils/generateModulemap.sh" "$abssysroot" >"$sdk/opt/swiftwasm-sdk/lib/swift/wasm/wasm32/glibc.modulemap" + +"$sdk/opt/swiftwasm-sdk/bin/swiftc" -target wasm32-unknown-unknown-wasm \ + -sdk "$sysroot" -O -c \ + -o "$tmpobj" \ + "${@:1:$#-1}" +"$sdk/wasi-sdk/bin/wasm-ld" --error-limit=0 -o "$outputfile" \ + "$sysroot/lib/wasm32-wasi/crt1.o" \ + "$sdk/opt/swiftwasm-sdk/lib/swift_static/wasm/wasm32/swiftrt.o" \ + "$tmpobj" \ + "-L$sdk/opt/swiftwasm-sdk/lib/swift_static/wasm" \ + "-L$sysroot/lib/wasm32-wasi" \ + "-L$sdk/icu_out/lib" \ + -lswiftCore \ + -lc -lc++ -lc++abi -lswiftImageInspectionShared \ + -licuuc -licudata \ + "$sdk/wasi-sdk/lib/clang/9.0.0/lib/wasi/libclang_rt.builtins-wasm32.a" \ + "$sdk/extra_objs/fakepthread.o" \ + --no-gc-sections \ + --no-threads +rm "$tmpobj" From f995fb0607f014f29367ecdc71c6e95074391970 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 14 Dec 2019 21:28:46 +0900 Subject: [PATCH 465/478] [WASM] Link start/stop symbol weakly (#21) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR fixed my runtime implementation in SwiftRT. I've inserted dummy `char` data in each metadata sections to ensure that all start/stop symbols are generated in https://github.com/swiftwasm/swift/pull/11. But of cource this dummy data can be inserted anywhere in the section, so metadata sections were broken by this 1 byte. I changed to link these start/stop symbols weakly. Non-generated start/stop variables get to be uninitialized. So `stop-start` results 0 length, and runtime library can avoid to load empty section. After this and https://github.com/swiftwasm/swift/pull/6 are merged, `print("Hello")` will work again! 🎉 --- stdlib/public/runtime/SwiftRT-WASM.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/stdlib/public/runtime/SwiftRT-WASM.cpp b/stdlib/public/runtime/SwiftRT-WASM.cpp index 1b4c84f3f2ae9..24dc7130def4d 100644 --- a/stdlib/public/runtime/SwiftRT-WASM.cpp +++ b/stdlib/public/runtime/SwiftRT-WASM.cpp @@ -14,14 +14,10 @@ #include -// Create empty sections to ensure that the start/stop symbols are synthesized -// by the linker. Otherwise, we may end up with undefined symbol references as -// the linker table section was never constructed. - +// Link start/stop symbols weakly to link them if they aren't synthesized by the linker. #define DECLARE_SWIFT_SECTION(name) \ - __attribute__((__used__,__section__(#name),__aligned__(1))) const char __dummy_##name = 0x00; \ - __attribute__((__visibility__("hidden"),__aligned__(1))) extern const char __start_##name; \ - __attribute__((__visibility__("hidden"),__aligned__(1))) extern const char __stop_##name; + __attribute__((__visibility__("hidden"),__aligned__(1),weak)) extern const char __start_##name; \ + __attribute__((__visibility__("hidden"),__aligned__(1),weak)) extern const char __stop_##name; extern "C" { DECLARE_SWIFT_SECTION(swift5_protocols) From 480f2c1a08af717cbb257cc8e08db32e274d1c73 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 14 Dec 2019 22:06:45 +0900 Subject: [PATCH 466/478] Emit thunk function for specific ABI on WebAssembly. (#6) Changed to make thunk to convert thin-to-thick and non-throws-to-throws. We needs it on WebAssembly host because WASM checks number of arguments strictly for indirect function call. This patch allows you to run the Swift code below. ```swift func f(_ a: (Int) -> Void) { g(a) } func g(_ b: (Int) throws -> Void) { try! b(1) } f { _ in } ``` --- lib/SIL/TypeLowering.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index 8489ae68ae6f3..be4957ea46dbd 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -2625,6 +2625,14 @@ TypeConverter::checkFunctionForABIDifferences(SILModule &M, return ABIDifference::NeedsThunk; } + // There is no ABI compatibility between non-throws and throws on WebAssembly, + // so need thunk. + if (M.getASTContext().LangOpts.Target.isOSBinFormatWasm()) { + if (!fnTy1->hasErrorResult() && fnTy2->hasErrorResult()) { + return ABIDifference::NeedsThunk; + } + } + auto rep1 = fnTy1->getRepresentation(), rep2 = fnTy2->getRepresentation(); if (rep1 != rep2) { if (rep1 == SILFunctionTypeRepresentation::Thin && @@ -2636,8 +2644,14 @@ TypeConverter::checkFunctionForABIDifferences(SILModule &M, } else { return ABIDifference::CompatibleRepresentation_ThinToThick; } - } + // There is no ABI compatibility between thin and thick on WebAssembly, + // so need thunk. + if (M.getASTContext().LangOpts.Target.isOSBinFormatWasm()) { + return ABIDifference::NeedsThunk; + } + return ABIDifference::ThinToThick; + } return ABIDifference::NeedsThunk; } From 0a363e4c75f71004c3fe893113815c6ca3b58780 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 15 Dec 2019 01:43:11 +0000 Subject: [PATCH 467/478] [WebAssembly] Remove conflicted default case --- lib/Basic/LangOptions.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/Basic/LangOptions.cpp b/lib/Basic/LangOptions.cpp index 9242a605eaa89..95c937cbd2f43 100644 --- a/lib/Basic/LangOptions.cpp +++ b/lib/Basic/LangOptions.cpp @@ -308,8 +308,6 @@ std::pair LangOptions::setTarget(llvm::Triple triple) { case llvm::Triple::ArchType::systemz: addPlatformConditionValue(PlatformConditionKind::Endianness, "big"); break; - default: - llvm_unreachable("undefined architecture endianness"); } // Set the "runtime" platform condition. From c38802f65a96fb545c480c4e6248ffdb50b7cb2c Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 15 Dec 2019 03:46:40 +0000 Subject: [PATCH 468/478] [WASM] Replace Wasm with WASI to switch target OS --- CMakeLists.txt | 12 +++---- build-linux.sh | 18 +++++----- build-mac.sh | 4 +-- cmake/modules/AddSwift.cmake | 14 ++++---- cmake/modules/SwiftConfigureSDK.cmake | 12 +++---- stdlib/private/StdlibUnittest/CMakeLists.txt | 2 +- stdlib/private/StdlibUnittest/RaceTest.swift | 4 +-- .../StdlibUnittest/StdlibCoreExtras.swift | 2 +- .../StdlibUnittest/StdlibUnittest.swift | 14 ++++---- .../private/StdlibUnittest/SymbolLookup.swift | 6 ++-- .../SwiftPrivateLibcExtras/CMakeLists.txt | 2 +- .../SwiftPrivateLibcExtras/Subprocess.swift | 8 ++--- .../SwiftPrivateLibcExtras.swift | 6 ++-- .../SwiftPrivateThreadExtras/CMakeLists.txt | 2 +- .../SwiftPrivateThreadExtras.swift | 2 +- .../ThreadBarriers.swift | 2 +- stdlib/public/Platform/CMakeLists.txt | 4 +-- stdlib/public/Platform/Glibc.swift.gyb | 2 +- stdlib/public/Platform/Platform.swift | 4 +-- stdlib/public/Platform/glibc.modulemap.gyb | 22 ++++++------ stdlib/public/runtime/CMakeLists.txt | 2 +- utils/build-script | 34 +++++++++---------- utils/build-script-impl | 28 +++++++-------- utils/build_swift/driver_arguments.py | 12 +++---- .../host_specific_configuration.py | 6 ++-- .../swift_build_support/targets.py | 8 ++--- 26 files changed, 116 insertions(+), 116 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f3fe9bda9f530..7f455c6a4a1ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -258,7 +258,7 @@ set(SWIFT_ANDROID_DEPLOY_DEVICE_PATH "" CACHE STRING # User-configurable ICU specific options for Android, FreeBSD, Linux, Haiku, and WebAssembly. # -foreach(sdk ANDROID;FREEBSD;LINUX;WINDOWS;HAIKU;WASM) +foreach(sdk ANDROID;FREEBSD;LINUX;WINDOWS;HAIKU;WASI) foreach(arch aarch64;armv6;armv7;i686;powerpc64;powerpc64le;s390x;wasm32;x86_64) set(SWIFT_${sdk}_${arch}_ICU_UC "" CACHE STRING "Path to a directory containing the icuuc library for ${sdk}") @@ -820,8 +820,8 @@ if(swift_build_windows AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") endif() # Should we cross-compile the standard library for WebAssembly (WASI)? -is_sdk_requested(WASM swift_build_wasm) -if(swift_build_wasm AND NOT "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "WASM") +is_sdk_requested(WASI swift_build_wasm) +if(swift_build_wasm AND NOT "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "WASI") #if ("${SWIFT_ANDROID_NDK_PATH}" STREQUAL "") # message(FATAL_ERROR "You must set SWIFT_ANDROID_NDK_PATH to cross-compile the Swift runtime for Android") #endif() @@ -829,10 +829,10 @@ if(swift_build_wasm AND NOT "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "WASM") # message(FATAL_ERROR "A Darwin or Linux host is required to build the Swift runtime for Android") #endif() - if("${SWIFT_SDK_WASM_ARCHITECTURES}" STREQUAL "") - set(SWIFT_SDK_WASM_ARCHITECTURES wasm32) + if("${SWIFT_SDK_WASI_ARCHITECTURES}" STREQUAL "") + set(SWIFT_SDK_WASI_ARCHITECTURES wasm32) endif() - configure_sdk_unix("Wasm" "${SWIFT_SDK_WASM_ARCHITECTURES}") + configure_sdk_unix("WASI" "${SWIFT_SDK_WASI_ARCHITECTURES}") endif() if("${SWIFT_SDKS}" STREQUAL "") diff --git a/build-linux.sh b/build-linux.sh index 30285c85a2dc9..d5bad958f906d 100755 --- a/build-linux.sh +++ b/build-linux.sh @@ -5,23 +5,23 @@ export sourcedir=$PWD/.. ./utils/build-script --release --wasm --verbose \ --skip-build-benchmarks \ --extra-cmake-options=" \ - -DSWIFT_SDKS='WASM;LINUX' \ + -DSWIFT_SDKS='WASI;LINUX' \ -DSWIFT_BUILD_SOURCEKIT=FALSE \ -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ -DCMAKE_AR='$sourcedir/wasi-sdk/bin/llvm-ar' \ -DCMAKE_RANLIB='$sourcedir/wasi-sdk/bin/llvm-ranlib' \ " \ - --build-stdlib-deployment-targets "wasm-wasm32" \ + --build-stdlib-deployment-targets "wasi-wasm32" \ --build-swift-static-stdlib \ --install-destdir="$sourcedir/install" \ --install-prefix="/opt/swiftwasm-sdk" \ --install-swift \ --installable-package="$sourcedir/swiftwasm-linux.tar.gz" \ --llvm-targets-to-build "X86;WebAssembly" \ - --stdlib-deployment-targets "wasm-wasm32" \ - --wasm-icu-data "todo-icu-data" \ - --wasm-icu-i18n "$sourcedir/icu_out/lib" \ - --wasm-icu-i18n-include "$sourcedir/icu_out/include" \ - --wasm-icu-uc "$sourcedir/icu_out/lib" \ - --wasm-icu-uc-include "$sourcedir/icu_out/include" \ - --wasm-wasi-sdk "$sourcedir/wasi-sdk" + --stdlib-deployment-targets "wasi-wasm32" \ + --wasi-icu-data "todo-icu-data" \ + --wasi-icu-i18n "$sourcedir/icu_out/lib" \ + --wasi-icu-i18n-include "$sourcedir/icu_out/include" \ + --wasi-icu-uc "$sourcedir/icu_out/lib" \ + --wasi-icu-uc-include "$sourcedir/icu_out/include" \ + --wasi-sdk "$sourcedir/wasi-sdk" diff --git a/build-mac.sh b/build-mac.sh index aee098f9703cc..90a8be35acb1e 100755 --- a/build-mac.sh +++ b/build-mac.sh @@ -13,12 +13,12 @@ export sourcedir=$PWD/.. -DCMAKE_AR='/usr/local/opt/llvm/bin/llvm-ar' \ -DCMAKE_RANLIB='/usr/local/opt/llvm/bin/llvm-ranlib' \ " \ - --build-stdlib-deployment-targets "wasm-wasm32" \ + --build-stdlib-deployment-targets "wasi-wasm32" \ --build-swift-dynamic-sdk-overlay false \ --build-swift-static-sdk-overlay false \ --build-swift-static-stdlib \ --llvm-targets-to-build "X86;WebAssembly" \ - --stdlib-deployment-targets "wasm-wasm32" \ + --stdlib-deployment-targets "wasi-wasm32" \ --wasm-icu-data "todo-icu-data" \ --wasm-icu-i18n "$sourcedir/icu_out/lib" \ --wasm-icu-i18n-include "$sourcedir/icu_out/include" \ diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 788e08f1d25a2..3fdbeda5edf70 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -500,7 +500,7 @@ function(_add_variant_link_flags) foreach(path IN LISTS ${LFLAGS_ARCH}_LIB) list(APPEND library_search_directories ${path}) endforeach() - elseif("${LFLAGS_SDK}" STREQUAL "WASM") + elseif("${LFLAGS_SDK}" STREQUAL "WASI") # No extra libraries needed. else() # If lto is enabled, we need to add the object path flag so that the LTO code @@ -531,7 +531,7 @@ function(_add_variant_link_flags) if(NOT SWIFT_COMPILER_IS_MSVC_LIKE) # FIXME: On Apple platforms, find_program needs to look for "ld64.lld" find_program(LDLLD_PATH "ld.lld") - if("${LFLAGS_SDK}" STREQUAL "WASM") + if("${SWIFT_SDK_${LFLAGS_SDK}_OBJECT_FORMAT}" STREQUAL "WASM") list(APPEND result "-fuse-ld=${CMAKE_SOURCE_DIR}/fakeld") elseif((SWIFT_ENABLE_LLD_LINKER AND LDLLD_PATH AND NOT APPLE) OR ("${LFLAGS_SDK}" STREQUAL "WINDOWS" AND @@ -1617,8 +1617,8 @@ endfunction() # SWIFT_MODULE_DEPENDS_HAIKU # Swift modules this library depends on when built for Haiku. # -# SWIFT_MODULE_DEPENDS_WASM -# Swift modules this library depends on when built for WebAssembly. +# SWIFT_MODULE_DEPENDS_WASI +# Swift modules this library depends on when built for WASI. # # FRAMEWORK_DEPENDS # System frameworks this library depends on. @@ -1728,7 +1728,7 @@ function(add_swift_target_library name) SWIFT_MODULE_DEPENDS_OSX SWIFT_MODULE_DEPENDS_TVOS SWIFT_MODULE_DEPENDS_WATCHOS - SWIFT_MODULE_DEPENDS_WASM + SWIFT_MODULE_DEPENDS_WASI SWIFT_MODULE_DEPENDS_WINDOWS SWIFT_MODULE_DEPENDS_FROM_SDK TARGET_SDKS) @@ -1839,9 +1839,9 @@ function(add_swift_target_library name) elseif(${sdk} STREQUAL HAIKU) list(APPEND swiftlib_module_depends_flattened ${SWIFTLIB_SWIFT_MODULE_DEPENDS_HAIKU}) - elseif(${sdk} STREQUAL WASM) + elseif(${sdk} STREQUAL WASI) list(APPEND swiftlib_module_depends_flattened - ${SWIFTLIB_SWIFT_MODULE_DEPENDS_WASM}) + ${SWIFTLIB_SWIFT_MODULE_DEPENDS_WASI}) elseif(${sdk} STREQUAL WINDOWS) list(APPEND swiftlib_module_depends_flattened ${SWIFTLIB_SWIFT_MODULE_DEPENDS_WINDOWS}) diff --git a/cmake/modules/SwiftConfigureSDK.cmake b/cmake/modules/SwiftConfigureSDK.cmake index 0749eaea22082..0bc415c563b7f 100644 --- a/cmake/modules/SwiftConfigureSDK.cmake +++ b/cmake/modules/SwiftConfigureSDK.cmake @@ -203,7 +203,7 @@ macro(configure_sdk_unix name architectures) set(SWIFT_SDK_${prefix}_ARCHITECTURES "${architectures}") if("${prefix}" STREQUAL "CYGWIN") set(SWIFT_SDK_${prefix}_OBJECT_FORMAT "COFF") - elseif("${prefix}" STREQUAL "WASM") + elseif("${prefix}" STREQUAL "WASI") set(SWIFT_SDK_${prefix}_OBJECT_FORMAT "WASM") else() set(SWIFT_SDK_${prefix}_OBJECT_FORMAT "ELF") @@ -321,15 +321,15 @@ macro(configure_sdk_unix name architectures) message(FATAL_ERROR "unsupported arch for Haiku: ${arch}") endif() set(SWIFT_SDK_HAIKU_ARCH_x86_64_TRIPLE "x86_64-unknown-haiku") - elseif("${prefix}" STREQUAL "WASM") + elseif("${prefix}" STREQUAL "WASI") if(NOT arch STREQUAL wasm32) message(FATAL_ERROR "unsupported arch for WebAssembly: ${arch}") endif() - set(SWIFT_SDK_WASM_ARCH_wasm32_PATH "${SWIFT_WASM_WASI_SDK_PATH}/share/sysroot") + set(SWIFT_SDK_WASI_ARCH_wasm32_PATH "${SWIFT_WASI_SDK_PATH}/share/sysroot") # fixme: Wasi is wasm32-unknown-wasi-musl. This LLVM doesn't have it yet. - set(SWIFT_SDK_WASM_ARCH_wasm32_TRIPLE "wasm32-unknown-unknown-wasm") - set(SWIFT_SDK_WASM_ARCH_wasm32_LIBC_INCLUDE_DIRECTORY "${SWIFT_WASM_WASI_SDK_PATH}/share/sysroot/include") - set(SWIFT_SDK_WASM_ARCH_wasm32_LIBC_ARCHITECTURE_INCLUDE_DIRECTORY "${SWIFT_WASM_WASI_SDK_PATH}/share/sysroot/include") + set(SWIFT_SDK_WASI_ARCH_wasm32_TRIPLE "wasm32-unknown-unknown-wasi") + set(SWIFT_SDK_WASI_ARCH_wasm32_LIBC_INCLUDE_DIRECTORY "${SWIFT_WASI_SDK_PATH}/share/sysroot/include") + set(SWIFT_SDK_WASI_ARCH_wasm32_LIBC_ARCHITECTURE_INCLUDE_DIRECTORY "${SWIFT_WASI_SDK_PATH}/share/sysroot/include") else() message(FATAL_ERROR "unknown Unix OS: ${prefix}") endif() diff --git a/stdlib/private/StdlibUnittest/CMakeLists.txt b/stdlib/private/StdlibUnittest/CMakeLists.txt index 5f6cc350fbe3a..9614bfbdcbc04 100644 --- a/stdlib/private/StdlibUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnittest/CMakeLists.txt @@ -40,7 +40,7 @@ add_swift_target_library(swiftStdlibUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WASM Glibc + SWIFT_MODULE_DEPENDS_WASI Glibc SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental diff --git a/stdlib/private/StdlibUnittest/RaceTest.swift b/stdlib/private/StdlibUnittest/RaceTest.swift index 60ba5e368c083..d793930527e71 100644 --- a/stdlib/private/StdlibUnittest/RaceTest.swift +++ b/stdlib/private/StdlibUnittest/RaceTest.swift @@ -41,7 +41,7 @@ import SwiftPrivateLibcExtras import SwiftPrivateThreadExtras #if os(macOS) || os(iOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -562,7 +562,7 @@ class _InterruptibleSleep { return } -#if os(Wasm) +#if os(WASI) // WebAssembly/WASI on wasm32 is the only 32-bit platform with Int64 time_t var timeout = timeval(tv_sec: time_t(duration), tv_usec: 0) #else diff --git a/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift b/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift index 8b356c2acff2a..bc8bd4fe980e0 100644 --- a/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift +++ b/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift @@ -14,7 +14,7 @@ import SwiftPrivate import SwiftPrivateLibcExtras #if os(macOS) || os(iOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT diff --git a/stdlib/private/StdlibUnittest/StdlibUnittest.swift b/stdlib/private/StdlibUnittest/StdlibUnittest.swift index 03e0ca2c9cbec..51ac487875af9 100644 --- a/stdlib/private/StdlibUnittest/StdlibUnittest.swift +++ b/stdlib/private/StdlibUnittest/StdlibUnittest.swift @@ -18,7 +18,7 @@ import SwiftPrivateLibcExtras #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Foundation import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -748,7 +748,7 @@ extension ProcessTerminationStatus { case .signal(let signal): #if os(Windows) return CInt(signal) == SIGILL -#elseif os(Wasm) +#elseif os(WASI) return false #else return CInt(signal) == SIGILL || CInt(signal) == SIGTRAP @@ -1748,7 +1748,7 @@ public enum OSVersion : CustomStringConvertible { case windowsCygnus case windows case haiku - case wasm + case wasi public var description: String { switch self { @@ -1780,8 +1780,8 @@ public enum OSVersion : CustomStringConvertible { return "Windows" case .haiku: return "Haiku" - case .wasm: - return "Wasm" + case .wasi: + return "WASI" } } } @@ -1826,8 +1826,8 @@ func _getOSVersion() -> OSVersion { return .windows #elseif os(Haiku) return .haiku -#elseif os(Wasm) - return .wasm +#elseif os(WASI) + return .wasi #else let productVersion = _getSystemVersionPlistProperty("ProductVersion")! let (major, minor, bugFix) = _parseDottedVersionTriple(productVersion) diff --git a/stdlib/private/StdlibUnittest/SymbolLookup.swift b/stdlib/private/StdlibUnittest/SymbolLookup.swift index 6967c11327e0e..f827f6e2fe956 100644 --- a/stdlib/private/StdlibUnittest/SymbolLookup.swift +++ b/stdlib/private/StdlibUnittest/SymbolLookup.swift @@ -12,7 +12,7 @@ #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -23,7 +23,7 @@ #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2) -#elseif os(Linux) || os(Wasm) +#elseif os(Linux) || os(WASI) let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: 0) #elseif os(Android) #if arch(arm) || arch(i386) @@ -43,7 +43,7 @@ public func pointerToSwiftCoreSymbol(name: String) -> UnsafeMutableRawPointer? { #if os(Windows) return unsafeBitCast(GetProcAddress(hStdlibCore, name), to: UnsafeMutableRawPointer?.self) -#elseif os(Wasm) +#elseif os(WASI) fatalError("\(#function) is not supported on WebAssembly") #else return dlsym(RTLD_DEFAULT, name) diff --git a/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt index 86ad055eb4f0b..a4545a05728ae 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt @@ -17,7 +17,7 @@ add_swift_target_library(swiftSwiftPrivateLibcExtras ${SWIFT_STDLIB_LIBRARY_BUIL SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WASM Glibc + SWIFT_MODULE_DEPENDS_WASI Glibc SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift index f8ec3333de45d..84ab3b44398b2 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift @@ -13,7 +13,7 @@ import SwiftPrivate #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -21,7 +21,7 @@ import WinSDK #endif internal func _signalToString(_ signal: Int) -> String { -#if os(Wasm) +#if os(WASI) return "unsupported" #else switch CInt(signal) { @@ -36,7 +36,7 @@ internal func _signalToString(_ signal: Int) -> String { #endif default: return "SIG???? (\(signal))" } -#endif // os(Wasm) +#endif // os(WASI) } public enum ProcessTerminationStatus : CustomStringConvertible { @@ -145,7 +145,7 @@ public func waitProcess(_ process: HANDLE) -> ProcessTerminationStatus { } return .exit(Int(status)) } -#elseif os(Wasm) +#elseif os(WASI) // Oops, we can't launch tests in subprocesses yet! public func spawnChild(_ args: [String]) -> (pid: pid_t, stdinFD: CInt, stdoutFD: CInt, stderrFD: CInt) { diff --git a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift index 6042ba6550ad8..68e955f7d648a 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift @@ -13,14 +13,14 @@ import SwiftPrivate #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT #endif public func _stdlib_mkstemps(_ template: inout String, _ suffixlen: CInt) -> CInt { -#if os(Android) || os(Haiku) || os(Windows) || os(Wasm) +#if os(Android) || os(Haiku) || os(Windows) || os(WASI) preconditionFailure("mkstemps doesn't work on your platform") #else var utf8CStr = template.utf8CString @@ -125,7 +125,7 @@ public func _stdlib_pipe() -> (readEnd: CInt, writeEnd: CInt, error: CInt) { let ret = fds.withUnsafeMutableBufferPointer { unsafeFds -> CInt in #if os(Windows) return _pipe(unsafeFds.baseAddress, 0, 0) -#elseif os(Wasm) +#elseif os(WASI) fatalError("no pipes on WebAssembly") #else return pipe(unsafeFds.baseAddress) diff --git a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt index b6d794dfc051d..82a68f1962560 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt @@ -14,7 +14,7 @@ add_swift_target_library(swiftSwiftPrivateThreadExtras ${SWIFT_STDLIB_LIBRARY_BU SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WASM Glibc + SWIFT_MODULE_DEPENDS_WASI Glibc SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental diff --git a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift index feff077024252..831d721f3c588 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift @@ -17,7 +17,7 @@ #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT diff --git a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift index 68a7880d18a07..b06ed029bf9b0 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift @@ -12,7 +12,7 @@ #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT diff --git a/stdlib/public/Platform/CMakeLists.txt b/stdlib/public/Platform/CMakeLists.txt index 6e0c46477e558..cc468ff668577 100644 --- a/stdlib/public/Platform/CMakeLists.txt +++ b/stdlib/public/Platform/CMakeLists.txt @@ -39,7 +39,7 @@ add_swift_target_library(swiftGlibc ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_O SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - TARGET_SDKS ANDROID CYGWIN FREEBSD LINUX HAIKU WASM + TARGET_SDKS ANDROID CYGWIN FREEBSD LINUX HAIKU WASI INSTALL_IN_COMPONENT sdk-overlay DEPENDS glibc_modulemap) @@ -63,7 +63,7 @@ foreach(sdk ${SWIFT_SDKS}) NOT "${sdk}" STREQUAL "ANDROID" AND NOT "${sdk}" STREQUAL "CYGWIN" AND NOT "${sdk}" STREQUAL "HAIKU" AND - NOT "${sdk}" STREQUAL "WASM") + NOT "${sdk}" STREQUAL "WASI") continue() endif() diff --git a/stdlib/public/Platform/Glibc.swift.gyb b/stdlib/public/Platform/Glibc.swift.gyb index 3258bcd28b10c..dc5ca8b711484 100644 --- a/stdlib/public/Platform/Glibc.swift.gyb +++ b/stdlib/public/Platform/Glibc.swift.gyb @@ -70,7 +70,7 @@ public let ${prefix}_TRUE_MIN = ${type}.leastNonzeroMagnitude % end %end -#if os(Wasm) +#if os(WASI) // WebAssembly's error.h uses a macro that Swift can't import. public let EINTR:Int32 = 27 public let EINVAL:Int32 = 28 diff --git a/stdlib/public/Platform/Platform.swift b/stdlib/public/Platform/Platform.swift index 17c60c276036b..fce8fb75eadf3 100644 --- a/stdlib/public/Platform/Platform.swift +++ b/stdlib/public/Platform/Platform.swift @@ -366,7 +366,7 @@ public var SIG_IGN: _crt_signal_t { public var SIG_ERR: _crt_signal_t { return unsafeBitCast(-1, to: _crt_signal_t.self) } -#elseif os(Wasm) +#elseif os(WASI) // WebAssembly/WASI doesn't have signals. #else internal var _ignore = _UnsupportedPlatformError() @@ -382,7 +382,7 @@ public var SEM_FAILED: UnsafeMutablePointer? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) // The value is ABI. Value verified to be correct for OS X, iOS, watchOS, tvOS. return UnsafeMutablePointer(bitPattern: -1) -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(Wasm) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) // The value is ABI. Value verified to be correct on Glibc. return UnsafeMutablePointer(bitPattern: 0) #else diff --git a/stdlib/public/Platform/glibc.modulemap.gyb b/stdlib/public/Platform/glibc.modulemap.gyb index eba7fde86588a..5207835d369d2 100644 --- a/stdlib/public/Platform/glibc.modulemap.gyb +++ b/stdlib/public/Platform/glibc.modulemap.gyb @@ -126,7 +126,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/math.h" export * } -% if CMAKE_SDK != "WASM": +% if CMAKE_SDK != "WASI": module setjmp { header "${GLIBC_INCLUDE_PATH}/setjmp.h" export * @@ -321,7 +321,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/dirent.h" export * } -% if CMAKE_SDK != "WASM": +% if CMAKE_SDK != "WASI": module dl { header "${GLIBC_INCLUDE_PATH}/link.h" export * @@ -339,7 +339,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/fnmatch.h" export * } -% if CMAKE_SDK != "WASM": +% if CMAKE_SDK != "WASI": module grp { header "${GLIBC_INCLUDE_PATH}/grp.h" export * @@ -353,7 +353,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/libgen.h" export * } -% if CMAKE_SDK != "WASM": +% if CMAKE_SDK != "WASI": module net { module if { header "${GLIBC_INCLUDE_PATH}/net/if.h" @@ -381,7 +381,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/pthread.h" export * } -% if CMAKE_SDK != "WASM": +% if CMAKE_SDK != "WASI": module pwd { header "${GLIBC_INCLUDE_PATH}/pwd.h" export * @@ -432,7 +432,7 @@ module SwiftGlibc [system] { } % end -% if CMAKE_SDK != "WASM": +% if CMAKE_SDK != "WASI": module ipc { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/ipc.h" export * @@ -442,7 +442,7 @@ module SwiftGlibc [system] { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/mman.h" export * } -% if CMAKE_SDK != "WASM": +% if CMAKE_SDK != "WASI": module msg { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/msg.h" export * @@ -456,7 +456,7 @@ module SwiftGlibc [system] { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/select.h" export * } -% if CMAKE_SDK != "FREEBSD" and CMAKE_SDK != "HAIKU" and CMAKE_SDK != "WASM": +% if CMAKE_SDK != "FREEBSD" and CMAKE_SDK != "HAIKU" and CMAKE_SDK != "WASI": module sendfile { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/sendfile.h" export * @@ -506,7 +506,7 @@ module SwiftGlibc [system] { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/utsname.h" export * } -% if CMAKE_SDK != "WASM": +% if CMAKE_SDK != "WASI": module wait { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/wait.h" export * @@ -519,7 +519,7 @@ module SwiftGlibc [system] { export * } % end -% if CMAKE_SDK != "WASM": +% if CMAKE_SDK != "WASI": module termios { header "${GLIBC_INCLUDE_PATH}/termios.h" export * @@ -536,7 +536,7 @@ module SwiftGlibc [system] { } } -% if CMAKE_SDK != "WASM": +% if CMAKE_SDK != "WASI": module CUUID [system] { header "${GLIBC_INCLUDE_PATH}/uuid/uuid.h" link "uuid" diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 1e869fa0690a4..6a630fbc843c5 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -92,7 +92,7 @@ list(APPEND swift_runtime_library_compile_flags -I${SWIFT_SOURCE_DIR}/include) set(image_inspection_shared_sdk) if(SWIFT_BUILD_STATIC_STDLIB AND "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "LINUX") set(image_inspection_shared_sdk "${SWIFT_HOST_VARIANT_SDK}") -elseif("${SWIFT_PRIMARY_VARIANT_SDK}" STREQUAL "WASM") +elseif("${SWIFT_PRIMARY_VARIANT_SDK}" STREQUAL "WASI") set(image_inspection_shared_sdk "${SWIFT_PRIMARY_VARIANT_SDK}") endif() diff --git a/utils/build-script b/utils/build-script index 7de82769a9b43..be4d66a8d43e5 100755 --- a/utils/build-script +++ b/utils/build-script @@ -120,17 +120,17 @@ class BuildScriptInvocation(object): "must be specified") if args.wasm: - if args.wasm_wasi_sdk is None or \ - args.wasm_icu_uc is None or \ - args.wasm_icu_uc_include is None or \ - args.wasm_icu_i18n is None or \ - args.wasm_icu_i18n_include is None or \ - args.wasm_icu_data is None: + if args.wasi_sdk is None or \ + args.wasi_icu_uc is None or \ + args.wasi_icu_uc_include is None or \ + args.wasi_icu_i18n is None or \ + args.wasi_icu_i18n_include is None or \ + args.wasi_icu_data is None: diagnostics.fatal( - "when building for WebAssembly, --wasm-wasi-sdk, " - "--wasm-icu-uc, " - "--wasm-icu-uc-include, --wasm-icu-i18n, " - "--wasm-icu-i18n-include, and --wasm-icu-data " + "when building for WebAssembly, --wasi-sdk, " + "--wasi-icu-uc, " + "--wasi-icu-uc-include, --wasi-icu-i18n, " + "--wasi-icu-i18n-include, and --wasi-icu-data " "must be specified") targets_needing_toolchain = [ @@ -229,7 +229,7 @@ class BuildScriptInvocation(object): StdlibDeploymentTarget.Android.aarch64.name) if args.wasm: args.stdlib_deployment_targets.append( - StdlibDeploymentTarget.Wasm.wasm32.name) + StdlibDeploymentTarget.WASI.wasm32.name) # Infer platform flags from manually-specified configure targets. # This doesn't apply to Darwin platforms, as they are @@ -603,12 +603,12 @@ class BuildScriptInvocation(object): if args.wasm: impl_args += [ - "--wasm-wasi-sdk", args.wasm_wasi_sdk, - "--wasm-icu-uc", args.wasm_icu_uc, - "--wasm-icu-uc-include", args.wasm_icu_uc_include, - "--wasm-icu-i18n", args.wasm_icu_i18n, - "--wasm-icu-i18n-include", args.wasm_icu_i18n_include, - "--wasm-icu-data", args.wasm_icu_data, + "--wasi-sdk", args.wasi_sdk, + "--wasi-icu-uc", args.wasi_icu_uc, + "--wasi-icu-uc-include", args.wasi_icu_uc_include, + "--wasi-icu-i18n", args.wasi_icu_i18n, + "--wasi-icu-i18n-include", args.wasi_icu_i18n_include, + "--wasi-icu-data", args.wasi_icu_data, ] if platform.system() == 'Darwin': diff --git a/utils/build-script-impl b/utils/build-script-impl index 5fd027d0ca502..4423086de2f6d 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -236,12 +236,12 @@ KNOWN_SETTINGS=( android-icu-data "" "Path to libicudata.so" android-deploy-device-path "" "Path on an Android device to which built Swift stdlib products will be deployed" android-arch "armv7" "The Android target architecture when building for Android" - wasm-wasi-sdk "" "An absolute path to the WASI SDK that will be used as a libc implementation for Wasm builds" - wasm-icu-uc "" "Path to libicuuc.so" - wasm-icu-uc-include "" "Path to a directory containing headers for libicuuc" - wasm-icu-i18n "" "Path to libicui18n.so" - wasm-icu-i18n-include "" "Path to a directory containing headers libicui18n" - wasm-icu-data "" "Path to libicudata.so" + wasi-sdk "" "An absolute path to the WASI SDK that will be used as a libc implementation for Wasm builds" + wasi-icu-uc "" "Path to libicuuc.so" + wasi-icu-uc-include "" "Path to a directory containing headers for libicuuc" + wasi-icu-i18n "" "Path to libicui18n.so" + wasi-icu-i18n-include "" "Path to a directory containing headers libicui18n" + wasi-icu-data "" "Path to libicudata.so" check-args-only "" "set to check all arguments are known. Exit with status 0 if success, non zero otherwise" common-cmake-options "" "CMake options used for all targets, including LLVM/Clang" cmark-cmake-options "" "CMake options used for all cmark targets" @@ -422,7 +422,7 @@ function verify_host_is_supported() { | watchos-armv7k \ | android-armv7 \ | android-aarch64 \ - | wasm-wasm32) + | wasi-wasm32) ;; *) echo "Unknown host tools target: ${host}" @@ -1262,7 +1262,7 @@ function common_cross_c_flags() { android-arm64) echo -n " -arch aarch64" ;; - wasm-wasm32) + wasi-wasm32) echo -n " -arch wasm32" ;; esac @@ -1667,12 +1667,12 @@ for host in "${ALL_HOSTS[@]}"; do if [[ ! "${SKIP_BUILD_WASM}" ]]; then cmake_options=( "${cmake_options[@]}" - -DSWIFT_WASM_WASI_SDK_PATH:STRING="${WASM_WASI_SDK}" - -DSWIFT_WASM_wasm32_ICU_UC:STRING="${WASM_ICU_UC}" - -DSWIFT_WASM_wasm32_ICU_UC_INCLUDE:STRING="${WASM_ICU_UC_INCLUDE}" - -DSWIFT_WASM_wasm32_ICU_I18N:STRING="${WASM_ICU_I18N}" - -DSWIFT_WASM_wasm32_ICU_I18N_INCLUDE:STRING="${WASM_ICU_I18N_INCLUDE}" - -DSWIFT_WASM_wasm32_ICU_DATA:STRING="${WASM_ICU_DATA}" + -DSWIFT_WASI_SDK_PATH:STRING="${WASI_SDK}" + -DSWIFT_WASI_wasm32_ICU_UC:STRING="${WASI_ICU_UC}" + -DSWIFT_WASI_wasm32_ICU_UC_INCLUDE:STRING="${WASI_ICU_UC_INCLUDE}" + -DSWIFT_WASI_wasm32_ICU_I18N:STRING="${WASI_ICU_I18N}" + -DSWIFT_WASI_wasm32_ICU_I18N_INCLUDE:STRING="${WASI_ICU_I18N_INCLUDE}" + -DSWIFT_WASI_wasm32_ICU_DATA:STRING="${WASI_ICU_DATA}" ) fi diff --git a/utils/build_swift/driver_arguments.py b/utils/build_swift/driver_arguments.py index e7e169d5dd70a..cdd4d250f3d3e 100644 --- a/utils/build_swift/driver_arguments.py +++ b/utils/build_swift/driver_arguments.py @@ -1077,19 +1077,19 @@ def create_argument_parser(): in_group('Build settings for Android') - option('--wasm-wasi-sdk', store_path, + option('--wasi-sdk', store_path, help='An absolute path to WASI SDK that will be used as a libc ' 'implementation for Wasm builds') - option('--wasm-icu-uc', store_path, + option('--wasi-icu-uc', store_path, help='Path to libicuuc.so') - option('--wasm-icu-uc-include', store_path, + option('--wasi-icu-uc-include', store_path, help='Path to a directory containing headers for libicuuc') - option('--wasm-icu-i18n', store_path, + option('--wasi-icu-i18n', store_path, help='Path to libicui18n.so') - option('--wasm-icu-i18n-include', store_path, + option('--wasi-icu-i18n-include', store_path, help='Path to a directory containing headers libicui18n') - option('--wasm-icu-data', store_path, + option('--wasi-icu-data', store_path, help='Path to libicudata.so') # ------------------------------------------------------------------------- diff --git a/utils/swift_build_support/swift_build_support/host_specific_configuration.py b/utils/swift_build_support/swift_build_support/host_specific_configuration.py index ba3b5def3231e..8a277c58b7657 100644 --- a/utils/swift_build_support/swift_build_support/host_specific_configuration.py +++ b/utils/swift_build_support/swift_build_support/host_specific_configuration.py @@ -192,7 +192,7 @@ def __platforms_to_skip_build(self, args): if not args.build_android: platforms_to_skip_build.add(StdlibDeploymentTarget.Android) if not args.build_wasm: - platforms_to_skip_build.add(StdlibDeploymentTarget.Wasm) + platforms_to_skip_build.add(StdlibDeploymentTarget.WASI) return platforms_to_skip_build def __platforms_to_skip_test(self, args): @@ -233,7 +233,7 @@ def __platforms_to_skip_test(self, args): if not args.test_android: platforms_to_skip_test.add(StdlibDeploymentTarget.Android) if not args.test_wasm: - platforms_to_skip_test.add(StdlibDeploymentTarget.Wasm) + platforms_to_skip_test.add(StdlibDeploymentTarget.WASI) return platforms_to_skip_test @@ -255,5 +255,5 @@ def __platforms_to_skip_test_host(self, args): if not args.test_watchos_host: platforms_to_skip_test_host.add(StdlibDeploymentTarget.AppleWatch) if not args.test_wasm_host: - platforms_to_skip_test_host.add(StdlibDeploymentTarget.Wasm) + platforms_to_skip_test_host.add(StdlibDeploymentTarget.WASI) return platforms_to_skip_test_host diff --git a/utils/swift_build_support/swift_build_support/targets.py b/utils/swift_build_support/swift_build_support/targets.py index f7a32c8293ee3..f1cffa21f627a 100644 --- a/utils/swift_build_support/swift_build_support/targets.py +++ b/utils/swift_build_support/swift_build_support/targets.py @@ -158,7 +158,7 @@ class StdlibDeploymentTarget(object): Haiku = Platform("haiku", archs=["x86_64"]) - Wasm = Platform("wasm", archs=["wasm32"]) + WASI = Platform("wasi", archs=["wasm32"]) # The list of known platforms. known_platforms = [ @@ -172,7 +172,7 @@ class StdlibDeploymentTarget(object): Android, Windows, Haiku, - Wasm] + WASI] # Cache of targets by name. _targets_by_name = dict((target.name, target) @@ -236,9 +236,9 @@ def host_target(): if machine == 'x86_64': return StdlibDeploymentTarget.Haiku.x86_64 - elif system == 'Wasm': + elif system == 'WASI': if machine == 'wasm32': - return StdlibDeploymentTarget.Wasm.wasm32 + return StdlibDeploymentTarget.WASI.wasm32 raise NotImplementedError('System "%s" with architecture "%s" is not ' 'supported' % (system, machine)) From e70f75970d7fe8cb393e1dc1c36ae2b2cde1526a Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 15 Dec 2019 03:47:36 +0000 Subject: [PATCH 469/478] [WebAssembly] Fix merge conflicts --- lib/Basic/Platform.cpp | 2 -- lib/IRGen/MetadataRequest.cpp | 3 --- lib/SIL/TypeLowering.cpp | 12 +++++------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/lib/Basic/Platform.cpp b/lib/Basic/Platform.cpp index 36050f446b0c3..85abc0ee97e43 100644 --- a/lib/Basic/Platform.cpp +++ b/lib/Basic/Platform.cpp @@ -138,8 +138,6 @@ static StringRef getPlatformNameForDarwin(const DarwinPlatformKind platform) { StringRef swift::getPlatformNameForTriple(const llvm::Triple &triple) { switch (triple.getOS()) { case llvm::Triple::UnknownOS: - if (triple.isOSBinFormatWasm()) - return "wasm"; llvm_unreachable("unknown OS"); case llvm::Triple::Ananas: case llvm::Triple::CloudABI: diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 47158041becc0..f075bcc2ada55 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -2430,9 +2430,6 @@ emitMetadataAccessByMangledName(IRGenFunction &IGF, CanType type, stringAddrOffset); stringAddr = subIGF.Builder.CreateIntToPtr(stringAddr, IGM.Int8PtrTy); } - auto stringAddr = subIGF.Builder.CreateAdd(stringAddrBase, - stringAddrOffset); - stringAddr = subIGF.Builder.CreateIntToPtr(stringAddr, IGM.Int8PtrTy); llvm::CallInst *call; if (request.isStaticallyAbstract()) { diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index be4957ea46dbd..8f37adcb6b6d8 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -2637,6 +2637,11 @@ TypeConverter::checkFunctionForABIDifferences(SILModule &M, if (rep1 != rep2) { if (rep1 == SILFunctionTypeRepresentation::Thin && rep2 == SILFunctionTypeRepresentation::Thick) { + // There is no ABI compatibility between thin and thick on WebAssembly, + // so need thunk. + if (M.getASTContext().LangOpts.Target.isOSBinFormatWasm()) { + return ABIDifference::NeedsThunk; + } if (DifferentFunctionTypesHaveDifferentRepresentation) { // FIXME: check whether the representations are compatible modulo // context @@ -2644,13 +2649,6 @@ TypeConverter::checkFunctionForABIDifferences(SILModule &M, } else { return ABIDifference::CompatibleRepresentation_ThinToThick; } - - // There is no ABI compatibility between thin and thick on WebAssembly, - // so need thunk. - if (M.getASTContext().LangOpts.Target.isOSBinFormatWasm()) { - return ABIDifference::NeedsThunk; - } - return ABIDifference::ThinToThick; } return ABIDifference::NeedsThunk; } From 300bd96d6eca6dd02b5101dc811ac60b866488f1 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 15 Dec 2019 04:12:16 +0000 Subject: [PATCH 470/478] [WebAssembly] Fix target triple for CI --- utils/webassembly/sdkroot/swiftwasm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/webassembly/sdkroot/swiftwasm b/utils/webassembly/sdkroot/swiftwasm index d87894df816cd..278ce7dbc256a 100755 --- a/utils/webassembly/sdkroot/swiftwasm +++ b/utils/webassembly/sdkroot/swiftwasm @@ -17,17 +17,17 @@ fi sysroot="$(dirname $0)/wasi-sdk/share/sysroot" abssysroot="$(cd "$(dirname "$sysroot")" && pwd)/$(basename "$sysroot")" -"$sdk/extra_utils/generateModulemap.sh" "$abssysroot" >"$sdk/opt/swiftwasm-sdk/lib/swift/wasm/wasm32/glibc.modulemap" +"$sdk/extra_utils/generateModulemap.sh" "$abssysroot" >"$sdk/opt/swiftwasm-sdk/lib/swift/wasi/wasm32/glibc.modulemap" -"$sdk/opt/swiftwasm-sdk/bin/swiftc" -target wasm32-unknown-unknown-wasm \ +"$sdk/opt/swiftwasm-sdk/bin/swiftc" -target wasm32-unknown-unknown-wasi \ -sdk "$sysroot" -O -c \ -o "$tmpobj" \ "${@:1:$#-1}" "$sdk/wasi-sdk/bin/wasm-ld" --error-limit=0 -o "$outputfile" \ "$sysroot/lib/wasm32-wasi/crt1.o" \ - "$sdk/opt/swiftwasm-sdk/lib/swift_static/wasm/wasm32/swiftrt.o" \ + "$sdk/opt/swiftwasm-sdk/lib/swift_static/wasi/wasm32/swiftrt.o" \ "$tmpobj" \ - "-L$sdk/opt/swiftwasm-sdk/lib/swift_static/wasm" \ + "-L$sdk/opt/swiftwasm-sdk/lib/swift_static/wasi" \ "-L$sysroot/lib/wasm32-wasi" \ "-L$sdk/icu_out/lib" \ -lswiftCore \ From 35c311c16016ffb6586fbdbca4bfbde5c44de43e Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 15 Dec 2019 05:04:44 +0000 Subject: [PATCH 471/478] Fix build comand script for macOS --- build-mac.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build-mac.sh b/build-mac.sh index 90a8be35acb1e..b9dd3a718ded2 100755 --- a/build-mac.sh +++ b/build-mac.sh @@ -5,7 +5,7 @@ export sourcedir=$PWD/.. ./utils/build-script --release --wasm --verbose \ --skip-build-benchmarks \ --extra-cmake-options=" \ - -DSWIFT_PRIMARY_VARIANT_SDK:STRING=WASM \ + -DSWIFT_PRIMARY_VARIANT_SDK:STRING=WASI \ -DSWIFT_PRIMARY_VARIANT_ARCH:STRING=wasm32 \ -DSWIFT_OSX_x86_64_ICU_STATICLIB=TRUE \ -DSWIFT_BUILD_SOURCEKIT=FALSE \ @@ -19,12 +19,12 @@ export sourcedir=$PWD/.. --build-swift-static-stdlib \ --llvm-targets-to-build "X86;WebAssembly" \ --stdlib-deployment-targets "wasi-wasm32" \ - --wasm-icu-data "todo-icu-data" \ - --wasm-icu-i18n "$sourcedir/icu_out/lib" \ - --wasm-icu-i18n-include "$sourcedir/icu_out/include" \ - --wasm-icu-uc "$sourcedir/icu_out/lib" \ - --wasm-icu-uc-include "$sourcedir/icu_out/include" \ - --wasm-wasi-sdk "$sourcedir/wasi-sdk" \ + --wasi-icu-data "todo-icu-data" \ + --wasi-icu-i18n "$sourcedir/icu_out/lib" \ + --wasi-icu-i18n-include "$sourcedir/icu_out/include" \ + --wasi-icu-uc "$sourcedir/icu_out/lib" \ + --wasi-icu-uc-include "$sourcedir/icu_out/include" \ + --wasi-sdk "$sourcedir/wasi-sdk" \ --install-swift \ --install-prefix="/opt/swiftwasm-sdk" \ --install-destdir="$sourcedir/install" \ From 84b7f907c7a6ab2d6c20ec462a36fe30911374bf Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sun, 15 Dec 2019 22:42:49 +0000 Subject: [PATCH 472/478] Fix paths in packaging scripts (#23) `wasm` was replaced with `wasi` in the platform triple, the packaging scripts are now updated accordingly. --- utils/webassembly/copy-shared-files.sh | 2 +- utils/webassembly/macos/unpack-prebuilts.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/webassembly/copy-shared-files.sh b/utils/webassembly/copy-shared-files.sh index 7e1ce8a81832d..daaf0c5d30f82 100755 --- a/utils/webassembly/copy-shared-files.sh +++ b/utils/webassembly/copy-shared-files.sh @@ -1,4 +1,4 @@ #!/bin/bash set -e cp -r ../sdkroot/* compiler/ -cp ../linux/compiler/opt/swiftwasm-sdk/lib/swift/wasm/wasm32/glibc.modulemap compiler/extra_utils +cp ../linux/compiler/opt/swiftwasm-sdk/lib/swift/wasi/wasm32/glibc.modulemap compiler/extra_utils diff --git a/utils/webassembly/macos/unpack-prebuilts.sh b/utils/webassembly/macos/unpack-prebuilts.sh index 169dd8cde7ce5..199552346db2e 100755 --- a/utils/webassembly/macos/unpack-prebuilts.sh +++ b/utils/webassembly/macos/unpack-prebuilts.sh @@ -24,7 +24,7 @@ mv compiler/wasi-sdk/share/wasi-sysroot compiler/wasi-sdk/share/sysroot rm -r compiler/wasi-sdk/bin mkdir compiler/wasi-sdk/bin cp tmpdir/clang+llvm-*-x86_64-darwin-apple/bin/wasm-ld compiler/wasi-sdk/bin -cp -a tmpdir/opt/swiftwasm-sdk/lib/swift/wasm compiler/opt/swiftwasm-sdk/lib/swift/wasm +cp -a tmpdir/opt/swiftwasm-sdk/lib/swift/wasi compiler/opt/swiftwasm-sdk/lib/swift/wasi cp -a tmpdir/opt/swiftwasm-sdk/lib/swift_static compiler/opt/swiftwasm-sdk/lib/swift_static # ok, finally copy over the shared files ../copy-shared-files.sh || true From 98037458f1d740aee97e3272b5ffe7bc03284d92 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 18 Dec 2019 08:03:24 +0000 Subject: [PATCH 473/478] Bump macOS to 10.15 in GitHub Actions (#25) * Bump macOS to 10.15 in GitHub Actions Potentially this should enable switching to Xcode 11.2.1 and even 11.3 when the latter is available. * Use macos-latest image in main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 46d9785712789..8ea42eb65111b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,7 +25,7 @@ jobs: macos_build: timeout-minutes: 0 - runs-on: macOS-10.14 + runs-on: macos-latest steps: - uses: actions/checkout@v1 @@ -100,7 +100,7 @@ jobs: macos_smoke_test: name: Compile hello.swift on macOS - runs-on: macOS-10.14 + runs-on: macos-latest needs: package steps: - name: Download SwiftWasm macOS package From 63db28302f3d2d580be63cdf0c88e177cb1a6279 Mon Sep 17 00:00:00 2001 From: Karl Date: Thu, 19 Dec 2019 16:08:31 +0100 Subject: [PATCH 474/478] Remove fakeld (#26) * Remove fakeld It isn't needed if we don't try to build dynamic libraries for WASM. * Don't build StdlibUnittestFoundationExtras when cross-compiling on Darwin * Build static SDK overlay on WASI --- CMakeLists.txt | 10 +++------- build-linux.sh | 3 +++ build-mac.sh | 3 ++- cmake/modules/AddSwift.cmake | 10 +++++++--- fakeld | 9 --------- stdlib/private/CMakeLists.txt | 3 ++- 6 files changed, 17 insertions(+), 21 deletions(-) delete mode 100755 fakeld diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f455c6a4a1ea..7af1c790c1303 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -822,13 +822,9 @@ endif() # Should we cross-compile the standard library for WebAssembly (WASI)? is_sdk_requested(WASI swift_build_wasm) if(swift_build_wasm AND NOT "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "WASI") - #if ("${SWIFT_ANDROID_NDK_PATH}" STREQUAL "") - # message(FATAL_ERROR "You must set SWIFT_ANDROID_NDK_PATH to cross-compile the Swift runtime for Android") - #endif() - #if (NOT ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Darwin" OR "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux")) - # message(FATAL_ERROR "A Darwin or Linux host is required to build the Swift runtime for Android") - #endif() - + if(SWIFT_BUILD_DYNAMIC_SDK_OVERLAY OR SWIFT_BUILD_DYNAMIC_STDLIB) + message(FATAL_ERROR "Unable to build dynamic overlay/stdlib for WASI. WASM does not support dynamic linking.") + endif() if("${SWIFT_SDK_WASI_ARCHITECTURES}" STREQUAL "") set(SWIFT_SDK_WASI_ARCHITECTURES wasm32) endif() diff --git a/build-linux.sh b/build-linux.sh index d5bad958f906d..c13e91b796dfc 100755 --- a/build-linux.sh +++ b/build-linux.sh @@ -12,6 +12,9 @@ export sourcedir=$PWD/.. -DCMAKE_RANLIB='$sourcedir/wasi-sdk/bin/llvm-ranlib' \ " \ --build-stdlib-deployment-targets "wasi-wasm32" \ + --build-swift-dynamic-sdk-overlay false \ + --build-swift-dynamic-stdlib false \ + --build-swift-static-sdk-overlay \ --build-swift-static-stdlib \ --install-destdir="$sourcedir/install" \ --install-prefix="/opt/swiftwasm-sdk" \ diff --git a/build-mac.sh b/build-mac.sh index b9dd3a718ded2..ba621a52b1636 100755 --- a/build-mac.sh +++ b/build-mac.sh @@ -15,7 +15,8 @@ export sourcedir=$PWD/.. " \ --build-stdlib-deployment-targets "wasi-wasm32" \ --build-swift-dynamic-sdk-overlay false \ - --build-swift-static-sdk-overlay false \ + --build-swift-dynamic-stdlib false \ + --build-swift-static-sdk-overlay \ --build-swift-static-stdlib \ --llvm-targets-to-build "X86;WebAssembly" \ --stdlib-deployment-targets "wasi-wasm32" \ diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 3fdbeda5edf70..43eb9102e1f17 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -531,9 +531,7 @@ function(_add_variant_link_flags) if(NOT SWIFT_COMPILER_IS_MSVC_LIKE) # FIXME: On Apple platforms, find_program needs to look for "ld64.lld" find_program(LDLLD_PATH "ld.lld") - if("${SWIFT_SDK_${LFLAGS_SDK}_OBJECT_FORMAT}" STREQUAL "WASM") - list(APPEND result "-fuse-ld=${CMAKE_SOURCE_DIR}/fakeld") - elseif((SWIFT_ENABLE_LLD_LINKER AND LDLLD_PATH AND NOT APPLE) OR + if((SWIFT_ENABLE_LLD_LINKER AND LDLLD_PATH AND NOT APPLE) OR ("${LFLAGS_SDK}" STREQUAL "WINDOWS" AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "WINDOWS")) list(APPEND result "-fuse-ld=lld") @@ -1320,6 +1318,12 @@ function(_add_swift_library_single target name) list(APPEND c_compile_flags -D_WINDLL) endif() endif() + # Double-check that we're not trying to build a dynamic library for WASM. + if(SWIFTLIB_SINGLE_SDK MATCHES WASM) + if(libkind STREQUAL SHARED) + message(FATAL_ERROR "WASM does not support shared libraries.") + endif() + endif() _add_variant_link_flags( SDK "${SWIFTLIB_SINGLE_SDK}" ARCH "${SWIFTLIB_SINGLE_ARCHITECTURE}" diff --git a/fakeld b/fakeld deleted file mode 100755 index 1df157004de91..0000000000000 --- a/fakeld +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python3 -import sys -def outputname(): - for i in range(len(sys.argv)): - if sys.argv[i] == "-o": - return sys.argv[i + 1] - return "a.out" -with open(outputname(), "wb") as outfile: - pass diff --git a/stdlib/private/CMakeLists.txt b/stdlib/private/CMakeLists.txt index 45cf31363574b..9e900d1178773 100644 --- a/stdlib/private/CMakeLists.txt +++ b/stdlib/private/CMakeLists.txt @@ -18,7 +18,8 @@ if(SWIFT_BUILD_SDK_OVERLAY) add_subdirectory(OSLog) - if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + list_intersect("${SWIFT_APPLE_PLATFORMS}" "${SWIFT_SDKS}" building_darwin_sdks) + if(building_darwin_sdks) add_subdirectory(StdlibUnittestFoundationExtras) if (SWIFT_INCLUDE_TESTS) add_subdirectory(SwiftReflectionTest) From 7b5516fffdea05cb1e56137d334bfcf928b4c7f0 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 25 Dec 2019 01:12:36 +0900 Subject: [PATCH 475/478] Re-enable tests (#24) * [WASM] Re-enable test targets * [WASM] Support WASI OS target for lit.cfg * [WASM] Link wasm32-wasi-unknown to wasm32-wasi * [WASM] Add sysroot to clang_linker to find crt1.o * [WASM] Separate WebAssembly test from linux * [WASM] Add static-executable-args.lnk for wasm * [WASM] First support of wasm in autolink-extract * [WASM] Extract link flags from named data segment from wasm object file * [WASM] Fix stdlib build on Linux * [WASM] Fix ICU lib flag to specify lib file instead of dir * [WASM] wip * [WASM] Iterate all configured sdks * [WASM] Remove object format specific code from macro * [WASM] Copy libswiftImageInspection.a to lib/swift_static/wasi * Use brew installed clang because Xcode version is old * [WASM] Fix new wasm/wasi triple * [WASM] Run executable test on wasmtime * Fix typo * [WASM] Cut environment from triple * Move build script into wasm dir * Run test on CI * Cleanup unused scripts * [WASM] Use -static instead of -static-executable to work emit-library * Proxy arguments to build-script * Ignore test failure temporarily * Fix packing command * Add missing x * Use wasi-sdk's clang compiler * [WASM] Avoid to build BlocksRuntime temporary due to os toolchains's clang limitation * [WASM] Comment out utime.h from glibc * [WASM] Change sysroot dir as wasi-sysroot * [WASM] Avoid to build BlocksRuntime on linux * [WASM] Add mman flag for wasi This eliminate clang hack which defines _WASI_EMULATED_MMAN as a predefined macro in clang ref: https://github.com/apple/llvm-project/compare/swift/master...swiftwasm:swiftwasm#diff-773abe7c69fccf723aa2d75447faa136R63-R66 * [WASM] Use latest wasi-sdk * [WASM] Avoid to build swift-reflection-test temporarily * [WASM] Install wasmtime on CI * [WASM] Set wasm as primary target to avoid to build SwiftRuntimeTests * [WASM] Fix macro arguments validation * [WASM] Copy ICU libs into toolchain * [WASM] Fix to specify libicu on mac * Remove extra space from build scripts --- .github/workflows/main.yml | 19 +++- CMakeLists.txt | 4 +- build-linux.sh | 30 ------ buildstartend.sh | 4 - ci-linux.sh | 34 ------- ci-mac.sh | 21 ---- cmake/modules/AddSwift.cmake | 6 +- cmake/modules/SwiftConfigureSDK.cmake | 9 +- lib/Driver/CMakeLists.txt | 43 +++++++- linkPlease.sh | 17 ---- stdlib/public/Platform/glibc.modulemap.gyb | 2 + stdlib/public/runtime/CMakeLists.txt | 99 +++++++++++++------ test/CMakeLists.txt | 45 +++++---- test/lit.cfg | 63 ++++++++++++ test/lit.site.cfg.in | 3 + tools/driver/autolink_extract_main.cpp | 29 ++++++ utils/webassembly/build-linux.sh | 35 +++++++ .../webassembly/build-mac.sh | 23 +++-- utils/webassembly/ci-linux.sh | 53 ++++++++++ utils/webassembly/ci-mac.sh | 43 ++++++++ utils/webassembly/static-executable-args.lnk | 14 +++ utils/webassembly/static-stdlib-args.lnk | 15 +++ vvv.sh | 11 --- 23 files changed, 429 insertions(+), 193 deletions(-) delete mode 100755 build-linux.sh delete mode 100755 buildstartend.sh delete mode 100755 ci-linux.sh delete mode 100755 ci-mac.sh delete mode 100755 linkPlease.sh create mode 100755 utils/webassembly/build-linux.sh rename build-mac.sh => utils/webassembly/build-mac.sh (55%) create mode 100755 utils/webassembly/ci-linux.sh create mode 100755 utils/webassembly/ci-mac.sh create mode 100644 utils/webassembly/static-executable-args.lnk create mode 100644 utils/webassembly/static-stdlib-args.lnk delete mode 100755 vvv.sh diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8ea42eb65111b..f300cbd721967 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,12 +16,19 @@ jobs: steps: - uses: actions/checkout@v1 - name: Build Linux installable archive - run: ./ci-linux.sh + run: ./utils/webassembly/ci-linux.sh - name: Upload Linux installable archive uses: actions/upload-artifact@v1 with: name: linux-installable path: ../swiftwasm-linux.tar.gz + - name: Pack test results + run: tar cJf swift-test-results.tar.gz ../build/*/swift-linux-x86_64/swift-test-results + - name: Upload test results + uses: actions/upload-artifact@v1 + with: + name: linux-test-results + path: ./swift-test-results.tar.gz macos_build: timeout-minutes: 0 @@ -30,7 +37,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Build macOS installable archive - run: ./ci-mac.sh + run: ./utils/webassembly/ci-mac.sh - name: Upload macOS installable archive uses: actions/upload-artifact@v1 with: @@ -41,7 +48,13 @@ jobs: with: name: packaging-scripts path: utils/webassembly - + - name: Pack test results + run: tar cJf swift-test-results.tar.gz ../build/*/swift-macosx-x86_64/swift-test-results + - name: Upload test results + uses: actions/upload-artifact@v1 + with: + name: macos-test-results + path: ./swift-test-results.tar.gz package: name: Build SwiftWasm packages needs: diff --git a/CMakeLists.txt b/CMakeLists.txt index 7af1c790c1303..460a6349515ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1152,8 +1152,8 @@ if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") endif() if(SWIFT_INCLUDE_TESTS) - #add_subdirectory(test) - #add_subdirectory(unittests) + add_subdirectory(test) + add_subdirectory(unittests) endif() if(SWIFT_INCLUDE_DOCS) add_subdirectory(docs) diff --git a/build-linux.sh b/build-linux.sh deleted file mode 100755 index c13e91b796dfc..0000000000000 --- a/build-linux.sh +++ /dev/null @@ -1,30 +0,0 @@ -#/bin/bash - -export sourcedir=$PWD/.. - -./utils/build-script --release --wasm --verbose \ - --skip-build-benchmarks \ - --extra-cmake-options=" \ - -DSWIFT_SDKS='WASI;LINUX' \ - -DSWIFT_BUILD_SOURCEKIT=FALSE \ - -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ - -DCMAKE_AR='$sourcedir/wasi-sdk/bin/llvm-ar' \ - -DCMAKE_RANLIB='$sourcedir/wasi-sdk/bin/llvm-ranlib' \ - " \ - --build-stdlib-deployment-targets "wasi-wasm32" \ - --build-swift-dynamic-sdk-overlay false \ - --build-swift-dynamic-stdlib false \ - --build-swift-static-sdk-overlay \ - --build-swift-static-stdlib \ - --install-destdir="$sourcedir/install" \ - --install-prefix="/opt/swiftwasm-sdk" \ - --install-swift \ - --installable-package="$sourcedir/swiftwasm-linux.tar.gz" \ - --llvm-targets-to-build "X86;WebAssembly" \ - --stdlib-deployment-targets "wasi-wasm32" \ - --wasi-icu-data "todo-icu-data" \ - --wasi-icu-i18n "$sourcedir/icu_out/lib" \ - --wasi-icu-i18n-include "$sourcedir/icu_out/include" \ - --wasi-icu-uc "$sourcedir/icu_out/lib" \ - --wasi-icu-uc-include "$sourcedir/icu_out/include" \ - --wasi-sdk "$sourcedir/wasi-sdk" diff --git a/buildstartend.sh b/buildstartend.sh deleted file mode 100755 index 4739c828f17f9..0000000000000 --- a/buildstartend.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -set -e -/home/zhuowei/wasi-sdk/bin/clang++ -c swift_start.cpp -/home/zhuowei/wasi-sdk/bin/clang++ -c swift_end.cpp diff --git a/ci-linux.sh b/ci-linux.sh deleted file mode 100755 index 467e38b1f1403..0000000000000 --- a/ci-linux.sh +++ /dev/null @@ -1,34 +0,0 @@ -#/bin/bash - -sudo apt update -sudo apt install \ - git ninja-build clang python \ - uuid-dev libicu-dev icu-devtools libbsd-dev \ - libedit-dev libxml2-dev libsqlite3-dev swig \ - libpython-dev libncurses5-dev pkg-config \ - libblocksruntime-dev libcurl4-openssl-dev \ - systemtap-sdt-dev tzdata rsync wget - -export current_sha=`git rev-parse HEAD` -./utils/update-checkout --clone --scheme wasm -git checkout $current_sha -export sourcedir=$PWD/.. -cd $sourcedir - -wget -O install_cmake.sh "https://github.com/Kitware/CMake/releases/download/v3.15.3/cmake-3.15.3-Linux-x86_64.sh" -chmod +x install_cmake.sh -sudo mkdir -p /opt/cmake -sudo ./install_cmake.sh --skip-license --prefix=/opt/cmake -sudo ln -sf /opt/cmake/bin/* /usr/local/bin -cmake --version - -wget -O wasi-sdk.tar.gz https://github.com/swiftwasm/wasi-sdk/releases/download/20191022.1/wasi-sdk-4.39g3025a5f47c04-linux.tar.gz -tar xfz wasi-sdk.tar.gz -mv wasi-sdk-4.39g3025a5f47c04 ./wasi-sdk -mv wasi-sdk/share/wasi-sysroot wasi-sdk/share/sysroot - -wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" -tar xf icu.tar.xz - -cd swift -./build-linux.sh diff --git a/ci-mac.sh b/ci-mac.sh deleted file mode 100755 index d3a08d7164af9..0000000000000 --- a/ci-mac.sh +++ /dev/null @@ -1,21 +0,0 @@ -#/bin/bash - -brew install cmake ninja llvm -export current_sha=`git rev-parse HEAD` -./utils/update-checkout --clone --scheme wasm -git checkout $current_sha -export sourcedir=$PWD/.. -cd $sourcedir -wget -O wasi-sdk.tar.gz https://github.com/swiftwasm/wasi-sdk/releases/download/20191022.1/wasi-sdk-4.39g3025a5f47c04-linux.tar.gz -tar xfz wasi-sdk.tar.gz -mv wasi-sdk-4.39g3025a5f47c04 ./wasi-sdk -mv wasi-sdk/share/wasi-sysroot wasi-sdk/share/sysroot -# Link sysroot/usr/include to sysroot/include because Darwin sysroot doesn't -# find header files in sysroot/include but sysroot/usr/include -mkdir wasi-sdk/share/sysroot/usr/ -ln -s ../include wasi-sdk/share/sysroot/usr/include -wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" -tar xf icu.tar.xz - -cd swift -./build-mac.sh diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 43eb9102e1f17..ec712098bac22 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -332,6 +332,8 @@ function(_add_variant_c_compile_flags) list(APPEND result -isystem;${path}) endforeach() list(APPEND result "-D__ANDROID_API__=${SWIFT_ANDROID_API_LEVEL}") + elseif("${CFLAGS_SDK}" STREQUAL "WASI") + list(APPEND result "-D_WASI_EMULATED_MMAN") elseif(CFLAGS_SDK STREQUAL WINDOWS) swift_windows_include_for_arch(${CFLAGS_ARCH} ${CFLAGS_ARCH}_INCLUDE) foreach(path ${${CFLAGS_ARCH}_INCLUDE}) @@ -391,6 +393,8 @@ function(_add_variant_swift_compile_flags foreach(path IN LISTS ${arch}_swift_include) list(APPEND result "\"${CMAKE_INCLUDE_FLAG_C}${path}\"") endforeach() + elseif("${sdk}" STREQUAL "WASI") + list(APPEND result "-Xcc" "-D_WASI_EMULATED_MMAN") endif() if(NOT BUILD_STANDALONE) @@ -501,7 +505,7 @@ function(_add_variant_link_flags) list(APPEND library_search_directories ${path}) endforeach() elseif("${LFLAGS_SDK}" STREQUAL "WASI") - # No extra libraries needed. + list(APPEND result "-Wl,wasi-emulated-mman") else() # If lto is enabled, we need to add the object path flag so that the LTO code # generator leaves the intermediate object file in a place where it will not diff --git a/cmake/modules/SwiftConfigureSDK.cmake b/cmake/modules/SwiftConfigureSDK.cmake index 0bc415c563b7f..f04e6c186f414 100644 --- a/cmake/modules/SwiftConfigureSDK.cmake +++ b/cmake/modules/SwiftConfigureSDK.cmake @@ -325,11 +325,10 @@ macro(configure_sdk_unix name architectures) if(NOT arch STREQUAL wasm32) message(FATAL_ERROR "unsupported arch for WebAssembly: ${arch}") endif() - set(SWIFT_SDK_WASI_ARCH_wasm32_PATH "${SWIFT_WASI_SDK_PATH}/share/sysroot") - # fixme: Wasi is wasm32-unknown-wasi-musl. This LLVM doesn't have it yet. - set(SWIFT_SDK_WASI_ARCH_wasm32_TRIPLE "wasm32-unknown-unknown-wasi") - set(SWIFT_SDK_WASI_ARCH_wasm32_LIBC_INCLUDE_DIRECTORY "${SWIFT_WASI_SDK_PATH}/share/sysroot/include") - set(SWIFT_SDK_WASI_ARCH_wasm32_LIBC_ARCHITECTURE_INCLUDE_DIRECTORY "${SWIFT_WASI_SDK_PATH}/share/sysroot/include") + set(SWIFT_SDK_WASI_ARCH_wasm32_PATH "${SWIFT_WASI_SDK_PATH}/share/wasi-sysroot") + set(SWIFT_SDK_WASI_ARCH_wasm32_TRIPLE "wasm32-unknown-wasi") + set(SWIFT_SDK_WASI_ARCH_wasm32_LIBC_INCLUDE_DIRECTORY "${SWIFT_WASI_SDK_PATH}/share/wasi-sysroot/include") + set(SWIFT_SDK_WASI_ARCH_wasm32_LIBC_ARCHITECTURE_INCLUDE_DIRECTORY "${SWIFT_WASI_SDK_PATH}/share/wasi-sysroot/include") else() message(FATAL_ERROR "unknown Unix OS: ${prefix}") endif() diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt index 83d6afa8ca55c..d1c4893e30152 100644 --- a/lib/Driver/CMakeLists.txt +++ b/lib/Driver/CMakeLists.txt @@ -33,9 +33,8 @@ target_link_libraries(swiftDriver PRIVATE if(SWIFT_BUILD_STATIC_STDLIB) set(static_stdlib_lnk_file_list) foreach(sdk ${SWIFT_CONFIGURED_SDKS}) - if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF" OR - "${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "WASM") - string(TOLOWER "${sdk}" lowercase_sdk) + string(TOLOWER "${sdk}" lowercase_sdk) + if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF") if(SWIFT_${SWIFT_HOST_VARIANT_SDK}_${SWIFT_HOST_VARIANT_ARCH}_ICU_STATICLIB) set(ICU_STATICLIB "TRUE") else() @@ -62,6 +61,44 @@ if(SWIFT_BUILD_STATIC_STDLIB) swift_install_in_component(FILES "${SWIFTSTATICLIB_DIR}/${linkfile}" DESTINATION "lib/swift_static/${lowercase_sdk}" COMPONENT stdlib) + elseif("${sdk}" STREQUAL "WASI") + set(linkfile_src "${SWIFT_SOURCE_DIR}/utils/webassembly/static-stdlib-args.lnk") + set(linkfile "${lowercase_sdk}/static-stdlib-args.lnk") + add_custom_command_target(swift_static_stdlib_${sdk}_args + COMMAND + "${CMAKE_COMMAND}" -E copy + "${linkfile_src}" + "${SWIFTSTATICLIB_DIR}/${linkfile}" + OUTPUT + "${SWIFTSTATICLIB_DIR}/${linkfile}" + DEPENDS + "${linkfile_src}") + + list(APPEND static_stdlib_lnk_file_list ${swift_static_stdlib_${sdk}_args}) + swift_install_in_component(FILES "${SWIFTSTATICLIB_DIR}/${linkfile}" + DESTINATION "lib/swift_static/${lowercase_sdk}" + COMPONENT stdlib) + set(swift_icu_libs_wasi_list) + set(icu_modules UC I18N DATA) + foreach(module IN LISTS icu_modules) + set(module_lib "${SWIFT_WASI_wasm32_ICU_${module}}") + get_filename_component(module_lib_name ${module_lib} NAME) + add_custom_command_target(swift_icu_${module}_${sdk} + COMMAND + "${CMAKE_COMMAND}" -E copy + "${module_lib}" + "${SWIFTSTATICLIB_DIR}/${lowercase_sdk}/${module_lib_name}" + OUTPUT + "${SWIFTSTATICLIB_DIR}/${lowercase_sdk}/${module_lib_name}" + DEPENDS + "${module_lib}") + list(APPEND swift_icu_libs_wasi_list ${swift_icu_${module}_${sdk}}) + swift_install_in_component(FILES "${SWIFTSTATICLIB_DIR}/${lowercase_sdk}/${module_lib_name}" + DESTINATION "lib/swift_static/${lowercase_sdk}" + COMPONENT stdlib) + endforeach() + add_custom_target(swift_icu_libs_wasi ALL DEPENDS ${swift_icu_libs_wasi_list}) + add_dependencies(stdlib swift_icu_libs_wasi) endif() endforeach() add_custom_target(swift_static_lnk_args ALL DEPENDS ${static_stdlib_lnk_file_list}) diff --git a/linkPlease.sh b/linkPlease.sh deleted file mode 100755 index d3b160ed6bd72..0000000000000 --- a/linkPlease.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -# Copy to ../build/Ninja-RelWithDebInfoAssert/swift-linux-x86_64/bin -exec /home/zhuowei/wasi-sdk/bin/wasm-ld --error-limit=0 -o hello.wasm \ - /home/zhuowei/wasi-sdk/share/sysroot/lib/wasm32-wasi/crt1.o \ - /home/zhuowei/swift-source/swift/swift_start.o \ - ../lib/swift_static/wasm/wasm32/swiftrt.o \ - hello.o \ - -L../lib/swift_static/wasm/wasm32 \ - -L../lib/swift/wasm/wasm32 \ - -L/home/zhuowei/wasi-sdk/share/sysroot/lib/wasm32-wasi \ - -L/home/zhuowei/Documents/BuildICU/icu_out/lib \ - -lswiftCore \ - -lc -lc++ -lc++abi -lswiftImageInspectionShared \ - -licuuc -licudata \ - /home/zhuowei/wasi-sdk/lib/clang/8.0.0/lib/wasi/libclang_rt.builtins-wasm32.a /home/zhuowei/Documents/FakePthread/*.o \ - /home/zhuowei/swift-source/swift/swift_end.o \ - --verbose --no-gc-sections diff --git a/stdlib/public/Platform/glibc.modulemap.gyb b/stdlib/public/Platform/glibc.modulemap.gyb index 5207835d369d2..963d3c7990cb7 100644 --- a/stdlib/public/Platform/glibc.modulemap.gyb +++ b/stdlib/public/Platform/glibc.modulemap.gyb @@ -529,10 +529,12 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/unistd.h" export * } +% if CMAKE_SDK != "WASI": module utime { header "${GLIBC_INCLUDE_PATH}/utime.h" export * } +% end } } diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 6a630fbc843c5..bff88cf1ed5c4 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -89,86 +89,121 @@ set(swift_runtime_library_compile_flags ${swift_runtime_compile_flags}) list(APPEND swift_runtime_library_compile_flags -DswiftCore_EXPORTS) list(APPEND swift_runtime_library_compile_flags -I${SWIFT_SOURCE_DIR}/include) -set(image_inspection_shared_sdk) -if(SWIFT_BUILD_STATIC_STDLIB AND "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "LINUX") - set(image_inspection_shared_sdk "${SWIFT_HOST_VARIANT_SDK}") -elseif("${SWIFT_PRIMARY_VARIANT_SDK}" STREQUAL "WASI") - set(image_inspection_shared_sdk "${SWIFT_PRIMARY_VARIANT_SDK}") -endif() - -if(NOT "${image_inspection_shared_sdk}" STREQUAL "") - set(sdk "${image_inspection_shared_sdk}") - list(REMOVE_ITEM swift_runtime_sources ImageInspectionELF.cpp) - set(static_binary_lnk_file_list) +set(static_binary_lnk_file_list) +set(static_binary_dependencies_list) +macro(add_image_inspection_shared sdk primary_arch inspection_file linkfile_src) + if(${inspection_file} IN_LIST swift_runtime_sources) + list(REMOVE_ITEM swift_runtime_sources ${inspection_file}) + endif() string(TOLOWER "${sdk}" lowercase_sdk) # These two libraries are only used with the static swiftcore add_swift_target_library(swiftImageInspectionShared STATIC - ImageInspectionELF.cpp + ${inspection_file} C_COMPILE_FLAGS ${swift_runtime_library_compile_flags} LINK_FLAGS ${swift_runtime_linker_flags} SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + TARGET_SDKS ${sdk} INSTALL_IN_COMPONENT stdlib) foreach(arch IN LISTS SWIFT_SDK_${sdk}_ARCHITECTURES) set(FragileSupportLibrary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}) set(LibraryLocation ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}/${arch}) - add_custom_command_target(swift_image_inspection_${arch}_static + + add_custom_command_target(swift_image_inspection_${lowercase_sdk}_${arch}_static COMMAND "${CMAKE_COMMAND}" -E copy $ ${LibraryLocation} OUTPUT "${LibraryLocation}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" DEPENDS ${FragileSupportLibrary}) + + list(APPEND static_binary_dependencies_list ${swift_image_inspection_${lowercase_sdk}_${arch}_static}) add_dependencies(stdlib ${FragileSupportLibrary}) swift_install_in_component(FILES $ DESTINATION "lib/swift_static/${lowercase_sdk}/${arch}" COMPONENT stdlib) endforeach() - set(FragileSupportLibraryPrimary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${SWIFT_PRIMARY_VARIANT_ARCH}) - set(LibraryLocationPrimary ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}) - add_custom_command_target(swift_image_inspection_static_primary_arch - COMMAND - "${CMAKE_COMMAND}" -E copy $ ${LibraryLocationPrimary} - OUTPUT - "${LibraryLocationPrimary}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" - DEPENDS - ${FragileSupportLibraryPrimary}) + if(NOT "${primary_arch}" STREQUAL "") + set(FragileSupportLibraryPrimary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${primary_arch}) + set(LibraryLocationPrimary ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}) + add_custom_command_target(swift_image_inspection_static_${lowercase_sdk}_primary_arch + COMMAND + "${CMAKE_COMMAND}" -E copy $ ${LibraryLocationPrimary} + OUTPUT + "${LibraryLocationPrimary}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" + DEPENDS + ${FragileSupportLibraryPrimary}) + list(APPEND static_binary_dependencies_list ${swift_image_inspection_static_${lowercase_sdk}_primary_arch}) add_dependencies(stdlib ${FragileSupportLibraryPrimary}) swift_install_in_component(FILES $ DESTINATION "lib/swift_static/${lowercase_sdk}" COMPONENT stdlib) + endif() - # Generate the static-executable-args.lnk file used for ELF systems (eg linux) set(linkfile "${lowercase_sdk}/static-executable-args.lnk") add_custom_command_target(swift_static_binary_${sdk}_args COMMAND "${CMAKE_COMMAND}" -E copy - "${SWIFT_SOURCE_DIR}/utils/static-executable-args.lnk" + "${linkfile_src}" "${SWIFTSTATICLIB_DIR}/${linkfile}" OUTPUT "${SWIFTSTATICLIB_DIR}/${linkfile}" DEPENDS - "${SWIFT_SOURCE_DIR}/utils/static-executable-args.lnk") + "${linkfile_src}") list(APPEND static_binary_lnk_file_list ${swift_static_binary_${sdk}_args}) swift_install_in_component(FILES "${SWIFTSTATICLIB_DIR}/${linkfile}" DESTINATION "lib/swift_static/${lowercase_sdk}" COMPONENT stdlib) - add_custom_target(static_binary_magic ALL DEPENDS ${static_binary_lnk_file_list}) - foreach(arch IN LISTS SWIFT_SDK_LINUX_ARCHITECTURES) - add_dependencies(static_binary_magic ${swift_image_inspection_${arch}_static}) - endforeach() - add_dependencies(static_binary_magic ${swift_image_inspection_static_primary_arch}) - add_dependencies(stdlib static_binary_magic) add_swift_target_library(swiftImageInspectionSharedObject OBJECT_LIBRARY - ImageInspectionELF.cpp + ${inspection_file} C_COMPILE_FLAGS ${swift_runtime_library_compile_flags} LINK_FLAGS ${swift_runtime_linker_flags} SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + TARGET_SDKS ${sdk} INSTALL_IN_COMPONENT never_install) +endmacro() + +set(is_image_inspection_required) +foreach(sdk IN LISTS SWIFT_SDKS) + set(image_inspection_shared_sdk) + set(primary_arch) + set(image_inspection_shared_file) + set(linkfile_src) + + if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF") + list(APPEND ELFISH_SDKS ${sdk}) + set(linkfile_src "${SWIFT_SOURCE_DIR}/utils/static-executable-args.lnk") + elseif("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "WASM") + set(linkfile_src "${SWIFT_SOURCE_DIR}/utils/webassembly/static-executable-args.lnk") + endif() + + if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") + set(image_inspection_shared_sdk "${sdk}") + set(image_inspection_shared_file ImageInspectionELF.cpp) + elseif(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "WASI") + set(image_inspection_shared_sdk "${sdk}") + set(image_inspection_shared_file ImageInspectionELF.cpp) + # Set default arch + set(primary_arch "wasm32") + endif() + + if("${sdk}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") + set(primary_arch ${SWIFT_PRIMARY_VARIANT_ARCH}) + endif() + + if(NOT "${image_inspection_shared_sdk}" STREQUAL "" AND NOT "${primary_arch}" STREQUAL "") + set(is_image_inspection_required TRUE) + add_image_inspection_shared(${image_inspection_shared_sdk} ${primary_arch} ${image_inspection_shared_file} ${linkfile_src}) + endif() +endforeach() + +if(is_image_inspection_required) + add_custom_target(static_binary_magic ALL DEPENDS ${static_binary_lnk_file_list} ${static_binary_dependencies_list}) + add_dependencies(stdlib static_binary_magic) endif() add_swift_target_library(swiftRuntime OBJECT_LIBRARY diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9ffc66df937d8..eae2c56ee1ce0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -76,7 +76,7 @@ function(get_test_dependencies SDK result_var_name) ("${SDK}" STREQUAL "ANDROID") OR ("${SDK}" STREQUAL "WINDOWS") OR ("${SDK}" STREQUAL "HAIKU") OR - ("${SDK}" STREQUAL "WASM")) + ("${SDK}" STREQUAL "WASI")) # No extra dependencies. else() message(FATAL_ERROR "Unknown SDK: ${SDK}") @@ -207,34 +207,39 @@ foreach(SDK ${SWIFT_SDKS}) # NOTE create a stub BlocksRuntime library that can be used for the # reflection tests - file(WRITE ${test_bin_dir}/Inputs/BlocksRuntime.c + if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin AND (SWIFT_BUILD_SYNTAXPARSERLIB OR SWIFT_BUILD_SOURCEKIT)) + file(WRITE ${test_bin_dir}/Inputs/BlocksRuntime.c "void #if defined(_WIN32) __declspec(dllexport) #endif _Block_release(void) { }\n") - _add_swift_library_single( - BlocksRuntimeStub${VARIANT_SUFFIX} - BlocksRuntimeStub - SHARED - DONT_EMBED_BITCODE - NOSWIFTRT - ARCHITECTURE ${ARCH} - SDK ${SDK} - INSTALL_IN_COMPONENT dev - ${test_bin_dir}/Inputs/BlocksRuntime.c) - set_target_properties(BlocksRuntimeStub${VARIANT_SUFFIX} PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY ${test_bin_dir} - LIBRARY_OUTPUT_DIRECTORY ${test_bin_dir} - RUNTIME_OUTPUT_DIRECTORY ${test_bin_dir} - OUTPUT_NAME BlocksRuntime) - list(APPEND test_dependencies BlocksRuntimeStub${VARIANT_SUFFIX}) + _add_swift_library_single( + BlocksRuntimeStub${VARIANT_SUFFIX} + BlocksRuntimeStub + SHARED + DONT_EMBED_BITCODE + NOSWIFTRT + ARCHITECTURE ${ARCH} + SDK ${SDK} + INSTALL_IN_COMPONENT dev + ${test_bin_dir}/Inputs/BlocksRuntime.c) + set_target_properties(BlocksRuntimeStub${VARIANT_SUFFIX} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${test_bin_dir} + LIBRARY_OUTPUT_DIRECTORY ${test_bin_dir} + RUNTIME_OUTPUT_DIRECTORY ${test_bin_dir} + OUTPUT_NAME BlocksRuntime) + list(APPEND test_dependencies BlocksRuntimeStub${VARIANT_SUFFIX}) + endif() if(SWIFT_BUILD_STDLIB AND SWIFT_INCLUDE_TESTS) list(APPEND test_dependencies "swift-test-stdlib-${SWIFT_SDK_${SDK}_LIB_SUBDIR}") - list(APPEND test_dependencies - "swift-reflection-test${VARIANT_SUFFIX}_signed") + # wasm: Avoid to build swift-reflection-test because it uses unsupported linker flags for wasm-ld + if(NOT "${SDK}" STREQUAL "WASI") + list(APPEND test_dependencies + "swift-reflection-test${VARIANT_SUFFIX}_signed") + endif() endif() if(NOT "${COVERAGE_DB}" STREQUAL "") diff --git a/test/lit.cfg b/test/lit.cfg index 4ee12fa1e9f31..4511a765d1972 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -1243,6 +1243,69 @@ elif run_os == 'linux-androideabi' or run_os == 'linux-android': '-L%s' % make_path(test_resource_dir, config.target_sdk_name)]) # The Swift interpreter is not available when targeting Android. config.available_features.discard('swift_interpreter') +elif run_os == 'wasi': + tools_directory = pipes.quote(make_path(config.wasi_sdk_path, "bin")) + + if run_cpu == 'wasm32': + lit_config.note("Testing WebAssembly/WASI " + config.variant_triple) + else: + lit_config.fatal("Unknown environment %s %s" % (run_os, run_cpu)) + + config.target_object_format = "wasm" + config.target_shared_library_prefix = 'lib' + config.target_shared_library_suffix = ".so" + config.target_sdk_name = "wasi" + config.target_runtime = "native" + + config.target_swift_autolink_extract = inferSwiftBinary("swift-autolink-extract") + + config.target_build_swift = ' '.join([ + '%s', '-target %s', '-static', '-static-executable', + '-Xcc --sysroot=%s', '-Xclang-linker --sysroot=%s', + '-tools-directory %s', + '-toolchain-stdlib-rpath %s', '%s %s %s %s' + ]) % (config.swiftc, config.variant_triple, + config.variant_sdk, config.variant_sdk, + tools_directory, resource_dir_opt, + mcp_opt, config.swift_test_options, + config.swift_driver_test_options, swift_execution_tests_extra_flags) + config.target_codesign = "echo" + config.target_build_swift_dylib = ( + "%s -parse-as-library -emit-library -o '\\1'" + % (config.target_build_swift)) + config.target_add_rpath = r'-Xlinker -rpath -Xlinker \1' + config.target_swift_frontend = ( + '%s -frontend -target %s %s %s %s %s ' + % (config.swift, config.variant_triple, resource_dir_opt, mcp_opt, + config.swift_test_options, config.swift_frontend_test_options)) + subst_target_swift_frontend_mock_sdk = config.target_swift_frontend + subst_target_swift_frontend_mock_sdk_after = "" + config.target_run = 'wasmtime' + if 'interpret' in lit_config.params: + use_interpreter_for_simple_runs() + config.target_sil_opt = ( + '%s -target %s %s %s %s' % + (config.sil_opt, config.variant_triple, resource_dir_opt, mcp_opt, config.sil_test_options)) + config.target_swift_ide_test = ( + '%s -target %s %s %s %s' % + (config.swift_ide_test, config.variant_triple, resource_dir_opt, + mcp_opt, ccp_opt)) + subst_target_swift_ide_test_mock_sdk = config.target_swift_ide_test + subst_target_swift_ide_test_mock_sdk_after = "" + config.target_swiftc_driver = ( + "%s -target %s -toolchain-stdlib-rpath %s %s" % + (config.swiftc, config.variant_triple, resource_dir_opt, mcp_opt)) + config.target_swift_modulewrap = ( + '%s -modulewrap -target %s' % + (config.swiftc, config.variant_triple)) + config.target_swift_emit_pcm = ( + '%s -emit-pcm -target %s' % + (config.swiftc, config.variant_triple)) + config.target_clang = ( + "clang++ -target %s %s -fobjc-runtime=ios-5.0" % + (config.variant_triple, clang_mcp_opt)) + config.target_ld = "ld -L%r" % (make_path(test_resource_dir, config.target_sdk_name)) + else: lit_config.fatal("Don't know how to define target_run and " diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index 69a41da6cc847..72eb316e92ac3 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -41,6 +41,9 @@ config.darwin_xcrun_toolchain = "@SWIFT_DARWIN_XCRUN_TOOLCHAIN@" config.android_ndk_path = "@SWIFT_ANDROID_NDK_PATH@" config.android_ndk_gcc_version = "@SWIFT_ANDROID_NDK_GCC_VERSION@" +# --- WebAssembly --- +config.wasi_sdk_path = "@SWIFT_WASI_SDK_PATH@" + # --- Windows --- msvc_runtime_flags = { 'MultiThreaded': 'MT', diff --git a/tools/driver/autolink_extract_main.cpp b/tools/driver/autolink_extract_main.cpp index f805714c30eb2..dfc70b9941f5d 100644 --- a/tools/driver/autolink_extract_main.cpp +++ b/tools/driver/autolink_extract_main.cpp @@ -32,6 +32,8 @@ #include "llvm/Object/Archive.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/Wasm.h" +#include "llvm/BinaryFormat/Wasm.h" using namespace swift; using namespace llvm::opt; @@ -140,6 +142,31 @@ extractLinkerFlagsFromObjectFile(const llvm::object::ObjectFile *ObjectFile, return false; } +/// Look inside the object file 'WasmObjectFile' and append any linker flags found in +/// its ".swift1_autolink_entries" section to 'LinkerFlags'. +/// Return 'true' if there was an error, and 'false' otherwise. +static bool +extractLinkerFlagsFromObjectFile(const llvm::object::WasmObjectFile *ObjectFile, + std::vector &LinkerFlags, + CompilerInstance &Instance) { + + // Search for the data segment we hold autolink entries in + for (const llvm::object::WasmSegment &Segment : ObjectFile->dataSegments()) { + if (Segment.Data.Name == ".swift1_autolink_entries") { + + StringRef SegmentData = llvm::toStringRef(Segment.Data.Content); + // entries are null-terminated, so extract them and push them into + // the set. + llvm::SmallVector SplitFlags; + SegmentData.split(SplitFlags, llvm::StringRef("\0", 1), -1, + /*KeepEmpty=*/false); + for (const auto &Flag : SplitFlags) + LinkerFlags.push_back(Flag); + } + } + return false; +} + /// Look inside the binary 'Bin' and append any linker flags found in its /// ".swift1_autolink_entries" section to 'LinkerFlags'. If 'Bin' is an archive, /// recursively look inside all children within the archive. Return 'true' if @@ -150,6 +177,8 @@ static bool extractLinkerFlags(const llvm::object::Binary *Bin, std::vector &LinkerFlags) { if (auto *ObjectFile = llvm::dyn_cast(Bin)) { return extractLinkerFlagsFromObjectFile(ObjectFile, LinkerFlags, Instance); + } else if (auto *ObjectFile = llvm::dyn_cast(Bin)) { + return extractLinkerFlagsFromObjectFile(ObjectFile, LinkerFlags, Instance); } else if (auto *Archive = llvm::dyn_cast(Bin)) { llvm::Error Error = llvm::Error::success(); for (const auto &Child : Archive->children(Error)) { diff --git a/utils/webassembly/build-linux.sh b/utils/webassembly/build-linux.sh new file mode 100755 index 0000000000000..7aa8b90e26cfe --- /dev/null +++ b/utils/webassembly/build-linux.sh @@ -0,0 +1,35 @@ +#/bin/bash + +SOURCE_PATH="$( cd "$(dirname $0)/../../.." && pwd )" +SWIFT_PATH=$SOURCE_PATH/swift + +$SWIFT_PATH/utils/build-script --wasm \ + --skip-build-benchmarks \ + --extra-cmake-options=" \ + -DSWIFT_PRIMARY_VARIANT_SDK:STRING=WASI \ + -DSWIFT_PRIMARY_VARIANT_ARCH:STRING=wasm32 \ + -DSWIFT_SDKS='WASI;LINUX' \ + -DSWIFT_BUILD_SOURCEKIT=FALSE \ + -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ + -DSWIFT_BUILD_SYNTAXPARSERLIB=FALSE \ + -DCMAKE_AR='$SOURCE_PATH/wasi-sdk/bin/llvm-ar' \ + -DCMAKE_RANLIB='$SOURCE_PATH/wasi-sdk/bin/llvm-ranlib' \ + " \ + --build-stdlib-deployment-targets "wasi-wasm32" \ + --build-swift-dynamic-sdk-overlay false \ + --build-swift-dynamic-stdlib false \ + --build-swift-static-sdk-overlay \ + --build-swift-static-stdlib \ + --install-destdir="$SOURCE_PATH/install" \ + --install-prefix="/opt/swiftwasm-sdk" \ + --install-swift \ + --installable-package="$SOURCE_PATH/swiftwasm-linux.tar.gz" \ + --llvm-targets-to-build "X86;WebAssembly" \ + --stdlib-deployment-targets "wasi-wasm32" \ + --wasi-icu-data "$SOURCE_PATH/icu_out/lib/libicudata.a" \ + --wasi-icu-i18n "$SOURCE_PATH/icu_out/lib/libicui18n.a" \ + --wasi-icu-i18n-include "$SOURCE_PATH/icu_out/include" \ + --wasi-icu-uc "$SOURCE_PATH/icu_out/lib/libicuuc.a" \ + --wasi-icu-uc-include "$SOURCE_PATH/icu_out/include" \ + --wasi-sdk "$SOURCE_PATH/wasi-sdk" \ + "$@" diff --git a/build-mac.sh b/utils/webassembly/build-mac.sh similarity index 55% rename from build-mac.sh rename to utils/webassembly/build-mac.sh index ba621a52b1636..1fef9d76ac8d6 100755 --- a/build-mac.sh +++ b/utils/webassembly/build-mac.sh @@ -1,8 +1,9 @@ #/bin/bash -export sourcedir=$PWD/.. +SOURCE_PATH="$( cd "$(dirname $0)/../../.." && pwd )" +SWIFT_PATH=$SOURCE_PATH/swift -./utils/build-script --release --wasm --verbose \ +$SWIFT_PATH/utils/build-script --wasm \ --skip-build-benchmarks \ --extra-cmake-options=" \ -DSWIFT_PRIMARY_VARIANT_SDK:STRING=WASI \ @@ -10,6 +11,7 @@ export sourcedir=$PWD/.. -DSWIFT_OSX_x86_64_ICU_STATICLIB=TRUE \ -DSWIFT_BUILD_SOURCEKIT=FALSE \ -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ + -DSWIFT_BUILD_SYNTAXPARSERLIB=FALSE \ -DCMAKE_AR='/usr/local/opt/llvm/bin/llvm-ar' \ -DCMAKE_RANLIB='/usr/local/opt/llvm/bin/llvm-ranlib' \ " \ @@ -20,13 +22,14 @@ export sourcedir=$PWD/.. --build-swift-static-stdlib \ --llvm-targets-to-build "X86;WebAssembly" \ --stdlib-deployment-targets "wasi-wasm32" \ - --wasi-icu-data "todo-icu-data" \ - --wasi-icu-i18n "$sourcedir/icu_out/lib" \ - --wasi-icu-i18n-include "$sourcedir/icu_out/include" \ - --wasi-icu-uc "$sourcedir/icu_out/lib" \ - --wasi-icu-uc-include "$sourcedir/icu_out/include" \ - --wasi-sdk "$sourcedir/wasi-sdk" \ + --wasi-icu-data "$SOURCE_PATH/icu_out/lib/libicudata.a" \ + --wasi-icu-i18n "$SOURCE_PATH/icu_out/lib/libicui18n.a" \ + --wasi-icu-i18n-include "$SOURCE_PATH/icu_out/include" \ + --wasi-icu-uc "$SOURCE_PATH/icu_out/lib/libicuuc.a" \ + --wasi-icu-uc-include "$SOURCE_PATH/icu_out/include" \ + --wasi-sdk "$SOURCE_PATH/wasi-sdk" \ --install-swift \ --install-prefix="/opt/swiftwasm-sdk" \ - --install-destdir="$sourcedir/install" \ - --installable-package="$sourcedir/swiftwasm-macos.tar.gz" + --install-destdir="$SOURCE_PATH/install" \ + --installable-package="$SOURCE_PATH/swiftwasm-macos.tar.gz" \ + "$@" diff --git a/utils/webassembly/ci-linux.sh b/utils/webassembly/ci-linux.sh new file mode 100755 index 0000000000000..8bfb2229a4574 --- /dev/null +++ b/utils/webassembly/ci-linux.sh @@ -0,0 +1,53 @@ +#/bin/bash + +sudo apt update +sudo apt install \ + git ninja-build clang python \ + uuid-dev libicu-dev icu-devtools libbsd-dev \ + libedit-dev libxml2-dev libsqlite3-dev swig \ + libpython-dev libncurses5-dev pkg-config \ + libblocksruntime-dev libcurl4-openssl-dev \ + systemtap-sdt-dev tzdata rsync wget + +SOURCE_PATH="$( cd "$(dirname $0)/../../.." && pwd )" +SWIFT_PATH=$SOURCE_PATH/swift +BUILD_SCRIPT=$SWIFT_PATH/utils/webassembly/build-linux.sh +cd $SWIFT_PATH + +export current_sha=`git rev-parse HEAD` +./utils/update-checkout --clone --scheme wasm +git checkout $current_sha + +# Install wasmtime + +sudo mkdir /opt/wasmtime && cd /opt/wasmtime +wget -O - "https://github.com/bytecodealliance/wasmtime/releases/download/v0.8.0/wasmtime-v0.8.0-x86_64-linux.tar.xz" | \ + sudo tar x --strip-components 1 +sudo ln -sf /opt/wasmtime/* /usr/local/bin + +cd $SOURCE_PATH + +wget -O install_cmake.sh "https://github.com/Kitware/CMake/releases/download/v3.15.3/cmake-3.15.3-Linux-x86_64.sh" +chmod +x install_cmake.sh +sudo mkdir -p /opt/cmake +sudo ./install_cmake.sh --skip-license --prefix=/opt/cmake +sudo ln -sf /opt/cmake/bin/* /usr/local/bin +cmake --version + +wget -O dist-wasi-sdk.tgz https://github.com/swiftwasm/wasi-sdk/suites/370986556/artifacts/809002 +unzip dist-wasi-sdk.tgz +WASI_SDK_TAR_PATH=$(find dist-ubuntu-latest.tgz -type f -name "wasi-sdk-*") +WASI_SDK_FULL_NAME=$(basename $WASI_SDK_TAR_PATH -linux.tar.gz) +tar xfz $WASI_SDK_TAR_PATH +mv $WASI_SDK_FULL_NAME ./wasi-sdk + +# Link wasm32-wasi-unknown to wasm32-wasi because clang finds crt1.o from sysroot +# with os and environment name `getMultiarchTriple`. +ln -s wasm32-wasi wasi-sdk/share/wasi-sysroot/lib/wasm32-wasi-unknown + +wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" +tar xf icu.tar.xz + +$BUILD_SCRIPT --release --debug-swift-stdlib --verbose +# Run test but ignore failure temporarily +$BUILD_SCRIPT --release --debug-swift-stdlib --verbose -t || true diff --git a/utils/webassembly/ci-mac.sh b/utils/webassembly/ci-mac.sh new file mode 100755 index 0000000000000..a1b52e648da8e --- /dev/null +++ b/utils/webassembly/ci-mac.sh @@ -0,0 +1,43 @@ +#/bin/bash + +brew install cmake ninja llvm + +SOURCE_PATH="$( cd "$(dirname $0)/../../.." && pwd )" +SWIFT_PATH=$SOURCE_PATH/swift +BUILD_SCRIPT=$SWIFT_PATH/utils/webassembly/build-mac.sh +cd $SWIFT_PATH + +export current_sha=`git rev-parse HEAD` +./utils/update-checkout --clone --scheme wasm +git checkout $current_sha + +# Install wasmtime + +sudo mkdir /opt/wasmtime && cd /opt/wasmtime +wget -O - "https://github.com/bytecodealliance/wasmtime/releases/download/v0.8.0/wasmtime-v0.8.0-x86_64-macos.tar.xz" | \ + sudo tar x --strip-components 1 +sudo ln -sf /opt/wasmtime/* /usr/local/bin + +cd $SOURCE_PATH + +wget -O dist-wasi-sdk.tgz https://github.com/swiftwasm/wasi-sdk/suites/370986556/artifacts/809001 +tar xfz dist-wasi-sdk.tgz +WASI_SDK_TAR_PATH=$(find dist-macos-latest.tgz -type f -name "wasi-sdk-*") +WASI_SDK_FULL_NAME=$(basename $WASI_SDK_TAR_PATH -macos.tar.gz) +tar xfz $WASI_SDK_TAR_PATH +mv $WASI_SDK_FULL_NAME ./wasi-sdk + +# Link sysroot/usr/include to sysroot/include because Darwin sysroot doesn't +# find header files in sysroot/include but sysroot/usr/include +mkdir wasi-sdk/share/wasi-sysroot/usr/ +ln -s ../include wasi-sdk/share/wasi-sysroot/usr/include +# Link wasm32-wasi-unknown to wasm32-wasi because clang finds crt1.o from sysroot +# with os and environment name `getMultiarchTriple`. +ln -s wasm32-wasi wasi-sdk/share/wasi-sysroot/lib/wasm32-wasi-unknown + +wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" +tar xf icu.tar.xz + +$BUILD_SCRIPT --release --debug-swift-stdlib --verbose +# Run test but ignore failure temporarily +$BUILD_SCRIPT --release --debug-swift-stdlib --verbose -t || true diff --git a/utils/webassembly/static-executable-args.lnk b/utils/webassembly/static-executable-args.lnk new file mode 100644 index 0000000000000..0f8105ca97de6 --- /dev/null +++ b/utils/webassembly/static-executable-args.lnk @@ -0,0 +1,14 @@ +-static +-lswiftCore +-lswiftImageInspectionShared +-lswiftSwiftOnoneSupport +-licui18n +-licuuc +-licudata +-ldl +-lstdc++ +-lm +-Xlinker --error-limit=0 +-Xlinker --no-gc-sections +-Xlinker --no-threads + diff --git a/utils/webassembly/static-stdlib-args.lnk b/utils/webassembly/static-stdlib-args.lnk new file mode 100644 index 0000000000000..5d1397e2b74b8 --- /dev/null +++ b/utils/webassembly/static-stdlib-args.lnk @@ -0,0 +1,15 @@ +-ldl +-lpthread +-latomic +-lswiftCore +-latomic +-lswiftImageInspectionShared +-licui18n +-licuuc +-licudata +-lstdc++ +-lm +-Xlinker +--exclude-libs +-Xlinker +ALL diff --git a/vvv.sh b/vvv.sh deleted file mode 100755 index 6f202440842fd..0000000000000 --- a/vvv.sh +++ /dev/null @@ -1,11 +0,0 @@ -utils/build-script --release-debuginfo --wasm \ - --llvm-targets-to-build "X86;ARM;AArch64;PowerPC;SystemZ;WebAssembly" \ - --llvm-max-parallel-lto-link-jobs 1 --swift-tools-max-parallel-lto-link-jobs 1 \ - --wasm-wasi-sdk "/home/zhuowei/wasi-sdk" \ - --wasm-icu-uc "todo" \ - --wasm-icu-uc-include "/home/zhuowei/Documents/BuildICU/icu_out/include" \ - --wasm-icu-i18n "todo" \ - --wasm-icu-i18n-include "todo" \ - --wasm-icu-data "todo" \ - --build-swift-static-stdlib \ - "$@" From a2bb34483e0e36a567d6c760e3a87c087594d5ed Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 25 Dec 2019 10:07:34 +0900 Subject: [PATCH 476/478] Eliminate fakepthread dependency (#27) * [WASM] Minimize dependences of pthread * [WASM] Built fakepthread as a part of swift project * [WASM] Always build WasiPthread as static * [WASM] Fix to emit libswiftWasiPthread.a in swift_static * [WASM] Remove unused header file --- include/swift/Basic/Lazy.h | 10 ++ include/swift/Runtime/Mutex.h | 4 +- include/swift/Runtime/MutexWASI.h | 66 ++++++++ lib/ClangImporter/ClangImporter.cpp | 4 + .../SwiftPrivateThreadExtras.swift | 6 + .../ThreadBarriers.swift | 15 ++ stdlib/public/CMakeLists.txt | 3 + stdlib/public/Platform/glibc.modulemap.gyb | 2 +- stdlib/public/WASI/CMakeLists.txt | 3 + stdlib/public/WASI/Pthread.cpp | 153 ++++++++++++++++++ stdlib/public/runtime/CMakeLists.txt | 1 + stdlib/public/runtime/Casting.cpp | 13 +- stdlib/public/runtime/MutexPThread.cpp | 2 +- stdlib/public/runtime/MutexWASI.cpp | 25 +++ stdlib/public/runtime/ThreadLocalStorage.h | 8 +- stdlib/public/stubs/ThreadLocalStorage.cpp | 20 +++ utils/webassembly/static-executable-args.lnk | 4 +- utils/webassembly/static-stdlib-args.lnk | 2 +- 18 files changed, 334 insertions(+), 7 deletions(-) create mode 100644 include/swift/Runtime/MutexWASI.h create mode 100644 stdlib/public/WASI/CMakeLists.txt create mode 100644 stdlib/public/WASI/Pthread.cpp create mode 100644 stdlib/public/runtime/MutexWASI.cpp diff --git a/include/swift/Basic/Lazy.h b/include/swift/Basic/Lazy.h index 594e2ed8b9fcd..647e5aff41c59 100644 --- a/include/swift/Basic/Lazy.h +++ b/include/swift/Basic/Lazy.h @@ -16,12 +16,18 @@ #include #ifdef __APPLE__ #include +#elif defined(__wasi__) +// No pthread on wasi #else #include #endif #include "swift/Basic/Malloc.h" #include "swift/Basic/type_traits.h" +#ifdef __wasi__ +void wasi_polyfill_call_once(int *flag, void *context, void (*func)(void *)); +#endif + namespace swift { #ifdef __APPLE__ @@ -36,6 +42,10 @@ namespace swift { using OnceToken_t = unsigned long; # define SWIFT_ONCE_F(TOKEN, FUNC, CONTEXT) \ _swift_once_f(&TOKEN, CONTEXT, FUNC) +#elif defined(__wasi__) + using OnceToken_t = int; +# define SWIFT_ONCE_F(TOKEN, FUNC, CONTEXT) \ + ::wasi_polyfill_call_once(&TOKEN, CONTEXT, FUNC) #else using OnceToken_t = std::once_flag; # define SWIFT_ONCE_F(TOKEN, FUNC, CONTEXT) \ diff --git a/include/swift/Runtime/Mutex.h b/include/swift/Runtime/Mutex.h index 3bf61177f9790..a24c555ccca03 100644 --- a/include/swift/Runtime/Mutex.h +++ b/include/swift/Runtime/Mutex.h @@ -20,10 +20,12 @@ #include -#if (defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__wasi__)) +#if (defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__HAIKU__)) #include "swift/Runtime/MutexPThread.h" #elif defined(_WIN32) #include "swift/Runtime/MutexWin32.h" +#elif defined(__wasi__) +#include "swift/Runtime/MutexWASI.h" #else #error "Implement equivalent of MutexPThread.h/cpp for your platform." #endif diff --git a/include/swift/Runtime/MutexWASI.h b/include/swift/Runtime/MutexWASI.h new file mode 100644 index 0000000000000..28f212d6f48e9 --- /dev/null +++ b/include/swift/Runtime/MutexWASI.h @@ -0,0 +1,66 @@ +//===--- MutexWin32.h - -----------------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Mutex, ConditionVariable, Read/Write lock, and Scoped lock implementations +// using Windows Slim Reader/Writer Locks and Conditional Variables. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_MUTEX_WASI_H +#define SWIFT_RUNTIME_MUTEX_WASI_H + +namespace swift { + +typedef void* ConditionHandle; +typedef void* MutexHandle; +typedef void* ReadWriteLockHandle; + +#define SWIFT_CONDITION_SUPPORTS_CONSTEXPR 1 +#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 1 +#define SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR 1 + +struct ConditionPlatformHelper { + static constexpr ConditionHandle staticInit() { + return nullptr; + }; + static void init(ConditionHandle &condition) {} + static void destroy(ConditionHandle &condition) {} + static void notifyOne(ConditionHandle &condition) {} + static void notifyAll(ConditionHandle &condition) {} + static void wait(ConditionHandle &condition, MutexHandle &mutex); +}; + +struct MutexPlatformHelper { + static constexpr MutexHandle staticInit() { return nullptr; } + static void init(MutexHandle &mutex, bool checked = false) {} + static void destroy(MutexHandle &mutex) {} + static void lock(MutexHandle &mutex) {} + static void unlock(MutexHandle &mutex) {} + static bool try_lock(MutexHandle &mutex) { return true; } + static void unsafeLock(MutexHandle &mutex) {} + static void unsafeUnlock(MutexHandle &mutex) {} +}; + +struct ReadWriteLockPlatformHelper { + static constexpr ReadWriteLockHandle staticInit() { return nullptr; } + static void init(ReadWriteLockHandle &rwlock) {} + static void destroy(ReadWriteLockHandle &rwlock) {} + static void readLock(ReadWriteLockHandle &rwlock) {} + static bool try_readLock(ReadWriteLockHandle &rwlock) { return true; } + static void readUnlock(ReadWriteLockHandle &rwlock) {} + static void writeLock(ReadWriteLockHandle &rwlock) {} + static bool try_writeLock(ReadWriteLockHandle &rwlock) { return true; } + static void writeUnlock(ReadWriteLockHandle &rwlock) {} +}; +} + +#endif diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index f12ca80cf037c..d2457e642fe71 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -598,6 +598,10 @@ getNormalInvocationArguments(std::vector &invocationArgStrs, }); } + if (triple.isOSWASI()) { + invocationArgStrs.insert(invocationArgStrs.end(), {"-D_WASI_EMULATED_MMAN"}); + } + if (triple.isOSWindows()) { switch (triple.getArch()) { default: llvm_unreachable("unsupported Windows architecture"); diff --git a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift index 831d721f3c588..c9d34e35017c2 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift @@ -98,6 +98,9 @@ public func _stdlib_thread_create_block( } else { return (0, ThreadHandle(bitPattern: threadID)) } +#elseif os(WASI) + // WASI environment has a only single thread + return (0, nil) #else var threadID = _make_pthread_t() let result = pthread_create(&threadID, nil, @@ -128,6 +131,9 @@ public func _stdlib_thread_join( } else { return (CInt(result), nil) } +#elseif os(WASI) + // WASI environment has a only single thread + return (0, nil) #else var threadResultRawPtr: UnsafeMutableRawPointer? let result = pthread_join(thread, &threadResultRawPtr) diff --git a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift index b06ed029bf9b0..8bef3340ec82a 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift @@ -36,6 +36,8 @@ public struct _stdlib_thread_barrier_t { #elseif os(Cygwin) || os(FreeBSD) var mutex: UnsafeMutablePointer? var cond: UnsafeMutablePointer? +#elseif os(WASI) + // No pthread for WASI #else var mutex: UnsafeMutablePointer? var cond: UnsafeMutablePointer? @@ -67,6 +69,8 @@ public func _stdlib_thread_barrier_init( barrier.pointee.cond = UnsafeMutablePointer.allocate(capacity: 1) InitializeConditionVariable(barrier.pointee.cond!) +#elseif os(WASI) + // WASI environment has a only single thread #else barrier.pointee.mutex = UnsafeMutablePointer.allocate(capacity: 1) if pthread_mutex_init(barrier.pointee.mutex!, nil) != 0 { @@ -89,6 +93,8 @@ public func _stdlib_thread_barrier_destroy( #if os(Windows) // Condition Variables do not need to be explicitly destroyed // Mutexes do not need to be explicitly destroyed +#elseif os(WASI) + // WASI environment has a only single thread #else if pthread_cond_destroy(barrier.pointee.cond!) != 0 { // FIXME: leaking memory, leaking a mutex. @@ -99,11 +105,14 @@ public func _stdlib_thread_barrier_destroy( return -1 } #endif + +#if !os(WASI) barrier.pointee.cond!.deinitialize(count: 1) barrier.pointee.cond!.deallocate() barrier.pointee.mutex!.deinitialize(count: 1) barrier.pointee.mutex!.deallocate() +#endif return 0 } @@ -113,6 +122,8 @@ public func _stdlib_thread_barrier_wait( ) -> CInt { #if os(Windows) AcquireSRWLockExclusive(barrier.pointee.mutex!) +#elseif os(WASI) + // WASI environment has a only single thread #else if pthread_mutex_lock(barrier.pointee.mutex!) != 0 { return -1 @@ -127,6 +138,8 @@ public func _stdlib_thread_barrier_wait( return -1 } ReleaseSRWLockExclusive(barrier.pointee.mutex!) +#elseif os(WASI) + // WASI environment has a only single thread #else if pthread_cond_wait(barrier.pointee.cond!, barrier.pointee.mutex!) != 0 { return -1 @@ -144,6 +157,8 @@ public func _stdlib_thread_barrier_wait( #if os(Windows) WakeAllConditionVariable(barrier.pointee.cond!) ReleaseSRWLockExclusive(barrier.pointee.mutex!) +#elseif os(WASI) + // WASI environment has a only single thread #else if pthread_cond_broadcast(barrier.pointee.cond!) != 0 { return -1 diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index fa15c1c9c889c..2f5c8b4f29cbf 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -61,6 +61,9 @@ if(SWIFT_BUILD_STDLIB) add_subdirectory(stubs) add_subdirectory(core) add_subdirectory(SwiftOnoneSupport) + if(WASI IN_LIST SWIFT_SDKS) + add_subdirectory(WASI) + endif() endif() # Build differentiable programming support library only if enabled. diff --git a/stdlib/public/Platform/glibc.modulemap.gyb b/stdlib/public/Platform/glibc.modulemap.gyb index 963d3c7990cb7..b6171c8c321a0 100644 --- a/stdlib/public/Platform/glibc.modulemap.gyb +++ b/stdlib/public/Platform/glibc.modulemap.gyb @@ -377,11 +377,11 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/poll.h" export * } +% if CMAKE_SDK != "WASI": module pthread { header "${GLIBC_INCLUDE_PATH}/pthread.h" export * } -% if CMAKE_SDK != "WASI": module pwd { header "${GLIBC_INCLUDE_PATH}/pwd.h" export * diff --git a/stdlib/public/WASI/CMakeLists.txt b/stdlib/public/WASI/CMakeLists.txt new file mode 100644 index 0000000000000..451f1f3af57d7 --- /dev/null +++ b/stdlib/public/WASI/CMakeLists.txt @@ -0,0 +1,3 @@ +add_swift_target_library(swiftWasiPthread STATIC IS_STDLIB + Pthread.cpp + INSTALL_IN_COMPONENT stdlib) diff --git a/stdlib/public/WASI/Pthread.cpp b/stdlib/public/WASI/Pthread.cpp new file mode 100644 index 0000000000000..5a64d9bdb4c39 --- /dev/null +++ b/stdlib/public/WASI/Pthread.cpp @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: 0BSD +// prototypes taken from opengroup +#include +#include +#include +#include + +#define STUB() do {fprintf(stderr, "FakePthread: unsupported %s\n", __func__);abort();}while(0) + +// mutexes: just no-ops + +int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { + return 0; +} + +int pthread_mutex_destroy(pthread_mutex_t *mutex) { + return 0; +} + +int pthread_mutexattr_init(pthread_mutexattr_t *attr) { + return 0; +} + +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) { + return 0; +} + +int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) { + return 0; +} + +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { + return 0; +} + +int pthread_mutex_lock(pthread_mutex_t *mutex) { + return 0; +} + +int pthread_mutex_trylock(pthread_mutex_t *mutex) { + return 0; +} + +int pthread_mutex_unlock(pthread_mutex_t *mutex) { + return 0; +} + +// pthread_cond: STUB + +int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) { + return 0; +} + +int pthread_cond_destroy(pthread_cond_t *cond) { + return 0; +} + +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { + STUB(); +} + +int pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mutex, const struct timespec *abstime) { + STUB(); +} + +int pthread_cond_broadcast(pthread_cond_t *cond) { + return 0; +} + +int pthread_cond_signal(pthread_cond_t *cond) { + return 0; +} + +// tls + +int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) { + STUB(); +} + +void *pthread_getspecific(pthread_key_t key) { + STUB(); +} + +int pthread_setspecific(pthread_key_t key, const void *value) { + STUB(); +} + +// threads + +pthread_t pthread_self() { + return (pthread_t)1234; +} + +#undef pthread_equal + +int pthread_equal(pthread_t t1, pthread_t t2) { + return t1 == t2; +} + +int pthread_join(pthread_t thread, void **value_ptr) { + STUB(); +} + +int pthread_detach(pthread_t thread) { + STUB(); +} + +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { + return 0; +} + +// once + +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) { + STUB(); +} + +// rwlock + +int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) { + return 0; +} + +int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { + return 0; +} + +int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { + return 0; +} + +int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) { + return 0; +} + +int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { + return 0; +} + +int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) { + return 0; +} + +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { + return 0; +} + +// named semaphores + +sem_t *sem_open(const char *name, int oflag, ...) { + STUB(); +} diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index bff88cf1ed5c4..1c72e7601ed4c 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -56,6 +56,7 @@ set(swift_runtime_sources MetadataLookup.cpp MutexPThread.cpp MutexWin32.cpp + MutexWASI.cpp Numeric.cpp Once.cpp Portability.cpp diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index d7cb076505cb9..fcdd9e5a04c08 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -30,7 +30,12 @@ #include "swift/Runtime/ExistentialContainer.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" -#include "swift/Runtime/Mutex.h" +#ifdef __wasi__ +# define SWIFT_CASTING_SUPPORTS_MUTEX 0 +#else +# define SWIFT_CASTING_SUPPORTS_MUTEX 1 +# include "swift/Runtime/Mutex.h" +#endif #include "swift/Runtime/Unreachable.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerIntPair.h" @@ -125,7 +130,9 @@ TypeNamePair swift::swift_getTypeName(const Metadata *type, bool qualified) { using Key = llvm::PointerIntPair; + #if SWIFT_CASTING_SUPPORTS_MUTEX static StaticReadWriteLock TypeNameCacheLock; + #endif static Lazy>> TypeNameCache; @@ -134,7 +141,9 @@ swift::swift_getTypeName(const Metadata *type, bool qualified) { // Attempt read-only lookup of cache entry. { + #if SWIFT_CASTING_SUPPORTS_MUTEX StaticScopedReadLock guard(TypeNameCacheLock); + #endif auto found = cache.find(key); if (found != cache.end()) { @@ -145,7 +154,9 @@ swift::swift_getTypeName(const Metadata *type, bool qualified) { // Read-only lookup failed to find item, we may need to create it. { + #if SWIFT_CASTING_SUPPORTS_MUTEX StaticScopedWriteLock guard(TypeNameCacheLock); + #endif // Do lookup again just to make sure it wasn't created by another // thread before we acquired the write lock. diff --git a/stdlib/public/runtime/MutexPThread.cpp b/stdlib/public/runtime/MutexPThread.cpp index 62e718c16abaa..92db58226cd09 100644 --- a/stdlib/public/runtime/MutexPThread.cpp +++ b/stdlib/public/runtime/MutexPThread.cpp @@ -15,7 +15,7 @@ // //===----------------------------------------------------------------------===// -#if !defined(_WIN32) +#if !defined(_WIN32) && !defined(__wasi__) #include "swift/Runtime/Mutex.h" #include "swift/Runtime/Debug.h" diff --git a/stdlib/public/runtime/MutexWASI.cpp b/stdlib/public/runtime/MutexWASI.cpp new file mode 100644 index 0000000000000..6288ecce03449 --- /dev/null +++ b/stdlib/public/runtime/MutexWASI.cpp @@ -0,0 +1,25 @@ +//===--- MutexWin32.cpp - -------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Mutex, ConditionVariable, Read/Write lock, and Scoped lock implementations +// using Windows Slim Reader/Writer Locks and Conditional Variables. +// +//===----------------------------------------------------------------------===// + +#if defined(__wasi__) +#include "swift/Runtime/Mutex.h" + +using namespace swift; + +void ConditionPlatformHelper::wait(void* &condition, + void* &mutex) {} +#endif diff --git a/stdlib/public/runtime/ThreadLocalStorage.h b/stdlib/public/runtime/ThreadLocalStorage.h index ebf57f6ceb69a..4e62b76b1d74e 100644 --- a/stdlib/public/runtime/ThreadLocalStorage.h +++ b/stdlib/public/runtime/ThreadLocalStorage.h @@ -98,7 +98,13 @@ static_assert(std::is_same<__swift_thread_key_t, DWORD>::value, # define SWIFT_THREAD_KEY_CREATE _stdlib_thread_key_create # define SWIFT_THREAD_GETSPECIFIC FlsGetValue # define SWIFT_THREAD_SETSPECIFIC(key, value) (FlsSetValue(key, value) == FALSE) - +# elif defined(__wasi__) +int wasi_polyfill_pthread_key_create(__swift_thread_key_t *key, void (*destructor)(void*)); +void *wasi_polyfill_pthread_getspecific(__swift_thread_key_t key); +int wasi_polyfill_pthread_setspecific(__swift_thread_key_t key, const void *value); +# define SWIFT_THREAD_KEY_CREATE wasi_polyfill_pthread_key_create +# define SWIFT_THREAD_GETSPECIFIC wasi_polyfill_pthread_getspecific +# define SWIFT_THREAD_SETSPECIFIC wasi_polyfill_pthread_setspecific # else // Otherwise use the pthread API. # include diff --git a/stdlib/public/stubs/ThreadLocalStorage.cpp b/stdlib/public/stubs/ThreadLocalStorage.cpp index 50168f955bc6c..4e855cb593b72 100644 --- a/stdlib/public/stubs/ThreadLocalStorage.cpp +++ b/stdlib/public/stubs/ThreadLocalStorage.cpp @@ -25,6 +25,26 @@ void *_stdlib_createTLS(void); #if !SWIFT_TLS_HAS_RESERVED_PTHREAD_SPECIFIC || (defined(_WIN32) && !defined(__CYGWIN__)) +#if defined(__wasi__) +#define STUB() do { /* fprintf(stderr, "%s is unsupported on WASI environment\n", __func__);*/ abort(); } while(0) +void wasi_polyfill_call_once(int *flag, void *context, void (*func)(void *)) + { + switch (*flag) { + case 0: + func(context); + *flag = 1; + return; + case 1: + return; + default: + STUB(); + } + } +int wasi_polyfill_pthread_key_create(__swift_thread_key_t *key, void (*destructor)(void*)) { STUB(); } +void *wasi_polyfill_pthread_getspecific(__swift_thread_key_t key) { STUB(); } +int wasi_polyfill_pthread_setspecific(__swift_thread_key_t key, const void *value) { STUB(); } +#endif + static void #if defined(_M_IX86) __stdcall diff --git a/utils/webassembly/static-executable-args.lnk b/utils/webassembly/static-executable-args.lnk index 0f8105ca97de6..d80b4a58a272c 100644 --- a/utils/webassembly/static-executable-args.lnk +++ b/utils/webassembly/static-executable-args.lnk @@ -2,13 +2,15 @@ -lswiftCore -lswiftImageInspectionShared -lswiftSwiftOnoneSupport +-lswiftWasiPthread -licui18n -licuuc -licudata -ldl -lstdc++ -lm +-lwasi-emulated-mman -Xlinker --error-limit=0 -Xlinker --no-gc-sections -Xlinker --no-threads - +-D_WASI_EMULATED_MMAN diff --git a/utils/webassembly/static-stdlib-args.lnk b/utils/webassembly/static-stdlib-args.lnk index 5d1397e2b74b8..af390c6413bd2 100644 --- a/utils/webassembly/static-stdlib-args.lnk +++ b/utils/webassembly/static-stdlib-args.lnk @@ -1,6 +1,6 @@ -ldl --lpthread -latomic +-lswiftWasiPthread -lswiftCore -latomic -lswiftImageInspectionShared From b335c43f4166386fdc8b5ec0c962dbf84a0d69a5 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 25 Dec 2019 10:07:49 +0900 Subject: [PATCH 477/478] Fix sil-func-extractor and driver (#28) * [WASM] Remove rpath flag and link objects using llvm-ar instead of ar * [WASM] Disable objc interop for sil-func-extractor when targeting wasm * [WASM] Exclude test cases which use -enable-objc-interop * [WASM] Make availability_returns_twice.swift unsupoorted on wasi due to lack of setjmp --- lib/Driver/UnixToolChains.cpp | 7 +++++- .../availability_returns_twice.swift | 1 + test/lit.cfg | 24 +++++++++++++++++-- .../SILFunctionExtractor.cpp | 2 ++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/Driver/UnixToolChains.cpp b/lib/Driver/UnixToolChains.cpp index 07a19d9e548ac..7607c31b86ff2 100644 --- a/lib/Driver/UnixToolChains.cpp +++ b/lib/Driver/UnixToolChains.cpp @@ -346,7 +346,12 @@ toolchains::GenericUnix::constructInvocation(const StaticLinkJobAction &job, ArgStringList Arguments; // Configure the toolchain. - const char *AR = "ar"; + const char *AR; + if (getTriple().isOSBinFormatWasm()) { + AR = "llvm-ar"; + } else { + AR = "ar"; + } Arguments.push_back("crs"); Arguments.push_back( diff --git a/test/ClangImporter/availability_returns_twice.swift b/test/ClangImporter/availability_returns_twice.swift index ef9ff6ffad251..c91e922fb0e89 100644 --- a/test/ClangImporter/availability_returns_twice.swift +++ b/test/ClangImporter/availability_returns_twice.swift @@ -1,5 +1,6 @@ // RUN: %target-typecheck-verify-swift // UNSUPPORTED: OS=windows-msvc +// UNSUPPORTED: OS=wasi // In Android jmp_buf is int[16], which doesn't convert to &Int (SR-9136) // XFAIL: OS=linux-androideabi // XFAIL: OS=linux-android diff --git a/test/lit.cfg b/test/lit.cfg index 4511a765d1972..77b91e355b30f 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -1253,10 +1253,30 @@ elif run_os == 'wasi': config.target_object_format = "wasm" config.target_shared_library_prefix = 'lib' - config.target_shared_library_suffix = ".so" + config.target_shared_library_suffix = ".a" config.target_sdk_name = "wasi" config.target_runtime = "native" + # Exclude test cases that use objc-interop because clang doesn't support it + # with WebAssembly binary file yet. + testfiles = glob.glob(os.path.join(config.test_source_root, "**", "*.swift")) + + def use_objc_interop(path): + with open(path) as f: + return '-enable-objc-interop' in f.read() + + import fnmatch + def objc_interop_enabled_filenames(path, filename_pat): + matches = [] + for root, dirnames, filenames in os.walk(path): + for filename in fnmatch.filter(filenames, filename_pat): + filepath = os.path.join(root, filename) + if not use_objc_interop(filepath): continue + matches.append(filename) + return matches + + config.excludes += objc_interop_enabled_filenames(config.test_source_root, "*.swift") + config.target_swift_autolink_extract = inferSwiftBinary("swift-autolink-extract") config.target_build_swift = ' '.join([ @@ -1273,7 +1293,7 @@ elif run_os == 'wasi': config.target_build_swift_dylib = ( "%s -parse-as-library -emit-library -o '\\1'" % (config.target_build_swift)) - config.target_add_rpath = r'-Xlinker -rpath -Xlinker \1' + config.target_add_rpath = '' config.target_swift_frontend = ( '%s -frontend -target %s %s %s %s %s ' % (config.swift, config.variant_triple, resource_dir_opt, mcp_opt, diff --git a/tools/sil-func-extractor/SILFunctionExtractor.cpp b/tools/sil-func-extractor/SILFunctionExtractor.cpp index 7c04d529c33fa..3e95fb3215418 100644 --- a/tools/sil-func-extractor/SILFunctionExtractor.cpp +++ b/tools/sil-func-extractor/SILFunctionExtractor.cpp @@ -249,6 +249,8 @@ int main(int argc, char **argv) { Invocation.getLangOptions().DisableAvailabilityChecking = true; Invocation.getLangOptions().EnableAccessControl = false; Invocation.getLangOptions().EnableObjCAttrRequiresFoundation = false; + if (Invocation.getLangOptions().Target.isOSBinFormatWasm()) + Invocation.getLangOptions().EnableObjCInterop = false; serialization::ExtendedValidationInfo extendedInfo; llvm::ErrorOr> FileBufOrErr = From 5ea67cc008dd53024a8301d488c774650441575e Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 11 Jan 2020 15:30:01 +0000 Subject: [PATCH 478/478] Trigger CI