diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt index fcf092f0dbb89..445ede959a985 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt @@ -19,6 +19,7 @@ swift_compiler_sources(Optimizer SimplifyInitEnumDataAddr.swift SimplifyLoad.swift SimplifyPartialApply.swift + SimplifyRetainReleaseValue.swift SimplifyStrongRetainRelease.swift SimplifyStructExtract.swift SimplifyTupleExtract.swift diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyRetainReleaseValue.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyRetainReleaseValue.swift new file mode 100644 index 0000000000000..cd61cdd972142 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyRetainReleaseValue.swift @@ -0,0 +1,153 @@ +//===--- SimplifyRetainReleaseValue.swift ---------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SIL + +extension RetainValueInst : Simplifyable, SILCombineSimplifyable { + func simplify(_ context: SimplifyContext) { + + // Remove pairs of + // ``` + // release_value %0 // the release is before the retain! + // retain_value %0 + // ``` + // which sometimes the ARC optimizations cannot do. + // + if optimizeReleaseRetainPair(context) { + return + } + + // Replace + // ``` + // %1 = enum #E.A, %0 + // retain_value %1 + // ``` + // with + // ``` + // %1 = enum #E.A, %0 // maybe dead + // retain_value %0 + // ``` + replaceOperandWithPayloadOfEnum(context) + + // Remove if the operand is trivial (but not necessarily its type), e.g. + // ``` + // %1 = value_to_bridge_object %0 : $UInt64 + // retain_value %1 : $Builtin.BridgeObject + // ``` + if removeIfOperandIsTrivial(context) { + return + } + + // Replace e.g. + // ``` + // retain_value %1 : $SomeClass + // ``` + // with + // ``` + // strong_retain %1 : $SomeClass + // ``` + replaceWithStrongOrUnownedRetain(context) + } +} + +extension ReleaseValueInst : Simplifyable, SILCombineSimplifyable { + func simplify(_ context: SimplifyContext) { + + // Replace + // ``` + // %1 = enum #E.A, %0 + // release_value %1 + // ``` + // with + // ``` + // %1 = enum #E.A, %0 // maybe dead + // release_value %0 + // ``` + replaceOperandWithPayloadOfEnum(context) + + // Remove if the operand is trivial (but not necessarily its type), e.g. + // ``` + // %1 = value_to_bridge_object %0 : $UInt64 + // release_value %1 : $Builtin.BridgeObject + // ``` + if removeIfOperandIsTrivial(context) { + return + } + + // Replace e.g. + // ``` + // release_value %1 : $SomeClass + // ``` + // with + // ``` + // release_value %1 : $SomeClass + // ``` + replaceWithStrongOrUnownedRelease(context) + } +} + +private extension RetainValueInst { + func optimizeReleaseRetainPair(_ context: SimplifyContext) -> Bool { + if let prevInst = self.previous, + let release = prevInst as? ReleaseValueInst, + release.value == self.value { + context.erase(instruction: release) + context.erase(instruction: self) + return true + } + return false + } + + func replaceWithStrongOrUnownedRetain(_ context: SimplifyContext) { + if value.type.isReferenceCounted(in: parentFunction) { + let builder = Builder(before: self, context) + if value.type.isUnownedStorageType { + builder.createUnownedRetain(operand: value) + } else { + builder.createStrongRetain(operand: value) + } + context.erase(instruction: self) + } + } +} + +private extension ReleaseValueInst { + func replaceWithStrongOrUnownedRelease(_ context: SimplifyContext) { + if value.type.isReferenceCounted(in: parentFunction) { + let builder = Builder(before: self, context) + if value.type.isUnownedStorageType { + builder.createUnownedRelease(operand: value) + } else { + builder.createStrongRelease(operand: value) + } + context.erase(instruction: self) + } + } +} + +private extension UnaryInstruction { + func replaceOperandWithPayloadOfEnum(_ context: SimplifyContext) { + if let e = operand.value as? EnumInst, + !e.type.isValueTypeWithDeinit, + let payload = e.payload { + operand.set(to: payload, context) + } + } + + func removeIfOperandIsTrivial(_ context: SimplifyContext) -> Bool { + if operand.value.isTrivial(context) { + context.erase(instruction: self) + return true + } + return false + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift index ccc38ca8b94b6..fdaa73acd4a53 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift @@ -87,6 +87,8 @@ private func registerSwiftPasses() { registerForSILCombine(GlobalValueInst.self, { run(GlobalValueInst.self, $0) }) registerForSILCombine(StrongRetainInst.self, { run(StrongRetainInst.self, $0) }) registerForSILCombine(StrongReleaseInst.self, { run(StrongReleaseInst.self, $0) }) + registerForSILCombine(RetainValueInst.self, { run(RetainValueInst.self, $0) }) + registerForSILCombine(ReleaseValueInst.self, { run(ReleaseValueInst.self, $0) }) registerForSILCombine(LoadInst.self, { run(LoadInst.self, $0) }) // Test passes diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index b05b4a6175a02..cf82c5b636485 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -16,6 +16,43 @@ extension Value { var nonDebugUses: LazyFilterSequence { uses.lazy.filter { !($0.instruction is DebugValueInst) } } + + /// Walks over all fields of an aggregate and checks if a reference count + /// operation for this value is required. This differs from a simple `Type.isTrivial` + /// check, because it treats a value_to_bridge_object instruction as "trivial". + /// It can also handle non-trivial enums with trivial cases. + func isTrivial(_ context: some Context) -> Bool { + if self is Undef { + return true + } + var worklist = ValueWorklist(context) + defer { worklist.deinitialize() } + + worklist.pushIfNotVisited(self) + while let v = worklist.pop() { + if v.type.isTrivial(in: parentFunction) { + continue + } + if v.type.isValueTypeWithDeinit { + return false + } + switch v { + case is ValueToBridgeObjectInst: + break + case is StructInst, is TupleInst: + let inst = (v as! SingleValueInstruction) + worklist.pushIfNotVisited(contentsOf: inst.operands.values.filter { !($0 is Undef) }) + case let en as EnumInst: + if let payload = en.payload, + !(payload is Undef) { + worklist.pushIfNotVisited(payload) + } + default: + return false + } + } + return true + } } extension Builder { diff --git a/SwiftCompilerSources/Sources/SIL/ApplySite.swift b/SwiftCompilerSources/Sources/SIL/ApplySite.swift index 3f470e0a69e55..24a6d9dfa5623 100644 --- a/SwiftCompilerSources/Sources/SIL/ApplySite.swift +++ b/SwiftCompilerSources/Sources/SIL/ApplySite.swift @@ -80,7 +80,7 @@ extension ApplySite { /// /// This does not include the callee function operand. public var arguments: LazyMapSequence { - argumentOperands.lazy.map { $0.value } + argumentOperands.values } public var substitutionMap: SubstitutionMap { diff --git a/SwiftCompilerSources/Sources/SIL/Builder.swift b/SwiftCompilerSources/Sources/SIL/Builder.swift index 8d3b4b47cb13a..51ad07ac5c596 100644 --- a/SwiftCompilerSources/Sources/SIL/Builder.swift +++ b/SwiftCompilerSources/Sources/SIL/Builder.swift @@ -136,6 +136,18 @@ public struct Builder { return notifyNew(release.getAs(StrongReleaseInst.self)) } + @discardableResult + public func createUnownedRetain(operand: Value) -> UnownedRetainInst { + let retain = bridged.createUnownedRetain(operand.bridged) + return notifyNew(retain.getAs(UnownedRetainInst.self)) + } + + @discardableResult + public func createUnownedRelease(operand: Value) -> UnownedReleaseInst { + let release = bridged.createUnownedRelease(operand.bridged) + return notifyNew(release.getAs(UnownedReleaseInst.self)) + } + public func createFunctionRef(_ function: Function) -> FunctionRefInst { let functionRef = bridged.createFunctionRef(function.bridged) return notifyNew(functionRef.getAs(FunctionRefInst.self)) diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index e78375671b33e..1a6595ab1b29f 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -197,7 +197,7 @@ public class MultipleValueInstruction : Instruction { } /// Instructions, which have a single operand. -public protocol UnaryInstruction : AnyObject { +public protocol UnaryInstruction : Instruction { var operands: OperandArray { get } var operand: Operand { get } } @@ -306,14 +306,24 @@ final public class StrongRetainInst : RefCountingInst { public var instance: Value { operand.value } } +final public class UnownedRetainInst : RefCountingInst { + public var instance: Value { operand.value } +} + final public class RetainValueInst : RefCountingInst { + public var value: Value { return operand.value } } final public class StrongReleaseInst : RefCountingInst { public var instance: Value { operand.value } } +final public class UnownedReleaseInst : RefCountingInst { + public var instance: Value { operand.value } +} + final public class ReleaseValueInst : RefCountingInst { + public var value: Value { return operand.value } } final public class DestroyValueInst : Instruction, UnaryInstruction { diff --git a/SwiftCompilerSources/Sources/SIL/Operand.swift b/SwiftCompilerSources/Sources/SIL/Operand.swift index 9737ae0ac3a85..eeae9d14d05f9 100644 --- a/SwiftCompilerSources/Sources/SIL/Operand.swift +++ b/SwiftCompilerSources/Sources/SIL/Operand.swift @@ -76,6 +76,10 @@ public struct OperandArray : RandomAccessCollection, CustomReflectable { base: OptionalBridgedOperand(op: base.advancedBy(bounds.lowerBound).op), count: bounds.upperBound - bounds.lowerBound) } + + public var values: LazyMapSequence.Elements, Value> { + self.lazy.map { $0.value } + } } public struct UseList : CollectionLikeSequence { diff --git a/SwiftCompilerSources/Sources/SIL/Registration.swift b/SwiftCompilerSources/Sources/SIL/Registration.swift index a1ecd5809e093..6f1a759d6aacb 100644 --- a/SwiftCompilerSources/Sources/SIL/Registration.swift +++ b/SwiftCompilerSources/Sources/SIL/Registration.swift @@ -54,8 +54,10 @@ public func registerSILClasses() { register(EndApplyInst.self) register(AbortApplyInst.self) register(StrongRetainInst.self) + register(UnownedRetainInst.self) register(RetainValueInst.self) register(StrongReleaseInst.self) + register(UnownedReleaseInst.self) register(ReleaseValueInst.self) register(DestroyValueInst.self) register(DestroyAddrInst.self) diff --git a/SwiftCompilerSources/Sources/SIL/Type.swift b/SwiftCompilerSources/Sources/SIL/Type.swift index 4020894d1aa21..b920b71a76b91 100644 --- a/SwiftCompilerSources/Sources/SIL/Type.swift +++ b/SwiftCompilerSources/Sources/SIL/Type.swift @@ -31,6 +31,10 @@ public struct Type : CustomStringConvertible, NoReflectionChildren { return !bridged.isNonTrivialOrContainsRawPointer(function.bridged.getFunction()) } + /// True if this type is a value type (struct/enum) that requires deinitialization beyond + /// destruction of its members. + public var isValueTypeWithDeinit: Bool { bridged.isValueTypeWithDeinit() } + public func isLoadable(in function: Function) -> Bool { return bridged.isLoadable(function.bridged.getFunction()) } @@ -39,6 +43,10 @@ public struct Type : CustomStringConvertible, NoReflectionChildren { return bridged.isReferenceCounted(function.bridged.getFunction()) } + public var isUnownedStorageType: Bool { + return bridged.isUnownedStorageType() + } + public var hasArchetype: Bool { bridged.hasArchetype() } public var isNominal: Bool { bridged.getNominalOrBoundGenericNominal() != nil } diff --git a/cmake/modules/AddPureSwift.cmake b/cmake/modules/AddPureSwift.cmake index b1bce09bdd7e7..722c52d3f239a 100644 --- a/cmake/modules/AddPureSwift.cmake +++ b/cmake/modules/AddPureSwift.cmake @@ -141,6 +141,12 @@ function(add_pure_swift_host_library name) set_property(TARGET ${name} PROPERTY BUILD_WITH_INSTALL_RPATH YES) + if(APSHL_SHARED AND CMAKE_SYSTEM_NAME STREQUAL Darwin) + # Allow install_name_tool to update paths (for rdar://109473564) + set_property(TARGET ${name} APPEND_STRING PROPERTY + LINK_FLAGS " -Xlinker -headerpad_max_install_names") + endif() + # Respect LLVM_COMMON_DEPENDS if it is set. # # LLVM_COMMON_DEPENDS if a global variable set in ./lib that provides targets @@ -157,7 +163,7 @@ function(add_pure_swift_host_library name) add_custom_command( TARGET ${name} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E touch_nocreate $ $ + COMMAND "${CMAKE_COMMAND}" -E touch_nocreate $ $ "${SWIFT_HOST_LIBRARIES_DEST_DIR}/${name}.swiftmodule" "${CMAKE_CURRENT_BINARY_DIR}/${name}.swiftmodule" COMMAND_EXPAND_LISTS COMMENT "Update mtime of library outputs workaround") @@ -298,6 +304,24 @@ function(add_pure_swift_host_tool name) target_include_directories(${name} PUBLIC ${SWIFT_HOST_LIBRARIES_DEST_DIR}) + # Workaround to touch the library and its objects so that we don't + # continually rebuild (again, see corresponding change in swift-syntax). + add_custom_command( + TARGET ${name} + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E touch_nocreate $ $ + COMMAND_EXPAND_LISTS + COMMENT "Update mtime of executable outputs workaround") + + # Even worse hack - ${name}.swiftmodule is added as an output, even though + # this is an executable target. Just touch it all the time to avoid having + # to rebuild it every time. + add_custom_command( + TARGET ${name} + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E touch "${CMAKE_CURRENT_BINARY_DIR}/${name}.swiftmodule" + COMMAND_EXPAND_LISTS + COMMENT "Update mtime of executable outputs workaround") # Export this target. set_property(GLOBAL APPEND PROPERTY SWIFT_EXPORTS ${name}) endfunction() diff --git a/docs/CppInteroperability/CppInteroperabilityManifesto.md b/docs/CppInteroperability/CppInteroperabilityManifesto.md index 72b7379196546..85a301b802d6e 100644 --- a/docs/CppInteroperability/CppInteroperabilityManifesto.md +++ b/docs/CppInteroperability/CppInteroperabilityManifesto.md @@ -2,7 +2,9 @@ # ⚠️ Warning: document is out of date. ⚠️ -**This document has not had significant updates in the last three years. The goals and design outlined here do not necessarily reflect those established by the C++ Interop Work Group. For an up-to-date document, please see [the Forward Vision which is being developed here](https://github.com/zoecarver/swift/blob/docs/interop-roadmap/docs/CppInteroperability/ForwardVision.md).** +**This document has not had significant updates in the last three years. The goals and design outlined here do not necessarily reflect those established by the C++ Interop Work Group. For an up-to-date document, please see [the Forward Vision document](https://github.com/apple/swift-evolution/blob/main/visions/using-swift-from-c%2B%2B.md).** + +[** ‼️ Additionally, the official C++ interoperability documentation is live at Swift.org and provides an up-to-date guide for mixing Swift and C++ ‼️ **](https://www.swift.org/documentation/cxx-interop/) This document discusses the design and tradeoffs for bidirectional API-level interoperability between Swift and C++. diff --git a/docs/CppInteroperability/CppInteroperabilityStatus.md b/docs/CppInteroperability/CppInteroperabilityStatus.md index 75f56b43f2e70..5a361e869d04f 100644 --- a/docs/CppInteroperability/CppInteroperabilityStatus.md +++ b/docs/CppInteroperability/CppInteroperabilityStatus.md @@ -1,3 +1,5 @@ +[** ‼️ The official C++ interoperability documentation and status page is live at Swift.org and provides an up-to-date guide for mixing Swift and C++ ‼️ **](https://www.swift.org/documentation/cxx-interop/status) + # C++ Interoperability Status Swift has some experimental ability to interoperate with C++. diff --git a/docs/CppInteroperability/GettingStartedWithC++Interop.md b/docs/CppInteroperability/GettingStartedWithC++Interop.md index 5ac044af9a037..27e7557cf8e3d 100644 --- a/docs/CppInteroperability/GettingStartedWithC++Interop.md +++ b/docs/CppInteroperability/GettingStartedWithC++Interop.md @@ -1,3 +1,5 @@ +[** ‼️ The official C++ interoperability documentation is live at Swift.org and provides an up-to-date guide for mixing Swift and C++ ‼️ **](https://www.swift.org/documentation/cxx-interop/) + # Getting started with C++ Interoperability This document is designed to get you started with bidirectional API-level interoperability between Swift and C++. diff --git a/docs/CppInteroperability/InteropOddities.md b/docs/CppInteroperability/InteropOddities.md index 51c8e6f5b8ff0..f6ba447e89f13 100644 --- a/docs/CppInteroperability/InteropOddities.md +++ b/docs/CppInteroperability/InteropOddities.md @@ -1,3 +1,5 @@ +[** ‼️ The official C++ interoperability documentation is live at Swift.org and provides an up-to-date guide for mixing Swift and C++ ‼️ **](https://www.swift.org/documentation/cxx-interop/) + # C++ Interoperability Oddities C++ APIs may have slightly different behavior than other C++ APIs. This is a general catch-all document where these diff --git a/docs/CppInteroperability/UserGuide-CallingSwiftFromC++.md b/docs/CppInteroperability/UserGuide-CallingSwiftFromC++.md index d0a84ce2eeb2f..d5ae70bd183b2 100644 --- a/docs/CppInteroperability/UserGuide-CallingSwiftFromC++.md +++ b/docs/CppInteroperability/UserGuide-CallingSwiftFromC++.md @@ -1,3 +1,5 @@ +[** ‼️ The official C++ interoperability documentation is live at Swift.org and provides an up-to-date guide for mixing Swift and C++ ‼️ **](https://www.swift.org/documentation/cxx-interop/) + # Guide: Calling Swift APIs from C++ A Swift library author might want to expose their interface to C++, to allow a C++ codebase to interoperate with the Swift library. This document describes how this can be accomplished, by first describing how Swift can expose its interface to C++, and then going into the details on how to use Swift APIs from C++. diff --git a/docs/CppInteroperability/UserManual.md b/docs/CppInteroperability/UserManual.md index b718aeadc53a1..a9afdc6ccd7d0 100644 --- a/docs/CppInteroperability/UserManual.md +++ b/docs/CppInteroperability/UserManual.md @@ -1,3 +1,5 @@ +[** ‼️ The official C++ interoperability documentation is live at Swift.org and provides an up-to-date guide for mixing Swift and C++ ‼️ **](https://www.swift.org/documentation/cxx-interop/) + # C++ Interop User Manual The following document explains how C++ APIs are imported into Swift and is targeted at users of C++ interoperability. Hopefully this document will help you understand why the compiler cannot import various APIs and help you update these APIs to be useable from Swift. First, the document will lay out some API patterns and definitions, then it will discuss how the Swift compiler decides if an API should be usable in Swift. diff --git a/include/swift/AST/AccessorKinds.def b/include/swift/AST/AccessorKinds.def index ad5f4dbd44330..7efc9a7f9efdc 100644 --- a/include/swift/AST/AccessorKinds.def +++ b/include/swift/AST/AccessorKinds.def @@ -100,6 +100,14 @@ ANY_ADDRESSOR(ID, KEYWORD) #endif +/// INIT_ACCESSOR(ID, KEYWORD) +/// The given accessor is an init accessor. +/// +/// Defaults to SINGLETON_ACCESSOR(ID, KEYWORD). +#ifndef INIT_ACCESSOR +#define INIT_ACCESSOR(ID, KEYWORD) SINGLETON_ACCESSOR(ID, KEYWORD) +#endif + // Suppress entries for accessors which can't be written in source code. #ifndef SUPPRESS_ARTIFICIAL_ACCESSORS #define SUPPRESS_ARTIFICIAL_ACCESSORS 0 @@ -174,11 +182,16 @@ IMMUTABLE_ADDRESSOR(Address, unsafeAddress) /// of the type). MUTABLE_ADDRESSOR(MutableAddress, unsafeMutableAddress) +/// This is an init accessor: a function that is called when DI +/// re-writes assignment to initialization. +INIT_ACCESSOR(Init, init) + #ifdef LAST_ACCESSOR -LAST_ACCESSOR(MutableAddress) +LAST_ACCESSOR(Init) #undef LAST_ACCESSOR #endif +#undef INIT_ACCESSOR #undef IMMUTABLE_ADDRESSOR #undef MUTABLE_ADDRESSOR #undef ANY_ADDRESSOR diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 9f053b3a7da13..7b6e8d713352b 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -55,6 +55,7 @@ class Decl; class AbstractFunctionDecl; class FuncDecl; class ClassDecl; +class AccessorDecl; class GenericFunctionType; class LazyConformanceLoader; class LazyMemberLoader; @@ -100,6 +101,7 @@ class DeclAttribute : public AttributeBase { friend class TypeAttributes; protected: + // clang-format off union { uint64_t OpaqueBits; @@ -177,6 +179,7 @@ class DeclAttribute : public AttributeBase { isCategoryNameInvalid : 1 ); } Bits; + // clang-format on DeclAttribute *Next = nullptr; @@ -1548,6 +1551,68 @@ class SpecializeAttr final } }; +class InitializesAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + + size_t numProperties; + + InitializesAttr(SourceLoc atLoc, SourceRange range, + ArrayRef properties); + +public: + static InitializesAttr *create(ASTContext &ctx, + SourceLoc atLoc, SourceRange range, + ArrayRef properties); + + size_t numTrailingObjects(OverloadToken) const { + return numProperties; + } + + unsigned getNumProperties() const { return numProperties; } + + ArrayRef getProperties() const { + return {getTrailingObjects(), numProperties}; + } + + ArrayRef getPropertyDecls(AccessorDecl *attachedTo) const; + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Initializes; + } +}; + +class AccessesAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + + size_t numProperties; + + AccessesAttr(SourceLoc atLoc, SourceRange range, + ArrayRef properties); + +public: + static AccessesAttr *create(ASTContext &ctx, + SourceLoc atLoc, SourceRange range, + ArrayRef properties); + + size_t numTrailingObjects(OverloadToken) const { + return numProperties; + } + + ArrayRef getProperties() const { + return {getTrailingObjects(), numProperties}; + } + + ArrayRef getPropertyDecls(AccessorDecl *attachedTo) const; + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Accesses; + } +}; + /// The @_implements attribute, which treats a decl as the implementation for /// some named protocol requirement (but otherwise not-visible by that name). class ImplementsAttr : public DeclAttribute { diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index f58642759ea26..792568c22d518 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -47,6 +47,7 @@ #include "swift/Basic/Range.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/TrailingObjects.h" +#include #include namespace swift { @@ -184,6 +185,7 @@ enum class DescriptiveDeclKind : uint8_t { DistributedMethod, Getter, Setter, + InitAccessor, Addressor, MutableAddressor, ReadAccessor, @@ -911,7 +913,13 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated { /// /// Auxiliary declarations can be property wrapper backing variables, /// backing variables for 'lazy' vars, or peer macro expansions. - void visitAuxiliaryDecls(AuxiliaryDeclCallback callback) const; + /// + /// When \p visitFreestandingExpanded is true (the default), this will also + /// visit the declarations produced by a freestanding macro expansion. + void visitAuxiliaryDecls( + AuxiliaryDeclCallback callback, + bool visitFreestandingExpanded = true + ) const; using MacroCallback = llvm::function_ref; @@ -3980,6 +3988,16 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// Return a collection of the stored member variables of this type. ArrayRef getStoredProperties() const; + /// Return a collection of all properties with init accessors in + /// this type. + ArrayRef getInitAccessorProperties() const; + + /// Establish a mapping between properties that could be iniitalized + /// via other properties by means of init accessors. This mapping is + /// one-to-many because we allow intersecting `initializes(...)`. + void collectPropertiesInitializableByInitAccessors( + std::multimap &result) const; + /// Return a collection of the stored member variables of this type, along /// with placeholders for unimportable stored properties. ArrayRef getStoredPropertiesAndMissingMemberPlaceholders() const; @@ -7563,6 +7581,10 @@ class AccessorDecl final : public FuncDecl { llvm_unreachable("bad accessor kind"); } + bool isInitAccessor() const { + return (getAccessorKind() == AccessorKind::Init); + } + /// \returns true if this is non-mutating due to applying a 'mutating' /// attribute. For example a "mutating set" accessor. bool isExplicitNonMutating() const; @@ -8579,6 +8601,9 @@ class MacroDecl : public GenericContext, public ValueDecl { /// Retrieve the definition of this macro. MacroDefinition getDefinition() const; + /// Set the definition of this macro + void setDefinition(MacroDefinition definition); + /// Retrieve the parameter list of this macro. ParameterList *getParameterList() const { return parameterList; } @@ -8621,8 +8646,9 @@ class MacroExpansionDecl : public Decl, public FreestandingMacroExpansion { return getExpansionInfo()->getSourceRange(); } SourceLoc getLocFromSource() const { return getExpansionInfo()->SigilLoc; } - using ExprOrStmtExpansionCallback = llvm::function_ref; - void forEachExpandedExprOrStmt(ExprOrStmtExpansionCallback) const; + + /// Enumerate the nodes produced by expanding this macro expansion. + void forEachExpandedNode(llvm::function_ref callback) const; /// Returns a discriminator which determines this macro expansion's index /// in the sequence of macro expansions within the current function. diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index dc0884c4c660e..df9130b971694 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -143,7 +143,7 @@ namespace swift { StringRef StringVal; DeclNameRef IdentifierVal; ObjCSelector ObjCSelectorVal; - ValueDecl *TheValueDecl; + const ValueDecl *TheValueDecl; Type TypeVal; TypeRepr *TyR; FullyQualified FullyQualifiedTypeVal; @@ -194,7 +194,7 @@ namespace swift { : Kind(DiagnosticArgumentKind::ObjCSelector), ObjCSelectorVal(S) { } - DiagnosticArgument(ValueDecl *VD) + DiagnosticArgument(const ValueDecl *VD) : Kind(DiagnosticArgumentKind::ValueDecl), TheValueDecl(VD) { } @@ -305,7 +305,7 @@ namespace swift { return ObjCSelectorVal; } - ValueDecl *getAsValueDecl() const { + const ValueDecl *getAsValueDecl() const { assert(Kind == DiagnosticArgumentKind::ValueDecl); return TheValueDecl; } diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index da94125b16c59..dfa6f57e42eac 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -309,6 +309,9 @@ ERROR(observing_accessor_in_subscript,none, ERROR(getset_cannot_be_implied,none, "variable with implied type cannot have implied getter/setter", ()) +ERROR(init_accessor_expected_name,none, + "expected property name in init accessor effect", ()) + // Import ERROR(decl_expected_module_name,none, "expected module name in import declaration", ()) @@ -2117,5 +2120,13 @@ ERROR(sil_markuncheckedreferencebinding_requires_attribute,none, ERROR(sil_markuncheckedreferencebinding_invalid_attribute,none, "Attribute '[%0]' can not be applied to mark_unchecked_reference_binding", (StringRef)) +//------------------------------------------------------------------------------ +// MARK: Init accessors +//------------------------------------------------------------------------------ + +ERROR(init_accessor_is_not_on_property,none, + "init accessors could only be associated with properties", + ()) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index cf176a032c90b..df082a3ce52fd 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -189,6 +189,9 @@ ERROR(ivar_not_initialized_at_superinit,none, ERROR(ivar_not_initialized_at_implicit_superinit,none, "property '%0' not initialized at implicitly generated super.init call", (StringRef, bool)) +ERROR(ivar_not_initialized_by_init_accessor,none, + "property %0 not initialized by init accessor", + (DeclName)) ERROR(self_use_before_fully_init,none, "'self' used in %select{method call|property access}1 %0 before " diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index d0e7a3fa5105e..314bfab93b424 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -197,6 +197,9 @@ ERROR(cannot_match_expr_tuple_pattern_with_nontuple_value,none, ERROR(cannot_match_unresolved_expr_pattern_with_value,none, "pattern cannot match values of type %0", (Type)) +ERROR(cannot_match_value_with_pattern,none, + "pattern of type %1 cannot match %0", + (Type, Type)) ERROR(cannot_reference_compare_types,none, "cannot check reference equality of functions; operands here have types " @@ -873,18 +876,54 @@ ERROR(need_hermetic_seal_to_import_module,none, (Identifier)) ERROR(modularization_issue_decl_moved,Fatal, - "reference to %select{top-level|type}0 %1 broken by a context change; " + "reference to %select{top-level declaration|type}0 %1 broken by a context change; " "%1 was expected to be in %2, but now a candidate is found only in %3", - (bool, DeclName, Identifier, Identifier)) + (bool, DeclName, const ModuleDecl*, const ModuleDecl*)) ERROR(modularization_issue_decl_type_changed,Fatal, - "reference to %select{top-level|type}0 %1 broken by a context change; " + "reference to %select{top-level declaration|type}0 %1 broken by a context change; " "the declaration kind of %1 %select{from %2|}5 changed since building '%3'" - "%select{|, it was in %2 and is now found in %4}5", - (bool, DeclName, Identifier, StringRef, Identifier, bool)) + "%select{|, it was in %2 and is now a candidate is found only in %4}5", + (bool, DeclName, const ModuleDecl*, StringRef, const ModuleDecl*, bool)) ERROR(modularization_issue_decl_not_found,Fatal, - "reference to %select{top-level|type}0 %1 broken by a context change; " + "reference to %select{top-level declaration|type}0 %1 broken by a context change; " "%1 is not found, it was expected to be in %2", - (bool, DeclName, Identifier)) + (bool, DeclName, const ModuleDecl*)) + +NOTE(modularization_issue_note_expected,none, + "the %select{declaration|type}0 was expected to be found in module %1 at '%2'", + (bool, const ModuleDecl*, StringRef)) +NOTE(modularization_issue_note_expected_underlying,none, + "or expected to be found in the underlying module '%0' defined at '%1'", + (StringRef, StringRef)) +NOTE(modularization_issue_note_found,none, + "the %select{declaration|type}0 was actually found in module %1 at '%2'", + (bool, const ModuleDecl*, StringRef)) + +NOTE(modularization_issue_stale_module,none, + "the module %0 has enabled library-evolution; " + "the following file may need to be deleted if the SDK was modified: '%1'", + (const ModuleDecl *, StringRef)) +NOTE(modularization_issue_swift_version,none, + "the module %0 was built with a Swift language version set to %1 " + "while the current invocation uses %2; " + "APINotes may change how clang declarations are imported", + (const ModuleDecl *, llvm::VersionTuple, llvm::VersionTuple)) +NOTE(modularization_issue_audit_headers,none, + "declarations in the %select{underlying|}0 clang module %1 " + "may be hidden by clang preprocessor macros", + (bool, const ModuleDecl*)) +NOTE(modularization_issue_layering_expected_local,none, + "the distributed module %0 refers to the local module %1; " + "this may be caused by header maps or search paths", + (const ModuleDecl*, const ModuleDecl*)) +NOTE(modularization_issue_layering_found_local,none, + "the reference may break layering; the candidate was found in " + "the local module %1 for a reference from the distributed module %0", + (const ModuleDecl*, const ModuleDecl*)) +NOTE(modularization_issue_related_modules,none, + "the %select{declaration|type}0 %1 moved between related modules; " + "clang preprocessor macros may affect headers shared between these modules", + (bool, DeclName)) NOTE(modularization_issue_side_effect_extension_error,none, "could not deserialize extension", @@ -893,9 +932,10 @@ NOTE(modularization_issue_side_effect_type_error,none, "could not deserialize type for %0", (DeclName)) -WARNING(modularization_issue_worked_around,none, - "attempting forced recovery enabled by -experimental-force-workaround-broken-modules", - ()) +NOTE(modularization_issue_worked_around,none, + "attempting forced recovery enabled by " + "-experimental-force-workaround-broken-modules", + ()) ERROR(reserved_member_name,none, "type member must not be named %0, since it would conflict with the" @@ -5853,6 +5893,8 @@ ERROR(no_opaque_return_type_of,none, ERROR(objc_observing_accessor, none, "observing accessors are not allowed to be marked @objc", ()) +ERROR(objc_init_accessor, none, + "init accessors cannot be marked @objc", ()) ERROR(objc_addressor, none, "addressors are not allowed to be marked @objc", ()) ERROR(objc_coroutine_accessor, none, @@ -7096,6 +7138,14 @@ ERROR(macro_in_nested,none, ERROR(macro_without_role,none, "macro %0 must declare its applicable roles via '@freestanding' or @attached'", (DeclName)) +ERROR(macro_result_type_cannot_be_used,none, + "only a freestanding expression macro can produce a result of type %0", + (Type)) +NOTE(macro_remove_result_type,none, + "remove the result type if the macro does not produce a value", + ()) +NOTE(macro_make_freestanding_expression,none, + "make this macro a freestanding expression macro", ()) ERROR(macro_expansion_missing_pound,none, "expansion of macro %0 requires leading '#'", (DeclName)) ERROR(macro_expansion_missing_arguments,none, @@ -7272,6 +7322,25 @@ NOTE(opt_out_from_missing_reflection_metadata_attr,none, " unavailable extension", (StringRef)) +//------------------------------------------------------------------------------ +// MARK: Init accessors +//------------------------------------------------------------------------------ + +ERROR(init_accessor_can_refer_only_to_properties,none, + "init accessor cannot refer to %0 %1; init accessors can refer only" + " to stored properties", + (DescriptiveDeclKind, DeclNameRef)) + +ERROR(init_accessor_initializes_attribute_on_other_declaration,none, + "initalizes(...) attribute could only be used with init accessors", + ()) +ERROR(init_accessor_accesses_attribute_on_other_declaration,none, + "accesses(...) attribute could only be used with init accessors", + ()) +ERROR(init_accessor_property_both_init_and_accessed,none, + "property %0 cannot be both initialized and accessed", + (DeclName)) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index c18d839b9d470..b43f5704d2d03 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -139,6 +139,7 @@ class alignas(8) Expr : public ASTAllocated { void operator=(const Expr&) = delete; protected: + // clang-format off union { uint64_t OpaqueBits; SWIFT_INLINE_BITFIELD_BASE(Expr, bitmax(NumExprKindBits,8)+1, @@ -371,6 +372,7 @@ class alignas(8) Expr : public ASTAllocated { ); } Bits; + // clang-format on private: /// Ty - This is the type of the expression. diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 56f8769a50244..bb8f06e820ae8 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -63,6 +63,7 @@ class alignas(8) Stmt : public ASTAllocated { Stmt& operator=(const Stmt&) = delete; protected: + // clang-format off union { uint64_t OpaqueBits; SWIFT_INLINE_BITFIELD_BASE(Stmt, bitmax(NumStmtKindBits,8) + 1, @@ -101,6 +102,7 @@ class alignas(8) Stmt : public ASTAllocated { ); } Bits; + // clang-format on /// Return the given value for the 'implicit' flag if present, or if None, /// return true if the location is invalid. diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index a5ce6fa03011c..1e1e9a515c36f 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -1671,6 +1671,25 @@ class StoredPropertiesAndMissingMembersRequest : bool isCached() const { return true; } }; +/// Request to obtain a list of computed properties with init accesors +/// in the given nominal type. +class InitAccessorPropertiesRequest : + public SimpleRequest(NominalTypeDecl *), + RequestFlags::Cached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + ArrayRef + evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const; + +public: + bool isCached() const { return true; } +}; + class StorageImplInfoRequest : public SimpleRequest(DeclAttribute *, AccessorDecl *, + ArrayRef), + RequestFlags::Cached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + ArrayRef evaluate(Evaluator &evaluator, DeclAttribute *attr, + AccessorDecl *attachedTo, + ArrayRef) const; + +public: + bool isCached() const { return true; } +}; + #define SWIFT_TYPEID_ZONE TypeChecker #define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def" #include "swift/Basic/DefineTypeIDZone.h" diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index efa1a9e384118..581a7bd27d8b2 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -306,6 +306,9 @@ SWIFT_REQUEST(TypeChecker, StoredPropertiesAndMissingMembersRequest, ArrayRef(NominalTypeDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, StoredPropertiesRequest, ArrayRef(NominalTypeDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, InitAccessorPropertiesRequest, + ArrayRef(NominalTypeDecl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, StructuralTypeRequest, Type(TypeAliasDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, SuperclassTypeRequest, @@ -481,3 +484,7 @@ SWIFT_REQUEST(TypeChecker, IsNonUserModuleRequest, SWIFT_REQUEST(TypeChecker, TypeCheckObjCImplementationRequest, unsigned(ExtensionDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, InitAccessorReferencedVariablesRequest, + ArrayRef(DeclAttribute *, AccessorDecl *, + ArrayRef), + Cached, NoLocationInfo) diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index 2befb5a11fc91..0a0985cd5347e 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -60,6 +60,7 @@ class alignas(1 << TypeReprAlignInBits) TypeRepr void operator=(const TypeRepr&) = delete; protected: + // clang-format off union { uint64_t OpaqueBits; SWIFT_INLINE_BITFIELD_BASE(TypeRepr, bitmax(NumTypeReprKindBits,8)+1+1, @@ -108,6 +109,7 @@ class alignas(1 << TypeReprAlignInBits) TypeRepr ); } Bits; + // clang-format on TypeRepr(TypeReprKind K) { Bits.OpaqueBits = 0; diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 111b01553a183..c23c1362c97aa 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -375,6 +375,8 @@ class alignas(1 << TypeAlignInBits) TypeBase protected: enum { NumAFTExtInfoBits = 11 }; enum { NumSILExtInfoBits = 11 }; + + // clang-format off union { uint64_t OpaqueBits; SWIFT_INLINE_BITFIELD_BASE(TypeBase, bitmax(NumTypeKindBits,8) + @@ -501,6 +503,7 @@ class alignas(1 << TypeAlignInBits) TypeBase ); } Bits; + // clang-format on protected: TypeBase(TypeKind kind, const ASTContext *CanTypeCtx, diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 67bdd87cff6ff..5ed336205881a 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -120,6 +120,7 @@ EXPERIMENTAL_FEATURE(NamedOpaqueTypes, false) EXPERIMENTAL_FEATURE(FlowSensitiveConcurrencyCaptures, false) EXPERIMENTAL_FEATURE(CodeItemMacros, true) EXPERIMENTAL_FEATURE(TupleConformances, false) +EXPERIMENTAL_FEATURE(InitAccessors, false) // Whether to enable @_used and @_section attributes EXPERIMENTAL_FEATURE(SymbolLinkageMarkers, true) diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index c928e4672715a..4022fc3b1fd08 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -143,6 +143,7 @@ NODE(ImplErrorResult) NODE(InOut) NODE(InfixOperator) CONTEXT_NODE(Initializer) +CONTEXT_NODE(InitAccessor) NODE(Isolated) NODE(KeyPathGetterThunkHelper) NODE(KeyPathSetterThunkHelper) diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index 77b361d2d7be4..a53e9e46c151a 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -578,6 +578,9 @@ class SourceEditConsumer { void insertAfter(SourceManager &SM, SourceLoc Loc, StringRef Text, ArrayRef SubRegions = {}); void accept(SourceManager &SM, Replacement Replacement) { accept(SM, RegionType::ActiveCode, {Replacement}); } void remove(SourceManager &SM, CharSourceRange Range); + void acceptMacroExpansionBuffer(SourceManager &SM, unsigned bufferID, + SourceFile *containingSF, + bool adjustExpansion, bool includeBufferName); }; /// This helper stream inserts text into a SourceLoc by calling functions in diff --git a/include/swift/IDETool/SyntacticMacroExpansion.h b/include/swift/IDETool/SyntacticMacroExpansion.h new file mode 100644 index 0000000000000..2f1c079ae9c28 --- /dev/null +++ b/include/swift/IDETool/SyntacticMacroExpansion.h @@ -0,0 +1,97 @@ +//===--- SyntacticMacroExpansion.h ----------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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_SYNTACTICMACROEXPANSION_H +#define SWIFT_IDE_SYNTACTICMACROEXPANSION_H + +#include "swift/AST/Decl.h" +#include "swift/AST/MacroDefinition.h" +#include "swift/AST/PluginRegistry.h" +#include "swift/Basic/Fingerprint.h" +#include "swift/Frontend/Frontend.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace swift { + +class ASTContext; +class SourceFile; + +namespace ide { +class SourceEditConsumer; + +/// Simple object to specify a syntactic macro expansion. +struct MacroExpansionSpecifier { + unsigned offset; + swift::MacroRoles macroRoles; + swift::MacroDefinition macroDefinition; +}; + +/// Instance of a syntactic macro expansion context. This is created for each +/// list of compiler arguments (i.e. 'argHash'), and reused as long as the +/// compiler arguments are not changed. +class SyntacticMacroExpansionInstance { + CompilerInvocation invocation; + + SourceManager SourceMgr; + DiagnosticEngine Diags{SourceMgr}; + std::unique_ptr Ctx; + ModuleDecl *TheModule = nullptr; + llvm::StringMap MacroDecls; + + /// Create 'SourceFile' using the buffer. + swift::SourceFile *getSourceFile(llvm::MemoryBuffer *inputBuf); + + /// Synthesize 'MacroDecl' AST object to use the expansion. + swift::MacroDecl * + getSynthesizedMacroDecl(swift::Identifier name, + const MacroExpansionSpecifier &expansion); + + /// Expand single 'expansion' in SF. + void expand(swift::SourceFile *SF, + const MacroExpansionSpecifier &expansion, + SourceEditConsumer &consumer); + +public: + SyntacticMacroExpansionInstance() {} + + /// Setup the instance with \p args . + bool setup(StringRef SwiftExecutablePath, ArrayRef args, + std::shared_ptr plugins, std::string &error); + + ASTContext &getASTContext() { return *Ctx; } + + /// Expand all macros in \p inputBuf and send the edit results to \p consumer. + /// Expansions are specified by \p expansions . + void expandAll(llvm::MemoryBuffer *inputBuf, + ArrayRef expansions, + SourceEditConsumer &consumer); +}; + +/// Manager object to vend 'SyntacticMacroExpansionInstance'. +class SyntacticMacroExpansion { + StringRef SwiftExecutablePath; + std::shared_ptr Plugins; + +public: + SyntacticMacroExpansion(StringRef SwiftExecutablePath, + std::shared_ptr Plugins) + : SwiftExecutablePath(SwiftExecutablePath), Plugins(Plugins) {} + + /// Get instance configured with the specified compiler arguments. + std::shared_ptr + getInstance(ArrayRef args, std::string &error); +}; + +} // namespace ide +} // namespace swift + +#endif // SWIFT_IDE_SYNTACTICMACROEXPANSION_H diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index ce504744911ea..984d69949697f 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -633,7 +633,7 @@ def emit_clang_header_nonmodular_includes : Flag<["-"], "emit-clang-header-nonmo HelpText<"Augment emitted Objective-C header with textual imports for every included modular import">; def emit_clang_header_path : Separate<["-"], "emit-clang-header-path">, - Flags<[FrontendOption, NoDriverOption, NoInteractiveOption, ArgumentIsPath, + Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath, SupplementaryOutput]>, HelpText<"Emit an Objective-C and C++ header file to ">, Alias; diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 347f967418bf7..ce377110167b9 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1266,6 +1266,10 @@ class Parser { AccessorKind currentKind, SourceLoc const& currentLoc); + ParserStatus parseInitAccessorEffects(ParsedAccessors &accessors, + AccessorKind currentKind, + DeclAttributes &Attributes); + /// Parse accessors provided as a separate list, for use in macro /// expansions. void parseTopLevelAccessors( diff --git a/include/swift/Runtime/Backtrace.h b/include/swift/Runtime/Backtrace.h index fcd67e8f5ce73..38981d6694b7e 100644 --- a/include/swift/Runtime/Backtrace.h +++ b/include/swift/Runtime/Backtrace.h @@ -17,10 +17,17 @@ #ifndef SWIFT_RUNTIME_BACKTRACE_H #define SWIFT_RUNTIME_BACKTRACE_H +#ifdef __linux__ +#include +#include + +#include +#endif // defined(__linux__) + #include "swift/Runtime/Config.h" +#include "swift/Runtime/CrashInfo.h" #include "swift/shims/Visibility.h" -#include "swift/shims/CrashInfo.h" #include @@ -50,7 +57,11 @@ typedef int ErrorCode; SWIFT_RUNTIME_STDLIB_INTERNAL ErrorCode _swift_installCrashHandler(); +#ifdef __linux__ +SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv, int memserver_fd); +#else SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv); +#endif enum class UnwindAlgorithm { Auto = 0, @@ -98,6 +109,7 @@ enum class SanitizePaths { }; enum class OutputTo { + Auto = -1, Stdout = 0, Stderr = 2, }; @@ -123,8 +135,40 @@ struct BacktraceSettings { SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings; -SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool _swift_isThunkFunction(const char *mangledName); - +SWIFT_RUNTIME_STDLIB_SPI +bool _swift_backtrace_isThunkFunction(const char *mangledName); + +/// Try to demangle a symbol. +/// +/// Unlike other entry points that do this, we try both Swift and C++ here. +/// +/// @param mangledName is the symbol name to be demangled. +/// @param mangledNameLength is the length of this name. +/// @param outputBuffer is a pointer to a buffer in which to place the result. +/// @param outputBufferSize points to a variable that contains the size of the +/// output buffer. +/// +/// If outputBuffer is nullptr, the function will allocate memory for the +/// result using malloc(). In this case, outputBufferSize may be nullptr; +/// if it is *not* nullptr, it will be set to the size of buffer that was +/// allocated. This is not necessarily the length of the string (it may be +/// somewhat higher). +/// +/// Otherwise, the result will be written into the output buffer, and the +/// size of the result will be written into outputBufferSize. If the buffer +/// is too small, the result will be truncated, but outputBufferSize will +/// still be set to the number of bytes that would have been required to +/// copy out the full result (including a trailing NUL). +/// +/// The unusual behaviour here is a consequence of the way the C++ ABI's +/// demangling function works. +/// +/// @returns a pointer to the output if demangling was successful. +SWIFT_RUNTIME_STDLIB_SPI +char *_swift_backtrace_demangle(const char *mangledName, + size_t mangledNameLength, + char *outputBuffer, + size_t *outputBufferSize); #ifdef __cplusplus } // namespace backtrace } // namespace runtime diff --git a/include/swift/Runtime/Config.h b/include/swift/Runtime/Config.h index ac44146508516..11e385bd13416 100644 --- a/include/swift/Runtime/Config.h +++ b/include/swift/Runtime/Config.h @@ -493,8 +493,8 @@ swift_auth_code(T value, unsigned extra) { #elif defined(_WIN32) # define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 0 # define SWIFT_BACKTRACE_SECTION ".sw5bckt" -#elif defined(__linux__) -# define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 0 +#elif defined(__linux__) && (defined(__aarch64__) || defined(__x86_64__)) +# define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 1 # define SWIFT_BACKTRACE_SECTION "swift5_backtrace" #else # define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 0 diff --git a/include/swift/Runtime/CrashInfo.h b/include/swift/Runtime/CrashInfo.h new file mode 100644 index 0000000000000..737888abbd25e --- /dev/null +++ b/include/swift/Runtime/CrashInfo.h @@ -0,0 +1,98 @@ +//===--- CrashInfo.h - Swift Backtracing Crash Information ------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the CrashInfo type that holds information about why the program +// crashed. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CRASHINFO_H +#define SWIFT_CRASHINFO_H + +#include + +#ifdef __cplusplus +namespace swift { +namespace runtime { +namespace backtrace { +extern "C" { +#endif + +// Note: The "pointers" below are pointers in a different process's address +// space, which might not even share our bitness. That is why they are +// `uint64_t`s, rather than pointers or `uintptr_t`s. + +// The address of this structure in memory is passed to swift-backtrace. +struct CrashInfo { + // The thread ID for the crashing thread. + uint64_t crashing_thread; + + // The signal number corresponding to this crash. + uint64_t signal; + + // The fault address. + uint64_t fault_address; + +#ifdef __APPLE__ + // Points to the mcontext_t structure for the crashing thread; other + // threads' contexts can be recovered using Mach APIs later. + uint64_t mctx; +#elif defined(__linux__) + // The head of the thread list; points at a "struct thread" (see below). + uint64_t thread_list; +#endif +}; + +#ifdef __linux__ + +// A memory server request packet. +struct memserver_req { + // Address to read. + uint64_t addr; + + // Number of bytes to read. + uint64_t len; +}; + +// A memory server response packet. +struct memserver_resp { + // Address that was read from. + uint64_t addr; + + // Number of bytes, *or* negative to indicate an error. + int64_t len; + + // Followed by len bytes of data if len > 0 +}; + +// Holds details of a running thread. +struct thread { + // Points at the next thread. + uint64_t next; + + // The thread ID for this thread. + int64_t tid; + + // Points to the Linux ucontext_t structure. + uint64_t uctx; +}; + +#endif + +#ifdef __cplusplus +} // extern "C" +} // namespace backtrace +} // namespace runtime +} // namespace swift +#endif + +#endif // SWIFT_CRASHINFO_H diff --git a/include/swift/Runtime/Debug.h b/include/swift/Runtime/Debug.h index 1559ca666e813..5d67923b603c2 100644 --- a/include/swift/Runtime/Debug.h +++ b/include/swift/Runtime/Debug.h @@ -124,6 +124,9 @@ swift_dynamicCastFailure(const void *sourceType, const char *sourceName, SWIFT_RUNTIME_EXPORT void swift_reportError(uint32_t flags, const char *message); +SWIFT_RUNTIME_EXPORT +void swift_reportWarning(uint32_t flags, const char *message); + // Halt due to an overflow in swift_retain(). SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_RUNTIME_ATTRIBUTE_NOINLINE void swift_abortRetainOverflow(); diff --git a/include/swift/SIL/InstructionUtils.h b/include/swift/SIL/InstructionUtils.h index 59aedfe4a404d..994e1e3cdbc7e 100644 --- a/include/swift/SIL/InstructionUtils.h +++ b/include/swift/SIL/InstructionUtils.h @@ -145,6 +145,10 @@ SILValue isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI); /// init or set function. bool onlyUsedByAssignByWrapper(PartialApplyInst *PAI); +/// Returns true if \p PAI is only used by an \c assign_or_init +/// instruction as init or set function. +bool onlyUsedByAssignOrInit(PartialApplyInst *PAI); + /// Returns the runtime effects of \p inst. /// /// Predicts which runtime calls are called in the generated code for `inst`. diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index 1ff0cefa55a17..9e607c821a5ac 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -1105,6 +1105,18 @@ struct BridgedBuilder{ return {b.createStrongRelease(regularLoc(), op.getSILValue(), b.getDefaultAtomicity())}; } + SWIFT_IMPORT_UNSAFE + BridgedInstruction createUnownedRetain(BridgedValue op) const { + auto b = builder(); + return {b.createUnownedRetain(regularLoc(), op.getSILValue(), b.getDefaultAtomicity())}; + } + + SWIFT_IMPORT_UNSAFE + BridgedInstruction createUnownedRelease(BridgedValue op) const { + auto b = builder(); + return {b.createUnownedRelease(regularLoc(), op.getSILValue(), b.getDefaultAtomicity())}; + } + SWIFT_IMPORT_UNSAFE BridgedInstruction createFunctionRef(BridgedFunction function) const { return {builder().createFunctionRef(regularLoc(), function.getFunction())}; diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index a0021f2a385b8..50dad6f9889c2 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -956,6 +956,14 @@ class SILBuilder { getSILDebugLocation(Loc), Src, Dest, Initializer, Setter, mode)); } + AssignOrInitInst *createAssignOrInit(SILLocation Loc, SILValue Src, + SILValue Initializer, + SILValue Setter, + AssignOrInitInst::Mode Mode) { + return insert(new (getModule()) AssignOrInitInst( + getSILDebugLocation(Loc), Src, Initializer, Setter, Mode)); + } + StoreBorrowInst *createStoreBorrow(SILLocation Loc, SILValue Src, SILValue DestAddr) { return insert(new (getModule()) @@ -1010,7 +1018,11 @@ class SILBuilder { SILValue src) { return createMarkUninitialized(Loc, src, MarkUninitializedInst::RootSelf); } - + MarkUninitializedInst *createMarkUninitializedOut(SILLocation Loc, + SILValue src) { + return createMarkUninitialized(Loc, src, MarkUninitializedInst::Out); + } + MarkFunctionEscapeInst *createMarkFunctionEscape(SILLocation Loc, ArrayRef vars) { return insert( diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 7603a0636679e..08ee52619e548 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1352,6 +1352,17 @@ void SILCloner::visitAssignByWrapperInst(AssignByWrapperInst *Inst) { getOpValue(Inst->getSetter()), Inst->getMode())); } +template +void SILCloner::visitAssignOrInitInst(AssignOrInitInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction( + Inst, getBuilder().createAssignOrInit( + getOpLocation(Inst->getLoc()), + getOpValue(Inst->getSrc()), + getOpValue(Inst->getInitializer()), + getOpValue(Inst->getSetter()), Inst->getMode())); +} + template void SILCloner::visitMarkUninitializedInst(MarkUninitializedInst *Inst) { diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 18fdd3ef53353..95c3b1a59b2b0 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -382,6 +382,9 @@ struct SILDeclRef { return kind == Kind::Initializer || kind == Kind::Destroyer; } + /// True if the SILDeclRef references an init accessor declaration. + bool isInitAccessor() const; + /// True if the function should be treated as transparent. bool isTransparent() const; /// True if the function should have its body serialized. diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 5f5b23fa2d727..8ded5a9ba68aa 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -4935,6 +4935,78 @@ class AssignByWrapperInst } }; +/// AssignOrInitInst - Represents an abstract assignment via a init accessor +/// or a setter, which may either be an initialization or a store sequence. +/// This is only valid in Raw SIL. +/// +/// Note that this instruction does not inherit from AssignInstBase because +/// there is no physical destination of the assignment. Both the init +/// and the setter are factored into functions. +class AssignOrInitInst + : public InstructionBase, + public CopyLikeInstruction { + friend SILBuilder; + USE_SHARED_UINT8; + + FixedOperandList<3> Operands; + + /// Marks all of the properties in `initializes(...)` list that + /// have been initialized before this intruction to help Raw SIL + /// lowering to emit destroys. + llvm::BitVector Assignments; + +public: + enum Mode { + /// The mode is not decided yet (by DefiniteInitialization). + Unknown, + + /// The initializer is called with Src as argument. + Init, + + /// The setter is called with Src as argument. + Set + }; + +private: + AssignOrInitInst(SILDebugLocation DebugLoc, + SILValue Src, SILValue Initializer, + SILValue Setter, Mode mode); + +public: + SILValue getSrc() const { return Operands[0].get(); } + SILValue getInitializer() const { return Operands[1].get(); } + SILValue getSetter() { return Operands[2].get(); } + + Mode getMode() const { + return Mode(sharedUInt8().AssignOrInitInst.mode); + } + + void setMode(Mode mode) { + sharedUInt8().AssignOrInitInst.mode = uint8_t(mode); + } + + /// Mark a property from `initializes(...)` list as initialized + /// before this instruction. + void markAsInitialized(VarDecl *property); + void markAsInitialized(unsigned propertyIdx); + + /// Check whether a property from `initializes(...)` list with + /// the given index has already been initialized and requires + /// destroy before it could be re-initialized. + bool isPropertyAlreadyInitialized(unsigned propertyIdx); + + unsigned getNumInitializedProperties() const; + + ArrayRef getInitializedProperties() const; + ArrayRef getAccessedProperties() const; + + ArrayRef getAllOperands() const { return Operands.asArray(); } + MutableArrayRef getAllOperands() { return Operands.asArray(); } + + AccessorDecl *getReferencedInitAccessor() const; +}; + /// Indicates that a memory location is uninitialized at this point and needs to /// be initialized by the end of the function and before any escape point for /// this instruction. This is only valid in Raw SIL. @@ -4974,6 +5046,10 @@ class MarkUninitializedInst /// DelegatingSelfAllocated designates "self" in a delegating class /// initializer where memory has already been allocated. DelegatingSelfAllocated, + + /// Out designates an indirectly returned result. + /// This is the result that has to be checked for initialization. + Out, }; private: Kind ThisKind; @@ -4988,6 +5064,7 @@ class MarkUninitializedInst Kind getMarkUninitializedKind() const { return ThisKind; } bool isVar() const { return ThisKind == Var; } + bool isOut() const { return ThisKind == Out; } bool isRootSelf() const { return ThisKind == RootSelf; } diff --git a/include/swift/SIL/SILNode.h b/include/swift/SIL/SILNode.h index c6bef9604fafc..ec9a7edfae6b6 100644 --- a/include/swift/SIL/SILNode.h +++ b/include/swift/SIL/SILNode.h @@ -187,6 +187,7 @@ class alignas(8) SILNode : SHARED_FIELD(LoadInst, uint8_t ownershipQualifier); SHARED_FIELD(AssignInst, uint8_t ownershipQualifier); SHARED_FIELD(AssignByWrapperInst, uint8_t mode); + SHARED_FIELD(AssignOrInitInst, uint8_t mode); SHARED_FIELD(StringLiteralInst, uint8_t encoding); SHARED_FIELD(SelectEnumInstBase, bool hasDefault); SHARED_FIELD(SwitchValueInst, bool hasDefault); diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index 443bc06064e34..31eec73a10239 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -820,6 +820,8 @@ NON_VALUE_INST(AssignInst, assign, SILInstruction, MayWrite, DoesNotRelease) NON_VALUE_INST(AssignByWrapperInst, assign_by_wrapper, SILInstruction, MayWrite, DoesNotRelease) +NON_VALUE_INST(AssignOrInitInst, assign_or_init, + SILInstruction, MayWrite, DoesNotRelease) NON_VALUE_INST(MarkFunctionEscapeInst, mark_function_escape, SILInstruction, None, DoesNotRelease) NON_VALUE_INST(DebugValueInst, debug_value, diff --git a/include/swift/SIL/SILType.h b/include/swift/SIL/SILType.h index 653e7ee129230..aa748d1c787de 100644 --- a/include/swift/SIL/SILType.h +++ b/include/swift/SIL/SILType.h @@ -394,6 +394,8 @@ class SILType { bool isReferenceCounted(SILFunction *f) const; + bool isUnownedStorageType() const { return is(); } + /// Returns true if the referenced type is a function type that never /// returns. bool isNoReturnFunction(SILModule &M, TypeExpansionContext context) const; @@ -745,6 +747,10 @@ class SILType { /// for a move only wrapped type. bool isPureMoveOnly() const; + /// Return true if this is a value type (struct/enum) that requires + /// deinitialization beyond destruction of its members. + bool isValueTypeWithDeinit() const; + /// Returns true if and only if this type is a first class move only /// type. NOTE: Returns false if the type is a move only wrapped type. bool isMoveOnlyNominalType() const; diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 45b7e95c7e79c..76166846fb8ad 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -482,6 +482,8 @@ SWIFT_SILCOMBINE_PASS(BeginCOWMutationInst) SWIFT_SILCOMBINE_PASS(GlobalValueInst) SWIFT_SILCOMBINE_PASS(StrongRetainInst) SWIFT_SILCOMBINE_PASS(StrongReleaseInst) +SWIFT_SILCOMBINE_PASS(RetainValueInst) +SWIFT_SILCOMBINE_PASS(ReleaseValueInst) SWIFT_SILCOMBINE_PASS(LoadInst) #undef IRGEN_PASS diff --git a/include/swift/Sema/CompletionContextFinder.h b/include/swift/Sema/CompletionContextFinder.h index 51dc3bfd515e2..dce6572908b7a 100644 --- a/include/swift/Sema/CompletionContextFinder.h +++ b/include/swift/Sema/CompletionContextFinder.h @@ -20,6 +20,10 @@ namespace swift { +namespace constraints { +class SyntacticElementTarget; +} + class CompletionContextFinder : public ASTWalker { enum class ContextKind { FallbackExpression, @@ -53,12 +57,9 @@ class CompletionContextFinder : public ASTWalker { return MacroWalking::Arguments; } - /// Finder for completion contexts within the provided initial expression. - CompletionContextFinder(ASTNode initialNode, DeclContext *DC) - : InitialExpr(initialNode.dyn_cast()), InitialDC(DC) { - assert(DC); - initialNode.walk(*this); - }; + /// Finder for completion contexts within the provided SyntacticElementTarget. + CompletionContextFinder(constraints::SyntacticElementTarget target, + DeclContext *DC); /// Finder for completion contexts within the outermost non-closure context of /// the code completion expression's direct context. diff --git a/include/swift/Sema/ConstraintLocator.h b/include/swift/Sema/ConstraintLocator.h index 71fd781385d97..981b70188ebfc 100644 --- a/include/swift/Sema/ConstraintLocator.h +++ b/include/swift/Sema/ConstraintLocator.h @@ -318,6 +318,9 @@ class ConstraintLocator : public llvm::FoldingSetNode { /// otherwise \c nullptr. NullablePtr getPatternMatch() const; + /// Whether the locator in question is for a pattern match. + bool isForPatternMatch() const; + /// Returns true if \p locator is ending with either of the following /// - Member /// - Member -> KeyPathDynamicMember diff --git a/include/swift/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def index 62649588386f0..c60640e114cd9 100644 --- a/include/swift/Sema/ConstraintLocatorPathElts.def +++ b/include/swift/Sema/ConstraintLocatorPathElts.def @@ -225,6 +225,10 @@ CUSTOM_LOCATOR_PATH_ELT(TernaryBranch) /// Performing a pattern patch. CUSTOM_LOCATOR_PATH_ELT(PatternMatch) +/// The constraint that models the allowed implicit casts for +/// an EnumElementPattern. +SIMPLE_LOCATOR_PATH_ELT(EnumPatternImplicitCastMatch) + /// Points to a particular attribute associated with one of /// the arguments e.g. `inout` or its type e.g. `@escaping`. /// diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 1951e3ad1c05d..6d6bb23a2712c 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -1500,6 +1500,10 @@ class Solution { llvm::SmallMapVector caseLabelItems; + /// A map of expressions to the ExprPatterns that they are being solved as + /// a part of. + llvm::SmallMapVector exprPatterns; + /// The set of parameters that have been inferred to be 'isolated'. llvm::SmallVector isolatedParams; @@ -1685,6 +1689,16 @@ class Solution { : nullptr; } + /// Retrieve the solved ExprPattern that corresponds to provided + /// sub-expression. + NullablePtr getExprPatternFor(Expr *E) const { + auto result = exprPatterns.find(E); + if (result == exprPatterns.end()) + return nullptr; + + return result->second; + } + /// This method implements functionality of `Expr::isTypeReference` /// with data provided by a given solution. bool isTypeReference(Expr *E) const; @@ -2148,6 +2162,10 @@ class ConstraintSystem { llvm::SmallMapVector caseLabelItems; + /// A map of expressions to the ExprPatterns that they are being solved as + /// a part of. + llvm::SmallMapVector exprPatterns; + /// The set of parameters that have been inferred to be 'isolated'. llvm::SmallSetVector isolatedParams; @@ -2745,6 +2763,9 @@ class ConstraintSystem { /// The length of \c caseLabelItems. unsigned numCaseLabelItems; + /// The length of \c exprPatterns. + unsigned numExprPatterns; + /// The length of \c isolatedParams. unsigned numIsolatedParams; @@ -3166,6 +3187,15 @@ class ConstraintSystem { caseLabelItems[item] = info; } + /// Record a given ExprPattern as the parent of its sub-expression. + void setExprPatternFor(Expr *E, ExprPattern *EP) { + assert(E); + assert(EP); + auto inserted = exprPatterns.insert({E, EP}).second; + assert(inserted && "Mapping already defined?"); + (void)inserted; + } + Optional getCaseLabelItemInfo( const CaseLabelItem *item) const { auto known = caseLabelItems.find(item); @@ -4315,6 +4345,11 @@ class ConstraintSystem { /// \returns \c true if constraint generation failed, \c false otherwise bool generateConstraints(SingleValueStmtExpr *E); + /// Generate constraints for an array of ExprPatterns, forming a conjunction + /// that solves each expression in turn. + void generateConstraints(ArrayRef exprPatterns, + ConstraintLocatorBuilder locator); + /// Generate constraints for the given (unchecked) expression. /// /// \returns a possibly-sanitized expression, or null if an error occurred. diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index 2d4da1a016ae5..33825e593cf15 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -37,6 +37,7 @@ namespace swift { enum class DeclRefKind; class Expr; class ExtensionDecl; + class FreestandingMacroExpansion; class FunctionType; class LabeledConditionalStmt; class LookupResult; @@ -355,6 +356,13 @@ namespace swift { SmallVector, 1> getShorthandShadows(LabeledConditionalStmt *CondStmt, DeclContext *DC = nullptr); + + SourceFile *evaluateFreestandingMacro(FreestandingMacroExpansion *expansion, + StringRef discriminator); + + SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, + CustomAttr *attr, bool passParentContext, + MacroRole role, StringRef discriminator); } #endif diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 21d3a5b001fa9..6b4de6b2e8976 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -81,6 +81,8 @@ static StringRef getCodeForAccessorKind(AccessorKind kind) { return "lu"; case AccessorKind::MutableAddress: return "au"; + case AccessorKind::Init: + return "i"; } llvm_unreachable("bad accessor kind"); } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 0827b4321e180..15b4c7a67a6e1 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3218,6 +3218,10 @@ static bool usesFeatureSymbolLinkageMarkers(Decl *decl) { }); } +static bool usesFeatureInitAccessors(Decl *decl) { + return false; +} + static bool usesFeatureLayoutPrespecialization(Decl *decl) { auto &attrs = decl->getAttrs(); return std::any_of(attrs.begin(), attrs.end(), [](auto *attr) { @@ -4328,6 +4332,7 @@ void PrintAST::visitAccessorDecl(AccessorDecl *decl) { break; case AccessorKind::Set: case AccessorKind::WillSet: + case AccessorKind::Init: recordDeclLoc(decl, [&]{ Printer << getAccessorLabel(decl->getAccessorKind()); @@ -4342,6 +4347,7 @@ void PrintAST::visitAccessorDecl(AccessorDecl *decl) { } } }); + break; } // handle effects specifiers before the body diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 1238515d24d2a..096a34484f7a1 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -455,22 +455,20 @@ class Traversal : public ASTVisitorvisitAuxiliaryDecls([&](Decl *decl) { - if (alreadyFailed) return; - if (!isa(decl)) - alreadyFailed = inherited::visit(decl); - }); - MED->forEachExpandedExprOrStmt([&](ASTNode expandedNode) { + MED->forEachExpandedNode([&](ASTNode expandedNode) { if (alreadyFailed) return; + if (auto *expr = expandedNode.dyn_cast()) { - if (!doIt(expr)) - alreadyFailed = true; + alreadyFailed = doIt(expr) == nullptr; } else if (auto *stmt = expandedNode.dyn_cast()) { - if (!doIt(stmt)) - alreadyFailed = true; + alreadyFailed = doIt(stmt) == nullptr; + } else { + auto decl = expandedNode.get(); + if (!isa(decl)) + alreadyFailed = doIt(decl); } }); } diff --git a/lib/AST/AccessRequests.cpp b/lib/AST/AccessRequests.cpp index ef3fbb9d31d92..a32ef4f720acb 100644 --- a/lib/AST/AccessRequests.cpp +++ b/lib/AST/AccessRequests.cpp @@ -61,6 +61,9 @@ AccessLevelRequest::evaluate(Evaluator &evaluator, ValueDecl *D) const { case AccessorKind::DidSet: // These are only needed to synthesize the setter. return AccessLevel::Private; + case AccessorKind::Init: + // These are only called from designated initializers. + return AccessLevel::Private; } } diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index fbbd910050475..daee76ed386e6 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1588,6 +1588,10 @@ StringRef DeclAttribute::getAttrName() const { return "<>"; case DAK_Specialize: return "_specialize"; + case DAK_Initializes: + return "initializes"; + case DAK_Accesses: + return "accesses"; case DAK_Implements: return "_implements"; case DAK_ClangImporterSynthesizedType: @@ -2362,6 +2366,38 @@ TransposeAttr *TransposeAttr::create(ASTContext &context, bool implicit, std::move(originalName), parameterIndices); } +InitializesAttr::InitializesAttr(SourceLoc atLoc, SourceRange range, + ArrayRef properties) + : DeclAttribute(DAK_Initializes, atLoc, range, /*implicit*/false), + numProperties(properties.size()) { + std::uninitialized_copy(properties.begin(), properties.end(), + getTrailingObjects()); +} + +InitializesAttr * +InitializesAttr::create(ASTContext &ctx, SourceLoc atLoc, SourceRange range, + ArrayRef properties) { + unsigned size = totalSizeToAlloc(properties.size()); + void *mem = ctx.Allocate(size, alignof(InitializesAttr)); + return new (mem) InitializesAttr(atLoc, range, properties); +} + +AccessesAttr::AccessesAttr(SourceLoc atLoc, SourceRange range, + ArrayRef properties) + : DeclAttribute(DAK_Accesses, atLoc, range, /*implicit*/false), + numProperties(properties.size()) { + std::uninitialized_copy(properties.begin(), properties.end(), + getTrailingObjects()); +} + +AccessesAttr * +AccessesAttr::create(ASTContext &ctx, SourceLoc atLoc, SourceRange range, + ArrayRef properties) { + unsigned size = totalSizeToAlloc(properties.size()); + void *mem = ctx.Allocate(size, alignof(AccessesAttr)); + return new (mem) AccessesAttr(atLoc, range, properties); +} + ImplementsAttr::ImplementsAttr(SourceLoc atLoc, SourceRange range, TypeRepr *TyR, DeclName MemberName, @@ -2504,6 +2540,26 @@ DeclAttributes::getEffectiveSendableAttr() const { return assumedAttr; } +ArrayRef +InitializesAttr::getPropertyDecls(AccessorDecl *attachedTo) const { + auto &ctx = attachedTo->getASTContext(); + return evaluateOrDefault( + ctx.evaluator, + InitAccessorReferencedVariablesRequest{ + const_cast(this), attachedTo, getProperties()}, + {}); +} + +ArrayRef +AccessesAttr::getPropertyDecls(AccessorDecl *attachedTo) const { + auto &ctx = attachedTo->getASTContext(); + return evaluateOrDefault( + ctx.evaluator, + InitAccessorReferencedVariablesRequest{const_cast(this), + attachedTo, getProperties()}, + {}); +} + void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) { if (attr) attr->print(out); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 05af5c01269cd..47dd732090d67 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -254,6 +254,9 @@ DescriptiveDeclKind Decl::getDescriptiveKind() const { case AccessorKind::Modify: return DescriptiveDeclKind::ModifyAccessor; + + case AccessorKind::Init: + return DescriptiveDeclKind::InitAccessor; } llvm_unreachable("bad accessor kind"); } @@ -354,6 +357,7 @@ StringRef Decl::getDescriptiveKindName(DescriptiveDeclKind K) { ENTRY(MutableAddressor, "mutableAddress accessor"); ENTRY(ReadAccessor, "_read accessor"); ENTRY(ModifyAccessor, "_modify accessor"); + ENTRY(InitAccessor, "init acecssor"); ENTRY(EnumElement, "enum case"); ENTRY(Module, "module"); ENTRY(Missing, "missing decl"); @@ -381,7 +385,10 @@ DeclAttributes Decl::getSemanticAttrs() const { return getAttrs(); } -void Decl::visitAuxiliaryDecls(AuxiliaryDeclCallback callback) const { +void Decl::visitAuxiliaryDecls( + AuxiliaryDeclCallback callback, + bool visitFreestandingExpanded +) const { auto &ctx = getASTContext(); auto *mutableThis = const_cast(this); SourceManager &sourceMgr = ctx.SourceMgr; @@ -414,13 +421,15 @@ void Decl::visitAuxiliaryDecls(AuxiliaryDeclCallback callback) const { } } - else if (auto *med = dyn_cast(mutableThis)) { - if (auto bufferID = evaluateOrDefault( - ctx.evaluator, ExpandMacroExpansionDeclRequest{med}, {})) { - auto startLoc = sourceMgr.getLocForBufferStart(*bufferID); - auto *sourceFile = moduleDecl->getSourceFileContainingLocation(startLoc); - for (auto *decl : sourceFile->getTopLevelDecls()) - callback(decl); + if (visitFreestandingExpanded) { + if (auto *med = dyn_cast(mutableThis)) { + if (auto bufferID = evaluateOrDefault( + ctx.evaluator, ExpandMacroExpansionDeclRequest{med}, {})) { + auto startLoc = sourceMgr.getLocForBufferStart(*bufferID); + auto *sourceFile = moduleDecl->getSourceFileContainingLocation(startLoc); + for (auto *decl : sourceFile->getTopLevelDecls()) + callback(decl); + } } } @@ -2787,13 +2796,26 @@ bool AbstractStorageDecl::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 AbstractStorageDecl::isResilient(ModuleDecl *M, ResilienceExpansion expansion) const { switch (expansion) { 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() && + !isOriginallyDefinedIn(this, M) && + isResilient()); } llvm_unreachable("bad resilience expansion"); } @@ -3348,12 +3370,18 @@ TypeRepr *ValueDecl::getResultTypeRepr() const { returnRepr = FD->getResultTypeRepr(); } else if (auto *SD = dyn_cast(this)) { returnRepr = SD->getElementTypeRepr(); + } else if (auto *MD = dyn_cast(this)) { + returnRepr = MD->resultType.getTypeRepr(); } return returnRepr; } TypeRepr *ValueDecl::getOpaqueResultTypeRepr() const { + // FIXME: Macros don't allow opaque result types yet. + if (isa(this)) + return nullptr; + auto *returnRepr = this->getResultTypeRepr(); auto *dc = getDeclContext(); @@ -4727,14 +4755,6 @@ DestructorDecl *NominalTypeDecl::getValueTypeDestructor() { return cast(found[0]); } -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) { @@ -4744,8 +4764,9 @@ 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() && !isOriginallyDefinedIn(this, M) && - isResilient(); + return (M != getModuleContext() && + !isOriginallyDefinedIn(this, M) && + isResilient()); } llvm_unreachable("bad resilience expansion"); } @@ -4880,6 +4901,28 @@ ArrayRef NominalTypeDecl::getStoredProperties() const { {}); } +ArrayRef +NominalTypeDecl::getInitAccessorProperties() const { + auto &ctx = getASTContext(); + auto mutableThis = const_cast(this); + return evaluateOrDefault( + ctx.evaluator, + InitAccessorPropertiesRequest{mutableThis}, + {}); +} + +void NominalTypeDecl::collectPropertiesInitializableByInitAccessors( + std::multimap &result) const { + for (auto *property : getInitAccessorProperties()) { + auto *initAccessor = property->getAccessor(AccessorKind::Init); + if (auto *initAttr = + initAccessor->getAttrs().getAttribute()) { + for (auto *subsumed : initAttr->getPropertyDecls(initAccessor)) + result.insert({subsumed, property}); + } + } +} + ArrayRef NominalTypeDecl::getStoredPropertiesAndMissingMemberPlaceholders() const { auto &ctx = getASTContext(); @@ -6689,10 +6732,28 @@ bool VarDecl::isSettable(const DeclContext *UseDC, // 'let's are only ever settable from a specific DeclContext. if (UseDC == nullptr) return false; - + // 'let' properties in structs/classes are only ever settable in their - // designated initializer(s). + // designated initializer(s) or by init accessors. if (isInstanceMember()) { + // Init accessors allow assignments to `let` properties if a + // property is part of `initializes(...)` list. + if (auto *accessor = + dyn_cast(const_cast(UseDC))) { + // Check whether this property is part of `initializes(...)` list, + // and allow assignment/mutation if so. DI would be responsible + // for checking for re-assignment. + if (auto *initAttr = + accessor->getAttrs().getAttribute()) { + return llvm::is_contained(initAttr->getPropertyDecls(accessor), + const_cast(this)); + } + + // If there is no `initializes` attribute, no referenced properties + // can be assignment to or mutated. + return false; + } + auto *CD = dyn_cast(UseDC); if (!CD) return false; @@ -8324,7 +8385,8 @@ DeclName AbstractFunctionDecl::getEffectiveFullName() const { case AccessorKind::Set: case AccessorKind::DidSet: - case AccessorKind::WillSet: { + case AccessorKind::WillSet: + case AccessorKind::Init: { SmallVector argNames; // The implicit value/buffer parameter. argNames.push_back(Identifier()); @@ -9305,6 +9367,7 @@ bool AccessorDecl::isAssumedNonMutating() const { case AccessorKind::DidSet: case AccessorKind::MutableAddress: case AccessorKind::Modify: + case AccessorKind::Init: return false; } llvm_unreachable("bad accessor kind"); @@ -10581,6 +10644,11 @@ MacroDefinition MacroDecl::getDefinition() const { MacroDefinition::forUndefined()); } +void MacroDecl::setDefinition(MacroDefinition definition) { + getASTContext().evaluator.cacheOutput(MacroDefinitionRequest{this}, + std::move(definition)); +} + Optional MacroDecl::getBuiltinKind() const { auto def = getDefinition(); if (def.kind != MacroDefinition::Kind::Builtin) @@ -10637,8 +10705,9 @@ unsigned MacroExpansionDecl::getDiscriminator() const { return getRawDiscriminator(); } -void MacroExpansionDecl::forEachExpandedExprOrStmt( - ExprOrStmtExpansionCallback callback) const { +void MacroExpansionDecl::forEachExpandedNode( + llvm::function_ref callback +) const { auto mutableThis = const_cast(this); auto bufferID = evaluateOrDefault( getASTContext().evaluator, @@ -10650,8 +10719,7 @@ void MacroExpansionDecl::forEachExpandedExprOrStmt( auto startLoc = sourceMgr.getLocForBufferStart(*bufferID); auto *sourceFile = moduleDecl->getSourceFileContainingLocation(startLoc); for (auto node : sourceFile->getTopLevelItems()) - if (node.is() || node.is()) - callback(node); + callback(node); } NominalTypeDecl * diff --git a/lib/AST/PrettyStackTrace.cpp b/lib/AST/PrettyStackTrace.cpp index e2dd3b6554f29..747faf50f5170 100644 --- a/lib/AST/PrettyStackTrace.cpp +++ b/lib/AST/PrettyStackTrace.cpp @@ -91,6 +91,9 @@ void swift::printDeclDescription(llvm::raw_ostream &out, const Decl *D, case AccessorKind::Modify: out << "modify"; break; + case AccessorKind::Init: + out << "init"; + break; } out << " for " << ASD->getName(); diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index faf0e86666494..df1bb91c57180 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -418,6 +418,16 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // For the second step, we're looking into the requirement signature for // this protocol. + if (conformance.isPack()) { + auto pack = conformance.getPack(); + conformance = ProtocolConformanceRef( + pack->getAssociatedConformance(step.first, step.second)); + if (conformance.isInvalid()) + return conformance; + + continue; + } + auto concrete = conformance.getConcrete(); auto normal = concrete->getRootNormalConformance(); diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 381bbe9d6bdd6..c83e5877ad9de 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3595,11 +3595,30 @@ namespace { return nullptr; } - auto importedType = - Impl.importType(decl->getType(), ImportTypeKind::RecordField, - ImportDiagnosticAdder(Impl, decl, decl->getLocation()), - isInSystemModule(dc), Bridgeability::None, - getImportTypeAttrs(decl)); + ImportedType importedType; + auto fieldType = decl->getType(); + if (auto elaborated = dyn_cast(fieldType)) + fieldType = elaborated->desugar(); + if (auto typedefType = dyn_cast(fieldType)) { + if (Impl.isUnavailableInSwift(typedefType->getDecl())) { + if (auto clangEnum = findAnonymousEnumForTypedef(Impl.SwiftContext, typedefType)) { + // If this fails, it means that we need a stronger predicate for + // determining the relationship between an enum and typedef. + assert(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() == + typedefType->getCanonicalTypeInternal()); + if (auto swiftEnum = Impl.importDecl(*clangEnum, Impl.CurrentVersion)) { + importedType = {cast(swiftEnum)->getDeclaredInterfaceType(), false}; + } + } + } + } + + if (!importedType) + importedType = + Impl.importType(decl->getType(), ImportTypeKind::RecordField, + ImportDiagnosticAdder(Impl, decl, decl->getLocation()), + isInSystemModule(dc), Bridgeability::None, + getImportTypeAttrs(decl)); if (!importedType) { Impl.addImportDiagnostic( decl, Diagnostic(diag::record_field_not_imported, decl), @@ -5828,6 +5847,13 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name, const clang::EnumDecl *decl) { ASTContext &ctx = Impl.SwiftContext; + auto Loc = Impl.importSourceLoc(decl->getLocation()); + + // Create a struct with the underlying type as a field. + auto structDecl = Impl.createDeclWithClangNode( + decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc); + Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = structDecl; + // Compute the underlying type. auto underlyingType = Impl.importTypeIgnoreIUO( decl->getIntegerType(), ImportTypeKind::Enum, @@ -5836,12 +5862,6 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name, if (!underlyingType) return nullptr; - auto Loc = Impl.importSourceLoc(decl->getLocation()); - - // Create a struct with the underlying type as a field. - auto structDecl = Impl.createDeclWithClangNode( - decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc); - synthesizer.makeStructRawValued(structDecl, underlyingType, {KnownProtocolKind::OptionSet}); auto selfType = structDecl->getDeclaredInterfaceType(); diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index c686357441ac2..c359cc0b799de 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -3704,6 +3704,7 @@ NodePointer Demangler::demangleAccessor(NodePointer ChildNode) { case 'W': Kind = Node::Kind::DidSet; break; case 'r': Kind = Node::Kind::ReadAccessor; break; case 'M': Kind = Node::Kind::ModifyAccessor; break; + case 'i': Kind = Node::Kind::InitAccessor; break; case 'a': switch (nextChar()) { case 'O': Kind = Node::Kind::OwningMutableAddressor; break; diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index ad5445054f444..248e0b1ce4493 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -415,6 +415,7 @@ class NodePrinter { case Node::Kind::GlobalGetter: case Node::Kind::Identifier: case Node::Kind::Index: + case Node::Kind::InitAccessor: case Node::Kind::IVarInitializer: case Node::Kind::IVarDestroyer: case Node::Kind::ImplDifferentiabilityKind: @@ -2597,6 +2598,9 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth, case Node::Kind::ModifyAccessor: return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "modify"); + case Node::Kind::InitAccessor: + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, + "init"); case Node::Kind::Allocator: return printEntity( Node, depth, asPrefixContext, TypePrinting::FunctionStyle, diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 33764e098936d..90bd6c0655c84 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -1244,6 +1244,11 @@ ManglingError Remangler::mangleDidSet(Node *node, EntityContext &ctx, return mangleAccessor(node->getFirstChild(), "W", ctx, depth + 1); } +ManglingError Remangler::mangleInitAccessor(Node *node, EntityContext &ctx, + unsigned depth) { + return mangleAccessor(node->getFirstChild(), "i", ctx, depth + 1); +} + ManglingError Remangler::mangleOwningMutableAddressor(Node *node, EntityContext &ctx, unsigned depth) { diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 3ef91629f5693..63b6043b72b8b 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -2091,6 +2091,10 @@ ManglingError Remangler::mangleInitializer(Node *node, unsigned depth) { return ManglingError::Success; } +ManglingError Remangler::mangleInitAccessor(Node *node, unsigned depth) { + return mangleAbstractStorage(node->getFirstChild(), "i", depth + 1); +} + ManglingError Remangler::manglePropertyWrapperBackingInitializer(Node *node, unsigned depth) { RETURN_IF_ERROR(mangleChildNodes(node, depth + 1)); diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 0ebdffe28fbc3..32867832d3852 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -375,7 +375,7 @@ Driver::buildToolChain(const llvm::opt::InputArgList &ArgList) { case llvm::Triple::Haiku: return std::make_unique(*this, target); case llvm::Triple::WASI: - return std::make_unique(*this, target); + return std::make_unique(*this, target); case llvm::Triple::UnknownOS: return std::make_unique(*this, target); default: diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index 63f0a3ee21a23..ba1135fc3d2c7 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -236,6 +236,22 @@ ASTWalker::PreWalkAction SemaAnnotator::walkToDeclPreProper(Decl *D) { ASTWalker::PostWalkAction SemaAnnotator::walkToDeclPost(Decl *D) { auto Action = walkToDeclPostProper(D); SEWalker.endBalancedASTOrderDeclVisit(D); + + if (Action.Action == PostWalkAction::Stop) + return Action; + + // Walk into peer and conformance expansions if walking expansions + if (shouldWalkMacroArgumentsAndExpansion().second) { + D->visitAuxiliaryDecls([&](Decl *auxDecl) { + if (Action.Action == PostWalkAction::Stop) + return; + + if (auxDecl->walk(*this)) { + Action = Action::Stop(); + } + }, /*visitFreestandingExpanded=*/false); + } + return Action; } diff --git a/lib/IDE/TypeCheckCompletionCallback.cpp b/lib/IDE/TypeCheckCompletionCallback.cpp index d2f652bd84a68..9739a53fbd679 100644 --- a/lib/IDE/TypeCheckCompletionCallback.cpp +++ b/lib/IDE/TypeCheckCompletionCallback.cpp @@ -81,7 +81,13 @@ Type swift::ide::getTypeForCompletion(const constraints::Solution &S, /// \endcode /// If the code completion expression occurs in such an AST, return the /// declaration of the \c $match variable, otherwise return \c nullptr. -static VarDecl *getMatchVarIfInPatternMatch(Expr *E, ConstraintSystem &CS) { +static VarDecl *getMatchVarIfInPatternMatch(Expr *E, const Solution &S) { + if (auto EP = S.getExprPatternFor(E)) + return EP.get()->getMatchVar(); + + // TODO: Once ExprPattern type-checking is fully moved into the solver, + // the below can be deleted. + auto &CS = S.getConstraintSystem(); auto &Context = CS.getASTContext(); auto *Binary = dyn_cast_or_null(CS.getParentExpr(E)); @@ -109,20 +115,21 @@ static VarDecl *getMatchVarIfInPatternMatch(Expr *E, ConstraintSystem &CS) { } Type swift::ide::getPatternMatchType(const constraints::Solution &S, Expr *E) { - if (auto MatchVar = getMatchVarIfInPatternMatch(E, S.getConstraintSystem())) { - Type MatchVarType; - // If the MatchVar has an explicit type, it's not part of the solution. But - // we can look it up in the constraint system directly. - if (auto T = S.getConstraintSystem().getVarType(MatchVar)) { - MatchVarType = T; - } else { - MatchVarType = getTypeForCompletion(S, MatchVar); - } - if (MatchVarType) { - return MatchVarType; - } - } - return nullptr; + auto MatchVar = getMatchVarIfInPatternMatch(E, S); + if (!MatchVar) + return nullptr; + + if (S.hasType(MatchVar)) + return S.getResolvedType(MatchVar); + + // If the ExprPattern wasn't solved as part of the constraint system, it's + // not part of the solution. + // TODO: This can be removed once ExprPattern type-checking is fully part + // of the constraint system. + if (auto T = S.getConstraintSystem().getVarType(MatchVar)) + return T; + + return getTypeForCompletion(S, MatchVar); } void swift::ide::getSolutionSpecificVarTypes( diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index 776f6a9e54450..6db691f0879ee 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/IDE/Utils.h" +#include "swift/AST/SourceFile.h" #include "swift/Basic/Edit.h" #include "swift/Basic/Platform.h" #include "swift/Basic/SourceManager.h" @@ -622,6 +623,102 @@ remove(SourceManager &SM, CharSourceRange Range) { accept(SM, Range, ""); } +/// Given the expanded code for a particular macro, perform whitespace +/// adjustments to make the refactoring more suitable for inline insertion. +static StringRef +adjustMacroExpansionWhitespace(GeneratedSourceInfo::Kind kind, + StringRef expandedCode, + llvm::SmallString<64> &scratch) { + scratch.clear(); + + switch (kind) { + case GeneratedSourceInfo::MemberAttributeMacroExpansion: + // Attributes are added to the beginning, add a space to separate from + // any existing. + scratch += expandedCode; + scratch += " "; + return scratch; + + case GeneratedSourceInfo::MemberMacroExpansion: + case GeneratedSourceInfo::PeerMacroExpansion: + case GeneratedSourceInfo::ConformanceMacroExpansion: + // All added to the end. Note that conformances are always expanded as + // extensions, hence treating them the same as peer. + scratch += "\n\n"; + scratch += expandedCode; + scratch += "\n"; + return scratch; + + case GeneratedSourceInfo::ExpressionMacroExpansion: + case GeneratedSourceInfo::FreestandingDeclMacroExpansion: + case GeneratedSourceInfo::AccessorMacroExpansion: + case GeneratedSourceInfo::ReplacedFunctionBody: + case GeneratedSourceInfo::PrettyPrinted: + return expandedCode; + } +} + +void swift::ide::SourceEditConsumer::acceptMacroExpansionBuffer( + SourceManager &SM, unsigned bufferID, SourceFile *containingSF, + bool adjustExpansion, bool includeBufferName) { + auto generatedInfo = SM.getGeneratedSourceInfo(bufferID); + if (!generatedInfo || generatedInfo->originalSourceRange.isInvalid()) + return; + + auto rewrittenBuffer = SM.extractText(generatedInfo->generatedSourceRange); + + // If there's no change, drop the edit entirely. + if (generatedInfo->originalSourceRange.getStart() == + generatedInfo->originalSourceRange.getEnd() && + rewrittenBuffer.empty()) + return; + + SmallString<64> scratchBuffer; + if (adjustExpansion) { + rewrittenBuffer = adjustMacroExpansionWhitespace( + generatedInfo->kind, rewrittenBuffer, scratchBuffer); + } + + // `containingFile` is the file of the actual expansion site, where as + // `originalFile` is the possibly enclosing buffer. Concretely: + // ``` + // // m.swift + // @AddMemberAttributes + // struct Foo { + // // --- expanded from @AddMemberAttributes eg. @_someBufferName --- + // @AddedAttribute + // // --- + // let someMember: Int + // } + // ``` + // + // When expanding `AddedAttribute`, the expansion actually applies to the + // original source (`m.swift`) rather than the buffer of the expansion + // site (`@_someBufferName`). Thus, we need to include the path to the + // original source as well. Note that this path could itself be another + // expansion. + auto originalSourceRange = generatedInfo->originalSourceRange; + SourceFile *originalFile = + containingSF->getParentModule()->getSourceFileContainingLocation( + originalSourceRange.getStart()); + StringRef originalPath; + if (originalFile->getBufferID().hasValue() && + containingSF->getBufferID() != originalFile->getBufferID()) { + originalPath = SM.getIdentifierForBuffer(*originalFile->getBufferID()); + } + + StringRef bufferName; + if (includeBufferName) { + bufferName = SM.getIdentifierForBuffer(bufferID); + } + + accept(SM, {originalPath, + originalSourceRange, + bufferName, + rewrittenBuffer, + {}}); +} + struct swift::ide::SourceEditJsonConsumer::Implementation { llvm::raw_ostream &OS; std::vector AllEdits; diff --git a/lib/IDETool/CMakeLists.txt b/lib/IDETool/CMakeLists.txt index c7555e0cb3d51..34994928c1459 100644 --- a/lib/IDETool/CMakeLists.txt +++ b/lib/IDETool/CMakeLists.txt @@ -4,6 +4,7 @@ add_swift_host_library(swiftIDETool STATIC CompilerInvocation.cpp IDEInspectionInstance.cpp DependencyChecking.cpp + SyntacticMacroExpansion.cpp ) target_link_libraries(swiftIDETool PRIVATE diff --git a/lib/IDETool/SyntacticMacroExpansion.cpp b/lib/IDETool/SyntacticMacroExpansion.cpp new file mode 100644 index 0000000000000..afcffa672721b --- /dev/null +++ b/lib/IDETool/SyntacticMacroExpansion.cpp @@ -0,0 +1,471 @@ +//===--- SyntacticMacroExpansion.cpp --------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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/IDETool/SyntacticMacroExpansion.h" +#include "swift/AST/ASTWalker.h" +#include "swift/AST/MacroDefinition.h" +#include "swift/AST/PluginLoader.h" +#include "swift/AST/TypeRepr.h" +#include "swift/Driver/FrontendUtil.h" +#include "swift/Frontend/Frontend.h" +#include "swift/IDE/Utils.h" +#include "swift/Sema/IDETypeChecking.h" + +using namespace swift; +using namespace ide; + +std::shared_ptr +SyntacticMacroExpansion::getInstance(ArrayRef args, + std::string &error) { + // Create and configure a new instance. + auto instance = std::make_shared(); + + bool failed = instance->setup(SwiftExecutablePath, args, Plugins, error); + if (failed) + return nullptr; + + return instance; +} + +bool SyntacticMacroExpansionInstance::setup( + StringRef SwiftExecutablePath, ArrayRef args, + std::shared_ptr plugins, std::string &error) { + SmallString<256> driverPath(SwiftExecutablePath); + llvm::sys::path::remove_filename(driverPath); + llvm::sys::path::append(driverPath, "swiftc"); + + // Setup CompilerInstance to configure the plugin search path correctly. + bool hadError = driver::getSingleFrontendInvocationFromDriverArguments( + driverPath, args, Diags, + [&](ArrayRef frontendArgs) { + return invocation.parseArgs( + frontendArgs, Diags, /*ConfigurationFileBuffers=*/nullptr, + /*workingDirectory=*/{}, SwiftExecutablePath); + }, + /*ForceNoOutput=*/true); + if (hadError) { + error = "failed to setup compiler invocation"; + return true; + } + + // Setup ASTContext. + Ctx.reset(ASTContext::get( + invocation.getLangOptions(), invocation.getTypeCheckerOptions(), + invocation.getSILOptions(), invocation.getSearchPathOptions(), + invocation.getClangImporterOptions(), invocation.getSymbolGraphOptions(), + SourceMgr, Diags)); + registerParseRequestFunctions(Ctx->evaluator); + registerTypeCheckerRequestFunctions(Ctx->evaluator); + + std::unique_ptr pluginLoader = + std::make_unique(*Ctx, /*DepTracker=*/nullptr); + pluginLoader->setRegistry(plugins.get()); + Ctx->setPluginLoader(std::move(pluginLoader)); + + // Create a module where SourceFiles reside. + Identifier ID = Ctx->getIdentifier(invocation.getModuleName()); + TheModule = ModuleDecl::create(ID, *Ctx); + + return false; +} + +SourceFile * +SyntacticMacroExpansionInstance::getSourceFile(llvm::MemoryBuffer *inputBuf) { + + // If there is a SourceFile with the same name and the content, use it. + // Note that this finds the generated source file that was created in the + // previous expansion requests. + if (auto bufID = + SourceMgr.getIDForBufferIdentifier(inputBuf->getBufferIdentifier())) { + if (inputBuf->getBuffer() == SourceMgr.getEntireTextForBuffer(*bufID)) { + SourceLoc bufLoc = SourceMgr.getLocForBufferStart(*bufID); + if (SourceFile *existing = + TheModule->getSourceFileContainingLocation(bufLoc)) { + return existing; + } + } + } + + // Otherwise, create a new SourceFile. + SourceFile *SF = new (getASTContext()) SourceFile( + *TheModule, SourceFileKind::Main, SourceMgr.addMemBufferCopy(inputBuf)); + SF->setImports({}); + TheModule->addFile(*SF); + + return SF; +} + +MacroDecl *SyntacticMacroExpansionInstance::getSynthesizedMacroDecl( + Identifier name, const MacroExpansionSpecifier &expansion) { + auto &ctx = getASTContext(); + + std::string macroID; + + switch (expansion.macroDefinition.kind) { + case MacroDefinition::Kind::External: { + // '.' + // It's safe to use without 'kind' because 'Expanded' always starts with a + // sigil '#' which can't be valid in a module name. + auto external = expansion.macroDefinition.getExternalMacro(); + macroID += external.moduleName.str(); + macroID += "."; + macroID += external.macroTypeName.str(); + break; + } + case MacroDefinition::Kind::Expanded: { + auto expanded = expansion.macroDefinition.getExpanded(); + macroID += expanded.getExpansionText(); + break; + } + case MacroDefinition::Kind::Builtin: + case MacroDefinition::Kind::Invalid: + case MacroDefinition::Kind::Undefined: + assert(false && "invalid macro definition for syntactic expansion"); + macroID += name.str(); + } + + // Reuse cached MacroDecl of the same name if it's already created. + MacroDecl *macro; + auto found = MacroDecls.find(macroID); + if (found != MacroDecls.end()) { + macro = found->second; + } else { + macro = new (ctx) MacroDecl( + /*macroLoc=*/{}, DeclName(name), /*nameLoc=*/{}, + /*genericParams=*/nullptr, /*parameterList=*/nullptr, + /*arrowLoc=*/{}, /*resultType=*/nullptr, + /*definition=*/nullptr, /*parent=*/TheModule); + macro->setImplicit(); + MacroDecls.insert({macroID, macro}); + } + + // Add missing role attributes to MacroDecl. + MacroRoles roles = expansion.macroRoles; + for (auto attr : macro->getAttrs().getAttributes()) { + roles -= attr->getMacroRole(); + } + for (MacroRole role : getAllMacroRoles()) { + if (!roles.contains(role)) + continue; + + MacroSyntax syntax = getFreestandingMacroRoles().contains(role) + ? MacroSyntax::Freestanding + : MacroSyntax::Attached; + + auto *attr = MacroRoleAttr::create(ctx, /*atLoc=*/{}, /*range=*/{}, syntax, + /*lParenLoc=*/{}, role, /*names=*/{}, + /*rParenLoc=*/{}, /*implicit=*/true); + macro->getAttrs().add(attr); + } + + // Set the macro definition. + macro->setDefinition(expansion.macroDefinition); + + return macro; +} + +/// Create a unique name of the expansion. The result is *appended* to \p out. +static void addExpansionDiscriminator(SmallString<32> &out, + const SourceFile *SF, SourceLoc loc, + Optional supplementalLoc = None, + Optional role = None) { + SourceManager &SM = SF->getASTContext().SourceMgr; + + StableHasher hasher = StableHasher::defaultHasher(); + + // Module name. + hasher.combine(SF->getParentModule()->getName().str()); + hasher.combine(uint8_t{0}); + + // File base name. + // Do not use the full path because we want this hash stable. + hasher.combine(llvm::sys::path::filename(SF->getFilename())); + hasher.combine(uint8_t{0}); + + // Line/column. + auto lineColumn = SM.getLineAndColumnInBuffer(loc); + hasher.combine(lineColumn.first); + hasher.combine(lineColumn.second); + + // Supplemental line/column. + if (supplementalLoc.has_value()) { + auto supLineColumn = SM.getLineAndColumnInBuffer(*supplementalLoc); + hasher.combine(supLineColumn.first); + hasher.combine(supLineColumn.second); + } + + // Macro role. + if (role.has_value()) { + hasher.combine(*role); + } + + Fingerprint hash(std::move(hasher)); + out.append(hash.getRawValue()); +} + +/// Perform expansion of the specified freestanding macro using the 'MacroDecl'. +static std::vector +expandFreestandingMacro(MacroDecl *macro, + FreestandingMacroExpansion *expansion) { + std::vector bufferIDs; + + SmallString<32> discriminator; + discriminator.append("__syntactic_macro_"); + addExpansionDiscriminator(discriminator, + expansion->getDeclContext()->getParentSourceFile(), + expansion->getPoundLoc()); + + expansion->setMacroRef(macro); + + SourceFile *expandedSource = + swift::evaluateFreestandingMacro(expansion, discriminator); + if (expandedSource) + bufferIDs.push_back(*expandedSource->getBufferID()); + + return bufferIDs; +} + +/// Perform expansion of the specified decl and the attribute using the +/// 'MacroDecl'. If the macro has multiple roles, evaluate it for all macro +/// roles. +static std::vector +expandAttachedMacro(MacroDecl *macro, CustomAttr *attr, Decl *attachedDecl) { + + std::vector bufferIDs; + auto evaluate = [&](Decl *target, bool passParent, MacroRole role) { + + SmallString<32> discriminator; + discriminator.append("macro_"); + addExpansionDiscriminator(discriminator, + target->getDeclContext()->getParentSourceFile(), + target->getLoc(), attr->getLocation(), role); + + SourceFile *expandedSource = swift::evaluateAttachedMacro( + macro, target, attr, passParent, role, discriminator); + if (expandedSource) + bufferIDs.push_back(*expandedSource->getBufferID()); + }; + + MacroRoles roles = macro->getMacroRoles(); + if (roles.contains(MacroRole::Accessor)) { + if (isa(attachedDecl)) + evaluate(attachedDecl, /*passParent=*/false, MacroRole::Accessor); + } + if (roles.contains(MacroRole::MemberAttribute)) { + if (auto *idc = dyn_cast(attachedDecl)) { + for (auto *member : idc->getParsedMembers()) { + // 'VarDecl' in 'IterableDeclContext' are part of 'PatternBindingDecl'. + if (isa(member)) + continue; + evaluate(member, /*passParent=*/true, MacroRole::MemberAttribute); + } + } + } + if (roles.contains(MacroRole::Member)) { + if (isa(attachedDecl)) + evaluate(attachedDecl, /*passParent=*/false, MacroRole::Member); + } + if (roles.contains(MacroRole::Peer)) { + evaluate(attachedDecl, /*passParent=*/false, MacroRole::Peer); + } + if (roles.contains(MacroRole::Conformance)) { + if (isa(attachedDecl)) + evaluate(attachedDecl, /*passParent=*/false, MacroRole::Conformance); + } + return bufferIDs; +} + +/// Get the name of the custom attribute. This is used to create a dummy +/// MacroDecl. +static Identifier getCustomAttrName(ASTContext &ctx, const CustomAttr *attr) { + TypeRepr *tyR = attr->getTypeRepr(); + if (auto ref = dyn_cast(tyR)) { + return ref->getNameRef().getBaseIdentifier(); + } + + // If the attribute is not an identifier type, create an identifier with its + // textual representation. This is *not* expected to be reachable. + // The only case is like `@Foo?` where the client should not send the + // expansion request on this in the first place. + SmallString<32> name; + llvm::raw_svector_ostream OS(name); + tyR->print(OS); + return ctx.getIdentifier(name); +} + +namespace { + +/// Find macro expansion i.e. '#foo' or '@foo' at the specified source location. +/// If a freestanding expansion (i.e. #foo) is found, the result 'ExpansionNode' +/// only has the node. If an attribute is found, the attribute and the attached +/// decl object is returned. +struct ExpansionNode { + CustomAttr *attribute; + ASTNode node; +}; +class MacroExpansionFinder : public ASTWalker { + SourceManager &SM; + SourceLoc locToResolve; + llvm::Optional result; + + bool rangeContainsLocToResolve(SourceRange Range) const { + return SM.rangeContainsTokenLoc(Range, locToResolve); + } + +public: + MacroExpansionFinder(SourceManager &SM, SourceLoc locToResolve) + : SM(SM), locToResolve(locToResolve) {} + + llvm::Optional getResult() const { return result; } + + MacroWalking getMacroWalkingBehavior() const override { + return MacroWalking::None; + } + + PreWalkAction walkToDeclPre(Decl *D) override { + // Visit all 'VarDecl' because 'getSourceRangeIncludingAttrs()' doesn't + // include its attribute ranges (because attributes are part of PBD.) + if (!isa(D) && + !rangeContainsLocToResolve(D->getSourceRangeIncludingAttrs())) { + return Action::SkipChildren(); + } + + // Check the attributes. + for (DeclAttribute *attr : D->getAttrs()) { + if (auto customAttr = dyn_cast(attr)) { + SourceRange nameRange(customAttr->getRangeWithAt().Start, + customAttr->getTypeExpr()->getEndLoc()); + if (rangeContainsLocToResolve(nameRange)) { + result = ExpansionNode{customAttr, ASTNode(D)}; + return Action::Stop(); + } + } + } + + // Check 'MacroExpansionDecl'. + if (auto med = dyn_cast(D)) { + SourceRange nameRange(med->getExpansionInfo()->SigilLoc, + med->getMacroNameLoc().getEndLoc()); + if (rangeContainsLocToResolve(nameRange)) { + result = ExpansionNode{nullptr, ASTNode(med)}; + return Action::Stop(); + } + } + + return Action::Continue(); + } + + PreWalkResult walkToExprPre(Expr *E) override { + if (!rangeContainsLocToResolve(E->getSourceRange())) { + return Action::SkipChildren(E); + } + + // Check 'MacroExpansionExpr'. + if (auto mee = dyn_cast(E)) { + SourceRange nameRange(mee->getExpansionInfo()->SigilLoc, + mee->getMacroNameLoc().getEndLoc()); + if (rangeContainsLocToResolve(nameRange)) { + result = ExpansionNode{nullptr, ASTNode(mee)}; + return Action::Stop(); + } + } + + return Action::Continue(E); + } + + PreWalkResult walkToStmtPre(Stmt *S) override { + if (!rangeContainsLocToResolve(S->getSourceRange())) { + return Action::SkipChildren(S); + } + return Action::Continue(S); + } + PreWalkResult + walkToArgumentListPre(ArgumentList *AL) override { + if (!rangeContainsLocToResolve(AL->getSourceRange())) { + return Action::SkipChildren(AL); + } + return Action::Continue(AL); + } + PreWalkAction walkToParameterListPre(ParameterList *PL) override { + if (!rangeContainsLocToResolve(PL->getSourceRange())) { + return Action::SkipChildren(); + } + return Action::Continue(); + } + PreWalkAction walkToTypeReprPre(TypeRepr *T) override { + // TypeRepr cannot have macro expansions in it. + return Action::SkipChildren(); + } +}; +} // namespace + +void SyntacticMacroExpansionInstance::expand( + SourceFile *SF, const MacroExpansionSpecifier &expansion, + SourceEditConsumer &consumer) { + + // Find the expansion at 'expantion.offset'. + MacroExpansionFinder expansionFinder( + SourceMgr, + SourceMgr.getLocForOffset(*SF->getBufferID(), expansion.offset)); + SF->walk(expansionFinder); + auto expansionNode = expansionFinder.getResult(); + if (!expansionNode) + return; + + // Expand the macro. + std::vector bufferIDs; + if (auto *attr = expansionNode->attribute) { + // Attached macros. + MacroDecl *macro = getSynthesizedMacroDecl( + getCustomAttrName(getASTContext(), attr), expansion); + auto *attachedTo = expansionNode->node.get(); + bufferIDs = expandAttachedMacro(macro, attr, attachedTo); + + // For an attached macro, remove the custom attribute; it's been fully + // subsumed by its expansions. + SourceRange range = attr->getRangeWithAt(); + auto charRange = Lexer::getCharSourceRangeFromSourceRange(SourceMgr, range); + consumer.remove(SourceMgr, charRange); + } else { + // Freestanding macros. + FreestandingMacroExpansion *freestanding; + auto node = expansionNode->node; + if (node.is()) { + freestanding = cast(node.get()); + } else { + freestanding = cast(node.get()); + } + + MacroDecl *macro = getSynthesizedMacroDecl( + freestanding->getMacroName().getBaseIdentifier(), expansion); + bufferIDs = expandFreestandingMacro(macro, freestanding); + } + + // Send all edits to the consumer. + for (unsigned bufferID : bufferIDs) { + consumer.acceptMacroExpansionBuffer(SourceMgr, bufferID, SF, + /*adjust=*/false, + /*includeBufferName=*/false); + } +} + +void SyntacticMacroExpansionInstance::expandAll( + llvm::MemoryBuffer *inputBuf, ArrayRef expansions, + SourceEditConsumer &consumer) { + + // Create a source file. + SourceFile *SF = getSourceFile(inputBuf); + + for (const auto &expansion : expansions) { + expand(SF, expansion, consumer); + } +} diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 41b7a44521bef..16fa1bc9e3a39 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -28,6 +28,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CodeGenABITypes.h" #include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Sema/Sema.h" #include "llvm/IR/GlobalPtrAuthInfo.h" #include "llvm/IR/GlobalValue.h" #include "llvm/Support/Compiler.h" @@ -4555,6 +4556,23 @@ bool IRGenModule::isForeignExceptionHandlingEnabled() const { !clangLangOpts.IgnoreExceptions; } +bool IRGenModule::isCxxNoThrow(clang::FunctionDecl *fd, bool defaultNoThrow) { + auto *fpt = fd->getType()->getAs(); + if (!fpt) + return defaultNoThrow; + if (fpt->getExceptionSpecType() == + clang::ExceptionSpecificationType::EST_Unevaluated) { + // Clang might not have evaluated the exception spec for + // a constructor, so force the evaluation of it. + auto &clangSema = Context.getClangModuleLoader()->getClangSema(); + clangSema.EvaluateImplicitExceptionSpec(fd->getLocation(), fd); + fpt = fd->getType()->getAs(); + if (!fpt) + return defaultNoThrow; + } + return fpt->isNothrow(); +} + /// Emit the epilogue for the function. void IRGenFunction::emitEpilogue() { if (EarliestIP != AllocaIP) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index eb4e9f44624cb..54443868ef378 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -3343,11 +3343,9 @@ llvm::CallBase *swift::irgen::emitCXXConstructorCall( IRGenFunction &IGF, const clang::CXXConstructorDecl *ctor, llvm::FunctionType *ctorFnType, llvm::Constant *ctorAddress, llvm::ArrayRef args) { - bool canThrow = IGF.IGM.isForeignExceptionHandlingEnabled(); - if (auto *fpt = ctor->getType()->getAs()) { - if (fpt->isNothrow()) - canThrow = false; - } + bool canThrow = + IGF.IGM.isForeignExceptionHandlingEnabled() && + !IGF.IGM.isCxxNoThrow(const_cast(ctor)); if (!canThrow) return IGF.Builder.CreateCall(ctorFnType, ctorAddress, args); llvm::CallBase *result; diff --git a/lib/IRGen/GenStruct.cpp b/lib/IRGen/GenStruct.cpp index 7c28d69673ce3..eed6782d6fbba 100644 --- a/lib/IRGen/GenStruct.cpp +++ b/lib/IRGen/GenStruct.cpp @@ -721,11 +721,8 @@ namespace { } bool canThrow = false; if (IGF.IGM.isForeignExceptionHandlingEnabled()) { - if (auto *fpt = - destructor->getType()->getAs()) { - if (!fpt->isNothrow()) - canThrow = true; - } + if (!IGF.IGM.isCxxNoThrow(destructor, /*defaultNoThrow=*/true)) + canThrow = true; } if (canThrow) { IGF.createExceptionTrapScope([&](llvm::BasicBlock *invokeNormalDest, diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index d397286df5437..d4ce0fbe47bce 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -545,6 +545,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { case AccessorKind::Modify: Kind = ".modify"; break; + case AccessorKind::Init: + Kind = ".init"; + break; } SmallVector Buf; diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 037e5c62994ad..f3e01a03a74e2 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1831,6 +1831,9 @@ private: \ bool isForeignExceptionHandlingEnabled() const; + /// Returns true if the given Clang function does not throw exceptions. + bool isCxxNoThrow(clang::FunctionDecl *fd, bool defaultNoThrow = false); + private: llvm::Constant * getAddrOfSharedContextDescriptor(LinkEntity entity, diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 990261912331b..4b1ffb0b71ea0 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1228,6 +1228,9 @@ class IRGenSILFunction : void visitAssignByWrapperInst(AssignByWrapperInst *i) { llvm_unreachable("assign_by_wrapper is not valid in canonical SIL"); } + void visitAssignOrInitInst(AssignOrInitInst *i) { + llvm_unreachable("assign_or_init is not valid in canonical SIL"); + } void visitMarkUninitializedInst(MarkUninitializedInst *i) { llvm_unreachable("mark_uninitialized is not valid in canonical SIL"); } @@ -2887,12 +2890,10 @@ void IRGenSILFunction::visitFunctionRefBaseInst(FunctionRefBaseInst *i) { FunctionPointer fp = FunctionPointer::forDirect(fpKind, value, secondaryValue, sig, useSignature); // Update the foreign no-throw information if needed. - if (const auto *cd = fn->getClangDecl()) { + if (auto *cd = fn->getClangDecl()) { if (auto *cfd = dyn_cast(cd)) { - if (auto *cft = cfd->getType()->getAs()) { - if (cft->isNothrow()) - fp.setForeignNoThrow(); - } + if (IGM.isCxxNoThrow(const_cast(cfd))) + fp.setForeignNoThrow(); } if (IGM.emittedForeignFunctionThunksWithExceptionTraps.count(fnPtr)) fp.setForeignCallCatchesExceptionInThunk(); diff --git a/lib/IRGen/TypeInfo.h b/lib/IRGen/TypeInfo.h index e2a778a6a02bc..0ac8933a4be30 100644 --- a/lib/IRGen/TypeInfo.h +++ b/lib/IRGen/TypeInfo.h @@ -95,6 +95,7 @@ class TypeInfo { friend class TypeConverter; protected: + // clang-format off union { uint64_t OpaqueBits; @@ -143,6 +144,8 @@ class TypeInfo { Size : 32 ); } Bits; + // clang-format on + enum { InvalidSubclassKind = 0x7 }; TypeInfo(llvm::Type *Type, Alignment A, IsTriviallyDestroyable_t pod, diff --git a/lib/IRGen/TypeLayout.cpp b/lib/IRGen/TypeLayout.cpp index 5dbb10abbfd5f..c89ea702061fe 100644 --- a/lib/IRGen/TypeLayout.cpp +++ b/lib/IRGen/TypeLayout.cpp @@ -77,7 +77,12 @@ class LayoutStringBuilder { Resilient = 0x0f, SinglePayloadEnumSimple = 0x10, SinglePayloadEnumFN = 0x11, - SinglePayloadEnumFNResolved = 0x12, + // reserved + // SinglePayloadEnumFNResolved = 0x12, + + MultiPayloadEnumFN = 0x13, + // reserved + // MultiPayloadEnumFNResolved = 0x14, Skip = 0x80, // We may use the MSB as flag that a count follows, @@ -101,13 +106,19 @@ class LayoutStringBuilder { const TypeLayoutEntry *payload; }; + struct MultiPayloadEnumFN { + llvm::Function *tagFn; + const EnumTypeLayoutEntry *entry; + }; + struct RefCounting { RefCountingKind kind; union { size_t size; - llvm::Function* metaTypeRef; + llvm::Function *metaTypeRef; SinglePayloadEnumSimple singlePayloadEnumSimple; SinglePayloadEnumFN singlePayloadEnumFN; + MultiPayloadEnumFN multiPayloadEnumFN; }; RefCounting() = default; @@ -151,6 +162,15 @@ class LayoutStringBuilder { refCountings.push_back(op); } + void addMultiPayloadEnumFN(llvm::Function *tagFn, + const EnumTypeLayoutEntry *entry) { + RefCounting op; + op.kind = RefCountingKind::MultiPayloadEnumFN; + op.multiPayloadEnumFN.tagFn = tagFn; + op.multiPayloadEnumFN.entry = entry; + refCountings.push_back(op); + } + void addSkip(size_t size) { if (refCountings.empty() || refCountings.back().kind != RefCountingKind::Skip) { @@ -280,6 +300,63 @@ class LayoutStringBuilder { break; } + case RefCountingKind::MultiPayloadEnumFN: { + uint64_t op = (static_cast(refCounting.kind) << 56) | skip; + B.addInt64(op); + + skip = 0; + + auto enumData = refCounting.multiPayloadEnumFN; + auto payloads = enumData.entry->cases; + + B.addRelativeOffset(IGM.IntPtrTy, enumData.tagFn); + + B.addSize(Size(payloads.size())); + + auto nestedRefCountBytesPlaceholder = + B.addPlaceholderWithSize(IGM.SizeTy); + B.addSize(*enumData.entry->fixedSize(IGM)); + + SmallVector< + clang::CodeGen::ConstantAggregateBuilderBase::PlaceholderPosition, + 4> + offsetPlaceholders; + for (auto *p : payloads) { + (void)p; + auto placeholder = B.addPlaceholderWithSize(IGM.SizeTy); + offsetPlaceholders.push_back(placeholder); + refCountBytes += IGM.getPointerSize().getValue(); + } + + size_t nestedRefCountBytes = 0; + for (auto p : llvm::zip(payloads, offsetPlaceholders)) { + auto *payload = std::get<0>(p); + + B.fillPlaceholderWithInt(std::get<1>(p), IGM.SizeTy, + nestedRefCountBytes); + + size_t nestedSkip = 0; + LayoutStringBuilder nestedBuilder{}; + payload->refCountString(IGM, nestedBuilder, genericSig); + addRefCountings(IGM, B, genericSig, nestedBuilder.refCountings, + nestedSkip, nestedRefCountBytes, flags); + + // NUL terminator + B.addInt64(0); + nestedRefCountBytes += sizeof(uint64_t); + } + + B.fillPlaceholderWithInt(nestedRefCountBytesPlaceholder, IGM.SizeTy, + nestedRefCountBytes); + + refCountBytes += sizeof(uint64_t) + + (4 * IGM.getPointerSize().getValue()) + + nestedRefCountBytes; + + flags |= LayoutStringFlags::HasRelativePointers; + break; + } + default: { uint64_t op = (static_cast(refCounting.kind) << 56) | skip; B.addInt64(op); @@ -2208,6 +2285,22 @@ bool EnumTypeLayoutEntry::buildSinglePayloadRefCountString( return true; } +bool EnumTypeLayoutEntry::buildMultiPayloadRefCountString( + IRGenModule &IGM, LayoutStringBuilder &B, + GenericSignature genericSig) const { + auto valid = std::all_of(cases.begin(), cases.end(), [&](auto *c) { + LayoutStringBuilder nestedBuilder{}; + return c->refCountString(IGM, nestedBuilder, genericSig); + }); + + if (valid) { + auto *tagFn = createFixedEnumLoadTag(IGM, *this); + B.addMultiPayloadEnumFN(tagFn, this); + } + + return valid; +} + llvm::Constant * EnumTypeLayoutEntry::layoutString(IRGenModule &IGM, GenericSignature genericSig) const { @@ -2245,12 +2338,20 @@ EnumTypeLayoutEntry::layoutString(IRGenModule &IGM, return nullptr; case CopyDestroyStrategy::Normal: { - if (!isFixedSize(IGM) || isMultiPayloadEnum() || - !buildSinglePayloadRefCountString(IGM, B, genericSig)) { - return *(_layoutString = llvm::Optional(nullptr)); + bool valid = false; + if (isFixedSize(IGM)) { + if (isMultiPayloadEnum()) { + valid = buildMultiPayloadRefCountString(IGM, B, genericSig); + } else { + valid = buildSinglePayloadRefCountString(IGM, B, genericSig); + } } - return createConstant(B); + if (valid) { + return createConstant(B); + } else { + return *(_layoutString = llvm::Optional(nullptr)); + } } case CopyDestroyStrategy::ForwardToPayload: @@ -2281,8 +2382,11 @@ bool EnumTypeLayoutEntry::refCountString(IRGenModule &IGM, case CopyDestroyStrategy::ForwardToPayload: return cases[0]->refCountString(IGM, B, genericSig); case CopyDestroyStrategy::Normal: { - if (!isMultiPayloadEnum() && - buildSinglePayloadRefCountString(IGM, B, genericSig)) { + + if (isMultiPayloadEnum() && + buildMultiPayloadRefCountString(IGM, B, genericSig)) { + return true; + } else if (buildSinglePayloadRefCountString(IGM, B, genericSig)) { return true; } diff --git a/lib/IRGen/TypeLayout.h b/lib/IRGen/TypeLayout.h index 9252caa1f47e9..dc2b13eda35e2 100644 --- a/lib/IRGen/TypeLayout.h +++ b/lib/IRGen/TypeLayout.h @@ -651,6 +651,9 @@ class EnumTypeLayoutEntry : public TypeLayoutEntry, LayoutStringBuilder &B, GenericSignature genericSig) const; + bool buildMultiPayloadRefCountString(IRGenModule &IGM, LayoutStringBuilder &B, + GenericSignature genericSig) const; + static bool classof(const TypeLayoutEntry *entry); }; diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index 6a13b9737ffb8..90c862a75a30e 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -56,6 +56,9 @@ printArtificialName(const swift::AbstractStorageDecl *ASD, AccessorKind AK, llvm case AccessorKind::WillSet: OS << "willSet:" << ASD->getName() ; return false; + case AccessorKind::Init: + OS << "init:" << ASD->getName(); + return false; case AccessorKind::Address: case AccessorKind::MutableAddress: diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 7b0708f6c0f91..0f30ebf3b387d 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -275,6 +275,7 @@ SymbolSubKind index::getSubKindForAccessor(AccessorKind AK) { return SymbolSubKind::SwiftAccessorMutableAddressor; case AccessorKind::Read: return SymbolSubKind::SwiftAccessorRead; case AccessorKind::Modify: return SymbolSubKind::SwiftAccessorModify; + case AccessorKind::Init: return SymbolSubKind::SwiftAccessorInit; } llvm_unreachable("Unhandled AccessorKind in switch."); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index c2f9193a8da19..a3ab9d65aa92c 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3377,6 +3377,14 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, break; } + case DAK_Initializes: { + llvm_unreachable("InitializesAttr not yet implemented"); + } + + case DAK_Accesses: { + llvm_unreachable("AccessesAttr not yet implemented"); + } + case DAK_Implements: { ParserResult Attr = parseImplementsAttribute(AtLoc, Loc); if (Attr.isNonNull()) { @@ -4616,6 +4624,7 @@ Parser::parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier, } if (Tok.isContextualKeyword("_const")) { + Tok.setKind(tok::contextual_keyword); ConstLoc = consumeToken(); continue; } @@ -4638,6 +4647,7 @@ Parser::parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier, } } } + Tok.setKind(tok::contextual_keyword); SpecifierLoc = consumeToken(); } @@ -6863,7 +6873,7 @@ static ParameterList *parseOptionalAccessorArgument(SourceLoc SpecifierLoc, // 'set' and 'willSet' have a (value) parameter, 'didSet' takes an (oldValue) // parameter and 'get' and always takes a () parameter. if (Kind != AccessorKind::Set && Kind != AccessorKind::WillSet && - Kind != AccessorKind::DidSet) + Kind != AccessorKind::DidSet && Kind != AccessorKind::Init) return nullptr; SourceLoc StartLoc, NameLoc, EndLoc; @@ -6981,6 +6991,8 @@ static StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind, return "'willSet'"; case AccessorKind::DidSet: return "'didSet'"; + case AccessorKind::Init: + return article ? "an init accessor" : "init accessor"; } llvm_unreachable("bad accessor kind"); } @@ -7005,7 +7017,7 @@ static void diagnoseRedundantAccessors(Parser &P, SourceLoc loc, /*already*/ true); } -static bool isAllowedInLimitedSyntax(AccessorKind kind) { +static bool isAllowedInProtocolRequirement(AccessorKind kind) { switch (kind) { case AccessorKind::Get: case AccessorKind::Set: @@ -7017,6 +7029,7 @@ static bool isAllowedInLimitedSyntax(AccessorKind kind) { case AccessorKind::DidSet: case AccessorKind::Read: case AccessorKind::Modify: + case AccessorKind::Init: return false; } llvm_unreachable("bad accessor kind"); @@ -7085,7 +7098,10 @@ static bool parseAccessorIntroducer(Parser &P, } } - if (!P.Tok.is(tok::identifier) || P.Tok.isEscapedIdentifier()) { + bool isInitAccessor = (P.Context.LangOpts.hasFeature(Feature::InitAccessors) + && P.Tok.is(tok::kw_init)); + if (!(P.Tok.is(tok::identifier) || isInitAccessor) || + P.Tok.isEscapedIdentifier()) { return true; } #define SUPPRESS_ARTIFICIAL_ACCESSORS 1 @@ -7172,6 +7188,71 @@ ParserStatus Parser::parseGetEffectSpecifier(ParsedAccessors &accessors, return Status; } +template +static ParserStatus parseInitAccessorEffect(Parser &P, + DeclAttributes &attributes, + StringRef attrName) { + ParserStatus status; + + if (P.Tok.isContextualKeyword(attrName)) { + auto effectLoc = P.consumeToken(); + if (!P.Tok.is(tok::l_paren)) { + P.diagnose(P.Tok.getLoc(), diag::attr_expected_lparen, + attrName, true); + status.setIsParseError(); + return status; + } + + // Consume '(' + P.consumeToken(); + + bool hasNextProperty = false; + // Consume the identifier list + SmallVector properties; + do { + Identifier propertyName; + SourceLoc propertyNameLoc; + if (P.parseIdentifier(propertyName, propertyNameLoc, + diag::init_accessor_expected_name, + /*diagnoseDollarPrefix=*/true)) { + status.setIsParseError(); + return status; + } + + properties.push_back(propertyName); + + // Parse the comma, if the list continues. + hasNextProperty = P.consumeIf(tok::comma); + } while (hasNextProperty); + + if (!P.Tok.is(tok::r_paren)) { + P.diagnose(P.Tok.getLoc(), diag::attr_expected_rparen, + attrName, true); + status.setIsParseError(); + return status; + } + + // Consume ')' + SourceLoc rParenLoc = P.consumeToken(); + + auto *attr = EffectAttr::create(P.Context, SourceLoc(), + SourceRange(effectLoc, rParenLoc), + properties); + attributes.add(attr); + } + + return status; +} + +ParserStatus Parser::parseInitAccessorEffects(ParsedAccessors &accessors, + AccessorKind currentKind, + DeclAttributes &attrs) { + ParserStatus status; + status |= parseInitAccessorEffect(*this, attrs, "initializes"); + status |= parseInitAccessorEffect(*this, attrs, "accesses"); + return status; +} + bool Parser::parseAccessorAfterIntroducer( SourceLoc Loc, AccessorKind Kind, ParsedAccessors &accessors, bool &hasEffectfulGet, ParameterList *Indices, bool &parsingLimitedSyntax, @@ -7186,6 +7267,7 @@ bool Parser::parseAccessorAfterIntroducer( SourceLoc throwsLoc; Status |= parseGetEffectSpecifier(accessors, asyncLoc, throwsLoc, hasEffectfulGet, Kind, Loc); + Status |= parseInitAccessorEffects(accessors, Kind, Attributes); // Set up a function declaration. auto accessor = @@ -7350,7 +7432,7 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags, ParameterList *Indices, // For now, immediately reject illegal accessors in protocols just to // avoid having to deal with them everywhere. - if (parsingLimitedSyntax && !isAllowedInLimitedSyntax(Kind)) { + if (parsingLimitedSyntax && !isAllowedInProtocolRequirement(Kind)) { diagnose(Loc, diag::expected_getset_in_protocol); continue; } @@ -7726,6 +7808,13 @@ void Parser::ParsedAccessors::classify(Parser &P, AbstractStorageDecl *storage, } else if (Modify) { diagnoseConflictingAccessors(P, Modify, MutableAddress); } + + if (Init) { + if (!storage->getDeclContext()->getSelfNominalTypeDecl() || + isa(storage)) { + P.diagnose(Init->getLoc(), diag::init_accessor_is_not_on_property); + } + } } diff --git a/lib/PrintAsClang/PrintClangValueType.cpp b/lib/PrintAsClang/PrintClangValueType.cpp index 2f01f75976f59..2835ab7b89c88 100644 --- a/lib/PrintAsClang/PrintClangValueType.cpp +++ b/lib/PrintAsClang/PrintClangValueType.cpp @@ -288,6 +288,7 @@ void ClangValueTypePrinter::printValueTypeDecl( os << " vwTable->destroy(_getOpaquePointer(), metadata._0);\n"; os << " }\n"; + // copy constructor. os << " "; printer.printInlineForThunk(); printer.printBaseName(typeDecl); @@ -306,6 +307,28 @@ void ClangValueTypePrinter::printValueTypeDecl( "*>(other._getOpaquePointer()), metadata._0);\n"; os << " }\n"; + // copy assignment. + os << " "; + printer.printInlineForThunk(); + printer.printBaseName(typeDecl); + os << " &operator =(const "; + printer.printBaseName(typeDecl); + os << " &other) noexcept {\n"; + ClangValueTypePrinter::printValueWitnessTableAccessAsVariable( + os, typeMetadataFuncName, typeMetadataFuncGenericParams); + os << " vwTable->assignWithCopy(_getOpaquePointer(), const_cast(other._getOpaquePointer()), metadata._0);\n"; + os << " return *this;\n"; + os << " }\n"; + + // FIXME: implement the move assignment. + os << " "; + printer.printInlineForThunk(); + printer.printBaseName(typeDecl); + os << " &operator =("; + printer.printBaseName(typeDecl); + os << " &&other) = delete;\n"; + // FIXME: implement the move constructor. os << " [[noreturn]] "; // NOTE: Do not apply attribute((used)) diff --git a/lib/Refactoring/Refactoring.cpp b/lib/Refactoring/Refactoring.cpp index 515b96a0e50e7..5cda1ef0ccf58 100644 --- a/lib/Refactoring/Refactoring.cpp +++ b/lib/Refactoring/Refactoring.cpp @@ -8754,40 +8754,6 @@ getMacroExpansionBuffers(SourceManager &sourceMgr, ResolvedCursorInfoPtr Info) { return {}; } -/// Given the expanded code for a particular macro, perform whitespace -/// adjustments to make the refactoring more suitable for inline insertion. -static StringRef adjustMacroExpansionWhitespace( - GeneratedSourceInfo::Kind kind, StringRef expandedCode, - llvm::SmallString<64> &scratch) { - scratch.clear(); - - switch (kind) { - case GeneratedSourceInfo::MemberAttributeMacroExpansion: - // Attributes are added to the beginning, add a space to separate from - // any existing. - scratch += expandedCode; - scratch += " "; - return scratch; - - case GeneratedSourceInfo::MemberMacroExpansion: - case GeneratedSourceInfo::PeerMacroExpansion: - case GeneratedSourceInfo::ConformanceMacroExpansion: - // All added to the end. Note that conformances are always expanded as - // extensions, hence treating them the same as peer. - scratch += "\n\n"; - scratch += expandedCode; - scratch += "\n"; - return scratch; - - case GeneratedSourceInfo::ExpressionMacroExpansion: - case GeneratedSourceInfo::FreestandingDeclMacroExpansion: - case GeneratedSourceInfo::AccessorMacroExpansion: - case GeneratedSourceInfo::ReplacedFunctionBody: - case GeneratedSourceInfo::PrettyPrinted: - return expandedCode; - } -} - static bool expandMacro(SourceManager &SM, ResolvedCursorInfoPtr cursorInfo, SourceEditConsumer &editConsumer, bool adjustExpansion) { auto bufferIDs = getMacroExpansionBuffers(SM, cursorInfo); @@ -8799,68 +8765,19 @@ static bool expandMacro(SourceManager &SM, ResolvedCursorInfoPtr cursorInfo, return true; // Send all of the rewritten buffer snippets. - CustomAttr *attachedMacroAttr = nullptr; - SmallString<64> scratchBuffer; for (auto bufferID: bufferIDs) { - auto generatedInfo = SM.getGeneratedSourceInfo(bufferID); - if (!generatedInfo || generatedInfo->originalSourceRange.isInvalid()) - continue; - - auto rewrittenBuffer = SM.extractText(generatedInfo->generatedSourceRange); - - // If there's no change, drop the edit entirely. - if (generatedInfo->originalSourceRange.getStart() == - generatedInfo->originalSourceRange.getEnd() && - rewrittenBuffer.empty()) - continue; - - if (adjustExpansion) { - rewrittenBuffer = adjustMacroExpansionWhitespace(generatedInfo->kind, rewrittenBuffer, scratchBuffer); - } - - // `containingFile` is the file of the actual expansion site, where as - // `originalFile` is the possibly enclosing buffer. Concretely: - // ``` - // // m.swift - // @AddMemberAttributes - // struct Foo { - // // --- expanded from @AddMemberAttributes eg. @_someBufferName --- - // @AddedAttribute - // // --- - // let someMember: Int - // } - // ``` - // - // When expanding `AddedAttribute`, the expansion actually applies to the - // original source (`m.swift`) rather than the buffer of the expansion - // site (`@_someBufferName`). Thus, we need to include the path to the - // original source as well. Note that this path could itself be another - // expansion. - auto originalSourceRange = generatedInfo->originalSourceRange; - SourceFile *originalFile = - containingSF->getParentModule()->getSourceFileContainingLocation(originalSourceRange.getStart()); - StringRef originalPath; - if (originalFile->getBufferID().hasValue() && - containingSF->getBufferID() != originalFile->getBufferID()) { - originalPath = SM.getIdentifierForBuffer(*originalFile->getBufferID()); - } - - editConsumer.accept(SM, {originalPath, - originalSourceRange, - SM.getIdentifierForBuffer(bufferID), - rewrittenBuffer, - {}}); - - if (generatedInfo->attachedMacroCustomAttr && !attachedMacroAttr) - attachedMacroAttr = generatedInfo->attachedMacroCustomAttr; + editConsumer.acceptMacroExpansionBuffer(SM, bufferID, containingSF, + adjustExpansion, /*includeBufferName=*/true); } // For an attached macro, remove the custom attribute; it's been fully // subsumed by its expansions. - if (attachedMacroAttr) { + if (auto attrRef = + cast(cursorInfo)->getCustomAttrRef()) { + const CustomAttr *attachedMacroAttr = attrRef->first; SourceRange range = attachedMacroAttr->getRangeWithAt(); auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, range); - editConsumer.accept(SM, charRange, StringRef()); + editConsumer.remove(SM, charRange); } return false; diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index 4e6bd470bfbfb..8689457c3085d 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -625,6 +625,16 @@ OperandOwnershipClassifier::visitAssignByWrapperInst(AssignByWrapperInst *i) { return OperandOwnership::InstantaneousUse; // initializer/setter closure } +OperandOwnership +OperandOwnershipClassifier::visitAssignOrInitInst(AssignOrInitInst *i) { + if (getValue() == i->getSrc()) { + return OperandOwnership::DestroyingConsume; + } + + // initializer/setter closure + return OperandOwnership::InstantaneousUse; +} + OperandOwnership OperandOwnershipClassifier::visitStoreInst(StoreInst *i) { if (getValue() != i->getSrc()) { return OperandOwnership::TrivialUse; diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 8e3a9fef8f3c5..cef7d684530d6 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -736,6 +736,16 @@ AbstractFunctionDecl *SILDeclRef::getAbstractFunctionDecl() const { return dyn_cast(getDecl()); } +bool SILDeclRef::isInitAccessor() const { + if (kind != Kind::Func || !hasDecl()) + return false; + + if (auto accessor = dyn_cast(getDecl())) + return accessor->getAccessorKind() == AccessorKind::Init; + + return false; +} + /// True if the function should be treated as transparent. bool SILDeclRef::isTransparent() const { if (isEnumElement()) diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 31dc6fd0daed9..4afc45feb516f 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2351,6 +2351,75 @@ static CanSILFunctionType getSILFunctionType( TC.Context, witnessMethodConformance); } +static CanSILFunctionType getSILFunctionTypeForInitAccessor( + TypeConverter &TC, TypeExpansionContext context, + AbstractionPattern origType, CanAnyFunctionType substAccessorType, + SILExtInfoBuilder extInfoBuilder, const Conventions &conventions, + SILDeclRef accessorRef) { + auto *accessor = cast(accessorRef.getDecl()); + + CanGenericSignature genericSig = substAccessorType.getOptGenericSignature(); + + Optional contextRAII; + if (genericSig) + contextRAII.emplace(TC, genericSig); + + SmallVector inputs; + + // First compute `initialValue` input. + { + ForeignInfo foreignInfo; + DestructureInputs destructurer(context, TC, conventions, foreignInfo, + inputs); + destructurer.destructure( + origType, substAccessorType.getParams(), + extInfoBuilder.withRepresentation(SILFunctionTypeRepresentation::Thin)); + } + + // Drop `self` parameter. + inputs.pop_back(); + + // `accesses(...)` appear as `inout` parameters because they could be + // read from and modified. + if (auto *attr = accessor->getAttrs().getAttribute()) { + for (auto *property : attr->getPropertyDecls(accessor)) { + inputs.push_back( + SILParameterInfo(property->getInterfaceType()->getCanonicalType(), + ParameterConvention::Indirect_Inout)); + } + } + + SmallVector results; + + // `initializes(...)` appear as `@out` result because they are initialized + // by the accessor. + if (auto *attr = accessor->getAttrs().getAttribute()) { + for (auto *property : attr->getPropertyDecls(accessor)) { + results.push_back( + SILResultInfo(property->getInterfaceType()->getCanonicalType(), + ResultConvention::Indirect)); + } + } + + auto calleeConvention = ParameterConvention::Direct_Unowned; + if (extInfoBuilder.hasContext()) + calleeConvention = conventions.getCallee(); + + // Map '@Sendable' to the appropriate `@Sendable` modifier. + auto silExtInfo = + SILExtInfoBuilder() + .withRepresentation(SILFunctionTypeRepresentation::Thin) + .withConcurrent(substAccessorType->getExtInfo().isSendable()) + .build(); + + return SILFunctionType::get( + /*genericSig=*/genericSig, silExtInfo, SILCoroutineKind::None, + calleeConvention, inputs, + /*yields=*/{}, results, /*errorResult=*/None, + /*patternSubs=*/SubstitutionMap(), + /*invocationSubs=*/SubstitutionMap(), TC.Context); +} + //===----------------------------------------------------------------------===// // Deallocator SILFunctionTypes //===----------------------------------------------------------------------===// @@ -2617,8 +2686,14 @@ static CanSILFunctionType getNativeSILFunctionType( case SILDeclRef::Kind::Func: { // If we have a setter, use the special setter convention. This ensures // that we take normal parameters at +1. - if (constant && constant->isSetter()) { - return getSILFunctionTypeForConventions(DefaultSetterConventions()); + if (constant) { + if (constant->isSetter()) { + return getSILFunctionTypeForConventions(DefaultSetterConventions()); + } else if (constant->isInitAccessor()) { + return getSILFunctionTypeForInitAccessor( + TC, context, origType, substInterfaceType, extInfoBuilder, + DefaultSetterConventions(), *constant); + } } return getSILFunctionTypeForConventions( DefaultConventions(NormalParameterConvention::Guaranteed)); diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 96fa4921cdc2c..e42d9e4ae1658 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1256,6 +1256,67 @@ AssignByWrapperInst::AssignByWrapperInst(SILDebugLocation Loc, sharedUInt8().AssignByWrapperInst.mode = uint8_t(mode); } +AssignOrInitInst::AssignOrInitInst(SILDebugLocation Loc, SILValue Src, + SILValue Initializer, SILValue Setter, + AssignOrInitInst::Mode Mode) + : InstructionBase(Loc), + Operands(this, Src, Initializer, Setter) { + assert(Initializer->getType().is()); + sharedUInt8().AssignOrInitInst.mode = uint8_t(Mode); + Assignments.resize(getNumInitializedProperties()); +} + +void AssignOrInitInst::markAsInitialized(VarDecl *property) { + auto toInitProperties = getInitializedProperties(); + for (unsigned index : indices(toInitProperties)) { + if (toInitProperties[index] == property) { + markAsInitialized(index); + break; + } + } +} + +void AssignOrInitInst::markAsInitialized(unsigned propertyIdx) { + assert(propertyIdx < getNumInitializedProperties()); + Assignments.set(propertyIdx); +} + +bool AssignOrInitInst::isPropertyAlreadyInitialized(unsigned propertyIdx) { + assert(propertyIdx < Assignments.size()); + return Assignments.test(propertyIdx); +} + +AccessorDecl *AssignOrInitInst::getReferencedInitAccessor() const { + auto *initRef = cast(getInitializer()); + auto *accessorRef = initRef->getReferencedFunctionOrNull(); + assert(accessorRef); + return dyn_cast_or_null(accessorRef->getDeclContext()); +} + +unsigned AssignOrInitInst::getNumInitializedProperties() const { + if (auto *accessor = getReferencedInitAccessor()) { + auto *initAttr = accessor->getAttrs().getAttribute(); + return initAttr ? initAttr->getNumProperties() : 0; + } + return 0; +} + +ArrayRef AssignOrInitInst::getInitializedProperties() const { + if (auto *accessor = getReferencedInitAccessor()) { + if (auto *initAttr = accessor->getAttrs().getAttribute()) + return initAttr->getPropertyDecls(accessor); + } + return {}; +} + +ArrayRef AssignOrInitInst::getAccessedProperties() const { + if (auto *accessor = getReferencedInitAccessor()) { + if (auto *accessAttr = accessor->getAttrs().getAttribute()) + return accessAttr->getPropertyDecls(accessor); + } + return {}; +} + MarkFunctionEscapeInst * MarkFunctionEscapeInst::create(SILDebugLocation Loc, ArrayRef Elements, SILFunction &F) { diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 6c31f61cf9495..1ccf6a64051f1 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -341,6 +341,9 @@ void SILDeclRef::print(raw_ostream &OS) const { case AccessorKind::Modify: OS << "!modify"; break; + case AccessorKind::Init: + OS << "!init"; + break; } break; } @@ -1768,11 +1771,36 @@ class SILPrinter : public SILInstructionVisitor { *this << "[assign_wrapped_value] "; break; } + *this << getIDAndType(AI->getDest()) << ", init " << getIDAndType(AI->getInitializer()) << ", set " << getIDAndType(AI->getSetter()); } + void visitAssignOrInitInst(AssignOrInitInst *AI) { + switch (AI->getMode()) { + case AssignOrInitInst::Unknown: + break; + case AssignOrInitInst::Init: + *this << "[init] "; + break; + case AssignOrInitInst::Set: + *this << "[set] "; + break; + } + + // Print all of the properties that have been previously initialized. + for (unsigned i = 0, n = AI->getNumInitializedProperties(); i != n; ++i) { + if (AI->isPropertyAlreadyInitialized(i)) { + *this << "[assign=" << i << "] "; + } + } + + *this << getIDAndType(AI->getSrc()); + *this << ", init " << getIDAndType(AI->getInitializer()) + << ", set " << getIDAndType(AI->getSetter()); + } + void visitMarkUninitializedInst(MarkUninitializedInst *MU) { switch (MU->getMarkUninitializedKind()) { case MarkUninitializedInst::Var: *this << "[var] "; break; @@ -1788,6 +1816,9 @@ class SILPrinter : public SILInstructionVisitor { case MarkUninitializedInst::DelegatingSelfAllocated: *this << "[delegatingselfallocated] "; break; + case MarkUninitializedInst::Out: + *this << "[out] "; + break; } *this << getIDAndType(MU->getOperand()); printForwardingOwnershipKind(MU, MU->getOperand()); diff --git a/lib/SIL/IR/SILType.cpp b/lib/SIL/IR/SILType.cpp index c0cfb4eef5a7b..e3feb8378fe7c 100644 --- a/lib/SIL/IR/SILType.cpp +++ b/lib/SIL/IR/SILType.cpp @@ -1048,6 +1048,15 @@ bool SILType::isPureMoveOnly() const { return false; } +bool SILType::isValueTypeWithDeinit() const { + // Do not look inside an aggregate type that has a user-deinit, for which + // memberwise-destruction is not equivalent to aggregate destruction. + if (auto *nominal = getNominalOrBoundGenericNominal()) { + return nominal->getValueTypeDestructor() != nullptr; + } + return false; +} + SILType SILType::getInstanceTypeOfMetatype(SILFunction *function) const { auto metaType = castTo(); CanType instanceTy = metaType.getInstanceType(); diff --git a/lib/SIL/IR/SILTypeSubstitution.cpp b/lib/SIL/IR/SILTypeSubstitution.cpp index 68f4e356569d8..5e82364476286 100644 --- a/lib/SIL/IR/SILTypeSubstitution.cpp +++ b/lib/SIL/IR/SILTypeSubstitution.cpp @@ -77,7 +77,9 @@ class SILTypeSubstituter : ProtocolConformanceRef(conformedProtocol), conformingReplacementType->getCanonicalType(), typeExpansionContext); - }, SubstFlags::SubstituteOpaqueArchetypes); + }, + SubstFlags::SubstituteOpaqueArchetypes | + SubstFlags::PreservePackExpansionLevel); } // Substitute a function type. diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 81bc973c2d2b6..c2d9693356487 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -2332,6 +2332,71 @@ static bool parseAssignByWrapperMode(AssignByWrapperInst::Mode &Result, return false; } +static bool parseAssignOrInitMode(AssignOrInitInst::Mode &Result, + SILParser &P) { + StringRef Str; + if (!parseSILOptional(Str, P)) { + Result = AssignOrInitInst::Unknown; + return false; + } + + auto Tmp = llvm::StringSwitch(Str) + .Case("init", AssignOrInitInst::Init) + .Case("set", AssignOrInitInst::Set) + .Default(AssignOrInitInst::Unknown); + + // Return true (following the conventions in this file) if we fail. + if (Tmp == AssignOrInitInst::Unknown) + return true; + + Result = Tmp; + return false; +} + +static bool +parseAssignOrInitAssignments(llvm::SmallVectorImpl &assignments, + SILParser &SP) { + // Could be more than one [assign=] attributes. + for (;;) { + SourceLoc loc; + + // Consume '[' + if (!SP.P.consumeIf(tok::l_square)) + return false; + + // Consume the identifier which should be "assign" + { + Identifier Id; + if (SP.parseSILIdentifier(Id, loc, diag::expected_in_attribute_list)) + return true; + + if (!Id.is("assign")) { + SP.P.diagnose(loc, diag::sil_invalid_attribute_for_expected, Id.str(), + "assign"); + return true; + } + } + + uint64_t index; + + // Consume '=' + if (!SP.P.consumeIf(tok::equal)) { + SP.P.diagnose(loc, diag::expected_equal_in_sil_instr); + return true; + } + + // Consume the property index. + if (SP.parseInteger(index, diag::expected_in_attribute_list)) + return true; + + // Consume ']' + if (SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list)) + return true; + + assignments.push_back(index); + } +} + // Parse a list of integer indices, prefaced with the given string label. // Returns true on error. static bool parseIndexList(Parser &P, StringRef label, @@ -4468,6 +4533,8 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, Kind = MarkUninitializedInst::DelegatingSelf; else if (KindId.str() == "delegatingselfallocated") Kind = MarkUninitializedInst::DelegatingSelfAllocated; + else if (KindId.str() == "out") + Kind = MarkUninitializedInst::Out; else { P.diagnose(KindLoc, diag::expected_tok_in_sil_instr, "var, rootself, crossmodulerootself, derivedself, " @@ -4673,6 +4740,31 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ResultVal = B.createCopyableToMoveOnlyWrapperAddr(InstLoc, addrVal); break; } + + case SILInstructionKind::AssignOrInitInst: { + SILValue Src, InitFn, SetFn; + AssignOrInitInst::Mode Mode; + llvm::SmallVector assignments; + + if (parseAssignOrInitMode(Mode, *this) || + parseAssignOrInitAssignments(assignments, *this) || + parseTypedValueRef(Src, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("init") || parseTypedValueRef(InitFn, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("set") || parseTypedValueRef(SetFn, B) || + parseSILDebugLocation(InstLoc, B)) + return true; + + auto *AI = B.createAssignOrInit(InstLoc, Src, InitFn, SetFn, Mode); + + for (unsigned index : assignments) + AI->markAsInitialized(index); + + ResultVal = AI; + break; + } + case SILInstructionKind::BeginAccessInst: case SILInstructionKind::BeginUnpairedAccessInst: case SILInstructionKind::EndAccessInst: diff --git a/lib/SIL/Utils/AddressWalker.cpp b/lib/SIL/Utils/AddressWalker.cpp index 54b2c9d935ef4..1984de1c25ba8 100644 --- a/lib/SIL/Utils/AddressWalker.cpp +++ b/lib/SIL/Utils/AddressWalker.cpp @@ -125,7 +125,8 @@ AddressUseKind TransitiveAddressWalker::walk(SILValue projectedAddress) && { isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || - isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 50131fdd14dd0..e1c1ba61b4b35 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -403,6 +403,25 @@ bool swift::onlyUsedByAssignByWrapper(PartialApplyInst *PAI) { return usedByAssignByWrapper; } +bool swift::onlyUsedByAssignOrInit(PartialApplyInst *PAI) { + bool usedByAssignOrInit = false; + for (Operand *Op : PAI->getUses()) { + SILInstruction *user = Op->getUser(); + if (isa(user)) { + usedByAssignOrInit = true; + continue; + } + + if (isa(user)) { + continue; + } + + return false; + } + + return usedByAssignOrInit; +} + static RuntimeEffect metadataEffect(SILType ty) { ClassDecl *cl = ty.getClassOrBoundGenericClass(); if (cl && !cl->hasKnownSwiftImplementation()) @@ -521,6 +540,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType) case SILInstructionKind::EndBorrowInst: case SILInstructionKind::AssignInst: case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::AssignOrInitInst: case SILInstructionKind::MarkFunctionEscapeInst: case SILInstructionKind::EndLifetimeInst: case SILInstructionKind::EndApplyInst: diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 78706279f1e62..e9ec5e80e38da 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -2624,6 +2624,7 @@ void swift::visitAccessedAddress(SILInstruction *I, case SILInstructionKind::AssignInst: case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::AssignOrInitInst: visitor(&I->getAllOperands()[AssignInst::Dest]); return; diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 9ae4951d62e24..919535d744857 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -2722,6 +2722,46 @@ class SILVerifier : public SILVerifierBase { checkAssignByWrapperArgs(Src->getType(), setterConv); } + void checkAssigOrInitInstAccessorArgs(SILType argTy, + SILFunctionConventions &conv) { + unsigned argIdx = conv.getSILArgIndexOfFirstParam(); + checkAssignByWrapperArgsRecursively(argTy, conv, argIdx); + } + + void checkAssignOrInitInst(AssignOrInitInst *AI) { + SILValue Src = AI->getSrc(); + require(AI->getModule().getStage() == SILStage::Raw, + "assign_or_init can only exist in raw SIL"); + + SILValue initFn = AI->getInitializer(); + SILValue setterFn = AI->getSetter(); + + CanSILFunctionType initTy = initFn->getType().castTo(); + // Check init - it's an unapplied reference that takes property addresses + // and `initialValue`. + { + // We need to map un-applied function reference into context before + // check `initialValue` argument. + auto subs = cast(setterFn)->getSubstitutionMap(); + initTy = initTy->substGenericArgs(F.getModule(), subs, + F.getTypeExpansionContext()); + + SILFunctionConventions initConv(initTy, AI->getModule()); + require(initConv.getNumIndirectSILResults() == + AI->getInitializedProperties().size(), + "init function has invalid number of indirect results"); + checkAssigOrInitInstAccessorArgs(Src->getType(), initConv); + } + + // Check setter - it's a partially applied reference which takes + // `initialValue`. + CanSILFunctionType setterTy = setterFn->getType().castTo(); + SILFunctionConventions setterConv(setterTy, AI->getModule()); + require(setterConv.getNumIndirectSILResults() == 0, + "set function has indirect results"); + checkAssignByWrapperArgs(Src->getType(), setterConv); + }; + #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ void checkLoad##Name##Inst(Load##Name##Inst *LWI) { \ require(LWI->getType().isObject(), "Result of load must be an object"); \ diff --git a/lib/SILGen/RValue.cpp b/lib/SILGen/RValue.cpp index 393c1a97320ee..bd9016ffa42c3 100644 --- a/lib/SILGen/RValue.cpp +++ b/lib/SILGen/RValue.cpp @@ -547,7 +547,7 @@ SILValue RValue::forwardAsSingleStorageValue(SILGenFunction &SGF, return SGF.emitConversionFromSemanticValue(l, result, storageType); } -void RValue::forwardInto(SILGenFunction &SGF, SILLocation loc, +void RValue::forwardInto(SILGenFunction &SGF, SILLocation loc, Initialization *I) && { assert(isComplete() && "rvalue is not complete"); assert(isPlusOneOrTrivial(SGF) && "Can not forward borrowed RValues"); diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 801fd654d64e9..996d654321465 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -875,6 +875,16 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { break; } + if (constant.isInitAccessor()) { + auto *accessor = cast(constant.getDecl()); + preEmitFunction(constant, f, accessor); + PrettyStackTraceSILFunction X("silgen init accessor", f); + f->createProfiler(constant); + SILGenFunction(*this, *f, accessor).emitInitAccessor(accessor); + postEmitFunction(constant, f); + break; + } + auto *fd = cast(constant.getDecl()); preEmitFunction(constant, f, fd); diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 55292c116cc6f..8212dbd7b7cc3 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -6455,7 +6455,12 @@ ArgumentSource AccessorBaseArgPreparer::prepareAccessorAddressBaseArg() { // If the base is currently an address, we may have to copy it. if (shouldLoadBaseAddress()) { if (selfParam.isConsumed() || - base.getType().isAddressOnly(SGF.F)) { + (base.getType().isAddressOnly(SGF.F) + // If a move-only base is borrowed, then we have to try our best to + // borrow it in-place without copying. + // TODO: Can we avoid copying a non-move-only value too in this + // circumstance? + && !base.getType().isMoveOnly())) { // The load can only be a take if the base is a +1 rvalue. auto shouldTake = IsTake_t(base.hasCleanup()); @@ -6464,6 +6469,11 @@ ArgumentSource AccessorBaseArgPreparer::prepareAccessorAddressBaseArg() { SGFContext(), shouldTake); return ArgumentSource(loc, RValue(SGF, loc, baseFormalType, base)); } + + // If the type is address-only, we can borrow the memory location as is. + if (base.getType().isAddressOnly(SGF.F)) { + return ArgumentSource(loc, RValue(SGF, loc, baseFormalType, base)); + } // If we do not have a consumed base and need to perform a load, perform a // formal access load borrow. diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 14ca717d1388a..93a158f8eeb60 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -30,6 +30,7 @@ #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILUndef.h" #include "swift/SIL/TypeLowering.h" +#include using namespace swift; using namespace Lowering; @@ -271,6 +272,47 @@ static RValue maybeEmitPropertyWrapperInitFromValue( subs, std::move(arg)); } +static void emitApplyOfInitAccessor(SILGenFunction &SGF, SILLocation loc, + AccessorDecl *accessor, SILValue selfValue, + SILType selfTy, RValue &&initialValue) { + SmallVector arguments; + + auto emitFieldReference = [&](VarDecl *field) { + auto fieldTy = + selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); + return SGF.B.createStructElementAddr(loc, selfValue, field, + fieldTy.getAddressType()); + }; + + // First, let's emit all of the indirect results. + if (auto *initAttr = accessor->getAttrs().getAttribute()) { + for (auto *property : initAttr->getPropertyDecls(accessor)) { + arguments.push_back(emitFieldReference(property)); + } + } + + // `initialValue` + std::move(initialValue).forwardAll(SGF, arguments); + + // And finally, all of the properties in `accesses(...)` list which are + // `inout` arguments. + if (auto *accessAttr = accessor->getAttrs().getAttribute()) { + for (auto *property : accessAttr->getPropertyDecls(accessor)) { + arguments.push_back(emitFieldReference(property)); + } + } + + SubstitutionMap subs; + if (auto *env = + accessor->getDeclContext()->getGenericEnvironmentOfContext()) { + subs = env->getForwardingSubstitutionMap(); + } + + SILValue accessorRef = + SGF.emitGlobalFunctionRef(loc, SGF.getAccessorDeclRef(accessor)); + (void)SGF.B.createApply(loc, accessorRef, subs, arguments, ApplyOptions()); +} + static SubstitutionMap getSubstitutionsForPropertyInitializer( DeclContext *dc, NominalTypeDecl *nominal) { @@ -312,6 +354,12 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, auto selfIfaceTy = selfDecl->getInterfaceType(); SILType selfTy = SGF.getSILTypeInContext(selfResultInfo, loweredFunctionTy); + auto *decl = selfTy.getStructOrBoundGenericStruct(); + assert(decl && "not a struct?!"); + + std::multimap initializedViaAccessor; + decl->collectPropertiesInitializableByInitAccessors(initializedViaAccessor); + // Emit the indirect return argument, if any. SILValue resultSlot; if (selfTy.isAddress()) { @@ -324,6 +372,10 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, VD->setSpecifier(ParamSpecifier::InOut); VD->setInterfaceType(selfIfaceTy); resultSlot = SGF.F.begin()->createFunctionArgument(selfTy, VD); + } else if (!initializedViaAccessor.empty()) { + // Allocate "self" on stack which we are going to use to + // reference/init fields and then load to return. + resultSlot = SGF.emitTemporaryAllocation(Loc, selfTy); } LoweredParamsInContextGenerator loweredParams(SGF); @@ -343,15 +395,34 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, (void) loweredParams.claimNext(); loweredParams.finish(); - auto *decl = selfTy.getStructOrBoundGenericStruct(); - assert(decl && "not a struct?!"); - auto subs = getSubstitutionsForPropertyInitializer(decl, decl); // If we have an indirect return slot, initialize it in-place. if (resultSlot) { + // Tracks all the init accessors we have emitted + // because they can initialize more than one property. + llvm::SmallPtrSet emittedInitAccessors; + auto elti = elements.begin(), eltEnd = elements.end(); for (VarDecl *field : decl->getStoredProperties()) { + + // Handle situations where this stored propery is initialized + // via a call to an init accessor on some other property. + if (initializedViaAccessor.count(field)) { + auto *initProperty = initializedViaAccessor.find(field)->second; + auto *initAccessor = initProperty->getAccessor(AccessorKind::Init); + + if (emittedInitAccessors.count(initAccessor)) + continue; + + emitApplyOfInitAccessor(SGF, Loc, initAccessor, resultSlot, selfTy, + std::move(*elti)); + + emittedInitAccessors.insert(initAccessor); + ++elti; + continue; + } + auto fieldTy = selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); SILValue slot = @@ -423,6 +494,16 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, } } + // Load as "take" from our stack allocation and return. + if (!selfTy.isAddress() && !initializedViaAccessor.empty()) { + auto resultValue = SGF.B.emitLoadValueOperation( + Loc, resultSlot, LoadOwnershipQualifier::Take); + + SGF.B.createReturn(ImplicitReturnLocation(Loc), resultValue, + std::move(functionLevelScope)); + return; + } + SGF.B.createReturn(ImplicitReturnLocation(Loc), SGF.emitEmptyTuple(Loc), std::move(functionLevelScope)); return; @@ -1481,3 +1562,81 @@ void SILGenFunction::emitIVarInitializer(SILDeclRef ivarInitializer) { emitEpilog(loc); } + +void SILGenFunction::emitInitAccessor(AccessorDecl *accessor) { + RegularLocation loc(accessor); + loc.markAutoGenerated(); + + auto accessorTy = F.getLoweredFunctionType(); + + auto createArgument = [&](VarDecl *property, SILType type, + bool markUninitialized = false) { + auto *arg = ParamDecl::createImplicit( + getASTContext(), property->getBaseIdentifier(), + property->getBaseIdentifier(), type.getASTType()->mapTypeOutOfContext(), + accessor, ParamSpecifier::InOut); + + RegularLocation loc(property); + loc.markAutoGenerated(); + + SILValue argValue = F.begin()->createFunctionArgument(type, arg); + VarLocs[arg] = + markUninitialized + ? VarLoc::get(B.createMarkUninitializedOut(loc, argValue)) + : VarLoc::get(argValue); + + InitAccessorArgumentMappings[property] = arg; + }; + + // First, emit results, this is our "initializes(...)" properties and + // require DI to check that each property is fully initialized. + if (auto *initAttr = accessor->getAttrs().getAttribute()) { + auto initializedProperties = initAttr->getPropertyDecls(accessor); + for (unsigned i = 0, n = initializedProperties.size(); i != n; ++i) { + auto *property = initializedProperties[i]; + auto propertyTy = + getSILTypeInContext(accessorTy->getResults()[i], accessorTy); + createArgument(property, propertyTy, /*markUninitialized=*/true); + } + } + + // Collect all of the parameters that represent properties listed by + // "accesses" attribute. They have to be emitted in order of arguments which + // means after the "newValue" which is emitted by \c emitBasicProlog. + Optional> accessedProperties; + { + if (auto *accessAttr = accessor->getAttrs().getAttribute()) + accessedProperties = accessAttr->getPropertyDecls(accessor); + } + + // Emit `newValue` argument. + emitBasicProlog(accessor->getParameters(), /*selfParam=*/nullptr, + TupleType::getEmpty(F.getASTContext()), accessor, + /*throws=*/false, /*throwsLoc=*/SourceLoc(), + /*ignored parameters*/ + accessedProperties ? accessedProperties->size() : 0); + + // Emit arguments for all `accesses(...)` properties. + if (accessedProperties) { + auto propertyIter = accessedProperties->begin(); + auto propertyArgs = accessorTy->getParameters().slice( + accessorTy->getNumParameters() - accessedProperties->size()); + + for (const auto &argument : propertyArgs) { + createArgument(*propertyIter, getSILTypeInContext(argument, accessorTy)); + ++propertyIter; + } + } + + prepareEpilog(accessor->getResultInterfaceType(), accessor->hasThrows(), + CleanupLocation(accessor)); + + emitProfilerIncrement(accessor->getTypecheckedBody()); + + // Emit the actual function body as usual + emitStmt(accessor->getTypecheckedBody()); + + emitEpilog(accessor); + + mergeCleanupBlocks(); +} diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 7ba6893f157e9..8d3efe9281ac1 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -1625,11 +1625,13 @@ void SILGenFunction::visitVarDecl(VarDecl *D) { } void SILGenFunction::visitMacroExpansionDecl(MacroExpansionDecl *D) { - D->forEachExpandedExprOrStmt([&](ASTNode node) { + D->forEachExpandedNode([&](ASTNode node) { if (auto *expr = node.dyn_cast()) emitIgnoredExpr(expr); else if (auto *stmt = node.dyn_cast()) emitStmt(stmt); + else + visit(node.get()); }); } @@ -1806,8 +1808,10 @@ void SILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FalseDest, InitializationPtr SILGenFunction::emitPatternBindingInitialization( Pattern *P, JumpDest failureDest, bool generateDebugInfo) { - return InitializationForPattern(*this, failureDest, generateDebugInfo) - .visit(P); + auto init = + InitializationForPattern(*this, failureDest, generateDebugInfo).visit(P); + init->setEmitDebugValueOnInit(generateDebugInfo); + return init; } /// Enter a cleanup to deallocate the given location. diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 41a938445dff7..b55b6667eac89 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -6294,11 +6294,13 @@ RValue RValueEmitter::visitMacroExpansionExpr(MacroExpansionExpr *E, } else if (auto *MED = E->getSubstituteDecl()) { Mangle::ASTMangler mangler; - MED->forEachExpandedExprOrStmt([&](ASTNode node) { + MED->forEachExpandedNode([&](ASTNode node) { if (auto *expr = node.dyn_cast()) visit(expr, C); else if (auto *stmt = node.dyn_cast()) SGF.emitStmt(stmt); + else + SGF.visit(node.get()); }); return RValue(); } diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index a749212a17846..6c04a9f890a5e 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -1720,3 +1720,14 @@ SILValue SILGenFunction::emitWrapIntegerLiteral(SILLocation loc, auto propertyValue = emitWrapIntegerLiteral(loc, propertyTy, value); return B.createStruct(loc, ty, propertyValue); } + +ParamDecl *SILGenFunction::isMappedToInitAccessorArgument(VarDecl *property) { + assert(isa(FunctionDC) && + cast(FunctionDC)->isInitAccessor()); + + auto arg = InitAccessorArgumentMappings.find(property); + if (arg == InitAccessorArgumentMappings.end()) + return nullptr; + + return arg->second; +} diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 0cadb928cc271..49f7ba54215b2 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -453,6 +453,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// need to be emitted inside the next brace statement. llvm::SmallVector LocalAuxiliaryDecls; + /// The mappings between instance properties referenced by this init + /// accessor (via initializes/accesses attributes) and and argument + /// declarations synthesized to access them in the body. + llvm::DenseMap InitAccessorArgumentMappings; + // Context information for tracking an `async let` child task. struct AsyncLetChildTask { SILValue asyncLet; // RawPointer to the async let state @@ -799,6 +804,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// Emit a method that destroys the ivars of a class. void emitIVarDestroyer(SILDeclRef ivarDestroyer); + /// Generates code for the given init accessor represented by AccessorDecl. + /// This emits the body code and replaces all `self.` references + /// with either argument (if property appears in `acesses` list`) or result + /// value assignment. + void emitInitAccessor(AccessorDecl *accessor); + /// Generates code to destroy the instance variables of a class. /// /// \param selfValue The 'self' value. @@ -2755,6 +2766,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction CanPackType formalPackType, unsigned componentIndex, SILValue currentIndexWithinComponent); + + /// If context is init accessor, find a mapping between the given type + /// property and argument declaration synthesized for it. + ParamDecl *isMappedToInitAccessorArgument(VarDecl *property); }; diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index f3769c5b90c92..c2097946bafe9 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1171,6 +1171,11 @@ namespace { assert(base && base.getType().isAddress() && "should have an address base to borrow from"); + // If the base value is address-only then we can borrow from the + // address in-place. + if (!base.getType().isLoadable(SGF.F)) { + return base; + } auto result = SGF.B.createLoadBorrow(loc, base.getValue()); return SGF.emitFormalEvaluationManagedBorrowedRValueWithCleanup(loc, base.getValue(), result); @@ -1509,6 +1514,33 @@ namespace { return false; } + /// Whether an assignment 'x = y' can be re-written as a call to an + /// init accessor declared by 'x'. + bool canRewriteSetAsInitAccessor(SILGenFunction &SGF) const { + auto *varDecl = dyn_cast(Storage); + if (!varDecl || varDecl->isStatic() || + varDecl->getDeclContext()->isLocalContext()) + return false; + + auto *fnDecl = SGF.FunctionDC->getAsDecl(); + bool isAssignmentToSelfParamInInit = + IsOnSelfParameter && isa(fnDecl) && + // Convenience initializers only contain assignments and not + // initializations. + !(cast(fnDecl)->isConvenienceInit()); + + // Assignment to a wrapped property can only be re-written to initialization for + // members of `self` in an initializer, and for local variables. + if (!isAssignmentToSelfParamInInit) + return false; + + auto *initAccessor = varDecl->getAccessor(AccessorKind::Init); + if (!initAccessor) + return false; + + return true; + } + void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc, LValue &&dest, ArgumentSource &&value) { assert(getAccessorDecl()->isSetter()); @@ -1675,6 +1707,36 @@ namespace { return Mval; }; + if (canRewriteSetAsInitAccessor(SGF)) { + // Emit an assign_or_init with the allocating initializer function and the + // setter function as arguments. DefiniteInitialization will then decide + // between the two functions, depending if it's an init call or a + // set call. + VarDecl *field = cast(Storage); + auto FieldType = field->getValueInterfaceType(); + if (!Substitutions.empty()) { + FieldType = FieldType.subst(Substitutions); + } + + // Emit the init accessor function partially applied to the base. + auto *initAccessor = field->getOpaqueAccessor(AccessorKind::Init); + auto initConstant = SGF.getAccessorDeclRef(initAccessor); + SILValue initFRef = SGF.emitGlobalFunctionRef(loc, initConstant); + + // Emit the set accessor function partially applied to the base. + auto setterFRef = getSetterFRef(); + auto setterTy = getSetterType(setterFRef); + SILFunctionConventions setterConv(setterTy, SGF.SGM.M); + auto setterFn = emitPartialSetterApply(setterFRef, setterConv); + + // Create the assign_or_init with the initializer and setter. + auto value = emitValue(field, FieldType, setterTy, setterConv); + SGF.B.createAssignOrInit(loc, value.forward(SGF), initFRef, + setterFn.getValue(), + AssignOrInitInst::Unknown); + return; + } + if (canRewriteSetAsPropertyWrapperInit(SGF) && !Storage->isStatic() && isBackingVarVisible(cast(Storage), @@ -2943,7 +3005,11 @@ namespace { case AccessorKind::WillSet: case AccessorKind::DidSet: llvm_unreachable("cannot use accessor directly to perform an access"); + + case AccessorKind::Init: + llvm_unreachable("init accessor not yet implemented"); } + llvm_unreachable("bad kind"); } }; @@ -3498,11 +3564,12 @@ static bool isCallToSelfOfCurrentFunction(SILGenFunction &SGF, LookupExpr *e) { cast(SGF.FunctionDC->getAsDecl()), false); } -static bool isCurrentFunctionReadAccess(SILGenFunction &SGF) { +static bool isCurrentFunctionAccessor(SILGenFunction &SGF, + AccessorKind accessorKind) { auto *contextAccessorDecl = dyn_cast_or_null(SGF.FunctionDC->getAsDecl()); return contextAccessorDecl && - contextAccessorDecl->getAccessorKind() == AccessorKind::Read; + contextAccessorDecl->getAccessorKind() == accessorKind; } LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, @@ -3512,6 +3579,19 @@ LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, // that can be an lvalue is a VarDecl. VarDecl *var = cast(e->getMember().getDecl()); + // A reference to an instance property in init accessor body + // has to be remapped into an argument reference because all + // of the properties from initialized/accesses lists are passed + // to init accessors individually via arguments. + if (isCurrentFunctionAccessor(SGF, AccessorKind::Init)) { + if (auto *arg = SGF.isMappedToInitAccessorArgument(var)) { + auto subs = e->getMember().getSubstitutions(); + return emitLValueForNonMemberVarDecl( + SGF, e, ConcreteDeclRef(arg, subs), getSubstFormalRValueType(e), + accessKind, options, AccessSemantics::Ordinary, None); + } + } + auto accessSemantics = e->getAccessSemantics(); AccessStrategy strategy = var->getAccessStrategy(accessSemantics, @@ -3521,7 +3601,7 @@ LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, bool isOnSelfParameter = isCallToSelfOfCurrentFunction(SGF, e); - bool isContextRead = isCurrentFunctionReadAccess(SGF); + bool isContextRead = isCurrentFunctionAccessor(SGF, AccessorKind::Read); // If we are inside _read, calling self.get, and the _read we are inside of is // the same as the as self's variable and the current function is a @@ -3722,7 +3802,7 @@ LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e, SGF.F.getResilienceExpansion()); bool isOnSelfParameter = isCallToSelfOfCurrentFunction(SGF, e); - bool isContextRead = isCurrentFunctionReadAccess(SGF); + bool isContextRead = isCurrentFunctionAccessor(SGF, AccessorKind::Read); // If we are inside _read, calling self.get, and the _read we are inside of is // the same as the as self's variable and the current function is a @@ -5027,7 +5107,8 @@ static bool trySetterPeephole(SILGenFunction &SGF, SILLocation loc, } auto &setterComponent = static_cast(component); - if (setterComponent.canRewriteSetAsPropertyWrapperInit(SGF)) + if (setterComponent.canRewriteSetAsPropertyWrapperInit(SGF) || + setterComponent.canRewriteSetAsInitAccessor(SGF)) return false; setterComponent.emitAssignWithSetter(SGF, loc, std::move(dest), diff --git a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp index 73138ef18e785..8581c8b3dad41 100644 --- a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp +++ b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp @@ -284,7 +284,8 @@ static void checkUsesOfAccess(BeginAccessInst *access) { auto user = use->getUser(); assert(!isa(user)); assert(!isa(user) || - onlyUsedByAssignByWrapper(cast(user))); + onlyUsedByAssignByWrapper(cast(user)) || + onlyUsedByAssignOrInit(cast(user))); } #endif } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 4c8e388b8f1ed..4b8278859f3c4 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -31,6 +31,10 @@ using namespace ownership; // Utility //===----------------------------------------------------------------------===// +static bool isVariableOrResult(MarkUninitializedInst *MUI) { + return MUI->isVar() || MUI->isOut(); +} + static void gatherDestroysOfContainer(const DIMemoryObjectInfo &memoryInfo, DIElementUseInfo &useInfo) { auto *uninitMemory = memoryInfo.getUninitializedValue(); @@ -106,7 +110,7 @@ computeMemorySILType(MarkUninitializedInst *MUI, SILValue Address) { // If this is a let variable we're initializing, remember this so we don't // allow reassignment. - if (!MUI->isVar()) + if (!isVariableOrResult(MUI)) return {MemorySILType, false}; auto *VDecl = MUI->getLoc().getAsASTNode(); @@ -417,7 +421,7 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, Result = "self"; else if (ValueDecl *VD = dyn_cast_or_null(getLoc().getAsASTNode())) - Result = std::string(VD->getBaseIdentifier()); + Result = VD->hasName() ? VD->getBaseIdentifier().str() : "_"; else Result = ""; @@ -459,7 +463,7 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, // If we are analyzing a variable, we can generally get the decl associated // with it. - if (MemoryInst->isVar()) + if (isVariableOrResult(MemoryInst)) return MemoryInst->getLoc().getAsASTNode(); // Otherwise, we can't. @@ -516,7 +520,7 @@ SingleValueInstruction *DIMemoryObjectInfo::findUninitializedSelfValue() const { // If instruction is not a local variable, it could only // be some kind of `self` (root, delegating, derived etc.) // see \c MarkUninitializedInst::Kind for more details. - if (!MUI->isVar()) + if (!isVariableOrResult(MUI)) return ::getUninitializedValue(MUI); } } @@ -525,7 +529,7 @@ SingleValueInstruction *DIMemoryObjectInfo::findUninitializedSelfValue() const { ConstructorDecl *DIMemoryObjectInfo::getActorInitSelf() const { // is it 'self'? - if (!MemoryInst->isVar()) + if (!isVariableOrResult(MemoryInst)) { if (auto decl = dyn_cast_or_null(getASTType()->getAnyNominal())) // is it for an actor? @@ -535,6 +539,7 @@ ConstructorDecl *DIMemoryObjectInfo::getActorInitSelf() const { if (auto *ctor = dyn_cast_or_null( silFn->getDeclContext()->getAsDecl())) return ctor; + } return nullptr; } @@ -549,7 +554,7 @@ bool DIMemoryUse::onlyTouchesTrivialElements( const DIMemoryObjectInfo &MI) const { // assign_by_wrapper calls functions to assign a value. This is not // considered as trivial. - if (isa(Inst)) + if (isa(Inst) || isa(Inst)) return false; auto *F = Inst->getFunction(); @@ -650,25 +655,28 @@ class ElementUseCollector { private: void collectUses(SILValue Pointer, unsigned BaseEltNo); bool addClosureElementUses(PartialApplyInst *pai, Operand *argUse); + void collectAssignOrInitUses(PartialApplyInst *pai, Operand *argUse, + unsigned BaseEltNo = 0); void collectClassSelfUses(SILValue ClassPointer); void collectClassSelfUses(SILValue ClassPointer, SILType MemorySILType, llvm::SmallDenseMap &EN); void addElementUses(unsigned BaseEltNo, SILType UseTy, SILInstruction *User, - DIUseKind Kind); + DIUseKind Kind, NullablePtr Field = 0); void collectTupleElementUses(TupleElementAddrInst *TEAI, unsigned BaseEltNo); void collectStructElementUses(StructElementAddrInst *SEAI, unsigned BaseEltNo); }; } // end anonymous namespace -/// addElementUses - An operation (e.g. load, store, inout use, etc) on a value +/// addElementUses - An operation (e.g. load, store, inout usec etc) on a value /// acts on all of the aggregate elements in that value. For example, a load /// of $*(Int,Int) is a use of both Int elements of the tuple. This is a helper /// to keep the Uses data structure up to date for aggregate uses. void ElementUseCollector::addElementUses(unsigned BaseEltNo, SILType UseTy, - SILInstruction *User, DIUseKind Kind) { + SILInstruction *User, DIUseKind Kind, + NullablePtr Field) { // 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; @@ -678,7 +686,7 @@ void ElementUseCollector::addElementUses(unsigned BaseEltNo, SILType UseTy, getElementCountRec(TypeExpansionContext(*User->getFunction()), Module, UseTy, IsSelfOfNonDelegatingInitializer); - trackUse(DIMemoryUse(User, Kind, BaseEltNo, NumElements)); + trackUse(DIMemoryUse(User, Kind, BaseEltNo, NumElements, Field)); } /// Given a tuple_element_addr or struct_element_addr, compute the new @@ -1083,7 +1091,12 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { if (auto *PAI = dyn_cast(User)) { if (onlyUsedByAssignByWrapper(PAI)) continue; - + + if (onlyUsedByAssignOrInit(PAI)) { + collectAssignOrInitUses(PAI, Op, BaseEltNo); + continue; + } + if (BaseEltNo == 0 && addClosureElementUses(PAI, Op)) continue; } @@ -1174,6 +1187,50 @@ bool ElementUseCollector::addClosureElementUses(PartialApplyInst *pai, return true; } +void +ElementUseCollector::collectAssignOrInitUses(PartialApplyInst *pai, + Operand *argUse, + unsigned BaseEltNo) { + for (Operand *Op : pai->getUses()) { + SILInstruction *User = Op->getUser(); + if (!isa(User) || Op->getOperandNumber() != 2) { + continue; + } + + /// AssignOrInit doesn't operate on `self` so we need to make sure + /// that the flag is dropped before calling \c addElementUses. + llvm::SaveAndRestore X(IsSelfOfNonDelegatingInitializer, false); + + auto *inst = cast(User); + auto *typeDC = inst->getReferencedInitAccessor() + ->getDeclContext() + ->getSelfNominalTypeDecl(); + + auto selfTy = pai->getOperand(1)->getType(); + + auto addUse = [&](VarDecl *property, DIUseKind useKind) { + auto expansionContext = TypeExpansionContext(*pai->getFunction()); + auto type = selfTy.getFieldType(property, Module, expansionContext); + addElementUses(Module.getFieldIndex(typeDC, property), type, User, + useKind, property); + }; + + auto initializedElts = inst->getInitializedProperties(); + if (initializedElts.empty()) { + // Add a placeholder use that doesn't touch elements to make sure that + // the `assign_or_init` instruction gets the kind set when `initializes` + // list is empty. + trackUse(DIMemoryUse(User, DIUseKind::InitOrAssign, BaseEltNo, 0)); + } else { + for (auto *property : initializedElts) + addUse(property, DIUseKind::InitOrAssign); + } + + for (auto *property : inst->getAccessedProperties()) + addUse(property, DIUseKind::Load); + } +} + /// collectClassSelfUses - Collect all the uses of a 'self' pointer in a class /// constructor. The memory object has class type. void ElementUseCollector::collectClassSelfUses(SILValue ClassPointer) { @@ -1576,6 +1633,11 @@ void ElementUseCollector::collectClassSelfUses( if (onlyUsedByAssignByWrapper(PAI)) continue; + if (onlyUsedByAssignOrInit(PAI)) { + collectAssignOrInitUses(PAI, Op); + continue; + } + if (addClosureElementUses(PAI, Op)) continue; diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index 99b6e9c00cf37..860b6a0261a3c 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -116,7 +116,9 @@ class DIMemoryObjectInfo { unsigned getNumElements() const { return NumElements; } /// Return true if this is 'self' in any kind of initializer. - bool isAnyInitSelf() const { return !MemoryInst->isVar(); } + bool isAnyInitSelf() const { + return !MemoryInst->isVar() && !MemoryInst->isOut(); + } /// Return uninitialized value of 'self' if current memory object /// is located in an initializer (of any kind). @@ -150,7 +152,7 @@ class DIMemoryObjectInfo { if (MemoryInst->isDelegatingSelf()) return false; - if (!MemoryInst->isVar()) { + if (!MemoryInst->isVar() && !MemoryInst->isOut()) { if (auto decl = getASTType()->getAnyNominal()) { if (isa(decl)) { return true; @@ -200,6 +202,7 @@ class DIMemoryObjectInfo { bool isNonDelegatingInit() const { switch (MemoryInst->getMarkUninitializedKind()) { case MarkUninitializedInst::Var: + case MarkUninitializedInst::Out: return false; case MarkUninitializedInst::RootSelf: case MarkUninitializedInst::CrossModuleRootSelf: @@ -222,6 +225,8 @@ class DIMemoryObjectInfo { return MemoryInst->isDelegatingSelfAllocated(); } + bool isOut() const { return MemoryInst->isOut(); } + enum class EndScopeKind { Borrow, Access }; /// Given an element number (in the flattened sense) return a pointer to a @@ -259,9 +264,10 @@ enum DIUseKind { /// value. Assign, - /// The instruction is an assignment of a wrapped value with an already initialized - /// backing property wrapper. - AssignWrappedValue, + /// The instruction is a setter call for a computed property after all of + /// self is initialized. This is used for property wrappers and for init + /// accessors. + Set, /// The instruction is a store to a member of a larger struct value. PartialStore, @@ -308,9 +314,12 @@ struct DIMemoryUse { /// track of which tuple elements are affected. unsigned FirstElement, NumElements; - DIMemoryUse(SILInstruction *Inst, DIUseKind Kind, unsigned FE, unsigned NE) - : Inst(Inst), Kind(Kind), FirstElement(FE), NumElements(NE) { - } + NullablePtr Field; + + DIMemoryUse(SILInstruction *Inst, DIUseKind Kind, unsigned FE, unsigned NE, + NullablePtr Field = 0) + : Inst(Inst), Kind(Kind), FirstElement(FE), NumElements(NE), + Field(Field) {} DIMemoryUse() : Inst(nullptr) {} diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 85263506253de..12cb54a377583 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -566,7 +566,7 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, case DIUseKind::Escape: continue; case DIUseKind::Assign: - case DIUseKind::AssignWrappedValue: + case DIUseKind::Set: case DIUseKind::IndirectIn: case DIUseKind::InitOrAssign: case DIUseKind::InOutArgument: @@ -1094,7 +1094,7 @@ void LifetimeChecker::doIt() { continue; case DIUseKind::Assign: - case DIUseKind::AssignWrappedValue: + case DIUseKind::Set: // Instructions classified as assign are only generated when lowering // InitOrAssign instructions in regions known to be initialized. Since // they are already known to be definitely init, don't reprocess them. @@ -1152,6 +1152,18 @@ void LifetimeChecker::doIt() { return; } + // All of the indirect results marked as "out" have to be fully initialized + // before their lifetime ends. + if (TheMemory.isOut() && Uses.empty()) { + auto loc = TheMemory.getLoc(); + + std::string propertyName; + auto *property = TheMemory.getPathStringToElement(0, propertyName); + diagnose(Module, F.getLocation(), + diag::ivar_not_initialized_by_init_accessor, property->getName()); + EmittedErrorLocs.push_back(loc); + } + // If the memory object has nontrivial type, then any destroy/release of the // memory object will destruct the memory. If the memory (or some element // thereof) is not initialized on some path, the bad things happen. Process @@ -1436,14 +1448,30 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { // If this is an initialization or a normal assignment, upgrade the store to // an initialization or assign in the uses list so that clients know about it. if (isFullyUninitialized) { - Use.Kind = DIUseKind::Initialization; + // If this is a placeholder use of `assign_or_init` instruction, + // check whether all of the fields are initialized - if so, call a setter, + // otherwise call init accessor. + if (isa(Use.Inst) && Use.NumElements == 0) { + auto allFieldsInitialized = + getAnyUninitializedMemberAtInst(Use.Inst, 0, + TheMemory.getNumElements()) == -1; + Use.Kind = + allFieldsInitialized ? DIUseKind::Set : DIUseKind::Initialization; + } else { + Use.Kind = DIUseKind::Initialization; + } } else if (isFullyInitialized && isa(Use.Inst)) { // If some fields are uninitialized, re-write assign_by_wrapper to assignment // of the backing wrapper. If all fields are initialized, assign to the wrapped // value. auto allFieldsInitialized = getAnyUninitializedMemberAtInst(Use.Inst, 0, TheMemory.getNumElements()) == -1; - Use.Kind = allFieldsInitialized ? DIUseKind::AssignWrappedValue : DIUseKind::Assign; + Use.Kind = allFieldsInitialized ? DIUseKind::Set : DIUseKind::Assign; + } else if (isFullyInitialized && isa(Use.Inst)) { + auto allFieldsInitialized = + getAnyUninitializedMemberAtInst(Use.Inst, 0, + TheMemory.getNumElements()) == -1; + Use.Kind = allFieldsInitialized ? DIUseKind::Set : DIUseKind::Assign; } else if (isFullyInitialized) { Use.Kind = DIUseKind::Assign; } else { @@ -1470,7 +1498,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { HasConditionalInitAssign = true; return; } - + // Otherwise, we have a definite init or assign. Make sure the instruction // itself is tagged properly. NeedsUpdateForInitState.push_back(UseID); @@ -1593,6 +1621,7 @@ void LifetimeChecker::handleInOutUse(const DIMemoryUse &Use) { case AccessorKind::MutableAddress: case AccessorKind::DidSet: case AccessorKind::WillSet: + case AccessorKind::Init: return true; } llvm_unreachable("bad kind"); @@ -2262,7 +2291,8 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { "delegating inits have a single elt"); // Lower Assign instructions if needed. - if (isa(Use.Inst) || isa(Use.Inst)) + if (isa(Use.Inst) || isa(Use.Inst) || + isa(Use.Inst)) NeedsUpdateForInitState.push_back(UseID); } else { // super.init also requires that all ivars are initialized before the @@ -2296,7 +2326,7 @@ void LifetimeChecker::updateInstructionForInitState(unsigned UseID) { Use.Kind == DIUseKind::SelfInit) InitKind = IsInitialization; else { - assert(Use.Kind == DIUseKind::Assign || Use.Kind == DIUseKind::AssignWrappedValue); + assert(Use.Kind == DIUseKind::Assign || Use.Kind == DIUseKind::Set); InitKind = IsNotInitialization; } @@ -2356,6 +2386,30 @@ void LifetimeChecker::updateInstructionForInitState(unsigned UseID) { return; } + + if (auto *AI = dyn_cast(Inst)) { + // Remove this instruction from our data structures, since we will be + // removing it. + Use.Inst = nullptr; + llvm::erase_if(NonLoadUses[Inst], [&](unsigned id) { return id == UseID; }); + + switch (Use.Kind) { + case DIUseKind::Assign: + AI->markAsInitialized(Use.Field.get()); + LLVM_FALLTHROUGH; + case DIUseKind::Initialization: + AI->setMode(AssignOrInitInst::Init); + break; + case DIUseKind::Set: + AI->setMode(AssignOrInitInst::Set); + break; + default: + llvm_unreachable("Wrong use kind for assign_or_init"); + } + + return; + } + if (auto *AI = dyn_cast(Inst)) { // Remove this instruction from our data structures, since we will be // removing it. @@ -2369,7 +2423,7 @@ void LifetimeChecker::updateInstructionForInitState(unsigned UseID) { case DIUseKind::Assign: AI->setMode(AssignByWrapperInst::Assign); break; - case DIUseKind::AssignWrappedValue: + case DIUseKind::Set: AI->setMode(AssignByWrapperInst::AssignWrappedValue); break; default: diff --git a/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp b/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp index e5e5fb1c4135a..2bd4aa8980e65 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp @@ -183,6 +183,7 @@ bool isUseOfSelfInInitializer(Operand *oper) { if (auto *MUI = dyn_cast(value)) { switch (MUI->getMarkUninitializedKind()) { case MarkUninitializedInst::Kind::Var: + case MarkUninitializedInst::Kind::Out: return false; case MarkUninitializedInst::Kind::RootSelf: case MarkUninitializedInst::Kind::CrossModuleRootSelf: diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyWrappedTypeEliminator.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyWrappedTypeEliminator.cpp index 0782d4f8592a3..92b594f1bcf63 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyWrappedTypeEliminator.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyWrappedTypeEliminator.cpp @@ -279,6 +279,18 @@ struct SILMoveOnlyWrappedTypeEliminator { } // namespace +/// Returns true if this is a moveonlywrapped type whose underlying type is a +/// trivial type /or/ if this is a boxed type of that sort. +static bool isMoveOnlyWrappedTrivial(SILValue value) { + auto *fn = value->getFunction(); + SILType type = value->getType(); + if (type.removingMoveOnlyWrapper().isTrivial(fn)) + return true; + if (type.isBoxedMoveOnlyWrappedType(fn)) + return type.getSILBoxFieldType(fn).removingMoveOnlyWrapper().isTrivial(fn); + return false; +} + bool SILMoveOnlyWrappedTypeEliminator::process() { bool madeChange = true; @@ -314,7 +326,7 @@ bool SILMoveOnlyWrappedTypeEliminator::process() { continue; if (trivialOnly && - !v->getType().removingMoveOnlyWrapper().isTrivial(*fn)) + !isMoveOnlyWrappedTrivial(v)) continue; v->unsafelyEliminateMoveOnlyWrapper(fn); diff --git a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp index d06d94ad30e6a..179b7f5beeb6a 100644 --- a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp +++ b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "raw-sil-inst-lowering" +#include "swift/AST/Decl.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" @@ -168,6 +169,15 @@ static void getAssignByWrapperArgs(SmallVectorImpl &args, "initializer or setter has too many arguments"); } +static void emitInitAccessorInitialValueArgument( + SmallVectorImpl &args, SILValue src, + const SILFunctionConventions &convention, SILBuilder &forProjections, + SILBuilder &forCleanup) { + unsigned argIdx = convention.getSILArgIndexOfFirstParam(); + getAssignByWrapperArgsRecursively(args, src, argIdx, convention, + forProjections, forCleanup); +} + static void lowerAssignByWrapperInstruction(SILBuilderWithScope &b, AssignByWrapperInst *inst, @@ -250,6 +260,119 @@ lowerAssignByWrapperInstruction(SILBuilderWithScope &b, inst->eraseFromParent(); } +static void +lowerAssignOrInitInstruction(SILBuilderWithScope &b, + AssignOrInitInst *inst, + SmallSetVector &toDelete) { + LLVM_DEBUG(llvm::dbgs() << " *** Lowering " << *inst << "\n"); + + ++numAssignRewritten; + + SILValue src = inst->getSrc(); + SILLocation loc = inst->getLoc(); + SILBuilderWithScope forCleanup(std::next(inst->getIterator())); + + switch (inst->getMode()) { + case AssignOrInitInst::Unknown: + assert(b.getModule().getASTContext().hadError() && + "assign_or_init must have a valid mode"); + // In case DefiniteInitialization already gave up with an error, just + // treat the assign_or_init as an "init". + LLVM_FALLTHROUGH; + case AssignOrInitInst::Init: { + SILValue initFn = inst->getInitializer(); + CanSILFunctionType fTy = initFn->getType().castTo(); + SILFunctionConventions convention(fTy, inst->getModule()); + + auto *setterPA = dyn_cast(inst->getSetter()); + assert(setterPA); + + auto selfValue = setterPA->getOperand(1); + auto isRefSelf = selfValue->getType().getASTType()->mayHaveSuperclass(); + + SILValue selfRef; + if (isRefSelf) { + selfRef = b.emitBeginBorrowOperation(loc, selfValue); + } else { + selfRef = b.createBeginAccess(loc, selfValue, SILAccessKind::Modify, + SILAccessEnforcement::Dynamic, + /*noNestedConflict=*/false, + /*fromBuiltin=*/false); + } + + auto emitFieldReference = [&](VarDecl *field, + bool emitDestroy = false) -> SILValue { + SILValue fieldRef; + if (isRefSelf) { + fieldRef = b.createRefElementAddr(loc, selfRef, field); + } else { + fieldRef = b.createStructElementAddr(loc, selfRef, field); + } + + if (emitDestroy) + b.createDestroyAddr(loc, fieldRef); + + return fieldRef; + }; + + SmallVector arguments; + + // First, emit all of the properties listed in `initializes(...)`. They + // are passed as indirect results. + { + auto toInitialize = inst->getInitializedProperties(); + for (unsigned index : indices(toInitialize)) { + arguments.push_back(emitFieldReference( + toInitialize[index], + /*emitDestroy=*/inst->isPropertyAlreadyInitialized(index))); + } + } + + // Now emit `initialValue` which is the only argument specified + // by the user. + emitInitAccessorInitialValueArgument(arguments, src, convention, b, + forCleanup); + + // And finally, emit all of the `accesses(...)` properties. + for (auto *property : inst->getAccessedProperties()) + arguments.push_back(emitFieldReference(property)); + + b.createApply(loc, initFn, setterPA->getSubstitutionMap(), arguments); + + if (isRefSelf) { + b.emitEndBorrowOperation(loc, selfRef); + } else { + b.createEndAccess(loc, selfRef, /*aborted=*/false); + } + + // The unused partial_apply violates memory lifetime rules in case "self" + // is an inout. Therefore we cannot keep it as a dead closure to be + // cleaned up later. We have to delete it in this pass. + toDelete.insert(inst->getSetter()); + + // Also the argument of the closure (which usually is a "load") has to be + // deleted to avoid memory lifetime violations. + if (setterPA->getNumArguments() == 1) + toDelete.insert(setterPA->getArgument(0)); + break; + } + case AssignOrInitInst::Set: { + SILValue setterFn = inst->getSetter(); + CanSILFunctionType fTy = setterFn->getType().castTo(); + SILFunctionConventions convention(fTy, inst->getModule()); + assert(!convention.hasIndirectSILResults()); + SmallVector args; + getAssignByWrapperArgs(args, src, convention, b, forCleanup); + b.createApply(loc, setterFn, SubstitutionMap(), args); + + // Again, we have to delete the unused init accessor reference. + toDelete.insert(inst->getInitializer()); + break; + } + } + inst->eraseFromParent(); +} + static void deleteDeadAccessMarker(BeginAccessInst *BA) { SmallVector Users; for (Operand *Op : BA->getUses()) { @@ -323,6 +446,13 @@ static bool lowerRawSILOperations(SILFunction &fn) { continue; } + if (auto *ai = dyn_cast(inst)) { + SILBuilderWithScope b(ai); + lowerAssignOrInitInstruction(b, ai, toDelete); + changed = true; + continue; + } + // mark_uninitialized just becomes a noop, resolving to its operand. if (auto *mui = dyn_cast(inst)) { mui->replaceAllUsesWith(mui->getOperand()); diff --git a/lib/SILOptimizer/SILCombiner/SILCombiner.h b/lib/SILOptimizer/SILCombiner/SILCombiner.h index 58c87bbe8efc6..e2a8198131192 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombiner.h +++ b/lib/SILOptimizer/SILCombiner/SILCombiner.h @@ -234,8 +234,6 @@ class SILCombiner : SILInstruction *visitSILInstruction(SILInstruction *I) { return nullptr; } /// Instruction visitors. - SILInstruction *visitReleaseValueInst(ReleaseValueInst *DI); - SILInstruction *visitRetainValueInst(RetainValueInst *CI); SILInstruction *visitPartialApplyInst(PartialApplyInst *AI); SILInstruction *visitApplyInst(ApplyInst *AI); SILInstruction *visitBeginApplyInst(BeginApplyInst *BAI); diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index 0351dcf23b08c..d34906d930da9 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -811,164 +811,6 @@ SILInstruction *SILCombiner::visitIndexAddrInst(IndexAddrInst *IA) { IA->needsStackProtection() || cast(base)->needsStackProtection()); } -/// Walks over all fields of an aggregate and checks if a reference count -/// operation for \p value is required. This differs from a simple `isTrivial` -/// check, because it treats a value_to_bridge_object instruction as "trivial". -/// It can also handle non-trivial enums with trivial cases. -/// -/// TODO: Define this as a top-level SILValue API. It needs to be updated -/// whenever we refine the specification of isTrivial. For example, in the -/// future we will check for the existence of a user-defined deinitializer and -/// handle C++ types specially. -static bool isValueTrivial(SILValue value, SILFunction *function) { - SmallVector workList; - SmallPtrSet visited; - workList.push_back(value); - while (!workList.empty()) { - SILValue v = workList.pop_back_val(); - if (v->getType().isTrivial(*function)) - continue; - if (isa(v)) - continue; - - // MoveOnly types may have a user-defined deinit. - if (hasValueDeinit(v)) - return false; - - if (isa(v) || isa(v)) { - for (SILValue op : cast(v)->getOperandValues()) { - if (visited.insert(op).second) - workList.push_back(op); - } - continue; - } - if (auto *en = dyn_cast(v)) { - if (en->hasOperand() && visited.insert(en->getOperand()).second) - workList.push_back(en->getOperand()); - continue; - } - return false; - } - return true; -} - -SILInstruction *SILCombiner::visitReleaseValueInst(ReleaseValueInst *RVI) { - assert(!RVI->getFunction()->hasOwnership()); - - SILValue Operand = RVI->getOperand(); - SILType OperandTy = Operand->getType(); - - // Do not remove a release that calls a value deinit. - if (hasValueDeinit(OperandTy)) - return nullptr; - - // Destroy value of an enum with a trivial payload or no-payload is a no-op. - if (auto *EI = dyn_cast(Operand)) { - if (!EI->hasOperand() || - EI->getOperand()->getType().isTrivial(*EI->getFunction())) - return eraseInstFromFunction(*RVI); - - // retain_value of an enum_inst where we know that it has a payload can be - // reduced to a retain_value on the payload. - if (EI->hasOperand()) { - return Builder.createReleaseValue(RVI->getLoc(), EI->getOperand(), - RVI->getAtomicity()); - } - } - - // ReleaseValueInst of a loadable reference storage type needs the - // corresponding release instruction. -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - if (OperandTy.is()) \ - return Builder.create##Name##Release(RVI->getLoc(), Operand, \ - RVI->getAtomicity()); -#include "swift/AST/ReferenceStorage.def" - - // ReleaseValueInst of a reference type is a strong_release. - if (OperandTy.isReferenceCounted(RVI->getModule())) - return Builder.createStrongRelease(RVI->getLoc(), Operand, - RVI->getAtomicity()); - - // ReleaseValueInst of a trivial type is a no-op. - if (isValueTrivial(Operand, RVI->getFunction())) - return eraseInstFromFunction(*RVI); - - // Do nothing for non-trivial non-reference types. - return nullptr; -} - -SILInstruction *SILCombiner::visitRetainValueInst(RetainValueInst *RVI) { - assert(!RVI->getFunction()->hasOwnership()); - - SILValue Operand = RVI->getOperand(); - SILType OperandTy = Operand->getType(); - - // retain_value of an enum with a trivial payload or no-payload is a no-op + - // RAUW. - if (auto *EI = dyn_cast(Operand)) { - if (!EI->hasOperand() || - EI->getOperand()->getType().isTrivial(*RVI->getFunction())) { - return eraseInstFromFunction(*RVI); - } - - // retain_value of an enum_inst where we know that it has a payload can be - // reduced to a retain_value on the payload. - if (EI->hasOperand()) { - return Builder.createRetainValue(RVI->getLoc(), EI->getOperand(), - RVI->getAtomicity()); - } - } - - // RetainValueInst of a loadable reference storage type needs the - // corresponding retain instruction. -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - if (OperandTy.is()) \ - return Builder.create##Name##Retain(RVI->getLoc(), Operand, \ - RVI->getAtomicity()); -#include "swift/AST/ReferenceStorage.def" - - // RetainValueInst of a reference type is a strong_release. - if (OperandTy.isReferenceCounted(RVI->getModule())) { - return Builder.createStrongRetain(RVI->getLoc(), Operand, - RVI->getAtomicity()); - } - - // RetainValueInst of a trivial type is a no-op + use propagation. - if (OperandTy.isTrivial(*RVI->getFunction())) { - return eraseInstFromFunction(*RVI); - } - - // Sometimes in the stdlib due to hand offs, we will see code like: - // - // release_value %0 - // retain_value %0 - // - // with the matching retain_value to the release_value in a predecessor basic - // block and the matching release_value for the retain_value_retain in a - // successor basic block. - // - // Due to the matching pairs being in different basic blocks, the ARC - // Optimizer (which is currently local to one basic block does not handle - // it). But that does not mean that we cannot eliminate this pair with a - // peephole. - - // If we are not the first instruction in this basic block... - if (RVI != &*RVI->getParent()->begin()) { - SILBasicBlock::iterator Pred = std::prev(RVI->getIterator()); - - // ...and the predecessor instruction is a release_value on the same value - // as our retain_value... - if (auto *Release = dyn_cast(&*Pred)) - // Remove them... - if (Release->getOperand() == RVI->getOperand()) { - eraseInstFromFunction(*Release); - return eraseInstFromFunction(*RVI); - } - } - - return nullptr; -} - SILInstruction *SILCombiner::visitCondFailInst(CondFailInst *CFI) { // Remove runtime asserts such as overflow checks and bounds checks. if (RemoveCondFails) diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index a02429ae68112..c3df1681f59ba 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -318,6 +318,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::StoreInst: case SILInstructionKind::AssignInst: case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::AssignOrInitInst: case SILInstructionKind::MarkFunctionEscapeInst: case SILInstructionKind::DebugValueInst: case SILInstructionKind::DebugStepInst: diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index d2c5ca4e21c5b..c9008d1388410 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -1029,6 +1029,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::WitnessMethodInst: case SILInstructionKind::AssignInst: case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::AssignOrInitInst: case SILInstructionKind::CheckedCastBranchInst: case SILInstructionKind::CheckedCastAddrBranchInst: case SILInstructionKind::ClassMethodInst: diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 43fa783a0d786..b6ae98679227c 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -984,7 +984,8 @@ Optional TypeChecker::applyResultBuilderBodyTransform( SmallVector solutions; cs.solveForCodeCompletion(solutions); - CompletionContextFinder analyzer(func, func->getDeclContext()); + SyntacticElementTarget funcTarget(func); + CompletionContextFinder analyzer(funcTarget, func->getDeclContext()); if (analyzer.hasCompletion()) { filterSolutionsForCodeCompletion(solutions, analyzer); for (const auto &solution : solutions) { diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1539f598b1149..6b246afdc5071 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -8705,6 +8705,9 @@ namespace { return Action::SkipChildren(); } + NullablePtr + rewritePattern(Pattern *pattern, DeclContext *DC); + /// Rewrite the target, producing a new target. Optional rewriteTarget(SyntacticElementTarget target); @@ -8951,12 +8954,68 @@ static Expr *wrapAsyncLetInitializer( return resultInit; } +static Pattern *rewriteExprPattern(const SyntacticElementTarget &matchTarget, + Type patternTy, + RewriteTargetFn rewriteTarget) { + auto *EP = matchTarget.getExprPattern(); + + // See if we can simplify to another kind of pattern. + if (auto simplified = TypeChecker::trySimplifyExprPattern(EP, patternTy)) + return simplified.get(); + + auto resultTarget = rewriteTarget(matchTarget); + if (!resultTarget) + return nullptr; + + EP->setMatchExpr(resultTarget->getAsExpr()); + EP->getMatchVar()->setInterfaceType(patternTy->mapTypeOutOfContext()); + EP->setType(patternTy); + return EP; +} + +/// Attempt to rewrite either an ExprPattern, or a pattern that was solved as +/// an ExprPattern, e.g an EnumElementPattern that could not refer to an enum +/// case. +static Optional +tryRewriteExprPattern(Pattern *P, Solution &solution, Type patternTy, + RewriteTargetFn rewriteTarget) { + // See if we have a match expression target. + auto matchTarget = solution.getTargetFor(P); + if (!matchTarget) + return None; + + return rewriteExprPattern(*matchTarget, patternTy, rewriteTarget); +} + +NullablePtr ExprWalker::rewritePattern(Pattern *pattern, + DeclContext *DC) { + auto &solution = Rewriter.solution; + + // Figure out the pattern type. + auto patternTy = solution.getResolvedType(pattern); + patternTy = patternTy->reconstituteSugar(/*recursive=*/false); + + // Coerce the pattern to its appropriate type. + TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); + patternOptions |= TypeResolutionFlags::OverrideType; + + auto tryRewritePattern = [&](Pattern *EP, Type ty) { + return ::tryRewriteExprPattern( + EP, solution, ty, [&](auto target) { return rewriteTarget(target); }); + }; + + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + return TypeChecker::coercePatternToType(contextualPattern, patternTy, + patternOptions, tryRewritePattern); +} + /// Apply the given solution to the initialization target. /// /// \returns the resulting initialization expression. static Optional applySolutionToInitialization(Solution &solution, SyntacticElementTarget target, - Expr *initializer) { + Expr *initializer, + RewriteTargetFn rewriteTarget) { auto wrappedVar = target.getInitializationWrappedVar(); Type initType; if (wrappedVar) { @@ -9021,10 +9080,14 @@ applySolutionToInitialization(Solution &solution, SyntacticElementTarget target, finalPatternType = finalPatternType->reconstituteSugar(/*recursive =*/false); + auto tryRewritePattern = [&](Pattern *EP, Type ty) { + return ::tryRewriteExprPattern(EP, solution, ty, rewriteTarget); + }; + // Apply the solution to the pattern as well. auto contextualPattern = target.getContextualPattern(); if (auto coercedPattern = TypeChecker::coercePatternToType( - contextualPattern, finalPatternType, options)) { + contextualPattern, finalPatternType, options, tryRewritePattern)) { resultTarget.setPattern(coercedPattern); } else { return None; @@ -9171,10 +9234,15 @@ static Optional applySolutionToForEachStmt( TypeResolutionOptions options(TypeResolverContext::ForEachStmt); options |= TypeResolutionFlags::OverrideType; + auto tryRewritePattern = [&](Pattern *EP, Type ty) { + return ::tryRewriteExprPattern(EP, solution, ty, rewriteTarget); + }; + // Apply the solution to the pattern as well. auto contextualPattern = target.getContextualPattern(); auto coercedPattern = TypeChecker::coercePatternToType( - contextualPattern, forEachStmtInfo.initType, options); + contextualPattern, forEachStmtInfo.initType, options, + tryRewritePattern); if (!coercedPattern) return None; @@ -9262,7 +9330,8 @@ ExprWalker::rewriteTarget(SyntacticElementTarget target) { switch (target.getExprContextualTypePurpose()) { case CTP_Initialization: { auto initResultTarget = applySolutionToInitialization( - solution, target, rewrittenExpr); + solution, target, rewrittenExpr, + [&](auto target) { return rewriteTarget(target); }); if (!initResultTarget) return None; @@ -9353,47 +9422,11 @@ ExprWalker::rewriteTarget(SyntacticElementTarget target) { ConstraintSystem &cs = solution.getConstraintSystem(); auto info = *cs.getCaseLabelItemInfo(*caseLabelItem); - // Figure out the pattern type. - Type patternType = solution.simplifyType(solution.getType(info.pattern)); - patternType = patternType->reconstituteSugar(/*recursive=*/false); - - // Check whether this enum element is resolved via ~= application. - if (auto *enumElement = dyn_cast(info.pattern)) { - if (auto target = cs.getTargetFor(enumElement)) { - auto *EP = target->getExprPattern(); - auto enumType = solution.getResolvedType(EP); - - auto *matchCall = target->getAsExpr(); - - auto *result = matchCall->walk(*this); - if (!result) - return None; - - { - auto *matchVar = EP->getMatchVar(); - matchVar->setInterfaceType(enumType->mapTypeOutOfContext()); - } - - EP->setMatchExpr(result); - EP->setType(enumType); - - (*caseLabelItem)->setPattern(EP, /*resolved=*/true); - return target; - } - } - - // Coerce the pattern to its appropriate type. - TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); - patternOptions |= TypeResolutionFlags::OverrideType; - auto contextualPattern = - ContextualPattern::forRawPattern(info.pattern, - target.getDeclContext()); - if (auto coercedPattern = TypeChecker::coercePatternToType( - contextualPattern, patternType, patternOptions)) { - (*caseLabelItem)->setPattern(coercedPattern, /*resolved=*/true); - } else { + auto pattern = rewritePattern(info.pattern, target.getDeclContext()); + if (!pattern) return None; - } + + (*caseLabelItem)->setPattern(pattern.get(), /*resolved=*/true); // If there is a guard expression, coerce that. if (auto *guardExpr = info.guardExpr) { @@ -9461,8 +9494,13 @@ ExprWalker::rewriteTarget(SyntacticElementTarget target) { options |= TypeResolutionFlags::OverrideType; } + auto tryRewritePattern = [&](Pattern *EP, Type ty) { + return ::tryRewriteExprPattern( + EP, solution, ty, [&](auto target) { return rewriteTarget(target); }); + }; + if (auto coercedPattern = TypeChecker::coercePatternToType( - contextualPattern, patternType, options)) { + contextualPattern, patternType, options, tryRewritePattern)) { auto resultTarget = target; resultTarget.setPattern(coercedPattern); return resultTarget; diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index fc9b3cc28d9e3..c2ed909db3352 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -2715,6 +2715,19 @@ bool ContextualFailure::diagnoseAsError() { return false; } + case ConstraintLocator::EnumPatternImplicitCastMatch: { + // In this case, the types are reversed, as we are checking whether we + // can convert the pattern type to the context type. + std::swap(fromType, toType); + diagnostic = diag::cannot_match_value_with_pattern; + break; + } + + case ConstraintLocator::PatternMatch: { + diagnostic = diag::cannot_match_value_with_pattern; + break; + } + default: return false; } @@ -2927,9 +2940,11 @@ bool ContextualFailure::diagnoseConversionToNil() const { void ContextualFailure::tryFixIts(InFlightDiagnostic &diagnostic) const { auto *locator = getLocator(); // Can't apply any of the fix-its below if this failure - // is related to `inout` argument. - if (locator->isLastElement()) + // is related to `inout` argument, or a pattern mismatch. + if (locator->isLastElement() || + locator->isForPatternMatch()) { return; + } if (trySequenceSubsequenceFixIts(diagnostic)) return; @@ -3549,6 +3564,8 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, return diag::cannot_convert_discard_value; case CTP_CaseStmt: + return diag::cannot_match_value_with_pattern; + case CTP_ThrowStmt: case CTP_ForEachStmt: case CTP_ForEachSequence: @@ -4476,6 +4493,7 @@ bool InvalidMemberRefOnExistential::diagnoseAsError() { case AccessorKind::Set: case AccessorKind::WillSet: case AccessorKind::DidSet: + case AccessorKind::Init: // Ignore references to the 'newValue' or 'oldValue' parameters. if (AccessorParams.front() == PD) { return true; @@ -8756,7 +8774,7 @@ bool InvalidWeakAttributeUse::diagnoseAsError() { return false; auto *var = pattern->getDecl(); - auto varType = OptionalType::get(getType(var)); + auto varType = getType(var); auto diagnostic = emitDiagnosticAt(var, diag::invalid_ownership_not_optional, diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index efcfbc8123cd8..c30c94dbc2792 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2438,37 +2438,21 @@ namespace { /// \param locator The locator to use for generated constraints and /// type variables. /// - /// \param externalPatternType The type imposed by the enclosing pattern, - /// if any. This will be non-null in cases where there is, e.g., a - /// pattern such as "is SubClass". - /// /// \param bindPatternVarsOneWay When true, generate fresh type variables /// for the types of each variable declared within the pattern, along /// with a one-way constraint binding that to the type to which the /// variable will be ascribed or inferred. Type getTypeForPattern( Pattern *pattern, ConstraintLocatorBuilder locator, - Type externalPatternType, bool bindPatternVarsOneWay, PatternBindingDecl *patternBinding = nullptr, unsigned patternBindingIndex = 0) { - // If there's no pattern, then we have an unknown subpattern. Create a - // type variable. - if (!pattern) { - return CS.createTypeVariable(CS.getConstraintLocator(locator), - TVO_CanBindToNoEscape); - } + assert(pattern); // Local function that must be called for each "return" throughout this // function, to set the type of the pattern. auto setType = [&](Type type) { CS.setType(pattern, type); - if (auto PE = dyn_cast(pattern)) { - // Set the type of the pattern's sub-expression as well, so code - // completion can retrieve the expression's type in case it is a code - // completion token. - CS.setType(PE->getSubExpr(), type); - } return type; }; @@ -2476,33 +2460,18 @@ namespace { case PatternKind::Paren: { auto *paren = cast(pattern); - // Parentheses don't affect the canonical type, but record them as - // type sugar. - if (externalPatternType && - isa(externalPatternType.getPointer())) { - externalPatternType = cast(externalPatternType.getPointer()) - ->getUnderlyingType(); - } - auto *subPattern = paren->getSubPattern(); auto underlyingType = getTypeForPattern( subPattern, locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), - externalPatternType, bindPatternVarsOneWay); - - if (!underlyingType) - return Type(); + bindPatternVarsOneWay); return setType(ParenType::get(CS.getASTContext(), underlyingType)); } case PatternKind::Binding: { auto *subPattern = cast(pattern)->getSubPattern(); - auto type = getTypeForPattern(subPattern, locator, externalPatternType, + auto type = getTypeForPattern(subPattern, locator, bindPatternVarsOneWay); - - if (!type) - return Type(); - // Var doesn't affect the type. return setType(type); } @@ -2526,9 +2495,7 @@ namespace { locator.withPathElement(LocatorPathElt::PatternMatch(pattern)); // Always prefer a contextual type when it's available. - if (externalPatternType) { - type = externalPatternType; - } else if (auto *initializer = getInitializerExpr()) { + if (auto *initializer = getInitializerExpr()) { // For initialization always assume a type of initializer. type = CS.getType(initializer)->getRValueType(); } else { @@ -2608,18 +2575,13 @@ namespace { // diagnostic in the middle of the solver path. CS.addConstraint(ConstraintKind::OneWayEqual, oneWayVarType, - externalPatternType ? externalPatternType : varType, - locator); + varType, locator); } // Ascribe a type to the declaration so it's always available to // constraint system. if (oneWayVarType) { CS.setType(var, oneWayVarType); - } else if (externalPatternType) { - // If there is an externally imposed type, that's what the - // declaration is going to be bound to. - CS.setType(var, externalPatternType); } else { // Otherwise, let's use the type of the pattern. The type // of the declaration has to be r-value, so let's add an @@ -2691,9 +2653,6 @@ namespace { Type type = TypeChecker::typeCheckPattern(contextualPattern); - if (!type) - return Type(); - // Look through reference storage types. type = type->getReferenceStorageReferent(); @@ -2708,13 +2667,13 @@ namespace { Type subPatternType = getTypeForPattern( subPattern, locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), - openedType, bindPatternVarsOneWay); - - if (!subPatternType) - return Type(); + bindPatternVarsOneWay); + // NOTE: The order here is important! Pattern matching equality is + // not symmetric (we need to fix that either by using a different + // constraint, or actually making it symmetric). CS.addConstraint( - ConstraintKind::Conversion, subPatternType, openedType, + ConstraintKind::Equal, openedType, subPatternType, locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); // FIXME [OPAQUE SUPPORT]: the distinction between where we want opaque @@ -2728,41 +2687,16 @@ namespace { case PatternKind::Tuple: { auto tuplePat = cast(pattern); - // If there's an externally-imposed type, decompose it into element - // types so long as we have the right number of such types. - SmallVector externalEltTypes; - if (externalPatternType) { - decomposeTuple(externalPatternType, externalEltTypes); - - // If we have the wrong number of elements, we may not be able to - // provide more specific types. - if (tuplePat->getNumElements() != externalEltTypes.size()) { - externalEltTypes.clear(); - - // Implicit tupling. - if (tuplePat->getNumElements() == 1) { - externalEltTypes.push_back( - AnyFunctionType::Param(externalPatternType)); - } - } - } - SmallVector tupleTypeElts; tupleTypeElts.reserve(tuplePat->getNumElements()); for (unsigned i = 0, e = tuplePat->getNumElements(); i != e; ++i) { auto &tupleElt = tuplePat->getElement(i); - Type externalEltType; - if (!externalEltTypes.empty()) - externalEltType = externalEltTypes[i].getPlainType(); auto *eltPattern = tupleElt.getPattern(); Type eltTy = getTypeForPattern( eltPattern, locator.withPathElement(LocatorPathElt::PatternMatch(eltPattern)), - externalEltType, bindPatternVarsOneWay); - - if (!eltTy) - return Type(); + bindPatternVarsOneWay); tupleTypeElts.push_back(TupleTypeElt(eltTy, tupleElt.getLabel())); } @@ -2771,28 +2705,12 @@ namespace { } case PatternKind::OptionalSome: { - // Remove an optional from the object type. - if (externalPatternType) { - Type objVar = CS.createTypeVariable( - CS.getConstraintLocator( - locator.withPathElement(ConstraintLocator::OptionalPayload)), - TVO_CanBindToNoEscape); - CS.addConstraint( - ConstraintKind::OptionalObject, externalPatternType, objVar, - locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); - - externalPatternType = objVar; - } - auto *subPattern = cast(pattern)->getSubPattern(); // The subpattern must have optional type. Type subPatternType = getTypeForPattern( subPattern, locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), - externalPatternType, bindPatternVarsOneWay); - - if (!subPatternType) - return Type(); + bindPatternVarsOneWay); return setType(OptionalType::get(subPatternType)); } @@ -2803,22 +2721,6 @@ namespace { const Type castType = resolveTypeReferenceInExpression( isPattern->getCastTypeRepr(), TypeResolverContext::InExpression, locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); - if (!castType) return Type(); - - auto *subPattern = isPattern->getSubPattern(); - Type subPatternType = getTypeForPattern( - subPattern, - locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), - castType, bindPatternVarsOneWay); - - if (!subPatternType) - return Type(); - - // Make sure we can cast from the subpattern type to the type we're - // checking; if it's impossible, fail. - CS.addConstraint( - ConstraintKind::CheckedCast, subPatternType, castType, - locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); // Allow `is` pattern to infer type from context which is then going // to be propaged down to its sub-pattern via conversion. This enables @@ -2828,9 +2730,26 @@ namespace { auto isType = CS.createTypeVariable(CS.getConstraintLocator(pattern), TVO_CanBindToNoEscape | TVO_CanBindToHole); + + // Make sure we can cast from the subpattern type to the type we're + // checking; if it's impossible, fail. CS.addConstraint( - ConstraintKind::Conversion, subPatternType, isType, + ConstraintKind::CheckedCast, isType, castType, locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); + + if (auto *subPattern = isPattern->getSubPattern()) { + auto subPatternType = getTypeForPattern( + subPattern, + locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), + bindPatternVarsOneWay); + + // NOTE: The order here is important! Pattern matching equality is + // not symmetric (we need to fix that either by using a different + // constraint, or actually making it symmetric). + CS.addConstraint( + ConstraintKind::Equal, castType, subPatternType, + locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); + } return setType(isType); } @@ -2864,6 +2783,9 @@ namespace { if (dyn_cast_or_null(enumPattern->getSubPattern())) functionRefKind = FunctionRefKind::Compound; + auto patternLocator = + locator.withPathElement(LocatorPathElt::PatternMatch(pattern)); + if (enumPattern->getParentType() || enumPattern->getParentTypeRepr()) { // Resolve the parent type. const auto parentType = [&] { @@ -2889,42 +2811,25 @@ namespace { TypeResolverContext::InExpression, patternMatchLoc); }(); - if (!parentType) - return Type(); - // Perform member lookup into the parent's metatype. Type parentMetaType = MetatypeType::get(parentType); - CS.addValueMemberConstraint( - parentMetaType, enumPattern->getName(), memberType, CurDC, - functionRefKind, {}, - CS.getConstraintLocator(locator, - LocatorPathElt::PatternMatch(pattern))); + CS.addValueMemberConstraint(parentMetaType, enumPattern->getName(), + memberType, CurDC, functionRefKind, {}, + patternLocator); // Parent type needs to be convertible to the pattern type; this // accounts for cases where the pattern type is existential. CS.addConstraint( ConstraintKind::Conversion, parentType, patternType, - locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); + patternLocator.withPathElement( + ConstraintLocator::EnumPatternImplicitCastMatch)); baseType = parentType; - // Perform member lookup into the external pattern metatype. e.g. - // `case let .test(tuple) as Test`. - } else if (externalPatternType) { - Type externalMetaType = MetatypeType::get(externalPatternType); - - CS.addValueMemberConstraint( - externalMetaType, enumPattern->getName(), memberType, CurDC, - functionRefKind, {}, - CS.getConstraintLocator(locator, - LocatorPathElt::PatternMatch(pattern))); - - baseType = externalPatternType; } else { // Use the pattern type for member lookup. CS.addUnresolvedValueMemberConstraint( MetatypeType::get(patternType), enumPattern->getName(), - memberType, CurDC, functionRefKind, - locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); + memberType, CurDC, functionRefKind, patternLocator); baseType = patternType; } @@ -2936,10 +2841,7 @@ namespace { Type subPatternType = getTypeForPattern( subPattern, locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), - Type(), bindPatternVarsOneWay); - - if (!subPatternType) - return Type(); + bindPatternVarsOneWay); SmallVector params; decomposeTuple(subPatternType, params); @@ -2957,28 +2859,29 @@ namespace { // FIXME: Verify ExtInfo state is correct, not working by accident. FunctionType::ExtInfo info; Type functionType = FunctionType::get(params, outputType, info); - // TODO: Convert to FunctionInput/FunctionResult constraints. - CS.addConstraint( - ConstraintKind::Equal, functionType, memberType, - locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); - CS.addConstraint( - ConstraintKind::Conversion, outputType, baseType, - locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); + // TODO: Convert to own constraint? Note that ApplicableFn isn't quite + // right, as pattern matching has data flowing *into* the apply result + // and call arguments, not the other way around. + // NOTE: The order here is important! Pattern matching equality is + // not symmetric (we need to fix that either by using a different + // constraint, or actually making it symmetric). + CS.addConstraint(ConstraintKind::Equal, functionType, memberType, + patternLocator); + + CS.addConstraint(ConstraintKind::Conversion, outputType, baseType, + patternLocator); } return setType(patternType); } - // Refutable patterns occur when checking the PatternBindingDecls in an - // if/let or while/let condition. They always require an initial value, - // so they always allow unspecified types. - case PatternKind::Expr: - // TODO: we could try harder here, e.g. for enum elements to provide the - // enum type. - return setType( - CS.createTypeVariable(CS.getConstraintLocator(locator), - TVO_CanBindToNoEscape | TVO_CanBindToHole)); + case PatternKind::Expr: { + // We generate constraints for ExprPatterns in a separate pass. For + // now, just create a type variable. + return setType(CS.createTypeVariable(CS.getConstraintLocator(locator), + TVO_CanBindToNoEscape)); + } } llvm_unreachable("Unhandled pattern kind"); @@ -4421,16 +4324,18 @@ bool ConstraintSystem::generateWrappedPropertyTypeConstraints( static bool generateInitPatternConstraints(ConstraintSystem &cs, SyntacticElementTarget target, Expr *initializer) { - auto pattern = target.getInitializationPattern(); auto locator = cs.getConstraintLocator( initializer, LocatorPathElt::ContextualType(CTP_Initialization)); - Type patternType = cs.generateConstraints( - pattern, locator, target.shouldBindPatternVarsOneWay(), - target.getInitializationPatternBindingDecl(), - target.getInitializationPatternBindingIndex()); - if (!patternType) - return true; + Type patternType; + if (auto pattern = target.getInitializationPattern()) { + patternType = cs.generateConstraints( + pattern, locator, target.shouldBindPatternVarsOneWay(), + target.getInitializationPatternBindingDecl(), + target.getInitializationPatternBindingIndex()); + } else { + patternType = cs.createTypeVariable(locator, TVO_CanBindToNoEscape); + } if (auto wrappedVar = target.getInitializationWrappedVar()) return cs.generateWrappedPropertyTypeConstraints( @@ -4845,8 +4750,20 @@ Type ConstraintSystem::generateConstraints( bool bindPatternVarsOneWay, PatternBindingDecl *patternBinding, unsigned patternIndex) { ConstraintGenerator cg(*this, nullptr); - return cg.getTypeForPattern(pattern, locator, Type(), bindPatternVarsOneWay, - patternBinding, patternIndex); + auto ty = cg.getTypeForPattern(pattern, locator, bindPatternVarsOneWay, + patternBinding, patternIndex); + assert(ty); + + // Gather the ExprPatterns, and form a conjunction for their expressions. + SmallVector exprPatterns; + pattern->forEachNode([&](Pattern *P) { + if (auto *EP = dyn_cast(P)) + exprPatterns.push_back(EP); + }); + if (!exprPatterns.empty()) + generateConstraints(exprPatterns, getConstraintLocator(pattern)); + + return ty; } bool ConstraintSystem::generateConstraints(StmtCondition condition, diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 553054a4f23a8..da48e9cd8286a 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2165,7 +2165,9 @@ class TupleMatcher { const auto &elt2 = tuple2->getElement(i); if (inPatternMatchingContext) { - if (elt1.hasName() && elt1.getName() != elt2.getName()) + // FIXME: The fact that this isn't symmetric is wrong since this logic + // is called for bind and equal constraints... + if (elt2.hasName() && elt1.getName() != elt2.getName()) return true; } else { // If the names don't match, we have a conflict. @@ -3506,21 +3508,12 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, // FIXME: We should check value ownership too, but it's not completely // trivial because of inout-to-pointer conversions. - // For equality contravariance doesn't matter, but let's make sure - // that types are matched in original order because that is important - // when function types are equated as part of pattern matching. - auto paramType1 = kind == ConstraintKind::Equal ? func1Param.getOldType() - : func2Param.getOldType(); - - auto paramType2 = kind == ConstraintKind::Equal ? func2Param.getOldType() - : func1Param.getOldType(); - - // Compare the parameter types. - auto result = matchTypes(paramType1, paramType2, subKind, subflags, - (func1Params.size() == 1 - ? argumentLocator - : argumentLocator.withPathElement( - LocatorPathElt::TupleElement(i)))); + // Compare the parameter types, taking contravariance into account. + auto result = matchTypes( + func2Param.getOldType(), func1Param.getOldType(), subKind, subflags, + (func1Params.size() == 1 ? argumentLocator + : argumentLocator.withPathElement( + LocatorPathElt::TupleElement(i)))); if (result.isFailure()) return result; } @@ -6350,8 +6343,20 @@ bool ConstraintSystem::repairFailures( break; } + case ConstraintLocator::EnumPatternImplicitCastMatch: { + // If either type is a placeholder, consider this fixed. + if (lhs->isPlaceholder() || rhs->isPlaceholder()) + return true; + + conversionsOrFixes.push_back(ContextualMismatch::create( + *this, lhs, rhs, getConstraintLocator(locator))); + break; + } + case ConstraintLocator::PatternMatch: { auto *pattern = elt.castTo().getPattern(); + + // TODO: We ought to introduce a new locator element for this. bool isMemberMatch = lhs->is() && isa(pattern); @@ -6366,13 +6371,12 @@ bool ConstraintSystem::repairFailures( if (lhs->isPlaceholder() || rhs->isPlaceholder()) return true; - // If member reference didn't match expected pattern, - // let's consider that a contextual mismatch. if (isMemberMatch) { recordAnyTypeVarAsPotentialHole(lhs); recordAnyTypeVarAsPotentialHole(rhs); conversionsOrFixes.push_back(AllowAssociatedValueMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); + break; } // `weak` declaration with an explicit non-optional type e.g. @@ -6385,7 +6389,7 @@ bool ConstraintSystem::repairFailures( if (auto *OA = var->getAttrs().getAttribute()) ROK = OA->get(); - if (!rhs->getOptionalObjectType() && + if (!lhs->getOptionalObjectType() && optionalityOf(ROK) == ReferenceOwnershipOptionality::Required) { conversionsOrFixes.push_back( AllowNonOptionalWeak::create(*this, getConstraintLocator(NP))); @@ -6394,6 +6398,9 @@ bool ConstraintSystem::repairFailures( } } + conversionsOrFixes.push_back(ContextualMismatch::create( + *this, lhs, rhs, getConstraintLocator(locator))); + break; } @@ -14816,8 +14823,10 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( if (recordFix(fix)) return SolutionKind::Error; - (void)matchTypes(type1, OptionalType::get(type2), - ConstraintKind::Conversion, + // NOTE: The order here is important! Pattern matching equality is + // not symmetric (we need to fix that either by using a different + // constraint, or actually making it symmetric). + (void)matchTypes(OptionalType::get(type1), type2, ConstraintKind::Equal, TypeMatchFlags::TMF_ApplyingFix, locator); return SolutionKind::Solved; diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index e80def2f385f1..97dcdef70744b 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -195,6 +195,7 @@ Solution ConstraintSystem::finalize() { solution.targets = targets; solution.caseLabelItems = caseLabelItems; + solution.exprPatterns = exprPatterns; solution.isolatedParams.append(isolatedParams.begin(), isolatedParams.end()); solution.preconcurrencyClosures.append(preconcurrencyClosures.begin(), preconcurrencyClosures.end()); @@ -327,6 +328,9 @@ void ConstraintSystem::applySolution(const Solution &solution) { isolatedParams.insert(param); } + for (auto &pair : solution.exprPatterns) + exprPatterns.insert(pair); + for (auto closure : solution.preconcurrencyClosures) { preconcurrencyClosures.insert(closure); } @@ -621,6 +625,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numContextualTypes = cs.contextualTypes.size(); numTargets = cs.targets.size(); numCaseLabelItems = cs.caseLabelItems.size(); + numExprPatterns = cs.exprPatterns.size(); numIsolatedParams = cs.isolatedParams.size(); numPreconcurrencyClosures = cs.preconcurrencyClosures.size(); numImplicitValueConversions = cs.ImplicitValueConversions.size(); @@ -737,6 +742,9 @@ ConstraintSystem::SolverScope::~SolverScope() { // Remove any case label item infos. truncate(cs.caseLabelItems, numCaseLabelItems); + // Remove any ExprPattern mappings. + truncate(cs.exprPatterns, numExprPatterns); + // Remove any isolated parameters. truncate(cs.isolatedParams, numIsolatedParams); diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 43e0ac33a50c9..8fdec6b319a5d 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -410,7 +410,7 @@ ElementInfo makeJoinElement(ConstraintSystem &cs, TypeJoinExpr *join, struct SyntacticElementContext : public llvm::PointerUnion { + SingleValueStmtExpr *, ExprPattern *> { // Inherit the constructors from PointerUnion. using PointerUnion::PointerUnion; @@ -441,6 +441,10 @@ struct SyntacticElementContext return context; } + static SyntacticElementContext forExprPattern(ExprPattern *EP) { + return SyntacticElementContext{EP}; + } + DeclContext *getAsDeclContext() const { if (auto *fn = this->dyn_cast()) { return fn; @@ -448,6 +452,8 @@ struct SyntacticElementContext return closure; } else if (auto *SVE = dyn_cast()) { return SVE->getDeclContext(); + } else if (auto *EP = dyn_cast()) { + return EP->getDeclContext(); } else { llvm_unreachable("unsupported kind"); } @@ -519,7 +525,32 @@ class SyntacticElementConstraintGenerator ConstraintLocator *locator) : cs(cs), context(context), locator(locator) {} - void visitPattern(Pattern *pattern, ContextualTypeInfo context) { + void visitExprPattern(ExprPattern *EP) { + auto target = SyntacticElementTarget::forExprPattern(EP); + + if (cs.preCheckTarget(target, /*replaceInvalidRefWithErrors=*/true, + /*leaveClosureBodyUnchecked=*/false)) { + hadError = true; + return; + } + cs.setType(EP->getMatchVar(), cs.getType(EP)); + + if (cs.generateConstraints(target)) { + hadError = true; + return; + } + cs.setTargetFor(EP, target); + cs.setExprPatternFor(EP->getSubExpr(), EP); + } + + void visitPattern(Pattern *pattern, ContextualTypeInfo contextInfo) { + if (context.is()) { + // This is for an ExprPattern conjunction, go ahead and generate + // constraints for the match expression. + visitExprPattern(cast(pattern)); + return; + } + auto parentElement = locator->getLastElementAs(); @@ -535,7 +566,7 @@ class SyntacticElementConstraintGenerator } if (isa(stmt)) { - visitCaseItemPattern(pattern, context); + visitCaseItemPattern(pattern, contextInfo); return; } } @@ -626,8 +657,11 @@ class SyntacticElementConstraintGenerator // Convert the contextual type to the pattern, which establishes the // bindings. - cs.addConstraint(ConstraintKind::Conversion, context.getType(), patternType, - locator); + auto *loc = cs.getConstraintLocator( + locator, {LocatorPathElt::PatternMatch(pattern), + LocatorPathElt::ContextualType(context.purpose)}); + cs.addConstraint(ConstraintKind::Equal, context.getType(), patternType, + loc); // For any pattern variable that has a parent variable (i.e., another // pattern variable with the same name in the same case), require that @@ -1438,6 +1472,24 @@ bool ConstraintSystem::generateConstraints(SingleValueStmtExpr *E) { return generator.hadError; } +void ConstraintSystem::generateConstraints(ArrayRef exprPatterns, + ConstraintLocatorBuilder locator) { + // Form a conjunction of ExprPattern elements, isolated from the rest of the + // pattern. + SmallVector elements; + SmallVector referencedTypeVars; + for (auto *EP : exprPatterns) { + auto ty = getType(EP)->castTo(); + referencedTypeVars.push_back(ty); + + ContextualTypeInfo context(ty, CTP_ExprPattern); + elements.push_back(makeElement(EP, getConstraintLocator(EP), context)); + } + auto *loc = getConstraintLocator(locator); + createConjunction(*this, elements, loc, /*isIsolated*/ true, + referencedTypeVars); +} + bool ConstraintSystem::isInResultBuilderContext(ClosureExpr *closure) const { if (!closure->hasSingleExpressionBody()) { auto *DC = closure->getParent(); @@ -1488,6 +1540,8 @@ ConstraintSystem::simplifySyntacticElementConstraint( context = SyntacticElementContext::forFunction(fn); } else if (auto *SVE = getAsExpr(anchor)) { context = SyntacticElementContext::forSingleValueStmtExpr(SVE); + } else if (auto *EP = getAsPattern(anchor)) { + context = SyntacticElementContext::forExprPattern(EP); } else { return SolutionKind::Error; } diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 4e3ca4c3b4ce4..26e6a088d0cea 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -144,6 +144,12 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var, if (!var->getParentPattern()->getSingleVar()) return; + // FIXME: Don't attempt to synthesize default arguments for init + // accessor properties because there could be multiple properties + // with default values they are going to initialize. + if (var->getAccessor(AccessorKind::Init)) + return; + // Whether we have explicit initialization. bool isExplicitlyInitialized = false; if (auto pbd = var->getParentPatternBinding()) { @@ -294,6 +300,12 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, if (ICK == ImplicitConstructorKind::Memberwise) { assert(isa(decl) && "Only struct have memberwise constructor"); + std::multimap initializedViaAccessor; + decl->collectPropertiesInitializableByInitAccessors(initializedViaAccessor); + + // A single property could be used to initialize N other stored + // properties via a call to its init accessor. + llvm::SmallPtrSet usedInitProperties; for (auto member : decl->getMembers()) { auto var = dyn_cast(member); if (!var) @@ -302,6 +314,20 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) continue; + // Check whether this property could be initialized via init accessor. + // + // Note that we check for a single match here because intersecting + // properties are going to be diagnosed. + if (initializedViaAccessor.count(var) == 1) { + auto *initializerProperty = initializedViaAccessor.find(var)->second; + // Parameter for this property is already emitted. + if (usedInitProperties.count(initializerProperty)) + continue; + + var = initializerProperty; + usedInitProperties.insert(initializerProperty); + } + accessLevel = std::min(accessLevel, var->getFormalAccess()); params.push_back(createMemberwiseInitParameter(decl, Loc, var)); diff --git a/lib/Sema/CompletionContextFinder.cpp b/lib/Sema/CompletionContextFinder.cpp index f4ef0f7a9336f..9279a038d160a 100644 --- a/lib/Sema/CompletionContextFinder.cpp +++ b/lib/Sema/CompletionContextFinder.cpp @@ -12,10 +12,19 @@ #include "swift/Sema/CompletionContextFinder.h" #include "swift/Parse/Lexer.h" +#include "swift/Sema/SyntacticElementTarget.h" using namespace swift; +using namespace constraints; using Fallback = CompletionContextFinder::Fallback; +CompletionContextFinder::CompletionContextFinder( + SyntacticElementTarget target, DeclContext *DC) + : InitialExpr(target.getAsExpr()), InitialDC(DC) { + assert(DC); + target.walk(*this); +} + ASTWalker::PreWalkResult CompletionContextFinder::walkToExprPre(Expr *E) { if (auto *closure = dyn_cast(E)) { diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index e3fe38458601b..4987c3a47cb21 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -90,6 +90,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::ImplicitCallAsFunction: case ConstraintLocator::TernaryBranch: case ConstraintLocator::PatternMatch: + case ConstraintLocator::EnumPatternImplicitCastMatch: case ConstraintLocator::ArgumentAttribute: case ConstraintLocator::UnresolvedMemberChainResult: case ConstraintLocator::PlaceholderType: @@ -403,6 +404,10 @@ void LocatorPathElt::dump(raw_ostream &out) const { out << "pattern match"; break; + case ConstraintLocator::EnumPatternImplicitCastMatch: + out << "enum pattern implicit cast match"; + break; + case ConstraintLocator::ArgumentAttribute: { using AttrLoc = LocatorPathElt::ArgumentAttribute; @@ -677,6 +682,10 @@ NullablePtr ConstraintLocator::getPatternMatch() const { return matchElt->getPattern(); } +bool ConstraintLocator::isForPatternMatch() const { + return getPatternMatch() != nullptr; +} + bool ConstraintLocator::isMemberRef() const { if (isLastElement()) { return true; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 36b34ccbd4c84..10563c1fe10f8 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -5824,6 +5824,11 @@ void constraints::simplifyLocator(ASTNode &anchor, continue; } + case ConstraintLocator::EnumPatternImplicitCastMatch: { + path = path.slice(1); + continue; + } + case ConstraintLocator::PackType: case ConstraintLocator::ParentType: case ConstraintLocator::KeyPathType: diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 7152cfae928d3..4b17e5bfb1859 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -4586,6 +4586,7 @@ class ObjCSelectorWalker : public ASTWalker { case AccessorKind::MutableAddress: case AccessorKind::Read: case AccessorKind::Modify: + case AccessorKind::Init: llvm_unreachable("cannot be @objc"); } } else { diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 27e5f5ecc6081..ae66572e3f267 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -294,6 +294,8 @@ class AttributeChecker : public AttributeVisitor { void visitDiscardableResultAttr(DiscardableResultAttr *attr); void visitDynamicReplacementAttr(DynamicReplacementAttr *attr); void visitTypeEraserAttr(TypeEraserAttr *attr); + void visitInitializesAttr(InitializesAttr *attr); + void visitAccessesAttr(AccessesAttr *attr); void visitImplementsAttr(ImplementsAttr *attr); void visitNoMetadataAttr(NoMetadataAttr *attr); @@ -3548,6 +3550,44 @@ void AttributeChecker::visitTypeEraserAttr(TypeEraserAttr *attr) { (void)attr->hasViableTypeEraserInit(cast(D)); } +void AttributeChecker::visitInitializesAttr(InitializesAttr *attr) { + auto *accessor = dyn_cast(D); + if (!accessor || accessor->getAccessorKind() != AccessorKind::Init) { + diagnose(attr->getLocation(), + diag::init_accessor_initializes_attribute_on_other_declaration); + return; + } + + (void)attr->getPropertyDecls(accessor); +} + +void AttributeChecker::visitAccessesAttr(AccessesAttr *attr) { + auto *accessor = dyn_cast(D); + if (!accessor || accessor->getAccessorKind() != AccessorKind::Init) { + diagnose(attr->getLocation(), + diag::init_accessor_accesses_attribute_on_other_declaration); + return; + } + + // Check whether there are any intersections between initializes(...) and + // accesses(...) attributes. + + Optional> initializedProperties; + if (auto *initAttr = D->getAttrs().getAttribute()) { + initializedProperties.emplace(initAttr->getPropertyDecls(accessor)); + } + + if (initializedProperties) { + for (auto *property : attr->getPropertyDecls(accessor)) { + if (llvm::is_contained(*initializedProperties, property)) { + diagnose(attr->getLocation(), + diag::init_accessor_property_both_init_and_accessed, + property->getName()); + } + } + } +} + void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) { DeclContext *DC = D->getDeclContext(); @@ -4901,6 +4941,8 @@ static DescriptiveDeclKind getAccessorDescriptiveDeclKind(AccessorKind kind) { return DescriptiveDeclKind::Addressor; case AccessorKind::MutableAddress: return DescriptiveDeclKind::MutableAddressor; + case AccessorKind::Init: + return DescriptiveDeclKind::InitAccessor; } } @@ -7511,3 +7553,65 @@ void TypeChecker::checkReflectionMetadataAttributes(ExtensionDecl *ED) { }); } } + +ArrayRef InitAccessorReferencedVariablesRequest::evaluate( + Evaluator &evaluator, DeclAttribute *attr, AccessorDecl *attachedTo, + ArrayRef referencedVars) const { + auto &ctx = attachedTo->getASTContext(); + + auto *storage = attachedTo->getStorage(); + + auto typeDC = storage->getDeclContext()->getSelfNominalTypeDecl(); + if (!typeDC) + return ctx.AllocateCopy(ArrayRef()); + + SmallVector results; + + bool failed = false; + for (auto name : referencedVars) { + auto propertyResults = typeDC->lookupDirect(DeclName(name)); + switch (propertyResults.size()) { + case 0: { + ctx.Diags.diagnose(attr->getLocation(), diag::cannot_find_type_in_scope, + DeclNameRef(name)); + failed = true; + break; + } + + case 1: { + auto *member = propertyResults.front(); + + // Only stored properties are supported. + if (auto *var = dyn_cast(member)) { + if (var->getImplInfo().hasStorage()) { + results.push_back(var); + break; + } + } + + ctx.Diags.diagnose(attr->getLocation(), + diag::init_accessor_can_refer_only_to_properties, + member->getDescriptiveKind(), member->createNameRef()); + failed = true; + break; + } + + default: + ctx.Diags.diagnose(attr->getLocation(), + diag::ambiguous_member_overload_set, + DeclNameRef(name)); + + for (auto *choice : propertyResults) { + ctx.Diags.diagnose(choice, diag::decl_declared_here, choice->getName()); + } + + failed = true; + break; + } + } + + if (failed) + return ctx.AllocateCopy(ArrayRef()); + + return ctx.AllocateCopy(results); +} diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index e93d22a6f06ca..58acb0c1e1965 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -574,15 +574,12 @@ bool TypeChecker::typeCheckForCodeCompletion( return false; } - auto node = target.getAsASTNode(); - if (!node) - return false; - - if (auto *expr = getAsExpr(node)) { - node = expr->walk(SanitizeExpr(Context)); + if (getAsExpr(target.getAsASTNode())) { + SanitizeExpr sanitizer(Context); + target = *target.walk(sanitizer); } - CompletionContextFinder contextAnalyzer(node, DC); + CompletionContextFinder contextAnalyzer(target, DC); // If there was no completion expr (e.g. if the code completion location was // among tokens that were skipped over during parser error recovery) bail. diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 2ab576ddda04e..dd0f7655608e3 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -373,6 +373,8 @@ static bool doesAccessorNeedDynamicAttribute(AccessorDecl *accessor) { storage->getWriteImpl() == WriteImplKind::StoredWithObservers) return storage->isDynamic(); return false; + case AccessorKind::Init: + return false; } llvm_unreachable("covered switch"); } @@ -1693,6 +1695,7 @@ SelfAccessKindRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const { case AccessorKind::Read: break; + case AccessorKind::Init: case AccessorKind::MutableAddress: case AccessorKind::Set: case AccessorKind::Modify: @@ -1784,6 +1787,7 @@ static ParamDecl *getOriginalParamFromAccessor(AbstractStorageDecl *storage, case AccessorKind::DidSet: case AccessorKind::WillSet: case AccessorKind::Set: + case AccessorKind::Init: if (param == accessorParams->get(0)) { // This is the 'newValue' or 'oldValue' parameter. return nullptr; @@ -2118,6 +2122,7 @@ ResultTypeRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { case AccessorKind::DidSet: case AccessorKind::WillSet: case AccessorKind::Set: + case AccessorKind::Init: return TupleType::getEmpty(ctx); // Addressor result types can get complicated because of the owner. diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 09501281c6aaa..2a0972ab954d7 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -717,6 +717,13 @@ bool swift::isRepresentableInObjC( .limitBehavior(behavior); Reason.describe(accessor); return false; + + case AccessorKind::Init: + diagnoseAndRemoveAttr(accessor, Reason.getAttr(), + diag::objc_init_accessor) + .limitBehavior(behavior); + Reason.describe(accessor); + return false; } llvm_unreachable("bad kind"); } @@ -1402,6 +1409,7 @@ Optional shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) { case AccessorKind::Modify: case AccessorKind::Read: case AccessorKind::WillSet: + case AccessorKind::Init: return false; case AccessorKind::MutableAddress: diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 3c341969b2f8f..d4aff7bc2bffc 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1581,6 +1581,8 @@ namespace { UNINTERESTING_ATTR(ObjCMembers) UNINTERESTING_ATTR(ObjCRuntimeName) UNINTERESTING_ATTR(RestatedObjCConformance) + UNINTERESTING_ATTR(Initializes) + UNINTERESTING_ATTR(Accesses) UNINTERESTING_ATTR(Implements) UNINTERESTING_ATTR(StaticInitializeObjCMetadata) UNINTERESTING_ATTR(ClangImporterSynthesizedType) @@ -2277,6 +2279,7 @@ OverriddenDeclsRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { case AccessorKind::DidSet: case AccessorKind::Address: case AccessorKind::MutableAddress: + case AccessorKind::Init: // These accessors are never part of the opaque set. Bail out early // to avoid computing the overridden declarations of the storage. return noResults; diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index f524ed70dcdd8..643b13afd8881 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -472,7 +472,6 @@ static void checkGenericParams(GenericContext *ownerCtx) { return; auto *decl = ownerCtx->getAsDecl(); - bool isGenericType = isa(decl); bool hasPack = false; for (auto gp : *genericParams) { @@ -1850,7 +1849,7 @@ class DeclChecker : public DeclVisitor { if (!isa(decl->getDeclContext())) { decl->visitAuxiliaryDecls([&](Decl *auxiliaryDecl) { this->visit(auxiliaryDecl); - }); + }, /*visitFreestandingExpanded=*/false); } if (auto *Stats = getASTContext().Stats) @@ -2061,13 +2060,29 @@ class DeclChecker : public DeclVisitor { break; } } + + // If the macro has a (non-Void) result type, it must have the freestanding + // expression role. Other roles cannot have result types. + if (auto resultTypeRepr = MD->getResultTypeRepr()) { + if (!MD->getMacroRoles().contains(MacroRole::Expression) && + !MD->getResultInterfaceType()->isEqual(Ctx.getVoidType())) { + auto resultType = MD->getResultInterfaceType(); + Ctx.Diags.diagnose( + MD->arrowLoc, diag::macro_result_type_cannot_be_used, resultType) + .highlight(resultTypeRepr->getSourceRange()); + Ctx.Diags.diagnose(MD->arrowLoc, diag::macro_make_freestanding_expression) + .fixItInsert(MD->getAttributeInsertionLoc(false), + "@freestanding(expression)\n"); + Ctx.Diags.diagnose(MD->arrowLoc, diag::macro_remove_result_type) + .fixItRemove(SourceRange(MD->arrowLoc, resultTypeRepr->getEndLoc())); + } + } } void visitMacroExpansionDecl(MacroExpansionDecl *MED) { // Assign a discriminator. (void)MED->getDiscriminator(); - // Decls in expansion already visited as auxiliary decls. - MED->forEachExpandedExprOrStmt([&](ASTNode node) { + MED->forEachExpandedNode([&](ASTNode node) { TypeChecker::typeCheckASTNode(node, MED->getDeclContext()); }); } diff --git a/lib/Sema/TypeCheckMacros.cpp b/lib/Sema/TypeCheckMacros.cpp index 54e3a62a3a3f8..6fb677cbefcc7 100644 --- a/lib/Sema/TypeCheckMacros.cpp +++ b/lib/Sema/TypeCheckMacros.cpp @@ -39,6 +39,7 @@ #include "swift/Demangling/Demangler.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/Parse/Lexer.h" +#include "swift/Sema/IDETypeChecking.h" #include "swift/Subsystems.h" #include "llvm/Config/config.h" @@ -861,7 +862,8 @@ createMacroSourceFile(std::unique_ptr buffer, /// Evaluate the given freestanding macro expansion. static SourceFile * -evaluateFreestandingMacro(FreestandingMacroExpansion *expansion) { +evaluateFreestandingMacro(FreestandingMacroExpansion *expansion, + StringRef discriminatorStr = "") { auto *dc = expansion->getDeclContext(); ASTContext &ctx = dc->getASTContext(); SourceLoc loc = expansion->getPoundLoc(); @@ -889,6 +891,8 @@ evaluateFreestandingMacro(FreestandingMacroExpansion *expansion) { /// The discriminator used for the macro. LazyValue discriminator([&]() -> std::string { + if (!discriminatorStr.empty()) + return discriminatorStr.str(); #if SWIFT_SWIFT_PARSER Mangle::ASTMangler mangler; return mangler.mangleMacroExpansion(expansion); @@ -983,7 +987,7 @@ evaluateFreestandingMacro(FreestandingMacroExpansion *expansion) { } Optional swift::expandMacroExpr(MacroExpansionExpr *mee) { - SourceFile *macroSourceFile = evaluateFreestandingMacro(mee); + SourceFile *macroSourceFile = ::evaluateFreestandingMacro(mee); if (!macroSourceFile) return None; @@ -1043,7 +1047,7 @@ Optional swift::expandMacroExpr(MacroExpansionExpr *mee) { /// Expands the given macro expansion declaration. Optional swift::expandFreestandingMacro(MacroExpansionDecl *med) { - SourceFile *macroSourceFile = evaluateFreestandingMacro(med); + SourceFile *macroSourceFile = ::evaluateFreestandingMacro(med); if (!macroSourceFile) return None; @@ -1065,9 +1069,10 @@ swift::expandFreestandingMacro(MacroExpansionDecl *med) { return *macroSourceFile->getBufferID(); } -static SourceFile * -evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, CustomAttr *attr, - bool passParentContext, MacroRole role) { +static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, + CustomAttr *attr, + bool passParentContext, MacroRole role, + StringRef discriminatorStr = "") { DeclContext *dc; if (role == MacroRole::Peer) { dc = attachedTo->getDeclContext(); @@ -1117,6 +1122,8 @@ evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, CustomAttr *attr, /// The discriminator used for the macro. LazyValue discriminator([&]() -> std::string { + if (!discriminatorStr.empty()) + return discriminatorStr.str(); #if SWIFT_SWIFT_PARSER Mangle::ASTMangler mangler; return mangler.mangleAttachedMacroExpansion(attachedTo, attr, role); @@ -1228,9 +1235,9 @@ Optional swift::expandAccessors( AbstractStorageDecl *storage, CustomAttr *attr, MacroDecl *macro ) { // Evaluate the macro. - auto macroSourceFile = evaluateAttachedMacro(macro, storage, attr, - /*passParentContext*/false, - MacroRole::Accessor); + auto macroSourceFile = + ::evaluateAttachedMacro(macro, storage, attr, + /*passParentContext=*/false, MacroRole::Accessor); if (!macroSourceFile) return None; @@ -1279,9 +1286,9 @@ ArrayRef ExpandAccessorMacros::evaluate( Optional swift::expandAttributes(CustomAttr *attr, MacroDecl *macro, Decl *member) { // Evaluate the macro. - auto macroSourceFile = evaluateAttachedMacro(macro, member, attr, - /*passParentContext*/true, - MacroRole::MemberAttribute); + auto macroSourceFile = ::evaluateAttachedMacro(macro, member, attr, + /*passParentContext=*/true, + MacroRole::MemberAttribute); if (!macroSourceFile) return None; @@ -1304,9 +1311,9 @@ swift::expandAttributes(CustomAttr *attr, MacroDecl *macro, Decl *member) { Optional swift::expandMembers(CustomAttr *attr, MacroDecl *macro, Decl *decl) { // Evaluate the macro. - auto macroSourceFile = evaluateAttachedMacro(macro, decl, attr, - /*passParentContext*/false, - MacroRole::Member); + auto macroSourceFile = + ::evaluateAttachedMacro(macro, decl, attr, + /*passParentContext=*/false, MacroRole::Member); if (!macroSourceFile) return None; @@ -1331,9 +1338,9 @@ swift::expandMembers(CustomAttr *attr, MacroDecl *macro, Decl *decl) { Optional swift::expandPeers(CustomAttr *attr, MacroDecl *macro, Decl *decl) { - auto macroSourceFile = evaluateAttachedMacro(macro, decl, attr, - /*passParentContext*/false, - MacroRole::Peer); + auto macroSourceFile = + ::evaluateAttachedMacro(macro, decl, attr, + /*passParentContext=*/false, MacroRole::Peer); if (!macroSourceFile) return None; @@ -1357,10 +1364,9 @@ ExpandConformanceMacros::evaluate(Evaluator &evaluator, Optional swift::expandConformances(CustomAttr *attr, MacroDecl *macro, NominalTypeDecl *nominal) { - auto macroSourceFile = - evaluateAttachedMacro(macro, nominal, attr, - /*passParentContext*/false, - MacroRole::Conformance); + auto macroSourceFile = ::evaluateAttachedMacro(macro, nominal, attr, + /*passParentContext=*/false, + MacroRole::Conformance); if (!macroSourceFile) return None; @@ -1447,3 +1453,19 @@ ConcreteDeclRef ResolveMacroRequest::evaluate(Evaluator &evaluator, return macroExpansion->getMacroRef(); } + +// MARK: for IDE. + +SourceFile *swift::evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, + CustomAttr *attr, + bool passParentContext, MacroRole role, + StringRef discriminator) { + return ::evaluateAttachedMacro(macro, attachedTo, attr, passParentContext, + role, discriminator); +} + +SourceFile * +swift::evaluateFreestandingMacro(FreestandingMacroExpansion *expansion, + StringRef discriminator) { + return ::evaluateFreestandingMacro(expansion, discriminator); +} diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 946e86da0d858..69260e9a0975e 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -1034,15 +1034,61 @@ void repairTupleOrAssociatedValuePatternIfApplicable( enumCase->getName()); } +NullablePtr TypeChecker::trySimplifyExprPattern(ExprPattern *EP, + Type patternTy) { + auto *subExpr = EP->getSubExpr(); + auto &ctx = EP->getDeclContext()->getASTContext(); + + if (patternTy->isBool()) { + // The type is Bool. + // Check if the pattern is a Bool literal + auto *semanticSubExpr = subExpr->getSemanticsProvidingExpr(); + if (auto *BLE = dyn_cast(semanticSubExpr)) { + auto *BP = new (ctx) BoolPattern(BLE->getLoc(), BLE->getValue()); + BP->setType(patternTy); + return BP; + } + } + + // case nil is equivalent to .none when switching on Optionals. + if (auto *NLE = dyn_cast(EP->getSubExpr())) { + if (patternTy->getOptionalObjectType()) { + auto *NoneEnumElement = ctx.getOptionalNoneDecl(); + auto *BaseTE = TypeExpr::createImplicit(patternTy, ctx); + auto *EEP = new (ctx) + EnumElementPattern(BaseTE, NLE->getLoc(), DeclNameLoc(NLE->getLoc()), + NoneEnumElement->createNameRef(), NoneEnumElement, + nullptr, EP->getDeclContext()); + EEP->setType(patternTy); + return EEP; + } else { + // ...but for non-optional types it can never match! Diagnose it. + ctx.Diags + .diagnose(NLE->getLoc(), diag::value_type_comparison_with_nil_illegal, + patternTy) + .warnUntilSwiftVersion(6); + + if (ctx.isSwiftVersionAtLeast(6)) + return nullptr; + } + } + return nullptr; +} + /// Perform top-down type coercion on the given pattern. -Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, - Type type, - TypeResolutionOptions options) { +Pattern *TypeChecker::coercePatternToType( + ContextualPattern pattern, Type type, TypeResolutionOptions options, + llvm::function_ref(Pattern *, Type)> + tryRewritePattern) { auto P = pattern.getPattern(); auto dc = pattern.getDeclContext(); auto &Context = dc->getASTContext(); auto &diags = Context.Diags; + // See if we can rewrite this using the constraint system. + if (auto result = tryRewritePattern(P, type)) + return *result; + options = applyContextualPatternOptions(options, pattern); auto subOptions = options; subOptions.setContext(None); @@ -1061,8 +1107,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, if (tupleType->getNumElements() == 1) { auto element = tupleType->getElement(0); sub = coercePatternToType( - pattern.forSubPattern(sub, /*retainTopLevel=*/true), element.getType(), - subOptions); + pattern.forSubPattern(sub, /*retainTopLevel=*/true), + element.getType(), subOptions, tryRewritePattern); if (!sub) return nullptr; TuplePatternElt elt(element.getName(), SourceLoc(), sub); @@ -1077,7 +1123,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, } sub = coercePatternToType( - pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions); + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions, + tryRewritePattern); if (!sub) return nullptr; @@ -1090,7 +1137,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, Pattern *sub = VP->getSubPattern(); sub = coercePatternToType( - pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions); + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions, + tryRewritePattern); if (!sub) return nullptr; VP->setSubPattern(sub); @@ -1123,7 +1171,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, Pattern *sub = TP->getSubPattern(); sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, - subOptions | TypeResolutionFlags::FromNonInferredPattern); + subOptions | TypeResolutionFlags::FromNonInferredPattern, + tryRewritePattern); if (!sub) return nullptr; @@ -1212,9 +1261,9 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, auto decayToParen = [&]() -> Pattern * { assert(canDecayToParen); Pattern *sub = TP->getElement(0).getPattern(); - sub = TypeChecker::coercePatternToType( + sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, - subOptions); + subOptions, tryRewritePattern); if (!sub) return nullptr; @@ -1271,7 +1320,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, auto sub = coercePatternToType( pattern.forSubPattern(elt.getPattern(), /*retainTopLevel=*/false), - CoercionType, subOptions); + CoercionType, subOptions, tryRewritePattern); if (!sub) return nullptr; @@ -1291,37 +1340,9 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, assert(cast(P)->isResolved() && "coercing unresolved expr pattern!"); auto *EP = cast(P); - if (type->isBool()) { - // The type is Bool. - // Check if the pattern is a Bool literal - if (auto *BLE = dyn_cast( - EP->getSubExpr()->getSemanticsProvidingExpr())) { - P = new (Context) BoolPattern(BLE->getLoc(), BLE->getValue()); - P->setType(type); - return P; - } - } - - // case nil is equivalent to .none when switching on Optionals. - if (auto *NLE = dyn_cast(EP->getSubExpr())) { - if (type->getOptionalObjectType()) { - auto *NoneEnumElement = Context.getOptionalNoneDecl(); - auto *BaseTE = TypeExpr::createImplicit(type, Context); - P = new (Context) EnumElementPattern( - BaseTE, NLE->getLoc(), DeclNameLoc(NLE->getLoc()), - NoneEnumElement->createNameRef(), NoneEnumElement, nullptr, dc); - return TypeChecker::coercePatternToType( - pattern.forSubPattern(P, /*retainTopLevel=*/true), type, options); - } else { - // ...but for non-optional types it can never match! Diagnose it. - diags.diagnose(NLE->getLoc(), - diag::value_type_comparison_with_nil_illegal, type) - .warnUntilSwiftVersion(6); - if (type->getASTContext().isSwiftVersionAtLeast(6)) - return nullptr; - } - } + if (auto P = trySimplifyExprPattern(EP, type)) + return P.get(); if (TypeChecker::typeCheckExprPattern(EP, dc, type)) return nullptr; @@ -1370,7 +1391,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, P = sub; return coercePatternToType( - pattern.forSubPattern(P, /*retainTopLevel=*/true), type, options); + pattern.forSubPattern(P, /*retainTopLevel=*/true), type, options, + tryRewritePattern); } CheckedCastKind castKind = TypeChecker::typeCheckCheckedCast( @@ -1419,7 +1441,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), IP->getCastType(), - subOptions | TypeResolutionFlags::FromNonInferredPattern); + subOptions | TypeResolutionFlags::FromNonInferredPattern, + tryRewritePattern); if (!sub) return nullptr; @@ -1457,7 +1480,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, EEP->getEndLoc()); return coercePatternToType( pattern.forSubPattern(P, /*retainTopLevel=*/true), type, - options); + options, tryRewritePattern); } else { diags.diagnose(EEP->getLoc(), diag::enum_element_pattern_member_not_found, @@ -1472,7 +1495,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, Context, EEP->getUnresolvedOriginalExpr(), dc); return coercePatternToType( pattern.forSubPattern(P, /*retainTopLevel=*/true), type, - options); + options, tryRewritePattern); } } } @@ -1547,9 +1570,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, type, parentTy, CheckedCastContextKind::EnumElementPattern, dc); // If the cast failed, we can't resolve the pattern. if (foundCastKind < CheckedCastKind::First_Resolved) { - diags - .diagnose(EEP->getLoc(), diag::downcast_to_unrelated, type, - parentTy) + diags.diagnose(EEP->getLoc(), diag::cannot_match_value_with_pattern, + type, parentTy) .highlight(EEP->getSourceRange()); return nullptr; } @@ -1559,9 +1581,9 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, castKind = foundCastKind; enumTy = parentTy; } else { - diags.diagnose(EEP->getLoc(), - diag::enum_element_pattern_not_member_of_enum, - EEP->getName(), type); + diags.diagnose(EEP->getLoc(), diag::cannot_match_value_with_pattern, + type, parentTy) + .highlight(EEP->getSourceRange()); return nullptr; } } @@ -1595,7 +1617,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, - newSubOptions); + newSubOptions, tryRewritePattern); if (!sub) return nullptr; @@ -1630,7 +1652,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, newSubOptions |= TypeResolutionFlags::FromNonInferredPattern; sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, - newSubOptions); + newSubOptions, tryRewritePattern); if (!sub) return nullptr; EEP->setSubPattern(sub); @@ -1674,7 +1696,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, newSubOptions |= TypeResolutionFlags::FromNonInferredPattern; sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, - newSubOptions); + newSubOptions, tryRewritePattern); if (!sub) return nullptr; diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 47ba8b414b25a..e8197806b3d10 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -6799,6 +6799,7 @@ swift::findWitnessedObjCRequirements(const ValueDecl *witness, case AccessorKind::MutableAddress: case AccessorKind::Read: case AccessorKind::Modify: + case AccessorKind::Init: // These accessors are never exposed to Objective-C. return result; case AccessorKind::DidSet: diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 3dd3b13c7cfa0..59e5aed5f5b63 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -283,6 +283,22 @@ StoredPropertiesAndMissingMembersRequest::evaluate(Evaluator &evaluator, return decl->getASTContext().AllocateCopy(results); } +ArrayRef +InitAccessorPropertiesRequest::evaluate(Evaluator &evaluator, + NominalTypeDecl *decl) const { + SmallVector results; + for (auto *member : decl->getMembers()) { + auto *var = dyn_cast(member); + if (!var || !var->getAccessor(AccessorKind::Init)) { + continue; + } + + results.push_back(var); + } + + return decl->getASTContext().AllocateCopy(results); +} + /// Check whether the pattern may have storage. /// /// This query is careful not to trigger accessor macro expansion, which @@ -2058,6 +2074,9 @@ synthesizeAccessorBody(AbstractFunctionDecl *fn, void *) { case AccessorKind::Address: case AccessorKind::MutableAddress: break; + + case AccessorKind::Init: + llvm_unreachable("init accessor not yet implemented"); } llvm_unreachable("bad synthesized function kind"); } @@ -2568,6 +2587,7 @@ IsAccessorTransparentRequest::evaluate(Evaluator &evaluator, case AccessorKind::Read: case AccessorKind::Modify: + case AccessorKind::Init: break; case AccessorKind::WillSet: @@ -3477,6 +3497,7 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator, bool hasSetter = storage->getParsedAccessor(AccessorKind::Set); bool hasModify = storage->getParsedAccessor(AccessorKind::Modify); bool hasMutableAddress = storage->getParsedAccessor(AccessorKind::MutableAddress); + bool hasInit = storage->getParsedAccessor(AccessorKind::Init); auto *DC = storage->getDeclContext(); // 'get', 'read', and a non-mutable addressor are all exclusive. @@ -3490,7 +3511,7 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator, // If there's a writing accessor of any sort, there must also be a // reading accessor. - } else if (hasSetter || hasModify || hasMutableAddress) { + } else if (hasInit || hasSetter || hasModify || hasMutableAddress) { readImpl = ReadImplKind::Get; // Subscripts always have to have some sort of accessor; they can't be diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 6538ae8cd4f87..4cba8b17db863 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -733,15 +733,26 @@ Pattern *resolvePattern(Pattern *P, DeclContext *dc, bool isStmtCondition); /// unbound generic types. Type typeCheckPattern(ContextualPattern pattern); +/// Attempt to simplify an ExprPattern into a BoolPattern or +/// OptionalSomePattern. Returns \c nullptr if the pattern could not be +/// simplified. +NullablePtr trySimplifyExprPattern(ExprPattern *EP, Type patternTy); + /// Coerce a pattern to the given type. /// /// \param pattern The contextual pattern. /// \param type the type to coerce the pattern to. /// \param options Options that control the coercion. +/// \param tryRewritePattern A function that attempts to externally rewrite +/// the given pattern. This is used by the constraint system to take over +/// rewriting for ExprPatterns. /// /// \returns the coerced pattern, or nullptr if the coercion failed. -Pattern *coercePatternToType(ContextualPattern pattern, Type type, - TypeResolutionOptions options); +Pattern *coercePatternToType( + ContextualPattern pattern, Type type, TypeResolutionOptions options, + llvm::function_ref(Pattern *, Type)> tryRewritePattern = + [](Pattern *, Type) { return None; }); + bool typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, Type type); /// Coerce the specified parameter list of a ClosureExpr to the specified diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index e74bf4846f906..620012cd55556 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -40,6 +40,7 @@ #include "swift/ClangImporter/SwiftAbstractBasicReader.h" #include "swift/Serialization/SerializedModuleLoader.h" #include "clang/AST/DeclTemplate.h" +#include "clang/Basic/SourceManager.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" @@ -185,10 +186,25 @@ SourceLoc ModuleFile::getSourceLoc() const { auto filename = getModuleFilename(); auto bufferID = SourceMgr.getIDForBufferIdentifier(filename); if (!bufferID) - bufferID = SourceMgr.addMemBufferCopy(StringRef(), filename); + bufferID = SourceMgr.addMemBufferCopy("", filename); return SourceMgr.getLocForBufferStart(*bufferID); } +SourceLoc ModularizationError::getSourceLoc() const { + auto &SourceMgr = referenceModule->getContext().Diags.SourceMgr; + auto filename = referenceModule->getModuleFilename(); + + // Synthesize some context. We don't have an actual decl here + // so try to print a simple representation of the reference. + std::string S; + llvm::raw_string_ostream OS(S); + OS << expectedModule->getName() << "." << name; + + // If we enable these remarks by default we may want to reuse these buffers. + auto bufferID = SourceMgr.addMemBufferCopy(S, filename); + return SourceMgr.getLocForBufferStart(bufferID); +} + void ModularizationError::diagnose(const ModuleFile *MF, DiagnosticBehavior limit) const { @@ -197,18 +213,21 @@ ModularizationError::diagnose(const ModuleFile *MF, auto diagnoseError = [&](Kind errorKind) { switch (errorKind) { case Kind::DeclMoved: - return ctx.Diags.diagnose(MF->getSourceLoc(), diag::modularization_issue_decl_moved, - declIsType, name, expectedModuleName, - foundModuleName); + return ctx.Diags.diagnose(getSourceLoc(), + diag::modularization_issue_decl_moved, + declIsType, name, expectedModule, + foundModule); case Kind::DeclKindChanged: return - ctx.Diags.diagnose(MF->getSourceLoc(), diag::modularization_issue_decl_type_changed, - declIsType, name, expectedModuleName, - referencedFromModuleName, foundModuleName, - foundModuleName != expectedModuleName); + ctx.Diags.diagnose(getSourceLoc(), + diag::modularization_issue_decl_type_changed, + declIsType, name, expectedModule, + referenceModule->getName(), foundModule, + foundModule != expectedModule); case Kind::DeclNotFound: - return ctx.Diags.diagnose(MF->getSourceLoc(), diag::modularization_issue_decl_not_found, - declIsType, name, expectedModuleName); + return ctx.Diags.diagnose(getSourceLoc(), + diag::modularization_issue_decl_not_found, + declIsType, name, expectedModule); } llvm_unreachable("Unhandled ModularizationError::Kind in switch."); }; @@ -220,6 +239,103 @@ ModularizationError::diagnose(const ModuleFile *MF, // We could pass along the `path` information through notes. // However, for a top-level decl a path would just duplicate the // expected module name and the decl name from the diagnostic. + + // Show context with relevant file paths. + ctx.Diags.diagnose(SourceLoc(), + diag::modularization_issue_note_expected, + declIsType, expectedModule, + expectedModule->getModuleSourceFilename()); + + const clang::Module *expectedUnderlying = + expectedModule->findUnderlyingClangModule(); + if (!expectedModule->isNonSwiftModule() && + expectedUnderlying) { + auto CML = ctx.getClangModuleLoader(); + auto &CSM = CML->getClangASTContext().getSourceManager(); + StringRef filename = CSM.getFilename(expectedUnderlying->DefinitionLoc); + ctx.Diags.diagnose(SourceLoc(), + diag::modularization_issue_note_expected_underlying, + expectedUnderlying->Name, + filename); + } + + if (foundModule) + ctx.Diags.diagnose(SourceLoc(), + diag::modularization_issue_note_found, + declIsType, foundModule, + foundModule->getModuleSourceFilename()); + + // A Swift language version mismatch could lead to a different set of rules + // from APINotes files being applied when building the module vs when reading + // from it. + version::Version + moduleLangVersion = referenceModule->getCompatibilityVersion(), + clientLangVersion = MF->getContext().LangOpts.EffectiveLanguageVersion; + ModuleDecl *referenceModuleDecl = referenceModule->getAssociatedModule(); + if (clientLangVersion != moduleLangVersion) { + ctx.Diags.diagnose(SourceLoc(), + diag::modularization_issue_swift_version, + referenceModuleDecl, moduleLangVersion, + clientLangVersion); + } + + // If the error is in a resilient swiftmodule adjacent to a swiftinterface, + // deleting the module to rebuild from the swiftinterface may fix the issue. + // Limit this suggestion to distributed Swift modules to not hint at + // deleting local caches and such. + bool referenceModuleIsDistributed = referenceModuleDecl && + referenceModuleDecl->isNonUserModule(); + if (referenceModule->getResilienceStrategy() == + ResilienceStrategy::Resilient && + referenceModuleIsDistributed) { + ctx.Diags.diagnose(SourceLoc(), + diag::modularization_issue_stale_module, + referenceModuleDecl, + referenceModule->getModuleFilename()); + } + + // If the missing decl was expected to be in a clang module, + // it may be hidden by some clang defined passed via `-Xcc` affecting how + // headers are seen. + if (expectedUnderlying) { + ctx.Diags.diagnose(SourceLoc(), + diag::modularization_issue_audit_headers, + expectedModule->isNonSwiftModule(), expectedModule); + } + + // If the reference goes from a distributed module to a local module, + // the compiler may have picked up an undesired module. We usually expect + // distributed modules to only reference other distributed modules. + // Local modules can reference both local modules and distributed modules. + if (referenceModuleIsDistributed) { + if (!expectedModule->isNonUserModule()) { + ctx.Diags.diagnose(SourceLoc(), + diag::modularization_issue_layering_expected_local, + referenceModuleDecl, expectedModule); + } else if (foundModule && !foundModule->isNonUserModule()) { + ctx.Diags.diagnose(SourceLoc(), + diag::modularization_issue_layering_found_local, + referenceModuleDecl, foundModule); + } + } + + // If a type moved between MyModule and MyModule_Private, it can be caused + // by the use of `-Xcc -D` to change the API of the modules, leading to + // decls moving between both modules. + if (errorKind == Kind::DeclMoved || + errorKind == Kind::DeclKindChanged) { + StringRef foundModuleName = foundModule->getName().str(); + StringRef expectedModuleName = expectedModule->getName().str(); + if (foundModuleName != expectedModuleName && + (foundModuleName.startswith(expectedModuleName) || + expectedModuleName.startswith(foundModuleName)) && + (expectedUnderlying || + expectedModule->findUnderlyingClangModule())) { + ctx.Diags.diagnose(SourceLoc(), + diag::modularization_issue_related_modules, + declIsType, name); + } + } } void TypeError::diagnose(const ModuleFile *MF) const { @@ -1957,7 +2073,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { SmallVector strScratch; auto errorKind = ModularizationError::Kind::DeclNotFound; - Identifier foundIn; + ModuleDecl *foundIn = nullptr; bool isType = false; if (recordID == XREF_TYPE_PATH_PIECE || @@ -2011,7 +2127,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { // one because otherwise it would have succeeded on the first search. // This is usually caused by the use of poorly modularized headers. errorKind = ModularizationError::Kind::DeclMoved; - foundIn = otherModule->getName(); + foundIn = otherModule; break; } else if (hadAMatchBeforeFiltering) { // Found a match that was filtered out. This may be from the same @@ -2019,20 +2135,18 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { // by the use of different Swift language versions between a library // with serialized SIL and a client. errorKind = ModularizationError::Kind::DeclKindChanged; - foundIn = otherModule->getName(); + foundIn = otherModule; break; } } } auto declName = getXRefDeclNameForError(); - auto expectedIn = baseModule->getName(); - auto referencedFrom = getName(); auto error = llvm::make_error(declName, isType, errorKind, - expectedIn, - referencedFrom, + baseModule, + this, foundIn, pathTrace); @@ -2043,13 +2157,13 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { if (getContext().LangOpts.ForceWorkaroundBrokenModules && errorKind == ModularizationError::Kind::DeclMoved && !values.empty()) { - // Print the error as a remark and notify of the recovery attempt. - getContext().Diags.diagnose(getSourceLoc(), - diag::modularization_issue_worked_around); + // Print the error as a warning and notify of the recovery attempt. llvm::handleAllErrors(std::move(error), [&](const ModularizationError &modularError) { - modularError.diagnose(this, DiagnosticBehavior::Note); + modularError.diagnose(this, DiagnosticBehavior::Warning); }); + getContext().Diags.diagnose(SourceLoc(), + diag::modularization_issue_worked_around); } else { return std::move(error); } @@ -5483,6 +5597,36 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { break; } + case decls_block::Initializes_DECL_ATTR: { + ArrayRef rawPropertyIDs; + serialization::decls_block::InitializesDeclAttrLayout:: + readRecord(scratch, rawPropertyIDs); + + SmallVector properties; + for (auto rawID : rawPropertyIDs) { + properties.push_back(MF.getIdentifier(rawID)); + } + + Attr = InitializesAttr::create(ctx, SourceLoc(), SourceRange(), + properties); + break; + } + + case decls_block::Accesses_DECL_ATTR: { + ArrayRef rawPropertyIDs; + serialization::decls_block::AccessesDeclAttrLayout:: + readRecord(scratch, rawPropertyIDs); + + SmallVector properties; + for (auto rawID : rawPropertyIDs) { + properties.push_back(MF.getIdentifier(rawID)); + } + + Attr = AccessesAttr::create(ctx, SourceLoc(), SourceRange(), + properties); + break; + } + case decls_block::DynamicReplacement_DECL_ATTR: { bool isImplicit; uint64_t numArgs; diff --git a/lib/Serialization/DeserializationErrors.h b/lib/Serialization/DeserializationErrors.h index 57234737e3c23..6c4cb95efb131 100644 --- a/lib/Serialization/DeserializationErrors.h +++ b/lib/Serialization/DeserializationErrors.h @@ -355,46 +355,55 @@ class ModularizationError : public llvm::ErrorInfo { DeclName name; bool declIsType; Kind errorKind; - Identifier expectedModuleName; - StringRef referencedFromModuleName; - Identifier foundModuleName; + + /// Module targeted by the reference that should define the decl. + const ModuleDecl *expectedModule; + + /// Binary module file with the outgoing reference. + const ModuleFile *referenceModule; + + /// Module where the compiler did find the decl, if any. + const ModuleDecl *foundModule; + XRefTracePath path; public: explicit ModularizationError(DeclName name, bool declIsType, Kind errorKind, - Identifier expectedModuleName, - StringRef referencedFromModuleName, - Identifier foundModuleName, + const ModuleDecl *expectedModule, + const ModuleFile *referenceModule, + const ModuleDecl *foundModule, XRefTracePath path): name(name), declIsType(declIsType), errorKind(errorKind), - expectedModuleName(expectedModuleName), - referencedFromModuleName(referencedFromModuleName), - foundModuleName(foundModuleName), path(path) {} + expectedModule(expectedModule), + referenceModule(referenceModule), + foundModule(foundModule), path(path) {} void diagnose(const ModuleFile *MF, DiagnosticBehavior limit = DiagnosticBehavior::Fatal) const; void log(raw_ostream &OS) const override { OS << "modularization issue on '" << name << "', reference from '"; - OS << referencedFromModuleName << "' not resolvable: "; + OS << referenceModule->getName() << "' not resolvable: "; switch (errorKind) { case Kind::DeclMoved: - OS << "expected in '" << expectedModuleName << "' but found in '"; - OS << foundModuleName << "'"; + OS << "expected in '" << expectedModule->getName() << "' but found in '"; + OS << foundModule->getName() << "'"; break; case Kind::DeclKindChanged: OS << "decl details changed between what was imported from '"; - OS << expectedModuleName << "' and what is now imported from '"; - OS << foundModuleName << "'"; + OS << expectedModule->getName() << "' and what is now imported from '"; + OS << foundModule->getName() << "'"; break; case Kind::DeclNotFound: - OS << "not found, expected in '" << expectedModuleName << "'"; + OS << "not found, expected in '" << expectedModule->getName() << "'"; break; } OS << "\n"; path.print(OS); } + SourceLoc getSourceLoc() const; + std::error_code convertToErrorCode() const override { return llvm::inconvertibleErrorCode(); } diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 8f2895a7cb49a..5308c97b99e37 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -2439,6 +2439,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, break; } case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::AssignOrInitInst: llvm_unreachable("not supported"); case SILInstructionKind::BindMemoryInst: { assert(RecordKind == SIL_ONE_TYPE_VALUES && diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index e213184de491a..e2f871b82df3f 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,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 = 788; // noimplicitcopy addr +const uint16_t SWIFTMODULE_VERSION_MINOR = 790; // add `out` kind to mark uninitialized instruction /// A standard hash seed used for all string hashes in a serialized module. /// @@ -330,6 +330,7 @@ enum AccessorKind : uint8_t { MutableAddress, Read, Modify, + Init, }; using AccessorKindField = BCFixed<4>; @@ -2226,6 +2227,16 @@ namespace decls_block { BCArray // target function pieces, spi groups, type erased params >; + using InitializesDeclAttrLayout = BCRecordLayout< + Initializes_DECL_ATTR, + BCArray // initialized properties + >; + + using AccessesDeclAttrLayout = BCRecordLayout< + Accesses_DECL_ATTR, + BCArray // initialized properties + >; + using DifferentiableDeclAttrLayout = BCRecordLayout< Differentiable_DECL_ATTR, BCFixed<1>, // Implicit flag. diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 80db9cfb78ec6..02bc669075a96 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2906,6 +2906,34 @@ class Serializer::DeclSerializer : public DeclVisitor { return; } + case DAK_Initializes: { + auto abbrCode = S.DeclTypeAbbrCodes[InitializesDeclAttrLayout::Code]; + auto attr = cast(DA); + + SmallVector properties; + for (auto identifier : attr->getProperties()) { + properties.push_back(S.addDeclBaseNameRef(identifier)); + } + + InitializesDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, properties); + return; + } + + case DAK_Accesses: { + auto abbrCode = S.DeclTypeAbbrCodes[AccessesDeclAttrLayout::Code]; + auto attr = cast(DA); + + SmallVector properties; + for (auto identifier : attr->getProperties()) { + properties.push_back(S.addDeclBaseNameRef(identifier)); + } + + AccessesDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, properties); + return; + } + case DAK_DynamicReplacement: { auto abbrCode = S.DeclTypeAbbrCodes[DynamicReplacementDeclAttrLayout::Code]; diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index e9cf5f492565e..841942ea9afe5 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -2183,6 +2183,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { break; } case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::AssignOrInitInst: llvm_unreachable("not supported"); case SILInstructionKind::BindMemoryInst: { auto *BI = cast(&SI); diff --git a/stdlib/public/Backtracing/ArrayImageSource.swift b/stdlib/public/Backtracing/ArrayImageSource.swift new file mode 100644 index 0000000000000..db38033e1b789 --- /dev/null +++ b/stdlib/public/Backtracing/ArrayImageSource.swift @@ -0,0 +1,50 @@ +//===--- ArrayImageSource.swift - An image source backed by an Array -------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines ArrayImageSource, an image source that is backed by a Swift Array. +// +//===----------------------------------------------------------------------===// + +import Swift + +@_implementationOnly import OS.Libc + +enum ArrayImageSourceError: Error { + case outOfBoundsRead(UInt64, UInt64) +} + +struct ArrayImageSource: ImageSource { + private var array: Array + + public init(array: Array) { + self.array = array + } + + public var isMappedImage: Bool { return false } + public var path: String? { return nil } + public var bounds: Bounds? { + return Bounds(base: 0, size: Size(array.count * MemoryLayout.stride)) + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try array.withUnsafeBytes{ + let size = Size($0.count) + let requested = Size(buffer.count * MemoryLayout.stride) + if addr > size || requested > size - addr { + throw ArrayImageSourceError.outOfBoundsRead(addr, requested) + } + + memcpy(buffer.baseAddress!, $0.baseAddress! + Int(addr), Int(requested)) + } + } +} diff --git a/stdlib/public/Backtracing/Backtrace.swift b/stdlib/public/Backtracing/Backtrace.swift index 13f49068c198b..025179f975887 100644 --- a/stdlib/public/Backtracing/Backtrace.swift +++ b/stdlib/public/Backtracing/Backtrace.swift @@ -16,10 +16,14 @@ import Swift -#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import OS.Libc -@_implementationOnly import _SwiftBacktracingShims +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import OS.Darwin +#endif +#if os(Linux) +@_implementationOnly import ImageFormats.Elf #endif /// Holds a backtrace. @@ -29,7 +33,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// This is intentionally _not_ a pointer, because you shouldn't be /// dereferencing them; they may refer to some other process, for /// example. - public typealias Address = UInt + public typealias Address = UInt64 /// The unwind algorithm to use. public enum UnwindAlgorithm { @@ -127,18 +131,23 @@ public struct Backtrace: CustomStringConvertible, Sendable { } /// A textual description of this frame. - public var description: String { + public func description(width: Int) -> String { switch self { case let .programCounter(addr): - return "\(hex(addr))" + return "\(hex(addr, width: width))" case let .returnAddress(addr): - return "\(hex(addr)) [ra]" + return "\(hex(addr, width: width)) [ra]" case let .asyncResumePoint(addr): - return "\(hex(addr)) [async]" + return "\(hex(addr, width: width)) [async]" case .omittedFrames(_), .truncated: return "..." } } + + /// A textual description of this frame. + public var description: String { + return description(width: MemoryLayout
.size * 2) + } } /// Represents an image loaded in the process's address space @@ -160,15 +169,26 @@ public struct Backtrace: CustomStringConvertible, Sendable { public var endOfText: Backtrace.Address /// Provide a textual description of an Image. - public var description: String { + public func description(width: Int) -> String { if let buildID = self.buildID { - return "\(hex(baseAddress))-\(hex(endOfText)) \(hex(buildID)) \(name) \(path)" + return "\(hex(baseAddress, width: width))-\(hex(endOfText, width: width)) \(hex(buildID)) \(name) \(path)" } else { - return "\(hex(baseAddress))-\(hex(endOfText)) \(name) \(path)" + return "\(hex(baseAddress, width: width))-\(hex(endOfText, width: width)) \(name) \(path)" } } + + /// A textual description of an Image. + public var description: String { + return description(width: MemoryLayout
.size * 2) + } } + /// The architecture of the system that captured this backtrace. + public var architecture: String + + /// The width of an address in this backtrace, in bits. + public var addressWidth: Int + /// A list of captured frame information. public var frames: [Frame] @@ -199,6 +219,17 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// be `nil`. public var sharedCacheInfo: SharedCacheInfo? + /// Format an address according to the addressWidth. + /// + /// @param address The address to format. + /// @param prefix Whether to include a "0x" prefix. + /// + /// @returns A String containing the formatted Address. + public func formatAddress(_ address: Address, + prefix: Bool = true) -> String { + return hex(address, prefix: prefix, width: (addressWidth + 3) / 4) + } + /// Capture a backtrace from the current program location. /// /// The `capture()` method itself will not be included in the backtrace; @@ -219,15 +250,23 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// /// @returns A new `Backtrace` struct. @inline(never) + @_semantics("use_frame_pointer") public static func capture(algorithm: UnwindAlgorithm = .auto, limit: Int? = 64, offset: Int = 0, top: Int = 16) throws -> Backtrace { + #if os(Linux) + let images = captureImages() + #else + let images: [Image]? = nil + #endif + // N.B. We use offset+1 here to skip this frame, rather than inlining // this code into the client. return try HostContext.withCurrentContext { ctx in try capture(from: ctx, using: UnsafeLocalMemoryReader(), + images: images, algorithm: algorithm, limit: limit, offset: offset + 1, @@ -236,23 +275,32 @@ public struct Backtrace: CustomStringConvertible, Sendable { } @_spi(Internal) - public static func capture(from context: some Context, - using memoryReader: some MemoryReader, - algorithm: UnwindAlgorithm = .auto, - limit: Int? = 64, - offset: Int = 0, - top: Int = 16) throws -> Backtrace { + public static func capture( + from context: Ctx, + using memoryReader: Rdr, + images: [Image]?, + algorithm: UnwindAlgorithm = .auto, + limit: Int? = 64, + offset: Int = 0, + top: Int = 16 + ) throws -> Backtrace { + let addressWidth = 8 * MemoryLayout.size + switch algorithm { // All of them, for now, use the frame pointer unwinder. In the long // run, we should be using DWARF EH frame data for .precise. case .auto, .fast, .precise: let unwinder = - FramePointerUnwinder(context: context, memoryReader: memoryReader) + FramePointerUnwinder(context: context, + images: images, + memoryReader: memoryReader) .dropFirst(offset) if let limit = limit { if limit <= 0 { - return Backtrace(frames: [.truncated]) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: [.truncated]) } let realTop = top < limit ? top : limit - 1 @@ -276,7 +324,9 @@ public struct Backtrace: CustomStringConvertible, Sendable { frames[limit - 1] = .truncated } - return Backtrace(frames: frames) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: frames) } else { // If we still have frames at this point, start tracking the @@ -312,10 +362,16 @@ public struct Backtrace: CustomStringConvertible, Sendable { with: topFrames.prefix(secondPart)) } - return Backtrace(frames: frames) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: frames, + images: images) } } else { - return Backtrace(frames: Array(unwinder)) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: Array(unwinder), + images: images) } } } @@ -326,20 +382,20 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// @returns A list of `Image`s. public static func captureImages() -> [Image] { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - return captureImages(for: _swift_backtrace_task_self()) + return captureImages(for: mach_task_self()) #else - return [] + return captureImages(using: UnsafeLocalMemoryReader()) #endif } #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - private static func withDyldProcessInfo(for task: __swift_task_t, + private static func withDyldProcessInfo(for task: task_t, fn: (OpaquePointer?) throws -> T) rethrows -> T { - var kret = __swift_kern_return_t(_SWIFT_KERN_SUCCESS) + var kret = kern_return_t(KERN_SUCCESS) let dyldInfo = _dyld_process_info_create(task, 0, &kret) - if kret != _SWIFT_KERN_SUCCESS { + if kret != KERN_SUCCESS { fatalError("error: cannot create dyld process info") } @@ -351,12 +407,11 @@ public struct Backtrace: CustomStringConvertible, Sendable { } #endif + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) @_spi(Internal) public static func captureImages(for process: Any) -> [Image] { var images: [Image] = [] - - #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - let task = process as! __swift_task_t + let task = process as! task_t withDyldProcessInfo(for: task) { dyldInfo in _dyld_process_info_for_each_image(dyldInfo) { @@ -365,7 +420,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { if let path = path, let uuid = uuid { let pathString = String(cString: path) let theUUID = Array(UnsafeBufferPointer(start: uuid, - count: MemoryLayout<__swift_uuid_t>.size)) + count: MemoryLayout.size)) let name: String if let slashIndex = pathString.lastIndex(of: "/") { name = String(pathString.suffix(from: @@ -393,17 +448,132 @@ public struct Backtrace: CustomStringConvertible, Sendable { } } } - #endif // os(macOS) || os(iOS) || os(watchOS) return images.sorted(by: { $0.baseAddress < $1.baseAddress }) } + #else // !(os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) + private struct AddressRange { + var low: Address = 0 + var high: Address = 0 + } + + @_spi(Internal) + public static func captureImages(using reader: M, + forProcess pid: Int? = nil) -> [Image] { + var images: [Image] = [] + + #if os(Linux) + let path: String + if let pid = pid { + path = "/proc/\(pid)/maps" + } else { + path = "/proc/self/maps" + } + + guard let procMaps = readString(from: path) else { + return [] + } + + // Find all the mapped files and get high/low ranges + var mappedFiles: [Substring:AddressRange] = [:] + for match in ProcMapsScanner(procMaps) { + let path = stripWhitespace(match.pathname) + if match.inode == "0" || path == "" { + continue + } + guard let start = Address(match.start, radix: 16), + let end = Address(match.end, radix: 16) else { + continue + } + + if let range = mappedFiles[path] { + mappedFiles[path] = AddressRange(low: min(start, range.low), + high: max(end, range.high)) + } else { + mappedFiles[path] = AddressRange(low: start, + high: end) + } + } + + // Look for ELF headers in the process' memory + typealias Source = MemoryImageSource + let source = Source(with: reader) + for match in ProcMapsScanner(procMaps) { + let path = stripWhitespace(match.pathname) + if match.inode == "0" || path == "" { + continue + } + + guard let start = Address(match.start, radix: 16), + let end = Address(match.end, radix: 16), + let offset = Address(match.offset, radix: 16) else { + continue + } + + if offset != 0 || end - start < EI_NIDENT { + continue + } + + // Extract the filename from path + let name: Substring + if let slashIndex = path.lastIndex(of: "/") { + name = path.suffix(from: path.index(after: slashIndex)) + } else { + name = path + } + + // Inspect the image and extract the UUID and end of text + let range = mappedFiles[path]! + let subSource = SubImageSource(parent: source, + baseAddress: Source.Address(range.low), + length: Source.Size(range.high + - range.low)) + var theUUID: [UInt8]? = nil + var endOfText: Address = range.low + + if let image = try? Elf32Image(source: subSource) { + theUUID = image.uuid + + for hdr in image.programHeaders { + if hdr.p_type == .PT_LOAD && (hdr.p_flags & PF_X) != 0 { + endOfText = max(endOfText, range.low + Address(hdr.p_vaddr + + hdr.p_memsz)) + } + } + } else if let image = try? Elf64Image(source: subSource) { + theUUID = image.uuid + + for hdr in image.programHeaders { + if hdr.p_type == .PT_LOAD && (hdr.p_flags & PF_X) != 0 { + endOfText = max(endOfText, range.low + Address(hdr.p_vaddr + + hdr.p_memsz)) + } + } + } else { + // Not a valid ELF image + continue + } + + let image = Image(name: String(name), + path: String(path), + buildID: theUUID, + baseAddress: range.low, + endOfText: endOfText) + + images.append(image) + } + #endif + + return images.sorted(by: { $0.baseAddress < $1.baseAddress }) + } + #endif /// Capture shared cache information. /// /// @returns A `SharedCacheInfo`. public static func captureSharedCacheInfo() -> SharedCacheInfo? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - return captureSharedCacheInfo(for: _swift_backtrace_task_self()) + return captureSharedCacheInfo(for: mach_task_self()) #else return nil #endif @@ -412,13 +582,13 @@ public struct Backtrace: CustomStringConvertible, Sendable { @_spi(Internal) public static func captureSharedCacheInfo(for t: Any) -> SharedCacheInfo? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - let task = t as! __swift_task_t + let task = t as! task_t return withDyldProcessInfo(for: task) { dyldInfo in var cacheInfo = dyld_process_cache_info() _dyld_process_info_get_cache(dyldInfo, &cacheInfo) let theUUID = withUnsafePointer(to: cacheInfo.cacheUUID) { Array(UnsafeRawBufferPointer(start: $0, - count: MemoryLayout<__swift_uuid_t>.size)) + count: MemoryLayout.size)) } return SharedCacheInfo(uuid: theUUID, baseAddress: Address(cacheInfo.cacheBaseAddress), @@ -462,10 +632,11 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// Provide a textual version of the backtrace. public var description: String { var lines: [String] = [] + let addressChars = (addressWidth + 3) / 4 var n = 0 for frame in frames { - lines.append("\(n)\t\(frame)") + lines.append("\(n)\t\(frame.description(width: addressChars))") switch frame { case let .omittedFrames(count): n += count @@ -479,7 +650,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { lines.append("Images:") lines.append("") for (n, image) in images.enumerated() { - lines.append("\(n)\t\(image)") + lines.append("\(n)\t\(image.description(width: addressChars))") } } @@ -488,8 +659,9 @@ public struct Backtrace: CustomStringConvertible, Sendable { lines.append("") lines.append("Shared Cache:") lines.append("") - lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") - lines.append(" Base: \(hex(sharedCacheInfo.baseAddress))") + lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") + lines.append(" Base: \(hex(sharedCacheInfo.baseAddress, width: addressChars))") + lines.append(" Active: \(!sharedCacheInfo.noCache)") } #endif diff --git a/stdlib/public/Backtracing/BacktraceFormatter.swift b/stdlib/public/Backtracing/BacktraceFormatter.swift index 9ac84850e1e13..7f0abf15f637d 100644 --- a/stdlib/public/Backtracing/BacktraceFormatter.swift +++ b/stdlib/public/Backtracing/BacktraceFormatter.swift @@ -17,7 +17,7 @@ import Swift -@_implementationOnly import _SwiftBacktracingShims +@_implementationOnly import OS.Libc /// A backtrace formatting theme. @_spi(Formatting) @@ -517,23 +517,25 @@ public struct BacktraceFormatter { /// Format an individual frame into a list of columns. /// - /// @param frame The frame to format. - /// @param index The frame index, if required. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result An array of strings, one per column. public func formatColumns(frame: Backtrace.Frame, + addressWidth: Int, index: Int? = nil) -> [String] { let pc: String var attrs: [String] = [] switch frame { case let .programCounter(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" case let .returnAddress(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" attrs.append("ra") case let .asyncResumePoint(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" attrs.append("async") case .omittedFrames(_), .truncated: pc = "..." @@ -555,37 +557,48 @@ public struct BacktraceFormatter { /// Format a frame into a list of rows. /// - /// @param frame The frame to format. - /// @param index The frame index, if required. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result An array of table rows. public func formatRows(frame: Backtrace.Frame, + addressWidth: Int, index: Int? = nil) -> [TableRow] { - return [.columns(formatColumns(frame: frame, index: index))] + return [.columns(formatColumns(frame: frame, + addressWidth: addressWidth, + index: index))] } /// Format just one frame. /// - /// @param frame The frame to format. - /// @param index (Optional) frame index. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result A `String` containing the formatted data. - public func format(frame: Backtrace.Frame, index: Int? = nil) -> String { - let rows = formatRows(frame: frame, index: index) + public func format(frame: Backtrace.Frame, + addressWidth: Int, + index: Int? = nil) -> String { + let rows = formatRows(frame: frame, + addressWidth: addressWidth, + index: index) return BacktraceFormatter.formatTable(rows, alignments: [.right]) } /// Format the frame list from a backtrace. /// - /// @param frames The frames to format. + /// @param frames The frames to format. + /// @param addressWidth The width, in characters, of an address. /// /// @result A `String` containing the formatted data. - public func format(frames: some Sequence) -> String { + public func format(frames: some Sequence, + addressWidth: Int) -> String { var rows: [TableRow] = [] var n = 0 for frame in frames { - rows += formatRows(frame: frame, index: n) + rows += formatRows(frame: frame, addressWidth: addressWidth, index: n) if case let .omittedFrames(count) = frame { n += count @@ -603,7 +616,8 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(backtrace: Backtrace) -> String { - return format(frames: backtrace.frames) + return format(frames: backtrace.frames, + addressWidth: (backtrace.addressWidth + 3) / 4) } /// Grab source lines for a symbolicated backtrace. @@ -613,11 +627,11 @@ public struct BacktraceFormatter { /// with the point at which the program crashed highlighted. private func formattedSourceLines(from sourceLocation: SymbolicatedBacktrace.SourceLocation, indent theIndent: Int = 2) -> String? { - guard let fp = _swift_backtrace_fopen(sourceLocation.path, "rt") else { + guard let fp = fopen(sourceLocation.path, "rt") else { return nil } defer { - _swift_backtrace_fclose(fp) + fclose(fp) } let indent = String(repeating: " ", count: theIndent) @@ -693,8 +707,8 @@ public struct BacktraceFormatter { } } - while _swift_backtrace_feof(fp) == 0 && _swift_backtrace_ferror(fp) == 0 { - guard let result = _swift_backtrace_fgets(buffer.baseAddress, + while feof(fp) == 0 && ferror(fp) == 0 { + guard let result = fgets(buffer.baseAddress, CInt(buffer.count), fp) else { break } @@ -720,18 +734,19 @@ public struct BacktraceFormatter { /// /// @result An array of strings, one per column. public func formatColumns(frame: SymbolicatedBacktrace.Frame, + addressWidth: Int, index: Int? = nil) -> [String] { let pc: String var attrs: [String] = [] switch frame.captured { case let .programCounter(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" case let .returnAddress(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" attrs.append("ra") case let .asyncResumePoint(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" attrs.append("async") case .omittedFrames(_), .truncated: pc = "" @@ -830,14 +845,18 @@ public struct BacktraceFormatter { /// Format a frame into a list of rows. /// - /// @param frame The frame to format. - /// @param index The frame index, if required. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result An array of table rows. public func formatRows(frame: SymbolicatedBacktrace.Frame, + addressWidth: Int, index: Int? = nil, showSource: Bool = true) -> [TableRow] { - let columns = formatColumns(frame: frame, index: index) + let columns = formatColumns(frame: frame, + addressWidth: addressWidth, + index: index) var rows: [TableRow] = [.columns(columns)] if showSource { @@ -855,14 +874,17 @@ public struct BacktraceFormatter { /// Format just one frame. /// - /// @param frame The frame to format. - /// @param index (Optional) frame index. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result A `String` containing the formatted data. public func format(frame: SymbolicatedBacktrace.Frame, + addressWidth: Int, index: Int? = nil, showSource: Bool = true) -> String { - let rows = formatRows(frame: frame, index: index, showSource: showSource) + let rows = formatRows(frame: frame, addressWidth: addressWidth, + index: index, showSource: showSource) return BacktraceFormatter.formatTable(rows, alignments: [.right]) } @@ -875,10 +897,12 @@ public struct BacktraceFormatter { /// Format the frame list from a symbolicated backtrace. /// - /// @param frames The frames to format. + /// @param frames The frames to format. + /// @param addressWidth The width, in characters, of an address. /// /// @result A `String` containing the formatted data. - public func format(frames: some Sequence) -> String { + public func format(frames: some Sequence, + addressWidth: Int) -> String { var rows: [TableRow] = [] var sourceLocationsShown = Set() @@ -898,7 +922,8 @@ public struct BacktraceFormatter { } } - rows += formatRows(frame: frame, index: n, showSource: showSource) + rows += formatRows(frame: frame, addressWidth: addressWidth, + index: n, showSource: showSource) if case let .omittedFrames(count) = frame.captured { n += count @@ -916,14 +941,16 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(backtrace: SymbolicatedBacktrace) -> String { - var result = format(frames: backtrace.frames) + let addressChars = (backtrace.addressWidth + 3) / 4 + var result = format(frames: backtrace.frames, addressWidth: addressChars) switch options._showImages { case .none: break case .all: result += "\n\nImages:\n" - result += format(images: backtrace.images) + result += format(images: backtrace.images, + addressWidth: addressChars) case .mentioned: var mentionedImages = Set() for frame in backtrace.frames { @@ -942,7 +969,7 @@ public struct BacktraceFormatter { } else { result += "\n\nImages (only mentioned):\n" } - result += format(images: images) + result += format(images: images, addressWidth: addressChars) } return result @@ -950,11 +977,13 @@ public struct BacktraceFormatter { /// Format a `Backtrace.Image` into a list of columns. /// - /// @param image The `Image` object to format. + /// @param image The `Image` object to format. + /// @param addressWidth The width of an address, in characters. /// /// @result An array of strings, one per column. - public func formatColumns(image: Backtrace.Image) -> [String] { - let addressRange = "\(hex(image.baseAddress))–\(hex(image.endOfText))" + public func formatColumns(image: Backtrace.Image, + addressWidth: Int) -> [String] { + let addressRange = "\(hex(image.baseAddress, width: addressWidth))–\(hex(image.endOfText, width: addressWidth))" let buildID: String if let bytes = image.buildID { buildID = hex(bytes) @@ -977,11 +1006,18 @@ public struct BacktraceFormatter { /// Format an array of `Backtrace.Image`s. /// - /// @param images The array of `Image` objects to format. + /// @param images The array of `Image` objects to format. + /// @param addressWidth The width of an address, in characters. /// /// @result A string containing the formatted data. - public func format(images: some Sequence) -> String { - let rows = images.map{ TableRow.columns(formatColumns(image: $0)) } + public func format(images: some Sequence, + addressWidth: Int) -> String { + let rows = images.map{ + TableRow.columns( + formatColumns(image: $0, + addressWidth: addressWidth) + ) + } return BacktraceFormatter.formatTable(rows) } diff --git a/stdlib/public/Backtracing/ByteSwapping.swift b/stdlib/public/Backtracing/ByteSwapping.swift new file mode 100644 index 0000000000000..0ab94299f0ebd --- /dev/null +++ b/stdlib/public/Backtracing/ByteSwapping.swift @@ -0,0 +1,95 @@ +//===--- ByteSwapping.swift - Utilities for byte swapping -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines a ByteSwappable protocol that types can implement to indicate that +// they are able to perform byte swap operations. +// +// Mostly the types that implement this should be defined in C. +// +//===----------------------------------------------------------------------===// + +import Swift + +protocol ByteSwappable { + var byteSwapped: Self { get } + var bigEndian: Self { get } + var littleEndian: Self { get } + + init(bigEndian: Self) + init(littleEndian: Self) +} + +extension ByteSwappable { + init(bigEndian value: Self) { +#if _endian(big) + self = value +#else + self = value.byteSwapped +#endif + } + + init(littleEndian value: Self) { +#if _endian(little) + self = value +#else + self = value.byteSwapped +#endif + } + + var littleEndian: Self { +#if _endian(little) + return self +#else + return self.byteSwapped +#endif + } + + var bigEndian: Self { +#if _endian(big) + return self +#else + return self.byteSwapped +#endif + } +} + +extension Array where Self.Element: ByteSwappable { + mutating func swapBytes() { + for n in 0.. UnsafeBufferPointer + typealias OutputSink = (_ used: UInt, _ done: Bool) throws + -> UnsafeMutableBufferPointer? + + func decompress(input: InputSource, output: OutputSink) throws -> UInt +} + +// .. Compression library bindings ............................................. + +private var lzmaHandle = dlopen("liblzma.so.5", RTLD_LAZY) +private var zlibHandle = dlopen("libz.so.1", RTLD_LAZY) +private var zstdHandle = dlopen("libzstd.so.1", RTLD_LAZY) + +private func symbol(_ handle: UnsafeMutableRawPointer?, _ name: String) -> T? { + guard let handle = handle, let result = dlsym(handle, name) else { + return nil + } + return unsafeBitCast(result, to: T.self) +} + +private enum Sym { + static let lzma_stream_decoder: ( + @convention(c) (UnsafeMutablePointer, + UInt64, UInt32) -> lzma_ret)? + = symbol(lzmaHandle, "lzma_stream_decoder") + + static let lzma_code: (@convention(c) (UnsafeMutablePointer, + lzma_action) -> lzma_ret)? + = symbol(lzmaHandle, "lzma_code") + + static let lzma_end: (@convention(c) (UnsafeMutablePointer) -> ())? + = symbol(lzmaHandle, "lzma_end") + + static let inflateInit_: (@convention(c) (z_streamp, + UnsafePointer, CInt) -> CInt)? + = symbol(zlibHandle, "inflateInit_") + + static func inflateInit(_ stream: z_streamp) -> CInt { + return inflateInit_!(stream, ZLIB_VERSION, CInt(MemoryLayout.size)) + } + + static let inflate: (@convention(c) (z_streamp, CInt) -> CInt)? + = symbol(zlibHandle, "inflate") + + static let inflateEnd: (@convention(c) (z_streamp) -> CInt)? + = symbol(zlibHandle, "inflateEnd") + + static let ZSTD_createDStream: ( + @convention(c) () -> UnsafeMutableRawPointer?)? + = symbol(zstdHandle, "ZSTD_createDStream") + + static let ZSTD_freeDStream: ( + @convention(c) (UnsafeMutableRawPointer) -> UInt)? + = symbol(zstdHandle, "ZSTD_freeDStream") + + static let ZSTD_decompressStream: ( + @convention(c) (UnsafeMutableRawPointer, + UnsafeMutablePointer, + UnsafeMutablePointer) -> UInt)? + = symbol(zstdHandle, "ZSTD_decompressStream") + + static let ZSTD_isError: (@convention(c) (UInt) -> UInt)? + = symbol(zstdHandle, "ZSTD_isError") +} + +// .. zlib (deflate) ........................................................... + +enum ZLibError: Error { + case decodeError(CInt) +} + +struct ZLibStream: CompressedStream { + init() {} + + func decompress(input: InputSource, output: OutputSink) throws -> UInt { + + if zlibHandle == nil { + throw CompressedImageSourceError.libraryNotFound("libz") + } + + var stream = zlib_stream_init() + + let ret = Sym.inflateInit(&stream) + if ret != Z_OK { + throw ZLibError.decodeError(ret) + } + defer { + _ = Sym.inflateEnd!(&stream) + } + + var outputBufferSize = UInt(0) + while true { + if stream.avail_in == 0 { + let buffer = try input() + + // Not really mutable; this is just an issue with z_const + stream.next_in = UnsafeMutablePointer(mutating: buffer.baseAddress) + stream.avail_in = CUnsignedInt(buffer.count) + } + + if stream.avail_out == 0 { + guard let buffer = try output(outputBufferSize, false) else { + throw CompressedImageSourceError.outputOverrun + } + + stream.next_out = buffer.baseAddress + stream.avail_out = CUnsignedInt(buffer.count) + outputBufferSize = UInt(buffer.count) + } + + let ret = Sym.inflate!(&stream, Z_NO_FLUSH) + + if ret == Z_STREAM_END { + _ = try output(outputBufferSize - UInt(stream.avail_out), true) + return stream.total_out + } + + if ret != Z_OK { + throw ZLibError.decodeError(ret) + } + } + } +} + +// .. zstd ..................................................................... + +enum ZStdError: Error { + case unableToCreateStream + case decodeError(UInt) +} + +struct ZStdStream: CompressedStream { + init() {} + + func decompress(input: InputSource, output: OutputSink) throws -> UInt { + + if zstdHandle == nil { + throw CompressedImageSourceError.libraryNotFound("libzstd") + } + + guard let stream = Sym.ZSTD_createDStream!() else { + throw ZStdError.unableToCreateStream + } + defer { + _ = Sym.ZSTD_freeDStream!(stream) + } + + var inBuffer = ZSTD_inBuffer(src: nil, size: 0, pos: 0) + var outBuffer = ZSTD_outBuffer(dst: nil, size: 0, pos: 0) + var totalOutput = UInt(0) + + while true { + if inBuffer.size == inBuffer.pos { + let buffer = try input() + + inBuffer.src = UnsafeRawPointer(buffer.baseAddress) + inBuffer.size = buffer.count + inBuffer.pos = 0 + } + + if outBuffer.size == outBuffer.pos { + let byteCount = UInt(outBuffer.pos) + + totalOutput += byteCount + + guard let buffer = try output(byteCount, false) else { + throw CompressedImageSourceError.outputOverrun + } + + outBuffer.dst = UnsafeMutableRawPointer(buffer.baseAddress) + outBuffer.size = buffer.count + outBuffer.pos = 0 + } + + let ret = Sym.ZSTD_decompressStream!(stream, &outBuffer, &inBuffer) + + if ret == 0 { + _ = try output(UInt(outBuffer.pos), true) + return totalOutput + } + + if Sym.ZSTD_isError!(ret) != 0 { + throw ZStdError.decodeError(ret) + } + } + } +} + + +// .. LZMA ..................................................................... + +enum LZMAError: Error { + case decodeError(lzma_ret) +} + +struct LZMAStream: CompressedStream { + private var memlimit: UInt64 + private var flags: UInt32 + + init(memlimit: UInt64 = ~UInt64(0), flags: UInt32 = 0) { + self.memlimit = memlimit + self.flags = flags + } + + func decompress(input: InputSource, output: OutputSink) throws -> UInt { + + if lzmaHandle == nil { + throw CompressedImageSourceError.libraryNotFound("liblzma") + } + + var stream = lzma_stream_init() + + let ret = Sym.lzma_stream_decoder!(&stream, memlimit, flags) + if ret != LZMA_OK { + throw LZMAError.decodeError(ret) + } + defer { + Sym.lzma_end!(&stream) + } + + var outputBufferSize = UInt(0) + while true { + if stream.avail_in == 0 { + let buffer = try input() + stream.next_in = buffer.baseAddress + stream.avail_in = buffer.count + } + + if stream.avail_out == 0 { + guard let buffer = try output(outputBufferSize, false) else { + throw CompressedImageSourceError.outputOverrun + } + + stream.next_out = buffer.baseAddress + stream.avail_out = buffer.count + outputBufferSize = UInt(buffer.count) + } + + let ret = Sym.lzma_code!(&stream, LZMA_RUN) + + if ret == LZMA_STREAM_END { + _ = try output(outputBufferSize - UInt(stream.avail_out), true) + return UInt(stream.total_out) + } + + if ret != LZMA_OK { + throw LZMAError.decodeError(ret) + } + } + } +} + +// .. Image Sources ............................................................ + +fileprivate func decompress( + stream: S, source: I, dataBounds: I.Bounds, uncompressedSize: UInt? = nil) + throws -> [UInt8] { + + var pos = dataBounds.base + var remaining = dataBounds.size + + let bufSize = 65536 + + if let uncompressedSize = uncompressedSize { + // If we know the uncompressed size, we can decompress directly into the + // array. + + let inputBuffer + = UnsafeMutableBufferPointer.allocate(capacity: bufSize) + defer { + inputBuffer.deallocate() + } + + return try [UInt8].init(unsafeUninitializedCapacity: Int(uncompressedSize)) { + (outputBuffer: inout UnsafeMutableBufferPointer, + count: inout Int) in + + count = Int(try stream.decompress( + input: { () throws -> UnsafeBufferPointer in + + let chunkSize = min(Int(remaining), inputBuffer.count) + let slice = inputBuffer[0.. UnsafeMutableBufferPointer? in + + if used == 0 { + return outputBuffer + } else { + return nil + } + } + )) + } + } else { + // Otherwise, we decompress in chunks and append them to the array. + + let buffer + = UnsafeMutableBufferPointer.allocate(capacity: 2 * bufSize) + defer { + buffer.deallocate() + } + + let inputBuffer = UnsafeMutableBufferPointer(rebasing: buffer[0.. UnsafeBufferPointer in + + let chunkSize = min(Int(remaining), inputBuffer.count) + let slice = inputBuffer[0.. UnsafeMutableBufferPointer? in + + data.append(contentsOf: outputBuffer[..: ImageSource { + + private var data: [UInt8] + + var isMappedImage: Bool { return false } + var path: String? { return nil } + var bounds: Bounds? { return Bounds(base: Address(0), size: Size(data.count)) } + + init(source: some ImageSource) throws { + guard let bounds = source.bounds else { + throw CompressedImageSourceError.unboundedImageSource + } + + if bounds.size < MemoryLayout.size { + throw CompressedImageSourceError.badCompressedData + } + + let chdr = try source.fetch(from: bounds.base, + as: Traits.Chdr.self) + let dataBounds = bounds.adjusted(by: MemoryLayout.stride) + let uncompressedSize = UInt(chdr.ch_size) + + switch chdr.ch_type { + case .ELFCOMPRESS_ZLIB: + data = try decompress(stream: ZLibStream(), + source: source, dataBounds: dataBounds, + uncompressedSize: uncompressedSize) + case .ELFCOMPRESS_ZSTD: + data = try decompress(stream: ZStdStream(), + source: source, dataBounds: dataBounds, + uncompressedSize: uncompressedSize) + default: + throw CompressedImageSourceError.unsupportedFormat + } + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let toFetch = buffer.count * MemoryLayout.stride + if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch { + throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) + } + + buffer.withMemoryRebound(to: UInt8.self) { outBuf in + for n in 0..(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let toFetch = buffer.count * MemoryLayout.stride + if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch { + throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) + } + + buffer.withMemoryRebound(to: UInt8.self) { outBuf in + for n in 0..(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let toFetch = buffer.count * MemoryLayout.stride + if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch { + throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) + } + + buffer.withMemoryRebound(to: UInt8.self) { outBuf in + for n in 0.. HostContext? { - return X86_64Context(from: thread as! __swift_thread_t) + return X86_64Context(from: thread as! thread_t) } public static func fromHostMContext(_ mcontext: Any) -> HostContext { return X86_64Context(with: mcontext as! darwin_x86_64_mcontext) } + #elseif os(Linux) && arch(x86_64) + init(with mctx: mcontext_t) { + gprs.setR(X86_64Register.rax.rawValue, to: UInt64(bitPattern: mctx.gregs.13)) + gprs.setR(X86_64Register.rbx.rawValue, to: UInt64(bitPattern: mctx.gregs.12)) + gprs.setR(X86_64Register.rcx.rawValue, to: UInt64(bitPattern: mctx.gregs.14)) + gprs.setR(X86_64Register.rdx.rawValue, to: UInt64(bitPattern: mctx.gregs.11)) + gprs.setR(X86_64Register.rdi.rawValue, to: UInt64(bitPattern: mctx.gregs.9)) + gprs.setR(X86_64Register.rsi.rawValue, to: UInt64(bitPattern: mctx.gregs.8)) + gprs.setR(X86_64Register.rbp.rawValue, to: UInt64(bitPattern: mctx.gregs.10)) + gprs.setR(X86_64Register.rsp.rawValue, to: UInt64(bitPattern: mctx.gregs.15)) + gprs.setR(X86_64Register.r8.rawValue, to: UInt64(bitPattern: mctx.gregs.0)) + gprs.setR(X86_64Register.r9.rawValue, to: UInt64(bitPattern: mctx.gregs.1)) + gprs.setR(X86_64Register.r10.rawValue, to: UInt64(bitPattern: mctx.gregs.2)) + gprs.setR(X86_64Register.r11.rawValue, to: UInt64(bitPattern: mctx.gregs.3)) + gprs.setR(X86_64Register.r12.rawValue, to: UInt64(bitPattern: mctx.gregs.4)) + gprs.setR(X86_64Register.r13.rawValue, to: UInt64(bitPattern: mctx.gregs.5)) + gprs.setR(X86_64Register.r14.rawValue, to: UInt64(bitPattern: mctx.gregs.6)) + gprs.setR(X86_64Register.r15.rawValue, to: UInt64(bitPattern: mctx.gregs.7)) + gprs.rip = UInt64(bitPattern: mctx.gregs.16) + gprs.rflags = UInt64(bitPattern: mctx.gregs.17) + gprs.cs = UInt16(mctx.gregs.18 & 0xffff) + gprs.fs = UInt16((mctx.gregs.18 >> 16) & 0xffff) + gprs.gs = UInt16((mctx.gregs.18 >> 32) & 0xffff) + gprs.valid = 0x1fffff + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return X86_64Context(with: mcontext as! mcontext_t) + } #endif #if os(Windows) || !SWIFT_ASM_AVAILABLE @@ -398,6 +438,8 @@ extension arm_gprs { var gprs = i386_gprs() + public var architecture: String { "i386" } + public var programCounter: GPRValue { get { return gprs.eip } set { @@ -423,6 +465,32 @@ extension arm_gprs { public static var registerCount: Int { return 50 } + #if os(Linux) && arch(i386) + init(with mctx: mcontext_t) { + gprs.setR(I386Register.eax.rawValue, to: UInt32(bitPattern: mctx.gregs.11)) + gprs.setR(I386Register.ecx.rawValue, to: UInt32(bitPattern: mctx.gregs.10)) + gprs.setR(I386Register.edx.rawValue, to: UInt32(bitPattern: mctx.gregs.9)) + gprs.setR(I386Register.ebx.rawValue, to: UInt32(bitPattern: mctx.gregs.8)) + gprs.setR(I386Register.esp.rawValue, to: UInt32(bitPattern: mctx.gregs.7)) + gprs.setR(I386Register.ebp.rawValue, to: UInt32(bitPattern: mctx.gregs.6)) + gprs.setR(I386Register.ebp.rawValue, to: UInt32(bitPattern: mctx.gregs.5)) + gprs.setR(I386Register.ebp.rawValue, to: UInt32(bitPattern: mctx.gregs.4)) + gprs.eip = UInt32(bitPattern: mctx.gregs.14) + gprs.eflags = UInt32(bitPattern: mctx.gregs.16) + gprs.segreg.0 = UInt16(bitPattern: mctx.gregs.2 & 0xffff) // es + gprs.segreg.1 = UInt16(bitPattern: mctx.gregs.15 & 0xffff) // cs + gprs.segreg.2 = UInt16(bitPattern: mctx.gregs.18 & 0xffff) // ss + gprs.segreg.3 = UInt16(bitPattern: mctx.gregs.3 & 0xffff) // ds + gprs.segreg.4 = UInt16(bitPattern: mctx.gregs.1 & 0xffff) // fs + gprs.segreg.5 = UInt16(bitPattern: mctx.gregs.0 & 0xffff) // gs + gprs.valid = 0x7fff + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return I386Context(with: mcontext as! mcontext_t) + } + #endif + #if os(Windows) || !SWIFT_ASM_AVAILABLE struct NotImplemented: Error {} public static func withCurrentContext(fn: (I386Context) throws -> T) throws -> T { @@ -553,6 +621,8 @@ extension arm_gprs { var gprs = arm64_gprs() + public var architecture: String { "arm64" } + public var programCounter: GPRValue { get { return gprs.pc } set { @@ -583,12 +653,12 @@ extension arm_gprs { public static var registerCount: Int { return 40 } #if os(macOS) && arch(arm64) - init?(from thread: __swift_thread_t) { + init?(from thread: thread_t) { var state = darwin_arm64_thread_state() let kr = thread_get_state(thread, - _SWIFT_ARM_THREAD_STATE64, + ARM_THREAD_STATE64, &state) - if kr != _SWIFT_KERN_SUCCESS { + if kr != KERN_SUCCESS { return nil } @@ -620,13 +690,35 @@ extension arm_gprs { } public static func fromHostThread(_ thread: Any) -> HostContext? { - return ARM64Context(from: thread as! __swift_thread_t) + return ARM64Context(from: thread as! thread_t) } public static func fromHostMContext(_ mcontext: Any) -> HostContext { return ARM64Context(with: mcontext as! darwin_arm64_mcontext) } -#endif + #elseif os(Linux) && arch(arm64) + init(with mctx: mcontext_t) { + withUnsafeMutablePointer(to: &gprs._x) { + $0.withMemoryRebound(to: UInt64.self, capacity: 32){ to in + withUnsafePointer(to: mctx.regs) { + $0.withMemoryRebound(to: UInt64.self, capacity: 31) { from in + for n in 0..<31 { + to[n] = from[n] + } + } + } + + to[31] = mctx.sp + } + } + gprs.pc = mctx.pc + gprs.valid = 0x1ffffffff + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return ARM64Context(with: mcontext as! mcontext_t) + } + #endif #if os(Windows) || !SWIFT_ASM_AVAILABLE struct NotImplemented: Error {} @@ -747,6 +839,8 @@ extension arm_gprs { var gprs = arm_gprs() + public var architecture: String { "arm" } + public var programCounter: GPRValue { get { return gprs.getR(ARMRegister.r15.rawValue) } set { gprs.setR(ARMRegister.r15.rawValue, to: newValue) } @@ -769,6 +863,27 @@ extension arm_gprs { public static var registerCount: Int { return 16 } + #if os(Linux) && arch(arm) + init(with mctx: mcontext_t) { + withUnsafeMutablePointer(to: &gprs._r) { + $0.withMemoryRebound(to: UInt32.self, capacity: 16) { + withUnsafePointer(to: &mctx.arm_r0) { + $0.withMemoryRebound(to: UInt32.self, capacity: 16) { + for n in 0..<16 { + to[n] = from[n] + } + } + } + } + } + gprs.valid = 0xffff + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return ARMContext(with: mcontext as! mcontext_t) + } + #endif + #if os(Windows) || !SWIFT_ASM_AVAILABLE struct NotImplemented: Error {} public static func withCurrentContext(fn: (ARMContext) throws -> T) throws -> T { @@ -860,20 +975,20 @@ extension arm_gprs { // .. Darwin specifics ......................................................... #if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) -private func thread_get_state(_ thread: __swift_thread_t, +private func thread_get_state(_ thread: thread_t, _ flavor: CInt, - _ result: inout T) -> __swift_kern_return_t { - var count: __swift_msg_type_number_t - = __swift_msg_type_number_t(MemoryLayout.stride - / MemoryLayout<__swift_natural_t>.stride) + _ result: inout T) -> kern_return_t { + var count: mach_msg_type_number_t + = mach_msg_type_number_t(MemoryLayout.stride + / MemoryLayout.stride) return withUnsafeMutablePointer(to: &result) { ptr in - ptr.withMemoryRebound(to: __swift_natural_t.self, + ptr.withMemoryRebound(to: natural_t.self, capacity: Int(count)) { intPtr in - return _swift_backtrace_thread_get_state(thread, - __swift_thread_state_flavor_t(flavor), - intPtr, - &count) + return thread_get_state(thread, + thread_state_flavor_t(flavor), + intPtr, + &count) } } } diff --git a/stdlib/public/Backtracing/CoreSymbolication.swift b/stdlib/public/Backtracing/CoreSymbolication.swift index 43388a5f15727..e5cbbeea03058 100644 --- a/stdlib/public/Backtracing/CoreSymbolication.swift +++ b/stdlib/public/Backtracing/CoreSymbolication.swift @@ -19,29 +19,28 @@ import Swift -@_implementationOnly import _SwiftBacktracingShims +@_implementationOnly import OS.Libc +@_implementationOnly import OS.Darwin // .. Dynamic binding .......................................................... private let coreFoundationPath = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" -private let coreFoundationHandle = - _swift_backtrace_dlopen_lazy(coreFoundationPath)! +private let coreFoundationHandle = dlopen(coreFoundationPath, RTLD_LAZY)! private let coreSymbolicationPath = "/System/Library/PrivateFrameworks/CoreSymbolication.framework/CoreSymbolication" -private let coreSymbolicationHandle = - _swift_backtrace_dlopen_lazy(coreSymbolicationPath)! +private let coreSymbolicationHandle = dlopen(coreSymbolicationPath, RTLD_LAZY)! private let crashReporterSupportPath = "/System/Library/PrivateFrameworks/CrashReporterSupport.framework/CrashReporterSupport" private let crashReporterSupportHandle - = _swift_backtrace_dlopen_lazy(crashReporterSupportPath)! + = dlopen(crashReporterSupportPath, RTLD_LAZY)! private func symbol(_ handle: UnsafeMutableRawPointer, _ name: String) -> T { - guard let result = _swift_backtrace_dlsym(handle, name) else { + guard let result = dlsym(handle, name) else { fatalError("Unable to look up \(name) in CoreSymbolication") } return unsafeBitCast(result, to: T.self) @@ -49,7 +48,7 @@ private func symbol(_ handle: UnsafeMutableRawPointer, _ name: String) -> T { private enum Sym { // CRCopySanitizedPath - static let CRCopySanitizedPath: @convention(c) (CFStringRef, CFIndex) -> CFStringRef = + static let CRCopySanitizedPath: @convention(c) (CFString, CFIndex) -> CFString = symbol(crashReporterSupportHandle, "CRCopySanitizedPath") // Base functionality @@ -69,7 +68,7 @@ private enum Sym { symbol(coreSymbolicationHandle, "CSSymbolicatorCreateWithBinaryImageList") static let CSSymbolicatorGetSymbolOwnerWithAddressAtTime: - @convention(c) (CSSymbolicatorRef, __swift_vm_address_t, + @convention(c) (CSSymbolicatorRef, vm_address_t, CSMachineTime) -> CSSymbolOwnerRef = symbol(coreSymbolicationHandle, "CSSymbolicatorGetSymbolOwnerWithAddressAtTime") static let CSSymbolicatorForeachSymbolOwnerAtTime: @@ -81,16 +80,16 @@ private enum Sym { @convention(c) (CSSymbolOwnerRef) -> UnsafePointer? = symbol(coreSymbolicationHandle, "CSSymbolOwnerGetName") static let CSSymbolOwnerGetSymbolWithAddress: - @convention(c) (CSSymbolOwnerRef, __swift_vm_address_t) -> CSSymbolRef = + @convention(c) (CSSymbolOwnerRef, vm_address_t) -> CSSymbolRef = symbol(coreSymbolicationHandle, "CSSymbolOwnerGetSymbolWithAddress") static let CSSymbolOwnerGetSourceInfoWithAddress: - @convention(c) (CSSymbolOwnerRef, __swift_vm_address_t) -> CSSourceInfoRef = + @convention(c) (CSSymbolOwnerRef, vm_address_t) -> CSSourceInfoRef = symbol(coreSymbolicationHandle, "CSSymbolOwnerGetSourceInfoWithAddress") static let CSSymbolOwnerForEachStackFrameAtAddress: - @convention(c) (CSSymbolOwnerRef, __swift_vm_address_t, CSStackFrameIterator) -> UInt = + @convention(c) (CSSymbolOwnerRef, vm_address_t, CSStackFrameIterator) -> UInt = symbol(coreSymbolicationHandle, "CSSymbolOwnerForEachStackFrameAtAddress") static let CSSymbolOwnerGetBaseAddress: - @convention(c) (CSSymbolOwnerRef) -> __swift_vm_address_t = + @convention(c) (CSSymbolOwnerRef) -> vm_address_t = symbol(coreSymbolicationHandle, "CSSymbolOwnerGetBaseAddress") // CSSymbol @@ -117,17 +116,17 @@ private enum Sym { // CFString static let CFStringCreateWithBytes: - @convention(c) (CFAllocatorRef?, UnsafeRawPointer?, CFIndex, - CFStringEncoding, Bool) -> CFStringRef? = + @convention(c) (CFAllocator?, UnsafeRawPointer?, CFIndex, + CFStringEncoding, Bool) -> CFString? = symbol(coreFoundationHandle, "CFStringCreateWithBytes") static let CFStringGetLength: - @convention(c) (CFStringRef) -> CFIndex = + @convention(c) (CFString) -> CFIndex = symbol(coreFoundationHandle, "CFStringGetLength") static let CFStringGetCStringPtr: - @convention(c) (CFStringRef, CFStringEncoding) -> UnsafePointer? = + @convention(c) (CFString, CFStringEncoding) -> UnsafePointer? = symbol(coreFoundationHandle, "CFStringGetCStringPtr") static let CFStringGetBytes: - @convention(c) (CFStringRef, CFRange, CFStringEncoding, UInt8, Bool, + @convention(c) (CFString, CFRange, CFStringEncoding, UInt8, Bool, UnsafeMutableRawPointer?, CFIndex, UnsafeMutablePointer?) -> CFIndex = symbol(coreFoundationHandle, "CFStringGetBytes") @@ -135,29 +134,16 @@ private enum Sym { // .. Core Foundation miscellany ............................................... -internal typealias CFTypeRef = OpaquePointer -internal typealias CFStringRef = CFTypeRef -internal typealias CFAllocatorRef = CFTypeRef -internal typealias CFIndex = __swift_backtrace_CFIndex -internal typealias CFRange = __swift_backtrace_CFRange -internal typealias CFUUIDBytes = __swift_backtrace_CFUUIDBytes -internal typealias CFStringEncoding = UInt32 - -internal enum CFStringBuiltInEncodings: UInt32 { - case ASCII = 0x0600 - case UTF8 = 0x08000100 -} - internal func CFRangeMake(_ location: CFIndex, _ length: CFIndex) -> CFRange { return CFRange(location: location, length: length) } -internal func CFStringCreateWithBytes(_ allocator: CFAllocatorRef?, +internal func CFStringCreateWithBytes(_ allocator: CFAllocator?, _ bytes: UnsafeRawPointer?, _ length: CFIndex, _ encoding: CFStringEncoding, _ isExternalRepresentation: Bool) - -> CFStringRef? { + -> CFString? { return Sym.CFStringCreateWithBytes(allocator, bytes, length, @@ -165,17 +151,17 @@ internal func CFStringCreateWithBytes(_ allocator: CFAllocatorRef?, isExternalRepresentation) } -internal func CFStringGetLength(_ s: CFStringRef) -> CFIndex { +internal func CFStringGetLength(_ s: CFString) -> CFIndex { return Sym.CFStringGetLength(s) } -internal func CFStringGetCStringPtr(_ s: CFStringRef, +internal func CFStringGetCStringPtr(_ s: CFString, _ encoding: CFStringEncoding) -> UnsafePointer? { return Sym.CFStringGetCStringPtr(s, encoding) } -internal func CFStringGetBytes(_ s: CFStringRef, +internal func CFStringGetBytes(_ s: CFString, _ range: CFRange, _ encoding: CFStringEncoding, _ lossByte: UInt8, @@ -194,7 +180,7 @@ internal func CFStringGetBytes(_ s: CFStringRef, // We can't import swiftFoundation here, so there's no automatic bridging for // CFString. As a result, we need to do the dance manually. -private func toCFString(_ s: String) -> CFStringRef! { +private func toCFString(_ s: String) -> CFString! { var s = s return s.withUTF8 { return CFStringCreateWithBytes(nil, @@ -205,7 +191,7 @@ private func toCFString(_ s: String) -> CFStringRef! { } } -private func fromCFString(_ cf: CFStringRef) -> String { +private func fromCFString(_ cf: CFString) -> String { let length = CFStringGetLength(cf) if length == 0 { return "" @@ -267,14 +253,14 @@ func CSIsNull(_ obj: CSTypeRef) -> Bool { let kCSSymbolicatorDisallowDaemonCommunication = UInt32(0x00000800) struct BinaryRelocationInformation { - var base: __swift_vm_address_t - var extent: __swift_vm_address_t + var base: vm_address_t + var extent: vm_address_t var name: String } struct BinaryImageInformation { - var base: __swift_vm_address_t - var extent: __swift_vm_address_t + var base: vm_address_t + var extent: vm_address_t var uuid: CFUUIDBytes var arch: CSArchitecture var path: String @@ -322,7 +308,7 @@ func CSSymbolicatorCreateWithBinaryImageList( imageList[n].relocationCount = UInt32(image.relocations.count) imageList[n].flags = image.flags - pathPtr += _swift_backtrace_strlen(pathPtr) + 1 + pathPtr += strlen(pathPtr) + 1 for relocation in image.relocations { relocationPtr.pointee.base = relocation.base @@ -355,7 +341,7 @@ func CSSymbolicatorCreateWithBinaryImageList( func CSSymbolicatorGetSymbolOwnerWithAddressAtTime( _ symbolicator: CSSymbolicatorRef, - _ addr: __swift_vm_address_t, + _ addr: vm_address_t, _ time: CSMachineTime ) -> CSSymbolOwnerRef { return Sym.CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, @@ -380,21 +366,21 @@ func CSSymbolOwnerGetName(_ sym: CSTypeRef) -> String? { func CSSymbolOwnerGetSymbolWithAddress( _ owner: CSSymbolOwnerRef, - _ address: __swift_vm_address_t + _ address: vm_address_t ) -> CSSymbolRef { return Sym.CSSymbolOwnerGetSymbolWithAddress(owner, address) } func CSSymbolOwnerGetSourceInfoWithAddress( _ owner: CSSymbolOwnerRef, - _ address: __swift_vm_address_t + _ address: vm_address_t ) -> CSSourceInfoRef { return Sym.CSSymbolOwnerGetSourceInfoWithAddress(owner, address) } func CSSymbolOwnerForEachStackFrameAtAddress( _ owner: CSSymbolOwnerRef, - _ address: __swift_vm_address_t, + _ address: vm_address_t, _ iterator: CSStackFrameIterator ) -> UInt { return Sym.CSSymbolOwnerForEachStackFrameAtAddress(owner, address, iterator) @@ -402,7 +388,7 @@ func CSSymbolOwnerForEachStackFrameAtAddress( func CSSymbolOwnerGetBaseAddress( _ owner: CSSymbolOwnerRef -) -> __swift_vm_address_t { +) -> vm_address_t { return Sym.CSSymbolOwnerGetBaseAddress(owner) } diff --git a/stdlib/public/Backtracing/Dwarf.swift b/stdlib/public/Backtracing/Dwarf.swift new file mode 100644 index 0000000000000..7d9fba1e44733 --- /dev/null +++ b/stdlib/public/Backtracing/Dwarf.swift @@ -0,0 +1,1786 @@ +//===--- Dwarf.swift - DWARF support for Swift ----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines various DWARF structures and provides types for working with +// DWARF data on disk and in memory. +// +//===----------------------------------------------------------------------===// + +#if os(Linux) + +import Swift + +@_implementationOnly import ImageFormats.Dwarf +@_implementationOnly import Runtime + +// .. Dwarf specific errors .................................................... + +private enum DwarfError: Error { + case noDebugInformation + case unsupportedVersion(Int) + case unknownEHValueEncoding + case unknownEHOffsetEncoding + case badAttribute(UInt64) + case badForm(UInt64) + case badTag(UInt64) + case badLength(UInt32) + case badAddressSize(Int) + case badLineContentType(UInt64) + case badString + case missingAbbrev(UInt64) + case doubleIndirectForm + case unknownForm(Dwarf_Form) + case missingBaseOffset + case missingAddrSection + case missingStrSection + case missingLineStrSection + case missingStrOffsetsSection + case missingAddrBase + case missingStrOffsetsBase + case missingLocListsBase + case unspecifiedAddressSize +} + +// .. Dwarf utilities for ImageSource .......................................... + +extension ImageSource { + + func fetchULEB128(from a: Address) throws -> (Address, UInt64) { + var addr = a + var shift = 0 + var value: UInt64 = 0 + while true { + let byte = try fetch(from: addr, as: UInt8.self) + addr += 1 + value |= UInt64(byte & 0x7f) << shift + if (byte & 0x80) == 0 { + break + } + shift += 7 + } + + return (addr, value) + } + + func fetchSLEB128(from a: Address) throws -> (Address, Int64) { + var addr = a + var shift = 0 + var sign: UInt8 = 0 + var value: Int64 = 0 + + while true { + let byte = try fetch(from: addr, as: UInt8.self) + addr += 1 + value |= Int64(byte & 0x7f) << shift + shift += 7 + sign = byte & 0x40 + if (byte & 0x80) == 0 { + break + } + } + + if shift < 64 && sign != 0 { + value |= -(1 << shift) + } + + return (addr, value) + } + + func fetchEHValue(from a: Address, with encoding: EHFrameEncoding, + pc: Address = 0, data: Address = 0, shouldSwap: Bool = false) throws + -> (Address, UInt64)? { + + func maybeSwap(_ x: T) -> T { + if shouldSwap { + return x.byteSwapped + } + return x + } + + let valueEnc = EHFrameEncoding(encoding & 0x0f) + var value: UInt64 = 0 + var addr = a + + switch valueEnc { + case DW_EH_PE_omit: + return nil + case DW_EH_PE_uleb128: + (addr, value) = try fetchULEB128(from: addr) + case DW_EH_PE_udata2: + let u2 = maybeSwap(try fetch(from: addr, as: UInt16.self)) + value = UInt64(u2) + addr += 2 + case DW_EH_PE_udata4: + let u4 = maybeSwap(try fetch(from: addr, as: UInt32.self)) + value = UInt64(u4) + addr += 4 + case DW_EH_PE_udata8: + let u8 = maybeSwap(try fetch(from: addr, as: UInt64.self)) + value = u8 + addr += 8 + case DW_EH_PE_sleb128: + let (newAddr, newValue) = try fetchSLEB128(from: addr) + value = UInt64(bitPattern: newValue) + addr = newAddr + case DW_EH_PE_sdata2: + let s2 = maybeSwap(try fetch(from: addr, as: Int16.self)) + value = UInt64(bitPattern: Int64(s2)) + addr += 2 + case DW_EH_PE_sdata4: + let s4 = maybeSwap(try fetch(from: addr, as: Int32.self)) + value = UInt64(bitPattern: Int64(s4)) + addr += 4 + case DW_EH_PE_sdata8: + let s8 = maybeSwap(try fetch(from: addr, as: Int64.self)) + value = UInt64(bitPattern: s8) + addr += 8 + default: + throw DwarfError.unknownEHValueEncoding + } + + let offsetEnc = EHFrameEncoding(encoding & 0xf0) + + switch offsetEnc { + case DW_EH_PE_absptr: + return (addr, value) + case DW_EH_PE_pcrel: + return (addr, UInt64(pc) &+ value) + case DW_EH_PE_datarel: + return (addr, UInt64(data) &+ value) + default: + throw DwarfError.unknownEHOffsetEncoding + } + } + + func fetchDwarfLength(from addr: Address) throws + -> (length: UInt64, isDwarf64: Bool) { + + let len32 = try fetch(from: addr, as: UInt32.self) + if len32 < 0xfffffff0 { + return (length: UInt64(len32), isDwarf64: false) + } else if len32 < 0xffffffff { + throw DwarfError.badLength(len32) + } else { + let len64 = try fetch(from: addr + 4, as: UInt64.self) + return (length: len64, isDwarf64: true) + } + } +} + +// .. Dwarf utilities for ImageSourceCursor ..................................... + +extension ImageSourceCursor { + + mutating func readULEB128() throws -> UInt64 { + let (next, result) = try source.fetchULEB128(from: pos) + pos = next + return result + } + + mutating func readSLEB128() throws -> Int64 { + let (next, result) = try source.fetchSLEB128(from: pos) + pos = next + return result + } + + mutating func readEHValue( + with encoding: EHFrameEncoding, + pc: Address = 0, + data: Address = 0, + shouldSwap: Bool = false + ) throws -> UInt64? { + guard let (next, result) + = try source.fetchEHValue(from: pos, + with: encoding, + pc: pc, + data: data, + shouldSwap: shouldSwap) else { + return nil + } + + pos = next + return result + } + + mutating func readDwarfLength() throws -> (length: UInt64, isDwarf64: Bool) { + let result = try source.fetchDwarfLength(from: pos) + pos += result.isDwarf64 ? 12 : 4 + return result + } + +} + +// .. DwarfReader ............................................................... + +enum DwarfSection { + case debugAbbrev + case debugAddr + case debugARanges + case debugFrame + case debugInfo + case debugLine + case debugLineStr + case debugLoc + case debugLocLists + case debugMacInfo + case debugMacro + case debugNames + case debugPubNames + case debugPubTypes + case debugRanges + case debugRngLists + case debugStr + case debugStrOffsets + case debugSup + case debugTypes + case debugCuIndex + case debugTuIndex +} + +protocol DwarfSource { + + func getDwarfSection(_ section: DwarfSection) -> (any ImageSource)? + +} + +struct DwarfReader { + + typealias Source = S + typealias Address = UInt64 + typealias Size = UInt64 + struct Bounds { + var base: Address + var size: Size + var end: Address { return base + size } + } + + var source: Source + + struct AbbrevInfo { + var tag: Dwarf_Tag + var hasChildren: Bool + var attributes: [(Dwarf_Attribute, Dwarf_Form, Int64?)] + } + + var infoSection: any ImageSource + var abbrevSection: any ImageSource + var lineSection: (any ImageSource)? + var addrSection: (any ImageSource)? + var strSection: (any ImageSource)? + var lineStrSection: (any ImageSource)? + var strOffsetsSection: (any ImageSource)? + var rangesSection: (any ImageSource)? + var shouldSwap: Bool + + typealias DwarfAbbrev = UInt64 + + struct Unit { + var baseOffset: Address + var version: Int + var isDwarf64: Bool + var unitType: Dwarf_UnitType + var addressSize: Int + var abbrevOffset: Address + var dieBounds: Bounds + + var lowPC: Address? + + var lineBase: UInt64? + var addrBase: UInt64? + var strOffsetsBase: UInt64? + var loclistsBase: UInt64? + + var abbrevs: [DwarfAbbrev: AbbrevInfo] + + var tag: Dwarf_Tag + var attributes: [Dwarf_Attribute:DwarfValue] = [:] + } + + struct FileInfo { + var path: String + var directoryIndex: Int? + var timestamp: Int? + var size: UInt64? + var md5sum: [UInt8]? + } + + struct LineNumberState: CustomStringConvertible { + var address: Address + var opIndex: UInt + var file: Int + var path: String + var line: Int + var column: Int + var isStmt: Bool + var basicBlock: Bool + var endSequence: Bool + var prologueEnd: Bool + var epilogueBegin: Bool + var isa: UInt + var discriminator: UInt + + var description: String { + var flags: [String] = [] + if isStmt { + flags.append("is_stmt") + } + if basicBlock { + flags.append("basic_block") + } + if endSequence { + flags.append("end_sequence") + } + if prologueEnd { + flags.append("prologue_end") + } + if epilogueBegin { + flags.append("epilogue_begin") + } + + let flagsString = flags.joined(separator:" ") + + return """ + \(hex(address)) \(pad(line, 6)) \(pad(column, 6)) \(pad(file, 6)) \ + \(pad(isa, 3)) \(pad(discriminator, 13)) \(flagsString) + """ + } + } + + struct LineNumberInfo { + var baseOffset: Address + var version: Int + var addressSize: Int? + var selectorSize: Int? + var headerLength: UInt64 + var minimumInstructionLength: UInt + var maximumOpsPerInstruction: UInt + var defaultIsStmt: Bool + var lineBase: Int8 + var lineRange: UInt8 + var opcodeBase: UInt8 + var standardOpcodeLengths: [UInt64] + var directories: [String] = [] + var files: [FileInfo] = [] + var program: [UInt8] = [] + var shouldSwap: Bool + + /// Compute the full path for a file, given its index in the file table. + func fullPathForFile(index: Int) -> String { + if index >= files.count { + return "" + } + + let info = files[index] + if info.path.hasPrefix("/") { + return info.path + } + + let dirName: String + if let dirIndex = info.directoryIndex, + dirIndex < directories.count { + dirName = directories[dirIndex] + } else { + dirName = "" + } + + return "\(dirName)/\(info.path)" + } + + /// Execute the line number program, calling a closure for every line + /// table entry. + mutating func executeProgram( + line: (LineNumberState, inout Bool) -> () + ) throws { + let source = ArrayImageSource(array: program) + let bounds = source.bounds! + var cursor = ImageSourceCursor(source: source) + + func maybeSwap(_ x: T) -> T { + if shouldSwap { + return x.byteSwapped + } + return x + } + + // Table 6.4: Line number program initial state + let initialState = LineNumberState( + address: 0, + opIndex: 0, + file: 1, + path: fullPathForFile(index: 1), + line: 1, + column: 0, + isStmt: defaultIsStmt, + basicBlock: false, + endSequence: false, + prologueEnd: false, + epilogueBegin: false, + isa: 0, + discriminator: 0 + ) + + var state = initialState + + // Flag to allow fast exit + var done = false + + while !done && cursor.pos < bounds.end { + let opcode = try cursor.read(as: Dwarf_LNS_Opcode.self) + + if opcode.rawValue >= opcodeBase { + // Special opcode + let adjustedOpcode = UInt(opcode.rawValue - opcodeBase) + let advance = adjustedOpcode / UInt(lineRange) + let lineAdvance = adjustedOpcode % UInt(lineRange) + let instrAdvance + = (state.opIndex + advance) / maximumOpsPerInstruction + let newOp = (state.opIndex + advance) % maximumOpsPerInstruction + state.address += Address(instrAdvance) + state.opIndex = newOp + state.line += Int(lineBase) + Int(lineAdvance) + + line(state, &done) + + state.discriminator = 0 + state.basicBlock = false + state.prologueEnd = false + state.epilogueBegin = false + } else if opcode == .DW_LNS_extended { + // Extended opcode + let length = try cursor.readULEB128() + let opcode = try cursor.read(as: Dwarf_LNE_Opcode.self) + + switch opcode { + case .DW_LNE_end_sequence: + state.endSequence = true + line(state, &done) + state = initialState + case .DW_LNE_set_address: + let address: UInt64 + guard let addressSize = addressSize else { + throw DwarfError.unspecifiedAddressSize + } + switch addressSize { + case 4: + address = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + case 8: + address = maybeSwap(try cursor.read(as: UInt64.self)) + default: + throw DwarfError.badAddressSize(addressSize) + } + state.address = Address(address) + case .DW_LNE_define_file: + guard let path = try cursor.readString() else { + throw DwarfError.badString + } + let directoryIndex = try cursor.readULEB128() + let timestamp = try cursor.readULEB128() + let size = try cursor.readULEB128() + files.append(FileInfo( + path: path, + directoryIndex: Int(directoryIndex), + timestamp: timestamp != 0 ? Int(timestamp) : nil, + size: size != 0 ? size : nil, + md5sum: nil + )) + case .DW_LNE_set_discriminator: + let discriminator = try cursor.readULEB128() + state.discriminator = UInt(discriminator) + default: + cursor.pos += length - 1 + } + } else { + // Standard opcode + switch opcode { + case .DW_LNS_copy: + line(state, &done) + state.discriminator = 0 + state.basicBlock = false + state.prologueEnd = false + state.epilogueBegin = false + case .DW_LNS_advance_pc: + let advance = UInt(try cursor.readULEB128()) + let instrAdvance + = (state.opIndex + advance) / maximumOpsPerInstruction + let newOp = (state.opIndex + advance) % maximumOpsPerInstruction + state.address += Address(instrAdvance) + state.opIndex = newOp + case .DW_LNS_advance_line: + let advance = try cursor.readSLEB128() + state.line += Int(advance) + case .DW_LNS_set_file: + let file = Int(try cursor.readULEB128()) + state.file = file + state.path = fullPathForFile(index: state.file) + case .DW_LNS_set_column: + let column = Int(try cursor.readULEB128()) + state.column = column + case .DW_LNS_negate_stmt: + state.isStmt = !state.isStmt + case .DW_LNS_set_basic_block: + state.basicBlock = true + case .DW_LNS_const_add_pc: + let adjustedOpcode = UInt(255 - opcodeBase) + let advance = adjustedOpcode / UInt(lineRange) + let instrAdvance + = (state.opIndex + advance) / maximumOpsPerInstruction + let newOp = (state.opIndex + advance) % maximumOpsPerInstruction + state.address += Address(instrAdvance) + state.opIndex = newOp + case .DW_LNS_fixed_advance_pc: + let advance = try cursor.read(as: Dwarf_Half.self) + state.address += Address(advance) + state.opIndex = 0 + case .DW_LNS_set_prologue_end: + state.prologueEnd = true + case .DW_LNS_set_epilogue_begin: + state.epilogueBegin = true + case .DW_LNS_set_isa: + let isa = UInt(try cursor.readULEB128()) + state.isa = isa + default: + // Skip this unknown opcode + let length = standardOpcodeLengths[Int(opcode.rawValue)] + for _ in 0..= 5 { + continue + } + + for unit in self.units { + if let lineBase = unit.lineBase, + lineNumberInfo[n].baseOffset == lineBase { + var filename = "" + if let nameVal = unit.attributes[.DW_AT_name], + case let .string(theName) = nameVal { + filename = theName + } + var dirname = "." + if let dirVal = unit.attributes[.DW_AT_comp_dir], + case let .string(theDir) = dirVal { + dirname = theDir + } + + lineNumberInfo[n].directories[0] = dirname + lineNumberInfo[n].files[0] = FileInfo( + path: filename, + directoryIndex: 0, + timestamp: nil, + size: nil, + md5sum: nil + ) + lineNumberInfo[n].addressSize = unit.addressSize + break + } + } + } + } + + private func maybeSwap(_ x: T) -> T { + if shouldSwap { + return x.byteSwapped + } else { + return x + } + } + + private func readUnits() throws -> [Unit] { + guard let bounds = infoSection.bounds else { + return [] + } + + var units: [Unit] = [] + var cursor = ImageSourceCursor(source: infoSection) + + while cursor.pos < bounds.end { + // See 7.5.1.1 Full and Partial Compilation Unit Headers + let base = cursor.pos + + // .1 unit_length + let (length, dwarf64) = try cursor.readDwarfLength() + let next = cursor.pos + length + + // .2 version + let version = Int(maybeSwap(try cursor.read(as: Dwarf_Half.self))) + + if version < 3 || version > 5 { + throw DwarfError.unsupportedVersion(version) + } + + var unitType: Dwarf_UnitType = .DW_UT_unknown + let addressSize: Int + let abbrevOffset: Address + let dieBounds: Bounds + + if dwarf64 { + if version >= 3 && version <= 4 { + // .3 debug_abbrev_offset + abbrevOffset = Address(maybeSwap(try cursor.read(as: Dwarf_Xword.self))) + + // .4 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + } else if version == 5 { + // .3 unit_type + unitType = try cursor.read(as: Dwarf_UnitType.self) + + // .4 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + + // .5 debug_abbrev_offset + abbrevOffset = Address(maybeSwap(try cursor.read(as: Dwarf_Xword.self))) + } else { + throw DwarfError.unsupportedVersion(version) + } + + dieBounds = Bounds(base: cursor.pos, size: next - cursor.pos) + } else { + if version >= 3 && version <= 4 { + // .3 debug_abbrev_offset + abbrevOffset = Address(maybeSwap(try cursor.read(as: Dwarf_Word.self))) + + // .4 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + } else if version == 5 { + // .3 unit_type + unitType = try cursor.read(as: Dwarf_UnitType.self) + + // .4 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + + // .5 debug_abbrev_offset + abbrevOffset = Address(maybeSwap(try cursor.read(as: Dwarf_Word.self))) + } else { + throw DwarfError.unsupportedVersion(version) + } + + dieBounds = Bounds(base: cursor.pos, size: next - cursor.pos) + } + + if unitType == .DW_UT_skeleton || unitType == .DW_UT_split_compile { + // .6 dwo_id + let _ = try cursor.read(as: UInt64.self) + } else if unitType == .DW_UT_type || unitType == .DW_UT_split_type { + // .6 type_signature + let _ = try cursor.read(as: UInt64.self) + + // .7 type_offset + if dwarf64 { + let _ = try cursor.read(as: UInt64.self) + } else { + let _ = try cursor.read(as: UInt32.self) + } + } + + let abbrevs = try readAbbrevs(at: abbrevOffset) + + let abbrev = try cursor.readULEB128() + + guard let abbrevInfo = abbrevs[abbrev] else { + throw DwarfError.missingAbbrev(abbrev) + } + let tag = abbrevInfo.tag + + var unit = Unit(baseOffset: base, + version: Int(version), + isDwarf64: dwarf64, + unitType: unitType, + addressSize: Int(addressSize), + abbrevOffset: abbrevOffset, + dieBounds: dieBounds, + abbrevs: abbrevs, + tag: tag) + + let attrPos = cursor.pos + let firstPass = try readDieAttributes( + at: &cursor, + unit: unit, + abbrevInfo: abbrevInfo, + shouldFetchIndirect: false + ) + + if let value = firstPass[.DW_AT_addr_base], + case let .sectionOffset(offset) = value { + unit.addrBase = offset + } + if let value = firstPass[.DW_AT_str_offsets_base], + case let .sectionOffset(offset) = value { + unit.strOffsetsBase = offset + } + if let value = firstPass[.DW_AT_loclists_base], + case let .sectionOffset(offset) = value { + unit.loclistsBase = offset + } + if let value = firstPass[.DW_AT_stmt_list], + case let .sectionOffset(offset) = value { + unit.lineBase = offset + } + if let value = firstPass[.DW_AT_low_pc], + case let .address(lowPC) = value { + unit.lowPC = lowPC + } + + // Re-read the attributes, with indirect fetching enabled; + // we can't do this in one step because attributes might be using + // indirections based on the base attributes, and those can come + // after the data needed to decode them. + cursor.pos = attrPos + + let attributes = try readDieAttributes( + at: &cursor, + unit: unit, + abbrevInfo: abbrevInfo, + shouldFetchIndirect: true + ) + + unit.attributes = attributes + + units.append(unit) + + cursor.pos = next + } + + return units + } + + private func readLineNumberInfo() throws -> [LineNumberInfo] { + guard let lineSection = lineSection, + let bounds = lineSection.bounds else { + return [] + } + + var result: [LineNumberInfo] = [] + var cursor = ImageSourceCursor(source: lineSection, offset: 0) + + while cursor.pos < bounds.end { + // 6.2.4 The Line Number Program Header + + // .1 unit_length + let baseOffset = cursor.pos + let (length, dwarf64) = try cursor.readDwarfLength() + if length == 0 { + break + } + + let nextOffset = cursor.pos + length + + // .2 version + let version = Int(maybeSwap(try cursor.read(as: Dwarf_Half.self))) + + if version < 3 || version > 5 { + cursor.pos = nextOffset + continue + } + + var addressSize: Int? = nil + var segmentSelectorSize: Int? = nil + + if version == 5 { + // .3 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + + // .4 segment_selector_size + segmentSelectorSize = Int(try cursor.read(as: Dwarf_Byte.self)) + } + + // .5 header_length + let headerLength: UInt64 + if dwarf64 { + headerLength = maybeSwap(try cursor.read(as: Dwarf_Xword.self)) + } else { + headerLength = UInt64(maybeSwap(try cursor.read(as: Dwarf_Word.self))) + } + + // .6 minimum_instruction_length + let minimumInstructionLength = UInt(try cursor.read(as: Dwarf_Byte.self)) + + // .7 maximum_operations_per_instruction + let maximumOpsPerInstruction = UInt(try cursor.read(as: Dwarf_Byte.self)) + + // .8 default_is_stmt + let defaultIsStmt = try cursor.read(as: Dwarf_Byte.self) != 0 + + // .9 line_base + let lineBase = try cursor.read(as: Dwarf_Sbyte.self) + + // .10 line_range + let lineRange = try cursor.read(as: Dwarf_Byte.self) + + // .11 opcode_base + let opcodeBase = try cursor.read(as: Dwarf_Byte.self) + + // .12 standard_opcode_lengths + var standardOpcodeLengths: [UInt64] = [0] + for _ in 1..", + directoryIndex: 0, + timestamp: nil, + size: nil, + md5sum: nil)) + + while true { + guard let path = try cursor.readString() else { + throw DwarfError.badString + } + + if path == "" { + break + } + + let dirIndex = try cursor.readULEB128() + let timestamp = try cursor.readULEB128() + let size = try cursor.readULEB128() + + fileInfo.append(FileInfo( + path: path, + directoryIndex: Int(dirIndex), + timestamp: timestamp != 0 ? Int(timestamp) : nil, + size: size != 0 ? size : nil, + md5sum: nil)) + } + } else if version == 5 { + // .13/.14 directory_entry_format + var dirEntryFormat: [(Dwarf_Lhdr_Format, Dwarf_Form)] = [] + let dirEntryFormatCount = Int(try cursor.read(as: Dwarf_Byte.self)) + for _ in 0..") + } + } + + // .17/.18 file_name_entry_format + var fileEntryFormat: [(Dwarf_Lhdr_Format, Dwarf_Form)] = [] + let fileEntryFormatCount = Int(try cursor.read(as: Dwarf_Byte.self)) + for _ in 0.. [DwarfAbbrev: AbbrevInfo] { + var abbrevs: [DwarfAbbrev: AbbrevInfo] = [:] + var cursor = ImageSourceCursor(source: abbrevSection, offset: offset) + while true { + let abbrev = try cursor.readULEB128() + + if abbrev == 0 { + break + } + + let rawTag = try cursor.readULEB128() + + guard let tag = Dwarf_Tag(rawValue: rawTag) else { + throw DwarfError.badTag(rawTag) + } + + let children = try cursor.read(as: Dwarf_ChildDetermination.self) + + // Fetch attributes + var attributes: [(Dwarf_Attribute, Dwarf_Form, Int64?)] = [] + while true { + let rawAttr = try cursor.readULEB128() + let rawForm = try cursor.readULEB128() + + if rawAttr == 0 && rawForm == 0 { + break + } + + guard let attr = Dwarf_Attribute(rawValue: UInt32(rawAttr)) else { + throw DwarfError.badAttribute(rawAttr) + } + guard let form = Dwarf_Form(rawValue: Dwarf_Byte(rawForm)) else { + throw DwarfError.badForm(rawForm) + } + + if form == .DW_FORM_implicit_const { + let value = try cursor.readSLEB128() + attributes.append((attr, form, value)) + } else { + attributes.append((attr, form, nil)) + } + } + + abbrevs[abbrev] = AbbrevInfo(tag: tag, + hasChildren: children != .DW_CHILDREN_no, + attributes: attributes) + } + + return abbrevs + } + + enum DwarfValue { + case flag(Bool) + case string(String) + case address(UInt64) + case integer(Int) + case unsignedInt8(UInt8) + case unsignedInt16(UInt16) + case unsignedInt32(UInt32) + case signedInt64(Int64) + case unsignedInt64(UInt64) + case dieOffset(UInt64) + case data([UInt8]) + case expression([UInt8]) + case locationList(UInt64) + case rangeList(UInt64) + case sectionOffset(UInt64) + case reference(UInt64) + case signature([UInt8]) + case supplementaryReference(UInt64) + case supplementaryString(UInt64) + case indirectAddress(UInt64) + case stringFromStrTab(UInt64) + case stringFromLineStrTab(UInt64) + case stringViaStrOffsets(UInt64) + + func uint64Value() -> UInt64? { + switch self { + case let .unsignedInt8(value): return UInt64(value) + case let .unsignedInt16(value): return UInt64(value) + case let .unsignedInt32(value): return UInt64(value) + case let .unsignedInt64(value): return value + default: + return nil + } + } + + func intValue() -> Int? { + switch self { + case let .unsignedInt8(value): return Int(value) + case let .unsignedInt16(value): return Int(value) + case let .unsignedInt32(value): return Int(value) + case let .unsignedInt64(value): return Int(value) + default: + return nil + } + } + } + + private func threeByteToOffset(_ bytes: (UInt8, UInt8, UInt8)) -> UInt64 { + let offset: UInt64 + #if _endian(big) + if shouldSwap { + offset = UInt64(bytes.0) | UInt64(bytes.1) << 8 | UInt64(bytes.2) << 16 + } else { + offset = UInt64(bytes.2) | UInt64(bytes.1) << 8 | UInt64(bytes.0) << 16 + } + #else + if shouldSwap { + offset = UInt64(bytes.2) | UInt64(bytes.1) << 8 | UInt64(bytes.0) << 16 + } else { + offset = UInt64(bytes.0) | UInt64(bytes.1) << 8 | UInt64(bytes.2) << 16 + } + #endif + return offset + } + + private func read(form theForm: Dwarf_Form, + at cursor: inout ImageSourceCursor, + addressSize: Int, isDwarf64: Bool, + unit: Unit?, + shouldFetchIndirect: Bool, + constantValue: Int64? = nil) throws -> DwarfValue { + let form: Dwarf_Form + if theForm == .DW_FORM_indirect { + let rawForm = try cursor.readULEB128() + guard let theForm = Dwarf_Form(rawValue: Dwarf_Byte(rawForm)) else { + throw DwarfError.badForm(rawForm) + } + form = theForm + } else { + form = theForm + } + + switch form { + case .DW_FORM_implicit_const: + return .signedInt64(constantValue!) + + case .DW_FORM_addr: + let address: UInt64 + switch addressSize { + case 4: + address = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + case 8: + address = maybeSwap(try cursor.read(as: UInt64.self)) + default: + throw DwarfError.badAddressSize(addressSize) + } + return .address(address) + case .DW_FORM_addrx, .DW_FORM_addrx1, .DW_FORM_addrx2, + .DW_FORM_addrx3, .DW_FORM_addrx4: + guard let addrSection = addrSection else { + throw DwarfError.missingAddrSection + } + + let ndx: UInt64 + switch form { + case .DW_FORM_addrx: + ndx = try cursor.readULEB128() + case .DW_FORM_addrx1: + ndx = UInt64(try cursor.read(as: UInt8.self)) + case .DW_FORM_addrx2: + ndx = UInt64(maybeSwap(try cursor.read(as: UInt16.self))) + case .DW_FORM_addrx3: + let bytes = try cursor.read(as: (UInt8, UInt8, UInt8).self) + ndx = threeByteToOffset(bytes) + case .DW_FORM_addrx4: + ndx = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + default: + fatalError("unreachable") + } + + if !shouldFetchIndirect { + return .indirectAddress(ndx) + } else { + guard let addrBase = unit?.addrBase else { + throw DwarfError.missingAddrBase + } + + let address: UInt64 + switch addressSize { + case 4: + address = UInt64(maybeSwap( + try addrSection.fetch(from: ndx * 4 + addrBase, + as: UInt32.self))) + case 8: + address = maybeSwap(try addrSection.fetch(from: ndx * 8 + addrBase, + as: UInt64.self)) + default: + throw DwarfError.badAddressSize(addressSize) + } + return .address(address) + } + case .DW_FORM_block: + let length = try cursor.readULEB128() + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .data(bytes) + case .DW_FORM_block1: + let length = try cursor.read(as: UInt8.self) + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .data(bytes) + case .DW_FORM_block2: + let length = maybeSwap(try cursor.read(as: UInt16.self)) + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .data(bytes) + case .DW_FORM_block4: + let length = maybeSwap(try cursor.read(as: UInt32.self)) + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .data(bytes) + + case .DW_FORM_sdata: + let data = try cursor.readSLEB128() + return .signedInt64(data) + + case .DW_FORM_udata: + let data = try cursor.readULEB128() + return .unsignedInt64(data) + + case .DW_FORM_data1: + let data = try cursor.read(as: UInt8.self) + return .unsignedInt8(data) + + case .DW_FORM_data2: + let data = maybeSwap(try cursor.read(as: UInt16.self)) + return .unsignedInt16(data) + + case .DW_FORM_data4: + let data = maybeSwap(try cursor.read(as: UInt32.self)) + return .unsignedInt32(data) + + case .DW_FORM_data8: + let data = maybeSwap(try cursor.read(as: UInt64.self)) + return .unsignedInt64(data) + + case .DW_FORM_data16: + let data = try cursor.read(count: 16, as: UInt8.self) + return .data(data) + + case .DW_FORM_exprloc: + let length = try cursor.readULEB128() + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .expression(bytes) + + case .DW_FORM_flag: + let flag = try cursor.read(as: UInt8.self) + return .flag(flag != 0) + + case .DW_FORM_flag_present: + return .flag(true) + + case .DW_FORM_loclistx: + let offset = try cursor.readULEB128() + return .locationList(offset) + + case .DW_FORM_sec_offset: + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + return .sectionOffset(offset) + + case .DW_FORM_rnglistx: + let offset = try cursor.readULEB128() + return .rangeList(offset) + + case .DW_FORM_ref1, .DW_FORM_ref2, .DW_FORM_ref4, .DW_FORM_ref8, + .DW_FORM_ref_udata: + guard let baseOffset = unit?.baseOffset else { + throw DwarfError.missingBaseOffset + } + + let offset: Address + switch form { + case .DW_FORM_ref1: + offset = UInt64(try cursor.read(as: UInt8.self)) + case .DW_FORM_ref2: + offset = UInt64(maybeSwap(try cursor.read(as: UInt16.self))) + case .DW_FORM_ref4: + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + case .DW_FORM_ref8: + offset = maybeSwap(try cursor.read(as: UInt64.self)) + case .DW_FORM_ref_udata: + offset = try cursor.readULEB128() + default: + fatalError("unreachable") + } + return .reference(offset + baseOffset) + + case .DW_FORM_ref_addr: + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + return .reference(offset) + + case .DW_FORM_ref_sig8: + let signature = try cursor.read(count: 8, as: UInt8.self) + return .signature(signature) + + case .DW_FORM_ref_sup4: + let offset = maybeSwap(try cursor.read(as: UInt32.self)) + return .supplementaryReference(Address(offset)) + + case .DW_FORM_ref_sup8: + let offset = maybeSwap(try cursor.read(as: UInt64.self)) + return .supplementaryReference(Address(offset)) + + case .DW_FORM_string: + guard let string = try cursor.readString() else { + throw DwarfError.badString + } + return .string(string) + + case .DW_FORM_strp: + guard let strSection = strSection else { + throw DwarfError.missingStrSection + } + + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + + if !shouldFetchIndirect { + return .stringFromStrTab(offset) + } else { + guard let string = try strSection.fetchString(from: offset) else { + throw DwarfError.badString + } + return .string(string) + } + + case .DW_FORM_strp_sup: + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + return .supplementaryString(offset) + + case .DW_FORM_line_strp: + guard let lineStrSection = lineStrSection else { + throw DwarfError.missingLineStrSection + } + + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + + if !shouldFetchIndirect { + return .stringFromLineStrTab(offset) + } else { + guard let string = try lineStrSection.fetchString(from: offset) else { + throw DwarfError.badString + } + return .string(string) + } + + case .DW_FORM_strx, + .DW_FORM_strx1, .DW_FORM_strx2, .DW_FORM_strx3,.DW_FORM_strx4: + guard let strOffsetsSection = strOffsetsSection else { + throw DwarfError.missingStrOffsetsSection + } + guard let strSection = strSection else { + throw DwarfError.missingStrSection + } + + let offset: UInt64 + switch form { + case .DW_FORM_strx: + offset = try cursor.readULEB128() + case .DW_FORM_strx1: + offset = UInt64(try cursor.read(as: UInt8.self)) + case .DW_FORM_strx2: + offset = UInt64(maybeSwap(try cursor.read(as: UInt16.self))) + case .DW_FORM_strx3: + let bytes = try cursor.read(as: (UInt8, UInt8, UInt8).self) + offset = threeByteToOffset(bytes) + case .DW_FORM_strx4: + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + default: + fatalError("unreachable") + } + + if !shouldFetchIndirect { + return .stringViaStrOffsets(offset) + } else { + guard let strBase = unit?.strOffsetsBase else { + throw DwarfError.missingStrOffsetsBase + } + + let actualOffset: UInt64 + if isDwarf64 { + actualOffset = maybeSwap(try strOffsetsSection.fetch( + from: offset * 8 + strBase, + as: UInt64.self)) + } else { + actualOffset = UInt64(maybeSwap(try strOffsetsSection.fetch( + from: offset * 4 + strBase, + as: UInt32.self))) + } + + guard let string = try strSection.fetchString(from: actualOffset) + else { + throw DwarfError.badString + } + return .string(string) + } + + case .DW_FORM_indirect: + // We should have handled this already + throw DwarfError.doubleIndirectForm + default: + throw DwarfError.unknownForm(theForm) + } + } + + private func readDieAttributes( + at cursor: inout ImageSourceCursor, + unit: Unit, + abbrevInfo: AbbrevInfo, + shouldFetchIndirect: Bool + ) throws -> [Dwarf_Attribute:DwarfValue] { + var attributes: [Dwarf_Attribute:DwarfValue] = [:] + + for (attribute, form, constantValue) in abbrevInfo.attributes { + attributes[attribute] = try read(form: form, + at: &cursor, + addressSize: unit.addressSize, + isDwarf64: unit.isDwarf64, + unit: unit, + shouldFetchIndirect: shouldFetchIndirect, + constantValue: constantValue) + } + + return attributes + } + + struct CallSiteInfo { + var depth: Int + var rawName: String? + var name: String? + var lowPC: Address + var highPC: Address + var filename: String + var line: Int + var column: Int + } + + private func buildCallSiteInfo( + depth: Int, + unit: Unit, + attributes: [Dwarf_Attribute:DwarfValue], + _ fn: (CallSiteInfo) -> () + ) throws { + guard let abstractOriginVal = attributes[.DW_AT_abstract_origin], + let callFile = attributes[.DW_AT_call_file]?.uint64Value(), + let callLine = attributes[.DW_AT_call_line]?.uint64Value(), + let callColumn = attributes[.DW_AT_call_column]?.uint64Value(), + case let .reference(abstractOrigin) = abstractOriginVal else { + return + } + + var cursor = ImageSourceCursor(source: infoSection, + offset: abstractOrigin) + let abbrev = try cursor.readULEB128() + if abbrev == 0 { + return + } + + guard let abbrevInfo = unit.abbrevs[abbrev] else { + throw DwarfError.missingAbbrev(abbrev) + } + + let tag = abbrevInfo.tag + + if tag != .DW_TAG_subprogram { + return + } + + let refAttrs = try readDieAttributes( + at: &cursor, + unit: unit, + abbrevInfo: abbrevInfo, + shouldFetchIndirect: true + ) + + var name: String? = nil + var rawName: String? = nil + + if let nameVal = refAttrs[.DW_AT_name], + case let .string(theName) = nameVal { + name = theName + } + + if let linkageNameVal = refAttrs[.DW_AT_linkage_name], + case let .string(theRawName) = linkageNameVal { + rawName = theRawName + } else { + rawName = name + } + + var filename: String = "" + for info in lineNumberInfo { + if info.baseOffset == unit.lineBase { + filename = info.fullPathForFile(index: Int(callFile)) + break + } + } + + if let lowPCVal = attributes[.DW_AT_low_pc], + let highPCVal = attributes[.DW_AT_high_pc], + case let .address(lowPC) = lowPCVal { + let highPC: Address + if case let .address(highPCAddr) = highPCVal { + highPC = highPCAddr + } else if let highPCOffset = highPCVal.uint64Value() { + highPC = lowPC + highPCOffset + } else { + return + } + + fn(CallSiteInfo( + depth: depth, + rawName: rawName, + name: name, + lowPC: lowPC, + highPC: highPC, + filename: filename, + line: Int(callLine), + column: Int(callColumn))) + } else if let rangeVal = attributes[.DW_AT_ranges], + let rangesSection = rangesSection, + case let .sectionOffset(offset) = rangeVal, + unit.version < 5 { + // We don't support .debug_rnglists at present (which is what we'd + // have if unit.version is 5 or higher). + var rangeCursor = ImageSourceCursor(source: rangesSection, + offset: offset) + var rangeBase: Address = unit.lowPC ?? 0 + + while true { + let beginning: Address + let ending: Address + + switch unit.addressSize { + case 4: + beginning = UInt64(maybeSwap(try rangeCursor.read(as: UInt32.self))) + ending = UInt64(maybeSwap(try rangeCursor.read(as: UInt32.self))) + if beginning == 0xffffffff { + rangeBase = ending + continue + } + case 8: + beginning = maybeSwap(try rangeCursor.read(as: UInt64.self)) + ending = maybeSwap(try rangeCursor.read(as: UInt64.self)) + if beginning == 0xffffffffffffffff { + rangeBase = ending + continue + } + default: + throw DwarfError.badAddressSize(unit.addressSize) + } + + if beginning == 0 && ending == 0 { + break + } + + fn(CallSiteInfo( + depth: depth, + rawName: rawName, + name: name, + lowPC: beginning + rangeBase, + highPC: ending + rangeBase, + filename: filename, + line: Int(callLine), + column: Int(callColumn))) + } + } + } + + lazy var inlineCallSites: [CallSiteInfo] = _buildCallSiteList() + + private func _buildCallSiteList() -> [CallSiteInfo] { + var callSites: [CallSiteInfo] = [] + + for unit in units { + do { + var cursor = ImageSourceCursor(source: infoSection, + offset: unit.dieBounds.base) + var depth = 0 + + while cursor.pos < unit.dieBounds.end { + let abbrev = try cursor.readULEB128() + + if abbrev == 0 { + depth -= 1 + if depth == 0 { + break + } + continue + } + + guard let abbrevInfo = unit.abbrevs[abbrev] else { + throw DwarfError.missingAbbrev(abbrev) + } + + let tag = abbrevInfo.tag + + let attributes = try readDieAttributes( + at: &cursor, + unit: unit, + abbrevInfo: abbrevInfo, + shouldFetchIndirect: tag == .DW_TAG_inlined_subroutine + ) + + if tag == .DW_TAG_inlined_subroutine { + try buildCallSiteInfo(depth: depth, + unit: unit, + attributes: attributes) { + callSites.append($0) + } + } + + if abbrevInfo.hasChildren { + depth += 1 + } + } + } catch { + let name: String + if let value = unit.attributes[.DW_AT_name], + case let .string(theName) = value { + name = theName + } else { + name = "" + } + swift_reportWarning(0, + """ + swift-runtime: warning: unable to fetch inline \ + frame data for DWARF unit \(name): \(error) + """) + } + } + + callSites.sort( + by: { (a, b) in + a.lowPC < b.lowPC || (a.lowPC == b.lowPC) && a.depth > b.depth + }) + + return callSites + } + +} + +// .. Testing .................................................................. + +@_spi(DwarfTest) +public func testDwarfReaderFor(path: String) -> Bool { + guard let source = try? FileImageSource(path: path) else { + print("\(path) was not accessible") + return false + } + + if let elfImage = try? Elf32Image(source: source) { + print("\(path) is a 32-bit ELF image") + + var reader: DwarfReader> + do { + reader = try DwarfReader(source: elfImage) + } catch { + print("Unable to create reader - \(error)") + return false + } + + print("Units:") + print(reader.units) + + print("Call Sites:") + print(reader.inlineCallSites) + return true + } else if let elfImage = try? Elf64Image(source: source) { + print("\(path) is a 64-bit ELF image") + + var reader: DwarfReader> + do { + reader = try DwarfReader(source: elfImage) + } catch { + print("Unable to create reader - \(error)") + return false + } + + print("Units:") + for unit in reader.units { + if let value = unit.attributes[.DW_AT_name], + case let .string(name) = value { + print(" \(name)") + } else { + print(" ") + } + } + + print("Call Sites:") + print(reader.inlineCallSites) + return true + } else { + print("\(path) is not an ELF image") + return false + } +} + +#endif // os(Linux) diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift new file mode 100644 index 0000000000000..40b2d12c9fc06 --- /dev/null +++ b/stdlib/public/Backtracing/Elf.swift @@ -0,0 +1,1769 @@ +//===--- Elf.swift - ELF support for Swift --------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines various ELF structures and provides types for working with ELF +// images on disk and in memory. +// +//===----------------------------------------------------------------------===// + +// ###FIXME: We shouldn't really use String for paths. + +#if os(Linux) + +import Swift + +@_implementationOnly import OS.Libc +@_implementationOnly import ImageFormats.Elf +@_implementationOnly import Runtime + +// .. Utilities ................................................................ + +private func realPath(_ path: String) -> String? { + guard let result = realpath(path, nil) else { + return nil + } + + let s = String(cString: result) + + free(result) + + return s +} + +private func dirname(_ path: String) -> Substring { + guard let lastSlash = path.lastIndex(of: "/") else { + return "" + } + return path.prefix(upTo: lastSlash) +} + +private let crc32Table: [UInt32] = [ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +] + +private func updateCrc(_ crc: UInt32, + _ bytes: UnsafeBufferPointer) -> UInt32 { + var theCrc = ~crc + for byte in bytes { + theCrc = crc32Table[Int(UInt8(truncatingIfNeeded: theCrc) + ^ byte)] ^ (theCrc >> 8) + } + return ~theCrc +} + +// .. Byte swapping ............................................................ + +extension Elf32_Ehdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Ehdr( + e_ident: e_ident, + e_type: Elf_Ehdr_Type(rawValue: e_type.rawValue.byteSwapped)!, + e_machine: Elf_Ehdr_Machine(rawValue: e_machine.rawValue.byteSwapped)!, + e_version: Elf_Ehdr_Version(rawValue: e_version.rawValue.byteSwapped)!, + e_entry: e_entry.byteSwapped, + e_phoff: e_phoff.byteSwapped, + e_shoff: e_shoff.byteSwapped, + e_flags: e_flags.byteSwapped, + e_ehsize: e_ehsize.byteSwapped, + e_phentsize: e_phentsize.byteSwapped, + e_phnum: e_phnum.byteSwapped, + e_shentsize: e_shentsize.byteSwapped, + e_shnum: e_shnum.byteSwapped, + e_shstrndx: e_shstrndx.byteSwapped + ) + } +} + +extension Elf64_Ehdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Ehdr( + e_ident: e_ident, + e_type: Elf_Ehdr_Type(rawValue: e_type.rawValue.byteSwapped)!, + e_machine: Elf_Ehdr_Machine(rawValue: e_machine.rawValue.byteSwapped)!, + e_version: Elf_Ehdr_Version(rawValue: e_version.rawValue.byteSwapped)!, + e_entry: e_entry.byteSwapped, + e_phoff: e_phoff.byteSwapped, + e_shoff: e_shoff.byteSwapped, + e_flags: e_flags.byteSwapped, + e_ehsize: e_ehsize.byteSwapped, + e_phentsize: e_phentsize.byteSwapped, + e_phnum: e_phnum.byteSwapped, + e_shentsize: e_shentsize.byteSwapped, + e_shnum: e_shnum.byteSwapped, + e_shstrndx: e_shstrndx.byteSwapped + ) + } +} + +extension Elf32_Shdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Shdr( + sh_name: sh_name.byteSwapped, + sh_type: Elf_Shdr_Type(rawValue: sh_type.rawValue.byteSwapped)!, + sh_flags: sh_flags.byteSwapped, + sh_addr: sh_addr.byteSwapped, + sh_offset: sh_offset.byteSwapped, + sh_size: sh_size.byteSwapped, + sh_link: sh_link.byteSwapped, + sh_info: sh_info.byteSwapped, + sh_addralign: sh_addralign.byteSwapped, + sh_entsize: sh_entsize.byteSwapped + ) + } +} + +extension Elf64_Shdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Shdr( + sh_name: sh_name.byteSwapped, + sh_type: Elf_Shdr_Type(rawValue: sh_type.rawValue.byteSwapped)!, + sh_flags: sh_flags.byteSwapped, + sh_addr: sh_addr.byteSwapped, + sh_offset: sh_offset.byteSwapped, + sh_size: sh_size.byteSwapped, + sh_link: sh_link.byteSwapped, + sh_info: sh_info.byteSwapped, + sh_addralign: sh_addralign.byteSwapped, + sh_entsize: sh_entsize.byteSwapped + ) + } +} + +protocol Elf_Chdr: ByteSwappable { + associatedtype Size: FixedWidthInteger + + init() + + var ch_type: Elf_Chdr_Type { get set } + var ch_size: Size { get set } + var ch_addralign: Size { get set } +} + +extension Elf32_Chdr: Elf_Chdr { + var byteSwapped: Self { + return Elf32_Chdr( + ch_type: Elf_Chdr_Type(rawValue: ch_type.rawValue.byteSwapped)!, + ch_size: ch_size.byteSwapped, + ch_addralign: ch_addralign.byteSwapped + ) + } +} + +extension Elf64_Chdr: Elf_Chdr { + var byteSwapped: Self { + return Elf64_Chdr( + ch_type: Elf_Chdr_Type(rawValue: ch_type.rawValue.byteSwapped)!, + ch_reserved: ch_reserved.byteSwapped, + ch_size: ch_size.byteSwapped, + ch_addralign: ch_addralign.byteSwapped + ) + } +} + +extension Elf32_Sym: ByteSwappable { + var byteSwapped: Self { + return Elf32_Sym( + st_name: st_name.byteSwapped, + st_value: st_value.byteSwapped, + st_size: st_size.byteSwapped, + st_info: st_info.byteSwapped, + st_other: st_other.byteSwapped, + st_shndx: st_shndx.byteSwapped + ) + } +} + +extension Elf64_Sym: ByteSwappable { + var byteSwapped: Self { + return Elf64_Sym( + st_name: st_name.byteSwapped, + st_info: st_info.byteSwapped, + st_other: st_other.byteSwapped, + st_shndx: st_shndx.byteSwapped, + st_value: st_value.byteSwapped, + st_size: st_size.byteSwapped + ) + } +} + +extension Elf32_Rel: ByteSwappable { + var byteSwapped: Self { + return Elf32_Rel( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped + ) + } +} + +extension Elf32_Rela: ByteSwappable { + var byteSwapped: Self { + return Elf32_Rela( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped, + r_addend: r_addend.byteSwapped + ) + } +} + +extension Elf64_Rel: ByteSwappable { + var byteSwapped: Self { + return Elf64_Rel( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped + ) + } +} + +extension Elf64_Rela: ByteSwappable { + var byteSwapped: Self { + return Elf64_Rela( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped, + r_addend: r_addend.byteSwapped + ) + } +} + +extension Elf32_Phdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Phdr( + p_type: Elf_Phdr_Type(rawValue: p_type.rawValue.byteSwapped)!, + p_offset: p_offset.byteSwapped, + p_vaddr: p_vaddr.byteSwapped, + p_paddr: p_paddr.byteSwapped, + p_filesz: p_filesz.byteSwapped, + p_memsz: p_memsz.byteSwapped, + p_flags: p_flags.byteSwapped, + p_align: p_align.byteSwapped + ) + } +} + +extension Elf64_Phdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Phdr( + p_type: Elf_Phdr_Type(rawValue: p_type.rawValue.byteSwapped)!, + p_flags: p_flags.byteSwapped, + p_offset: p_offset.byteSwapped, + p_vaddr: p_vaddr.byteSwapped, + p_paddr: p_paddr.byteSwapped, + p_filesz: p_filesz.byteSwapped, + p_memsz: p_memsz.byteSwapped, + p_align: p_align.byteSwapped + ) + } +} + +extension Elf32_Nhdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Nhdr( + n_namesz: n_namesz.byteSwapped, + n_descsz: n_descsz.byteSwapped, + n_type: n_type.byteSwapped + ) + } +} + +extension Elf64_Nhdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Nhdr( + n_namesz: n_namesz.byteSwapped, + n_descsz: n_descsz.byteSwapped, + n_type: n_type.byteSwapped + ) + } +} + +extension Elf32_Dyn: ByteSwappable { + var byteSwapped: Self { + return Elf32_Dyn( + d_tag: d_tag.byteSwapped, + d_un: .init(d_val: d_un.d_val.byteSwapped) + ) + } +} + +extension Elf64_Dyn: ByteSwappable { + var byteSwapped: Self { + return Elf64_Dyn( + d_tag: d_tag.byteSwapped, + d_un: .init(d_val: d_un.d_val.byteSwapped) + ) + } +} + +extension Elf32_Hash: ByteSwappable { + var byteSwapped: Self { + return Elf32_Hash( + h_nbucket: h_nbucket.byteSwapped, + h_nchain: h_nchain.byteSwapped + ) + } +} + +extension Elf64_Hash: ByteSwappable { + var byteSwapped: Self { + return Elf64_Hash( + h_nbucket: h_nbucket.byteSwapped, + h_nchain: h_nchain.byteSwapped + ) + } +} + +// .. Protocols ................................................................ + +typealias Elf_Magic = (UInt8, UInt8, UInt8, UInt8) + +typealias Elf_Ident = ( + UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8 +) + +let ElfMagic: Elf_Magic = (0x7f, 0x45, 0x4c, 0x46) + +protocol Elf_Ehdr : ByteSwappable { + associatedtype Address: FixedWidthInteger + associatedtype Offset: FixedWidthInteger + + init() + + var e_ident: Elf_Ident { get set } + var ei_magic: Elf_Magic { get set } + var ei_class: Elf_Ehdr_Class { get set } + var ei_data: Elf_Ehdr_Data { get set } + var ei_version: Elf_Byte { get set } + var ei_osabi: Elf_Ehdr_OsAbi { get set } + var ei_abiversion: Elf_Byte { get set } + + var e_type: Elf_Ehdr_Type { get set } + var e_machine: Elf_Ehdr_Machine { get set } + var e_version: Elf_Ehdr_Version { get set } + var e_entry: Address { get set } + var e_phoff: Offset { get set } + var e_shoff: Offset { get set } + var e_flags: Elf_Word { get set } + var e_ehsize: Elf_Half { get set } + var e_phentsize: Elf_Half { get set } + var e_phnum: Elf_Half { get set } + var e_shentsize: Elf_Half { get set } + var e_shnum: Elf_Half { get set } + var e_shstrndx: Elf_Half { get set } + + var shouldByteSwap: Bool { get } +} + +extension Elf_Ehdr { + var ei_magic: Elf_Magic { + get { + return (e_ident.0, e_ident.1, e_ident.2, e_ident.3) + } + set { + e_ident.0 = newValue.0 + e_ident.1 = newValue.1 + e_ident.2 = newValue.2 + e_ident.3 = newValue.3 + } + } + var ei_class: Elf_Ehdr_Class { + get { + return Elf_Ehdr_Class(rawValue: e_ident.4)! + } + set { + e_ident.4 = newValue.rawValue + } + } + var ei_data: Elf_Ehdr_Data { + get { + return Elf_Ehdr_Data(rawValue: e_ident.5)! + } + set { + e_ident.5 = newValue.rawValue + } + } + var ei_version: UInt8 { + get { + return e_ident.6 + } + set { + e_ident.6 = newValue + } + } + var ei_osabi: Elf_Ehdr_OsAbi { + get { + return Elf_Ehdr_OsAbi(rawValue: e_ident.7)! + } + set { + e_ident.7 = newValue.rawValue + } + } + var ei_abiversion: UInt8 { + get { + return e_ident.8 + } + set { + e_ident.8 = newValue + } + } + var ei_pad: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) { + get { + return (e_ident.9, e_ident.10, e_ident.11, + e_ident.12, e_ident.13, e_ident.14, + e_ident.15) + } + set { + e_ident.9 = newValue.0 + e_ident.10 = newValue.1 + e_ident.11 = newValue.2 + e_ident.12 = newValue.3 + e_ident.13 = newValue.4 + e_ident.14 = newValue.5 + e_ident.15 = newValue.6 + } + } + + var shouldByteSwap: Bool { + #if _endian(big) + return ei_data == .ELFDATA2LSB + #else + return ei_data == .ELFDATA2MSB + #endif + } +} + +extension Elf32_Ehdr : Elf_Ehdr { +} + +extension Elf64_Ehdr : Elf_Ehdr { +} + +protocol Elf_Shdr : ByteSwappable { + associatedtype Flags: FixedWidthInteger + associatedtype Address: FixedWidthInteger + associatedtype Offset: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + init() + + var sh_name: Elf_Word { get set } + var sh_type: Elf_Shdr_Type { get set } + var sh_flags: Flags { get set } + var sh_addr: Address { get set } + var sh_offset: Offset { get set } + var sh_size: Size { get set } + var sh_link: Elf_Word { get set } + var sh_info: Elf_Word { get set } + var sh_addralign: Size { get set } + var sh_entsize: Size { get set } +} + +extension Elf32_Shdr : Elf_Shdr { +} + +extension Elf64_Shdr : Elf_Shdr { +} + +protocol Elf_Phdr : ByteSwappable { + associatedtype Address: FixedWidthInteger + associatedtype Offset: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + init() + + var p_type: Elf_Phdr_Type { get set } + var p_flags: Elf_Phdr_Flags { get set } + var p_offset: Offset { get set } + var p_vaddr: Address { get set } + var p_paddr: Address { get set } + var p_filesz: Size { get set } + var p_memsz: Size { get set } + var p_align: Size { get set } +} + +extension Elf32_Phdr : Elf_Phdr { +} + +extension Elf64_Phdr : Elf_Phdr { +} + +protocol Elf_Nhdr : ByteSwappable { + init() + + var n_namesz: Elf_Word { get set } + var n_descsz: Elf_Word { get set } + var n_type: Elf_Word { get set } +} + +extension Elf32_Nhdr : Elf_Nhdr { +} + +extension Elf64_Nhdr : Elf_Nhdr { +} + +protocol Elf_Sym { + associatedtype Address: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + var st_name: Elf_Word { get set } + var st_value: Address { get set } + var st_size: Size { get set } + var st_info: Elf_Byte { get set } + var st_other: Elf_Byte { get set } + var st_shndx: Elf_Half { get set } + + var st_binding: Elf_Sym_Binding { get set } + var st_type: Elf_Sym_Type { get set } + var st_visibility: Elf_Sym_Visibility { get set } +} + +extension Elf32_Sym: Elf_Sym { + var st_binding: Elf_Sym_Binding { + get { + return ELF32_ST_BIND(st_info) + } + set { + st_info = ELF32_ST_INFO(newValue, ELF32_ST_TYPE(st_info)) + } + } + + var st_type: Elf_Sym_Type { + get { + return ELF32_ST_TYPE(st_info) + } + set { + st_info = ELF32_ST_INFO(ELF32_ST_BIND(st_info), newValue) + } + } + + var st_visibility: Elf_Sym_Visibility { + get { + return ELF32_ST_VISIBILITY(st_other) + } + set { + st_other = (st_other & ~3) | newValue.rawValue + } + } +} + +extension Elf64_Sym: Elf_Sym { + var st_binding: Elf_Sym_Binding { + get { + return ELF64_ST_BIND(st_info) + } + set { + st_info = ELF64_ST_INFO(newValue, ELF64_ST_TYPE(st_info)) + } + } + + var st_type: Elf_Sym_Type { + get { + return ELF64_ST_TYPE(st_info) + } + set { + st_info = ELF64_ST_INFO(ELF64_ST_BIND(st_info), newValue) + } + } + + var st_visibility: Elf_Sym_Visibility { + get { + return ELF64_ST_VISIBILITY(st_other) + } + set { + st_other = (st_other & ~3) | newValue.rawValue + } + } +} + +extension Elf32_Rel { + var r_sym: Elf32_Byte { + get { + return ELF32_R_SYM(r_info) + } + set { + r_info = ELF32_R_INFO(newValue, ELF32_R_TYPE(r_info)) + } + } + + var r_type: Elf32_Byte { + get { + return ELF32_R_TYPE(r_info) + } + set { + r_info = ELF32_R_INFO(ELF32_R_SYM(r_info), newValue) + } + } +} + +extension Elf32_Rela { + var r_sym: Elf32_Byte { + get { + return ELF32_R_SYM(r_info) + } + set { + r_info = ELF32_R_INFO(newValue, ELF32_R_TYPE(r_info)) + } + } + + var r_type: Elf32_Byte { + get { + return ELF32_R_TYPE(r_info) + } + set { + r_info = ELF32_R_INFO(ELF32_R_SYM(r_info), newValue) + } + } +} + +extension Elf64_Rel { + var r_sym: Elf64_Word { + get { + return ELF64_R_SYM(r_info) + } + set { + r_info = ELF64_R_INFO(newValue, ELF64_R_TYPE(r_info)) + } + } + + var r_type: Elf64_Word { + get { + return ELF64_R_TYPE(r_info) + } + set { + r_info = ELF64_R_INFO(ELF64_R_SYM(r_info), newValue) + } + } +} + +extension Elf64_Rela { + var r_sym: Elf64_Word { + get { + return ELF64_R_SYM(r_info) + } + set { + r_info = ELF64_R_INFO(newValue, ELF64_R_TYPE(r_info)) + } + } + + var r_type: Elf64_Word { + get { + return ELF64_R_TYPE(r_info) + } + set { + r_info = ELF64_R_INFO(ELF64_R_SYM(r_info), newValue) + } + } +} + +// .. Traits ................................................................... + +protocol ElfTraits { + associatedtype Address: FixedWidthInteger + associatedtype Offset: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + associatedtype Ehdr: Elf_Ehdr where Ehdr.Address == Address, + Ehdr.Offset == Offset + associatedtype Phdr: Elf_Phdr where Phdr.Address == Address, + Phdr.Offset == Offset, + Phdr.Size == Size + associatedtype Shdr: Elf_Shdr where Shdr.Address == Address, + Shdr.Offset == Offset, + Shdr.Size == Size + associatedtype Nhdr: Elf_Nhdr + associatedtype Chdr: Elf_Chdr where Chdr.Size == Size + associatedtype Sym: Elf_Sym where Sym.Address == Address, Sym.Size == Size + + static var elfClass: Elf_Ehdr_Class { get } +} + +struct Elf32Traits: ElfTraits { + typealias Address = UInt32 + typealias Offset = UInt32 + typealias Size = UInt32 + + typealias Ehdr = Elf32_Ehdr + typealias Phdr = Elf32_Phdr + typealias Shdr = Elf32_Shdr + typealias Nhdr = Elf32_Nhdr + typealias Chdr = Elf32_Chdr + typealias Sym = Elf32_Sym + + static let elfClass: Elf_Ehdr_Class = .ELFCLASS32 +} + +struct Elf64Traits: ElfTraits { + typealias Address = UInt64 + typealias Offset = UInt64 + typealias Size = UInt64 + + typealias Ehdr = Elf64_Ehdr + typealias Phdr = Elf64_Phdr + typealias Shdr = Elf64_Shdr + typealias Nhdr = Elf64_Nhdr + typealias Chdr = Elf64_Chdr + typealias Sym = Elf64_Sym + + static let elfClass: Elf_Ehdr_Class = .ELFCLASS64 +} + +// .. ElfStringSection ......................................................... + +struct ElfStringSection { + let bytes: [UInt8] + + func getStringAt(index: Int) -> String? { + if index < 0 || index >= bytes.count { + return nil + } + + let slice = bytes[index...] + var len: Int = 0 + slice.withUnsafeBufferPointer{ ptr in + len = strnlen(ptr.baseAddress!, ptr.count) + } + return String(decoding: bytes[index.. (any ImageSource)? + func getSection(_ name: String) -> (any ImageSource)? +} + +extension ElfGetSectionProtocol { + func getSection(_ name: String) -> (any ImageSource)? { + return getSection(name, debug: false) + } +} + +protocol ElfSymbolProtocol: Equatable { + associatedtype Address: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + var name: String { get set } + var value: Address { get set } + var size: Size { get set } + var sectionIndex: Int { get set } + var binding: Elf_Sym_Binding { get set } + var type: Elf_Sym_Type { get set } + var visibility: Elf_Sym_Visibility { get set } +} + +protocol ElfSymbolTableProtocol { + associatedtype Traits: ElfTraits + associatedtype Symbol: ElfSymbolProtocol where Symbol.Address == Traits.Address, + Symbol.Size == Traits.Size + + func lookupSymbol(address: Traits.Address) -> Symbol? +} + +protocol ElfImageProtocol: Image, ElfGetSectionProtocol, DwarfSource { + associatedtype Traits: ElfTraits + associatedtype SymbolTable: ElfSymbolTableProtocol + where SymbolTable.Traits == Traits + + var header: Traits.Ehdr { get } + var programHeaders: [Traits.Phdr] { get } + var sectionHeaders: [Traits.Shdr]? { get } + + var imageName: String { get } + var debugImage: (any ElfImageProtocol)? { get } + var debugLinkCRC: UInt32? { get } + + var symbolTable: SymbolTable { get } + + func _getSymbolTable(debug: Bool) -> SymbolTable +} + +struct ElfSymbolTable: ElfSymbolTableProtocol { + typealias Traits = SomeElfTraits + + struct Symbol: ElfSymbolProtocol { + typealias Address = Traits.Address + typealias Size = Traits.Size + + var name: String + var value: Address + var size: Size + var sectionIndex: Int + var binding: Elf_Sym_Binding + var type: Elf_Sym_Type + var visibility: Elf_Sym_Visibility + } + + private var _symbols: [Symbol] = [] + + init() {} + + init?(image: ElfImage) { + guard let strtab = image.getSection(".strtab", debug: false), + let symtab = image.getSection(".symtab", debug: false), + let strings = strtab.fetchAllBytes(), + let symdata = symtab.fetchAllBytes() else { + return nil + } + + let stringSect = ElfStringSection(bytes: strings) + + // Extract all the data + symdata.withUnsafeBufferPointer{ + $0.withMemoryRebound(to: Traits.Sym.self) { symbols in + for symbol in symbols { + // Ignore things that are not functions + if symbol.st_type != .STT_FUNC { + continue + } + + // Ignore anything undefined + if symbol.st_shndx == SHN_UNDEF { + continue + } + + _symbols.append( + Symbol( + name: (stringSect.getStringAt(index: Int(symbol.st_name)) + ?? ""), + value: symbol.st_value, + size: symbol.st_size, + sectionIndex: Int(symbol.st_shndx), + binding: symbol.st_binding, + type: symbol.st_type, + visibility: symbol.st_visibility + ) + ) + } + } + } + + // Now sort by address + _symbols.sort(by: { + $0.value < $1.value || ( + $0.value == $1.value && $0.size < $1.size + ) + }) + } + + private init(sortedSymbols: [Symbol]) { + _symbols = sortedSymbols + } + + public func merged(with other: ElfSymbolTable) -> ElfSymbolTable { + var merged: [Symbol] = [] + + var ourNdx = 0, theirNdx = 0 + + while ourNdx < _symbols.count && theirNdx < other._symbols.count { + let ourSym = _symbols[ourNdx] + let theirSym = other._symbols[theirNdx] + + if ourSym.value < theirSym.value { + merged.append(ourSym) + ourNdx += 1 + } else if ourSym.value > theirSym.value { + merged.append(theirSym) + theirNdx += 1 + } else if ourSym == theirSym { + merged.append(ourSym) + ourNdx += 1 + theirNdx += 1 + } else { + if ourSym.size <= theirSym.size { + merged.append(ourSym) + } + merged.append(theirSym) + if ourSym.size > theirSym.size { + merged.append(theirSym) + } + ourNdx += 1 + theirNdx += 1 + } + } + + if ourNdx < _symbols.count { + merged.append(contentsOf:_symbols[ourNdx...]) + } + if theirNdx < other._symbols.count { + merged.append(contentsOf:other._symbols[theirNdx...]) + } + + return ElfSymbolTable(sortedSymbols: merged) + } + + public func lookupSymbol(address: Traits.Address) -> Symbol? { + var min = 0 + var max = _symbols.count + + while min < max { + let mid = min + (max - min) / 2 + let symbol = _symbols[mid] + let nextValue: Traits.Address + if mid == _symbols.count - 1 { + nextValue = ~Traits.Address(0) + } else { + nextValue = _symbols[mid + 1].value + } + + if symbol.value <= address && nextValue > address { + var ndx = mid + while ndx > 0 && _symbols[ndx - 1].value == address { + ndx -= 1 + } + return _symbols[ndx] + } else if symbol.value < address { + min = mid + 1 + } else if symbol.value > address { + max = mid + } + } + + return nil + } +} + +class ElfImage: ElfImageProtocol { + typealias Traits = SomeElfTraits + typealias Source = SomeImageSource + typealias SymbolTable = ElfSymbolTable + + // This is arbitrary and it isn't in the spec + let maxNoteNameLength = 256 + + var baseAddress: Source.Address + var endAddress: Source.Address + + var source: SomeImageSource + var header: Traits.Ehdr + var programHeaders: [Traits.Phdr] + var sectionHeaders: [Traits.Shdr]? + var shouldByteSwap: Bool { return header.shouldByteSwap } + + required init(source: SomeImageSource, + baseAddress: Source.Address = 0, + endAddress: Source.Address = 0) throws { + self.source = source + self.baseAddress = baseAddress + self.endAddress = endAddress + + header = try source.fetch(from: 0, as: Traits.Ehdr.self) + if header.ei_magic != ElfMagic { + throw ElfImageError.notAnElfImage + } + + if header.ei_class != Traits.elfClass { + throw ElfImageError.wrongClass + } + + if header.shouldByteSwap { + header = header.byteSwapped + } + + let byteSwap = header.shouldByteSwap + func maybeSwap(_ x: T) -> T { + if byteSwap { + return x.byteSwapped + } + return x + } + + var phdrs: [Traits.Phdr] = [] + var phAddr = Source.Address(header.e_phoff) + for _ in 0..= header.e_shnum { + throw ElfImageError.badStringTableSectionIndex + } + } + + struct Note { + let name: String + let type: UInt32 + let desc: [UInt8] + } + + struct Notes: Sequence { + var image: ElfImage + + struct NoteIterator: IteratorProtocol { + var image: ElfImage + + var hdrNdx = -1 + var noteAddr = Source.Address() + var noteEnd = Source.Address() + + init(image: ElfImage) { + self.image = image + } + + mutating func startHeader() { + let ph = image.programHeaders[hdrNdx] + + if image.source.isMappedImage { + noteAddr = Source.Address(ph.p_vaddr) + noteEnd = noteAddr + Source.Address(ph.p_memsz) + } else { + noteAddr = Source.Address(ph.p_offset) + noteEnd = noteAddr + Source.Address(ph.p_filesz) + } + } + + mutating func next() -> Note? { + if hdrNdx >= image.programHeaders.count { + return nil + } + while true { + while noteAddr >= noteEnd { + repeat { + hdrNdx += 1 + if hdrNdx >= image.programHeaders.count { + return nil + } + } while image.programHeaders[hdrNdx].p_type != .PT_NOTE + startHeader() + } + + do { + let nhdr = try image.fetch(from: noteAddr, as: Traits.Nhdr.self) + + noteAddr += Source.Address(MemoryLayout.size) + + if noteEnd - noteAddr < nhdr.n_namesz { + // The segment is probably corrupted + noteAddr = noteEnd + continue + } + + let nameLen = nhdr.n_namesz > 0 ? nhdr.n_namesz - 1 : 0 + let nameBytes = try image.fetch(from: noteAddr, + count: Int(nameLen), + as: UInt8.self) + let name = String(decoding: nameBytes, as: UTF8.self) + + noteAddr += Source.Address(nhdr.n_namesz) + if (noteAddr & 3) != 0 { + noteAddr += 4 - (noteAddr & 3) + } + + if noteEnd - noteAddr < nhdr.n_descsz { + // The segment is probably corrupted + noteAddr = noteEnd + continue + } + + let desc = try image.fetch(from: noteAddr, + count: Int(nhdr.n_descsz), + as: UInt8.self) + + noteAddr += Source.Address(nhdr.n_descsz) + if (noteAddr & 3) != 0 { + noteAddr += 4 - (noteAddr & 3) + } + + return Note(name: name, type: UInt32(nhdr.n_type), desc: desc) + } catch { + hdrNdx = image.programHeaders.count + return nil + } + } + } + } + + func makeIterator() -> NoteIterator { + return NoteIterator(image: image) + } + } + + var notes: Notes { + return Notes(image: self) + } + + private var _uuid: [UInt8]? + var uuid: [UInt8]? { + if let uuid = _uuid { + return uuid + } + + for note in notes { + if note.name == "GNU" && note.type == ImageFormats.NT_GNU_BUILD_ID { + _uuid = note.desc + return _uuid + } + } + + return nil + } + + private var _debugLinkCRC: UInt32? + var debugLinkCRC: UInt32? { + guard let bounds = source.bounds else { + return nil + } + + if let crc = _debugLinkCRC { + return crc + } + + let bufSize = 65536 + let buffer = UnsafeMutableBufferPointer.allocate(capacity: bufSize) + defer { + buffer.deallocate() + } + + var pos = bounds.base + var remaining = bounds.size + var crc: UInt32 = 0 + do { + while remaining > 0 { + let todo = min(bufSize, Int(remaining)) + let slice = buffer[..(rebasing: slice) + + try fetch(from: pos, into: chunk) + + crc = updateCrc(crc, UnsafeBufferPointer(chunk)) + + remaining -= Source.Size(todo) + pos += Source.Address(todo) + } + } catch { + return nil + } + + return crc + } + + struct Range { + var base: Source.Address + var size: Source.Size + } + + struct EHFrameInfo { + var ehFrameSection: Range? + var ehFrameHdrSection: Range? + } + + private var _ehFrameInfo: EHFrameInfo? + var ehFrameInfo: EHFrameInfo? { + if let ehFrameInfo = _ehFrameInfo { + return ehFrameInfo + } + + var ehFrameInfo = EHFrameInfo() + + for phdr in programHeaders { + if phdr.p_type == .PT_GNU_EH_FRAME { + var ehFrameHdrRange: Range + if source.isMappedImage { + ehFrameHdrRange = Range(base: Source.Address(phdr.p_vaddr), + size: Source.Size(phdr.p_memsz)) + } else { + ehFrameHdrRange = Range(base: Source.Address(phdr.p_offset), + size: Source.Size(phdr.p_filesz)) + } + + if (ehFrameHdrRange.size < MemoryLayout.size) { + continue + } + + guard let ehdr = try? fetch(from: Source.Address(ehFrameHdrRange.base), + as: EHFrameHdr.self) else { + continue + } + + if ehdr.version != 1 { + continue + } + + let pc = ehFrameHdrRange.base + Source.Address(MemoryLayout.size) + guard let (_, eh_frame_ptr) = + try? source.fetchEHValue(from: Source.Address(pc), + with: ehdr.eh_frame_ptr_enc, + pc: Source.Address(pc)) else { + continue + } + + ehFrameInfo.ehFrameHdrSection = ehFrameHdrRange + + // The .eh_frame_hdr section doesn't specify the size of the + // .eh_frame section, so we just rely on it being properly + // terminated. This does mean that bulk fetching the entire + // thing isn't a good idea. + ehFrameInfo.ehFrameSection = Range(base: Source.Address(eh_frame_ptr), + size: ~Source.Size(0)) + } + } + + if let sectionHeaders = sectionHeaders { + let stringShdr = sectionHeaders[Int(header.e_shstrndx)] + do { + let bytes = try source.fetch(from: Source.Address(stringShdr.sh_offset), + count: Int(stringShdr.sh_size), + as: UInt8.self) + let stringSect = ElfStringSection(bytes: bytes) + + for shdr in sectionHeaders { + guard let name = stringSect.getStringAt(index: Int(shdr.sh_name)) else { + continue + } + + if name == ".eh_frame" { + ehFrameInfo.ehFrameSection = Range(base: Source.Address(shdr.sh_offset), + size: Source.Size(shdr.sh_size)) + } + } + } catch { + } + } + + return ehFrameInfo + } + + // Image name + private var _imageName: String? + var imageName: String { + if let imageName = _imageName { + return imageName + } + + let name: String + if let path = source.path { + name = path + } else if let uuid = uuid { + name = "image \(hex(uuid))" + } else { + name = "" + } + + _imageName = name + return name + } + + // If we have external debug information, this points at it + private var _checkedDebugImage: Bool? + private var _debugImage: (any ElfImageProtocol)? + var debugImage: (any ElfImageProtocol)? { + if let checked = _checkedDebugImage, checked { + return _debugImage + } + + let tryPath = { [self] (_ path: String) -> (any ElfImageProtocol)? in + do { + let fileSource = try FileImageSource(path: path) + let image = try ElfImage(source: fileSource) + _debugImage = image + return image + } catch { + return nil + } + } + + if let uuid = uuid { + let uuidString = hex(uuid) + let uuidSuffix = uuidString.dropFirst(2) + let uuidPrefix = uuidString.prefix(2) + let path = "/usr/lib/debug/.build-id/\(uuidPrefix)/\(uuidSuffix).debug" + if let image = tryPath(path) { + _debugImage = image + _checkedDebugImage = true + return image + } + } + + if let imagePath = source.path, let realImagePath = realPath(imagePath) { + let imageDir = dirname(realImagePath) + let debugLink = getDebugLink() + let debugAltLink = getDebugAltLink() + + let tryLink = { (_ link: String) -> (any ElfImageProtocol)? in + if let image = tryPath("\(imageDir)/\(link)") { + return image + } + if let image = tryPath("\(imageDir)/.debug/\(link)") { + return image + } + if let image = tryPath("/usr/lib/debug/\(imageDir)/\(link)") { + return image + } + return nil + } + + if let debugAltLink = debugAltLink, let image = tryLink(debugAltLink.link), + image.uuid == debugAltLink.uuid { + _debugImage = image + _checkedDebugImage = true + return image + } + + if let debugLink = debugLink, let image = tryLink(debugLink.link), + image.debugLinkCRC == debugLink.crc { + _debugImage = image + _checkedDebugImage = true + return image + } + } + + if let debugData = getSection(".gnu_debugdata") { + do { + let source = try LZMACompressedImageSource(source: debugData) + _debugImage = try ElfImage(source: source) + _checkedDebugImage = true + return _debugImage + } catch let CompressedImageSourceError.libraryNotFound(library) { + swift_reportWarning(0, + """ + swift-runtime: warning: \(library) not found, \ + unable to decode the .gnu_debugdata section in \ + \(imageName) + """) + } catch { + } + } + + _checkedDebugImage = true + return nil + } + + /// Find the named section and return an ImageSource pointing at it. + /// + /// In general, the section may be compressed or even in a different image; + /// this is particularly the case for debug sections. We will only attempt + /// to look for other images if `debug` is `true`. + func getSection(_ name: String, debug: Bool) -> (any ImageSource)? { + if let sectionHeaders = sectionHeaders { + let zname = ".z" + name.dropFirst() + let stringShdr = sectionHeaders[Int(header.e_shstrndx)] + do { + let bytes = try source.fetch(from: Source.Address(stringShdr.sh_offset), + count: Int(stringShdr.sh_size), + as: UInt8.self) + let stringSect = ElfStringSection(bytes: bytes) + + for shdr in sectionHeaders { + guard let sname + = stringSect.getStringAt(index: Int(shdr.sh_name)) else { + continue + } + + if name == sname { + let subSource = SubImageSource(parent: source, + baseAddress: Source.Address(shdr.sh_offset), + length: Source.Size(shdr.sh_size)) + if (shdr.sh_flags & Traits.Shdr.Flags(SHF_COMPRESSED)) != 0 { + return try ElfCompressedImageSource(source: subSource) + } else { + return subSource + } + } + + if zname == sname { + let subSource = SubImageSource(parent: source, + baseAddress: Source.Address(shdr.sh_offset), + length: Source.Size(shdr.sh_size)) + return try ElfGNUCompressedImageSource(source: subSource) + } + } + } catch let CompressedImageSourceError.libraryNotFound(library) { + swift_reportWarning(0, + """ + swift-runtime: warning: \(library) not found, \ + unable to decode the \(name) section in \ + \(imageName) + """) + } catch { + } + } + + if debug, let image = debugImage { + return image.getSection(name) + } + + return nil + } + + struct DebugLinkInfo { + var link: String + var crc: UInt32 + } + + struct DebugAltLinkInfo { + var link: String + var uuid: [UInt8] + } + + /// Get and decode a .gnu_debuglink section + func getDebugLink() -> DebugLinkInfo? { + guard let section = getSection(".gnu_debuglink") else { + return nil + } + + guard let bytes = section.fetchAllBytes() else { + return nil + } + + guard let nullIndex = bytes.firstIndex(of: UInt8(0)) else { + return nil + } + + let link = String(decoding: bytes[0.. DebugAltLinkInfo? { + guard let section = getSection(".gnu_debugaltlink") else { + return nil + } + + guard let bytes = section.fetchAllBytes() else { + return nil + } + + guard let nullIndex = bytes.firstIndex(of: UInt8(0)) else { + return nil + } + + let link = String(decoding: bytes[0.. String? { + guard let sectionSource = getSection(name) else { + return nil + } + + if let data = sectionSource.fetchAllBytes() { + return String(decoding: data, as: UTF8.self) + } + + return nil + } + + struct ElfSymbol { + var name: String + var value: Traits.Address + var size: Traits.Size + var sectionIndex: Int + var binding: Elf_Sym_Binding + var type: Elf_Sym_Type + var visibility: Elf_Sym_Visibility + } + + var _symbolTable: SymbolTable? = nil + var symbolTable: SymbolTable { return _getSymbolTable(debug: false) } + + func _getSymbolTable(debug: Bool) -> SymbolTable { + if let table = _symbolTable { + return table + } + + let debugTable: SymbolTable? + if !debug, let debugImage = debugImage { + debugTable = debugImage._getSymbolTable(debug: true) + as any ElfSymbolTableProtocol + as? SymbolTable + } else { + debugTable = nil + } + + guard let localTable = SymbolTable(image: self) else { + // If we have no symbol table, try the debug image + let table = debugTable ?? SymbolTable() + _symbolTable = table + return table + } + + // Check if we have a debug image; if we do, get its symbol table and + // merge it with this one. This means that it doesn't matter which + // symbols have been stripped in both images. + if let debugTable = debugTable { + let merged = localTable.merged(with: debugTable) + _symbolTable = merged + return merged + } + + _symbolTable = localTable + return localTable + } + + public func lookupSymbol(address: Source.Address) -> ImageSymbol? { + let relativeAddress = Traits.Address(address - baseAddress) + guard let symbol = symbolTable.lookupSymbol(address: relativeAddress) else { + return nil + } + + return ImageSymbol(name: symbol.name, + offset: Int(relativeAddress - symbol.value)) + } + + func getDwarfSection(_ section: DwarfSection) -> (any ImageSource)? { + switch section { + case .debugAbbrev: return getSection(".debug_abbrev") + case .debugAddr: return getSection(".debug_addr") + case .debugARanges: return getSection(".debug_aranges") + case .debugFrame: return getSection(".debug_frame") + case .debugInfo: return getSection(".debug_info") + case .debugLine: return getSection(".debug_line") + case .debugLineStr: return getSection(".debug_line_str") + case .debugLoc: return getSection(".debug_loc") + case .debugLocLists: return getSection(".debug_loclists") + case .debugMacInfo: return getSection(".debug_macinfo") + case .debugMacro: return getSection(".debug_macro") + case .debugNames: return getSection(".debug_names") + case .debugPubNames: return getSection(".debug_pubnames") + case .debugPubTypes: return getSection(".debug_pubtypes") + case .debugRanges: return getSection(".debug_ranges") + case .debugRngLists: return getSection(".debug_rnglists") + case .debugStr: return getSection(".debug_str") + case .debugStrOffsets: return getSection(".debug_str_offsets") + case .debugSup: return getSection(".debug_sup") + case .debugTypes: return getSection(".debug_types") + case .debugCuIndex: return getSection(".debug_cu_index") + case .debugTuIndex: return getSection(".debug_tu_index") + } + } + + private lazy var dwarfReader + = try? DwarfReader(source: self, shouldSwap: header.shouldByteSwap) + + typealias CallSiteInfo = DwarfReader.CallSiteInfo + + func inlineCallSites(at address: Address) -> ArraySlice { + guard let callSiteInfo = dwarfReader?.inlineCallSites else { + return [][0..<0] + } + + var min = 0 + var max = callSiteInfo.count + + while min < max { + let mid = min + (max - min) / 2 + let callSite = callSiteInfo[mid] + + if callSite.lowPC <= address && callSite.highPC > address { + var first = mid, last = mid + while first > 0 + && callSiteInfo[first - 1].lowPC <= address + && callSiteInfo[first - 1].highPC > address { + first -= 1 + } + while last < callSiteInfo.count - 1 + && callSiteInfo[last + 1].lowPC <= address + && callSiteInfo[last + 1].highPC > address { + last += 1 + } + + return callSiteInfo[first...last] + } else if callSite.highPC <= address { + min = mid + 1 + } else if callSite.lowPC > address { + max = mid + } + } + + return [] + } + + typealias SourceLocation = SymbolicatedBacktrace.SourceLocation + + func sourceLocation(for address: Address) throws -> SourceLocation? { + var result: SourceLocation? = nil + var prevState: DwarfReader.LineNumberState? = nil + guard let dwarfReader = dwarfReader else { + return nil + } + for ndx in 0..= oldState.address && address < state.address { + result = SourceLocation( + path: oldState.path, + line: oldState.line, + column: oldState.column + ) + done = true + } + + prevState = state + } + } + + return result + } +} + +typealias Elf32Image = ElfImage +typealias Elf64Image = ElfImage + +// .. Testing .................................................................. + +@_spi(ElfTest) +public func testElfImageAt(path: String) -> Bool { + guard let source = try? FileImageSource(path: path) else { + print("\(path) was not accessible") + return false + } + + let debugSections: [String] = [ + ".debug_info", + ".debug_line", + ".debug_abbrev", + ".debug_ranges", + ".debug_str", + ".debug_addr", + ".debug_str_offsets", + ".debug_line_str", + ".debug_rnglists" + ] + + if let elfImage = try? Elf32Image(source: source) { + print("\(path) is a 32-bit ELF image") + + if let uuid = elfImage.uuid { + print(" uuid: \(hex(uuid))") + } else { + print(" uuid: ") + } + + if let debugImage = elfImage.debugImage { + print(" debug image: \(debugImage.imageName)") + } else { + print(" debug image: ") + } + + for section in debugSections { + if let _ = elfImage.getSection(section, debug: true) { + print(" \(section): found") + } else { + print(" \(section): not found") + } + } + + return true + } else if let elfImage = try? Elf64Image(source: source) { + print("\(path) is a 64-bit ELF image") + + if let uuid = elfImage.uuid { + print(" uuid: \(hex(uuid))") + } else { + print(" uuid: ") + } + + if let debugImage = elfImage.debugImage { + print(" debug image: \(debugImage.imageName)") + } else { + print(" debug image: ") + } + + for section in debugSections { + if let _ = elfImage.getSection(section, debug: true) { + print(" \(section): found") + } else { + print(" \(section): not found") + } + } + + return true + } else { + print("\(path) is not an ELF image") + return false + } +} + +#endif // os(Linux) diff --git a/stdlib/public/Backtracing/FileImageSource.swift b/stdlib/public/Backtracing/FileImageSource.swift new file mode 100644 index 0000000000000..94ed7b420f94d --- /dev/null +++ b/stdlib/public/Backtracing/FileImageSource.swift @@ -0,0 +1,70 @@ +//===--- FileImageSource.swift - An image source that reads from a file ---===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines FileImageSource, an image source that reads data from a file. +// +//===----------------------------------------------------------------------===// + +import Swift + +@_implementationOnly import OS.Libc + +enum FileImageSourceError: Error { + case posixError(Int32) + case truncatedRead +} + +class FileImageSource: ImageSource { + private var fd: Int32 + + public var isMappedImage: Bool { return false } + + private var _path: String + public var path: String? { return _path } + + public lazy var bounds: Bounds? = { + let size = lseek(fd, 0, SEEK_END) + if size < 0 { + return nil + } + return Bounds(base: 0, size: Size(size)) + }() + + public init(path: String) throws { + _path = path + fd = _swift_open(path, O_RDONLY, 0) + if fd < 0 { + throw FileImageSourceError.posixError(_swift_get_errno()) + } + } + + deinit { + close(fd) + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + while true { + let size = MemoryLayout.stride * buffer.count + let result = pread(fd, buffer.baseAddress, size, off_t(addr)) + + if result < 0 { + throw FileImageSourceError.posixError(_swift_get_errno()) + } + + if result != size { + throw FileImageSourceError.truncatedRead + } + break + } + } +} diff --git a/stdlib/public/Backtracing/FramePointerUnwinder.swift b/stdlib/public/Backtracing/FramePointerUnwinder.swift index d8232e9be58ee..643fc557eb17a 100644 --- a/stdlib/public/Backtracing/FramePointerUnwinder.swift +++ b/stdlib/public/Backtracing/FramePointerUnwinder.swift @@ -28,15 +28,83 @@ public struct FramePointerUnwinder: Sequence, Itera var first: Bool var isAsync: Bool + #if os(Linux) + var elf32Cache: [Int:Elf32Image] = [:] + var elf64Cache: [Int:Elf64Image] = [:] + var images: [Backtrace.Image]? + #endif + var reader: MemoryReader - public init(context: Context, memoryReader: MemoryReader) { + public init(context: Context, + images: [Backtrace.Image]?, + memoryReader: MemoryReader) { + pc = Address(context.programCounter) fp = Address(context.framePointer) first = true isAsync = false asyncContext = 0 reader = memoryReader + + // On Linux, the unwinder needs images in order to spot async frames + #if os(Linux) + self.images = images + #endif + } + + private func isAsyncSymbol(_ mangledName: String) -> Bool { + let mangledUTF8 = mangledName.utf8 + if mangledUTF8.last == UInt8(ascii: "_") { + let withoutUnderscore = mangledUTF8.dropLast(1) + if let beforeIndexNdx = withoutUnderscore.lastIndex( + where: { $0 < UInt8(ascii: "0") || $0 > UInt8(ascii: "9") } + ) { + let beforeIndex = withoutUnderscore[...beforeIndexNdx] + let suffix = beforeIndex.suffix(2) + let awaitResume = "TY".utf8 + let suspendResume = "TQ".utf8 + return suffix.elementsEqual(awaitResume) || + suffix.elementsEqual(suspendResume) + } + } + return false + } + + private mutating func isAsyncPC(_ pc: Address) -> Bool { + // On Linux, we need to examine the PC to see if this is an async frame + #if os(Linux) + let address = FileImageSource.Address(pc) + + if let images = images, + let imageNdx = images.firstIndex( + where: { address >= $0.baseAddress && address < $0.endOfText } + ) { + let relativeAddress = address - FileImageSource.Address(images[imageNdx].baseAddress) + var elf32Image = elf32Cache[imageNdx] + var elf64Image = elf64Cache[imageNdx] + + if elf32Image == nil && elf64Image == nil { + if let source = try? FileImageSource(path: images[imageNdx].path) { + if let elfImage = try? Elf32Image(source: source) { + elf32Image = elfImage + elf32Cache[imageNdx] = elfImage + } else if let elfImage = try? Elf64Image(source: source) { + elf64Image = elfImage + elf64Cache[imageNdx] = elfImage + } + } + } + + if let theSymbol = elf32Image?.lookupSymbol(address: relativeAddress) { + return isAsyncSymbol(theSymbol.name) + } else if let theSymbol = elf64Image?.lookupSymbol(address: relativeAddress) { + return isAsyncSymbol(theSymbol.name) + } + } + #endif + + return false } private func isAsyncFrame(_ storedFp: Address) -> Bool { @@ -73,32 +141,34 @@ public struct FramePointerUnwinder: Sequence, Itera } if !isAsync { - // Try to read the next fp/pc pair - var next: Address = 0 - let strippedFp = stripPtrAuth(fp) - - if strippedFp == 0 - || !Context.isAlignedForStack(framePointer: - Context.Address(strippedFp)) { - return nil - } - - do { - pc = stripPtrAuth(try reader.fetch(from: - strippedFp + Address(MemoryLayout
.size), - as: Address.self)) - next = try reader.fetch(from: Address(strippedFp), as: Address.self) - } catch { - return nil - } - - if next <= fp { - return nil - } - - if !isAsyncFrame(next) { - fp = next - return .returnAddress(Backtrace.Address(pc)) + if !isAsyncPC(pc) { + // Try to read the next fp/pc pair + var next: Address = 0 + let strippedFp = stripPtrAuth(fp) + + if strippedFp == 0 + || !Context.isAlignedForStack(framePointer: + Context.Address(strippedFp)) { + return nil + } + + do { + pc = stripPtrAuth(try reader.fetch(from: + strippedFp + Address(MemoryLayout
.size), + as: Address.self)) + next = try reader.fetch(from: Address(strippedFp), as: Address.self) + } catch { + return nil + } + + if next <= fp { + return nil + } + + if !isAsyncFrame(next) { + fp = next + return .returnAddress(Backtrace.Address(pc)) + } } isAsync = true diff --git a/stdlib/public/Backtracing/Image.swift b/stdlib/public/Backtracing/Image.swift new file mode 100644 index 0000000000000..7f3d1ef6ebae1 --- /dev/null +++ b/stdlib/public/Backtracing/Image.swift @@ -0,0 +1,167 @@ +//===--- Image.swift - Binary image protocol for Swift --------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Defines a protocol for binary image files that allows us to fetch what +// we need without knowing all of the gory details. +// +//===----------------------------------------------------------------------===// + +import Swift + +struct ImageSymbol { + var name: String + var offset: Int +} + +internal protocol Image { + associatedtype Source: ImageSource + + typealias UUID = [UInt8] + typealias Address = Source.Address + + init(source: Source, baseAddress: Address, endAddress: Address) throws + + var baseAddress: Address { get set } + var endAddress: Address { get set } + + var source: Source { get } + var uuid: UUID? { get } + var shouldByteSwap: Bool { get } + + func swapIfRequired(_ x: T) -> T + func swapIfRequired(_ x: T) -> T + func swapIfRequired(_ x: T) -> T + + func swapIfRequired(array: inout [T]) + func swapIfRequired(array: inout [T]) + func swapIfRequired(array: inout [T]) + + func swapIfRequired(buffer: UnsafeMutableBufferPointer) + func swapIfRequired(buffer: UnsafeMutableBufferPointer) + func swapIfRequired(buffer: UnsafeMutableBufferPointer) + + func swapIfRequired(pointer: UnsafeMutablePointer) + func swapIfRequired(pointer: UnsafeMutablePointer) + func swapIfRequired(pointer: UnsafeMutablePointer) + + func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws + func fetch(from addr: Address, into pointer: UnsafeMutablePointer) throws + func fetch(from addr: Address, count: Int, as: T.Type) throws -> [T] + func fetch(from addr: Address, as type: T.Type) throws -> T + + func fetchUnswapped(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws + func fetchUnswapped(from addr: Address, + into pointer: UnsafeMutablePointer) throws + func fetchUnswapped(from addr: Address, count: Int, as: T.Type) throws -> [T] + func fetchUnswapped(from addr: Address, as type: T.Type) throws -> T + + func lookupSymbol(address: Address) -> ImageSymbol? +} + +extension Image { + public func swapIfRequired(_ x: T) -> T { + if shouldByteSwap { + return x.byteSwapped + } + return x + } + + public func swapIfRequired(_ x: T) -> T { + if shouldByteSwap { + return x.byteSwapped + } + return x + } + + public func swapIfRequired(_ x: T) -> T { + return x + } + + public func swapIfRequired(array: inout [T]) { + if shouldByteSwap { + array.swapBytes() + } + } + public func swapIfRequired(array: inout [T]) { + if shouldByteSwap { + array.swapBytes() + } + } + public func swapIfRequired(array: inout [T]) { + // Nothing to do + } + + public func swapIfRequired(buffer: UnsafeMutableBufferPointer) { + if shouldByteSwap { + buffer.swapBytes() + } + } + public func swapIfRequired(buffer: UnsafeMutableBufferPointer) { + if shouldByteSwap { + buffer.swapBytes() + } + } + public func swapIfRequired(buffer: UnsafeMutableBufferPointer) { + // Nothing to do + } + + public func swapIfRequired(pointer: UnsafeMutablePointer) { + if shouldByteSwap { + pointer.pointee = pointer.pointee.byteSwapped + } + } + public func swapIfRequired(pointer: UnsafeMutablePointer) { + if shouldByteSwap { + pointer.pointee = pointer.pointee.byteSwapped + } + } + public func swapIfRequired(pointer: UnsafeMutablePointer) { + // Nothing to do + } + + + public func fetchUnswapped(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + return try source.fetch(from: addr, into: buffer) + } + public func fetchUnswapped(from addr: Address, + into pointer: UnsafeMutablePointer) throws { + return try source.fetch(from: addr, into: pointer) + } + public func fetchUnswapped(from addr: Address, count: Int, as type: T.Type) throws -> [T] { + return try source.fetch(from: addr, count: count, as: type) + } + public func fetchUnswapped(from addr: Address, as type: T.Type) throws -> T { + return try source.fetch(from: addr, as: type) + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try fetchUnswapped(from: addr, into: buffer) + swapIfRequired(buffer: buffer) + } + public func fetch(from addr: Address, + into pointer: UnsafeMutablePointer) throws { + try fetchUnswapped(from: addr, into: pointer) + swapIfRequired(pointer: pointer) + } + public func fetch(from addr: Address, count: Int, as type: T.Type) throws -> [T]{ + var result = try fetchUnswapped(from: addr, count: count, as: type) + swapIfRequired(array: &result) + return result + } + public func fetch(from addr: Address, as type: T.Type) throws -> T { + return swapIfRequired(try fetchUnswapped(from: addr, as: type)) + } +} diff --git a/stdlib/public/Backtracing/ImageSource.swift b/stdlib/public/Backtracing/ImageSource.swift new file mode 100644 index 0000000000000..2c68a45b8605f --- /dev/null +++ b/stdlib/public/Backtracing/ImageSource.swift @@ -0,0 +1,153 @@ +//===--- ImageSource.swift - A place from which to read image data --------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines ImageSource, which is a protocol that can be implemented to +// provide an image reader with a way to read data from a file, a buffer +// in memory, or wherever we might wish to read an image from. +// +//===----------------------------------------------------------------------===// + +import Swift + +struct ImageBounds { + var base: Address + var size: Size + var end: Address { + return base + Address(size) + } + + func adjusted(by offset: some FixedWidthInteger) -> Self { + return Self(base: base + Address(offset), size: size - Size(offset)) + } +} + +protocol ImageSource: MemoryReader { + typealias Bounds = ImageBounds + + /// Says whether we are looking at a loaded image in memory or not. + /// The layout in memory may differ from the on-disk layout; in particular, + /// some information may not be available when the image is mapped into + /// memory (an example is ELF section headers). + var isMappedImage: Bool { get } + + /// If this ImageSource knows its path, this will be non-nil. + var path: String? { get } + + /// If this ImageSource knows its bounds, this will be non-nil. + var bounds: Bounds? { get } +} + +struct ImageSourceCursor { + typealias Address = UInt64 + typealias Size = UInt64 + typealias Bounds = ImageBounds + + var source: any ImageSource + var pos: Address + + init(source: any ImageSource, offset: Address = 0) { + self.source = source + self.pos = offset + } + + public mutating func read(into buffer: UnsafeMutableBufferPointer) throws { + try source.fetch(from: pos, into: buffer) + pos += UInt64(MemoryLayout.stride * buffer.count) + } + + public mutating func read(into pointer: UnsafeMutablePointer) throws { + try source.fetch(from: pos, into: pointer) + pos += UInt64(MemoryLayout.stride) + } + + public mutating func read(as type: T.Type) throws -> T { + let stride = MemoryLayout.stride + let result = try source.fetch(from: pos, as: type) + pos += UInt64(stride) + return result + } + + public mutating func read(count: Int, as type: T.Type) throws -> [T] { + let stride = MemoryLayout.stride + let result = try source.fetch(from: pos, count: count, as: type) + pos += UInt64(stride * count) + return result + } + + public mutating func readString() throws -> String? { + var bytes: [UInt8] = [] + while true { + let ch = try read(as: UInt8.self) + if ch == 0 { + break + } + bytes.append(ch) + } + + return String(decoding: bytes, as: UTF8.self) + } + +} + +extension ImageSource { + /// Fetch all the data from this image source (which must be bounded) + func fetchAllBytes() -> [UInt8]? { + guard let bounds = self.bounds else { + return nil + } + if let data = try? fetch(from: bounds.base, + count: Int(bounds.size), + as: UInt8.self) { + return data + } + return nil + } +} + +enum SubImageSourceError: Error { + case outOfRangeFetch(UInt64, Int) +} + +struct SubImageSource: ImageSource { + var parent: S + var baseAddress: Address + var length: Size + var path: String? { return parent.path } + + var bounds: Bounds? { + return Bounds(base: 0, size: length) + } + + public init(parent: S, baseAddress: Address, length: Size) { + self.parent = parent + self.baseAddress = baseAddress + self.length = length + } + + public var isMappedImage: Bool { + return parent.isMappedImage + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let toFetch = buffer.count * MemoryLayout.stride + if addr < 0 || addr > length { + throw SubImageSourceError.outOfRangeFetch(UInt64(addr), toFetch) + } + if Address(length) - addr < toFetch { + throw SubImageSourceError.outOfRangeFetch(UInt64(addr), toFetch) + } + + return try parent.fetch(from: baseAddress + addr, into: buffer) + } +} diff --git a/stdlib/public/Backtracing/MemoryImageSource.swift b/stdlib/public/Backtracing/MemoryImageSource.swift new file mode 100644 index 0000000000000..29baa7d163cd8 --- /dev/null +++ b/stdlib/public/Backtracing/MemoryImageSource.swift @@ -0,0 +1,35 @@ +//===--- MemoryImageSource.swift - An image source that reads from a file ---===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines MemoryImageSource, an image source that reads data using a +// MemoryReader. +// +//===----------------------------------------------------------------------===// + +import Swift + +class MemoryImageSource: ImageSource { + private var reader: M + + public var isMappedImage: Bool { return true } + public var path: String? { return nil } + public var bounds: Bounds? { return nil } + + public init(with reader: M) { + self.reader = reader + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try reader.fetch(from: addr, into: buffer) + } +} diff --git a/stdlib/public/Backtracing/MemoryReader.swift b/stdlib/public/Backtracing/MemoryReader.swift index b82853029c067..b63461a1a6e16 100644 --- a/stdlib/public/Backtracing/MemoryReader.swift +++ b/stdlib/public/Backtracing/MemoryReader.swift @@ -17,20 +17,36 @@ import Swift -@_implementationOnly import _SwiftBacktracingShims +@_implementationOnly import OS.Libc +#if os(macOS) +@_implementationOnly import OS.Darwin +#elseif os(Linux) +@_implementationOnly import OS.Linux +#endif + +@_implementationOnly import Runtime @_spi(MemoryReaders) public protocol MemoryReader { - associatedtype Address: FixedWidthInteger + typealias Address = UInt64 + typealias Size = UInt64 + /// Fill the specified buffer with data from the specified location in + /// the source. func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws + /// Write data from the specified location in the source through a pointer func fetch(from addr: Address, into pointer: UnsafeMutablePointer) throws + /// Fetch an array of Ts from the specified location in the source func fetch(from addr: Address, count: Int, as: T.Type) throws -> [T] + /// Fetch a T from the specified location in the source func fetch(from addr: Address, as: T.Type) throws -> T + + /// Fetch a NUL terminated string from the specified location in the source + func fetchString(from addr: Address) throws -> String? } extension MemoryReader { @@ -60,44 +76,59 @@ extension MemoryReader { } } + public func fetchString(from addr: Address) throws -> String? { + var bytes: [UInt8] = [] + var ptr = addr + while true { + let ch = try fetch(from: ptr, as: UInt8.self) + if ch == 0 { + break + } + bytes.append(ch) + ptr += 1 + } + + return String(decoding: bytes, as: UTF8.self) + } + } @_spi(MemoryReaders) public struct UnsafeLocalMemoryReader: MemoryReader { - public typealias Address = UInt + public init() {} public func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws { - buffer.baseAddress!.update(from: UnsafePointer(bitPattern: address)!, + buffer.baseAddress!.update(from: UnsafePointer(bitPattern: UInt(address))!, count: buffer.count) } } #if os(macOS) @_spi(MemoryReaders) public struct MachError: Error { - var result: __swift_kern_return_t + var result: kern_return_t } @_spi(MemoryReaders) public struct RemoteMemoryReader: MemoryReader { - public typealias Address = UInt64 - - private var task: __swift_task_t + private var task: task_t // Sadly we can't expose the type of this argument public init(task: Any) { - self.task = task as! __swift_task_t + self.task = task as! task_t } public func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws { let size = UInt64(MemoryLayout.stride * buffer.count) var sizeOut = UInt64(0) - let result = _swift_backtrace_vm_read(task, - UInt64(address), - UInt64(size), - buffer.baseAddress, - &sizeOut) + let result = mach_vm_read_overwrite(task, + UInt64(address), + UInt64(size), + mach_vm_address_t( + Int(bitPattern: buffer.baseAddress) + ), + &sizeOut) - if result != _SWIFT_KERN_SUCCESS { + if result != KERN_SUCCESS { throw MachError(result: result) } } @@ -105,10 +136,153 @@ extension MemoryReader { @_spi(MemoryReaders) public struct LocalMemoryReader: MemoryReader { public typealias Address = UInt64 + public typealias Size = UInt64 + + public func fetch(from address: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let reader = RemoteMemoryReader(task: mach_task_self()) + return try reader.fetch(from: address, into: buffer) + } +} +#endif + +#if os(Linux) +@_spi(MemoryReaders) public struct POSIXError: Error { + var errno: CInt +} + +@_spi(MemoryReaders) public struct MemserverError: Error { + var message: String +} + +@_spi(MemoryReaders) public struct MemserverMemoryReader: MemoryReader { + private var fd: CInt + + public init(fd: CInt) { + self.fd = fd + } + + private func safeRead(_ fd: CInt, _ buffer: UnsafeMutableRawBufferPointer) throws -> Int { + var done = 0 + while done < buffer.count { + var ret: ssize_t = 0 + repeat { + ret = read(fd, buffer.baseAddress! + done, buffer.count - done) + } while ret < 0 && _swift_get_errno() == EINTR + if ret < 0 { + throw POSIXError(errno: _swift_get_errno()) + } + if ret == 0 { + break + } + done += Int(ret) + } + + return done + } + + private func safeWrite(_ fd: CInt, _ buffer: UnsafeRawBufferPointer) throws -> Int { + var done = 0 + while done < buffer.count { + var ret: ssize_t = 0 + repeat { + ret = write(fd, buffer.baseAddress! + done, buffer.count - done) + } while ret < 0 && _swift_get_errno() == EINTR + if ret < 0 { + throw POSIXError(errno: _swift_get_errno()) + } + if ret == 0 { + break + } + done += Int(ret) + } + + return done + } + + private func sendRequest(for bytes: Size, from addr: Address) throws { + var request = memserver_req(addr: addr, len: bytes) + try withUnsafeBytes(of: &request){ ptr in + let ret = try safeWrite(fd, ptr) + if ret != ptr.count { + throw MemserverError(message: "Channel closed prematurely") + } + } + } + + private func receiveReply() throws -> memserver_resp { + var response = memserver_resp(addr: 0, len: 0) + try withUnsafeMutableBytes(of: &response){ ptr in + let ret = try safeRead(fd, ptr) + if ret != ptr.count { + throw MemserverError(message: "Channel closed prematurely") + } + } + return response + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try buffer.withMemoryRebound(to: UInt8.self) { + let bytes = UnsafeMutableRawBufferPointer($0) + try sendRequest(for: Size(bytes.count), from: addr) + + var done = 0 + while done < bytes.count { + let reply = try receiveReply() + + if reply.len < 0 { + throw MemserverError(message: "Unreadable at \(hex(addr))") + } + + if done + Int(reply.len) > bytes.count { + throw MemserverError(message: "Overrun at \(hex(addr)) trying to read \(bytes.count) bytes") + } + + let ret = try safeRead(fd, + UnsafeMutableRawBufferPointer( + rebasing: bytes[done..(from address: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let size = size_t(MemoryLayout.stride * buffer.count) + var fromIOVec = iovec(iov_base: UnsafeMutableRawPointer( + bitPattern: UInt(address)), + iov_len: size) + var toIOVec = iovec(iov_base: buffer.baseAddress, iov_len: size) + let result = process_vm_readv(pid, &toIOVec, 1, &fromIOVec, 1, 0) + if result != size { + throw POSIXError(errno: _swift_get_errno()) + } + } +} + +@_spi(MemoryReaders) public struct LocalMemoryReader: MemoryReader { + private var reader: RemoteMemoryReader + + init() { + reader = RemoteMemoryReader(pid: getpid()) + } public func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws { - let reader = RemoteMemoryReader(task: _swift_backtrace_task_self()) return try reader.fetch(from: address, into: buffer) } } diff --git a/stdlib/public/Backtracing/ProcMapsScanner.swift b/stdlib/public/Backtracing/ProcMapsScanner.swift new file mode 100644 index 0000000000000..fdf87613d5515 --- /dev/null +++ b/stdlib/public/Backtracing/ProcMapsScanner.swift @@ -0,0 +1,256 @@ +//===--- ProcMapsScanner.swift --------------------------------*- swift -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines ProcMapsScanner, which is for scanning the /proc//maps +// pseudofiles on Linux. +// +//===----------------------------------------------------------------------===// + +#if os(Linux) + +import Swift + +// Lines in /proc/pid/maps files match the following regex: +// +// ^(?[A-Fa-f0-9]+)-(?[A-Fa-f0-9]+)\s+ +// (?[-rwxsp]{4})\s+ +// (?[A-Fa-f0-9]+)\s+ +// (?[A-Fa-f0-9]+):(?[A-Fa-f0-9]+)\s+ +// (?\d+)\s+ +// (?.*)\s*$ + +struct ProcMapsScanner: Sequence, IteratorProtocol { + typealias SS = S.SubSequence + + struct Match { + var start: SS + var end: SS + var perms: SS + var offset: SS + var major: SS + var minor: SS + var inode: SS + var pathname: SS + } + + private enum State { + case start + case scanningStart + case scanningEnd + case afterEnd + case scanningPerms(Int) + case afterPerms + case scanningOffset + case afterOffset + case scanningMajor + case scanningMinor + case afterMinor + case scanningInode + case afterInode + case scanningPathname + case scanningPathnameWhitespace + } + + private var procMaps: S + private var procMapsUTF8: S.UTF8View + private var ndx: S.UTF8View.Index + + init(_ string: S) { + procMaps = string + procMapsUTF8 = string.utf8 + ndx = procMapsUTF8.startIndex + } + + mutating func scanMatch() -> Match? { + var match: Match = Match(start: "", + end: "", + perms: "", + offset: "", + major: "", + minor: "", + inode: "", + pathname: "") + var currentChunk = ndx + var state: State = .start + + func isPerm(_ ch: UInt8) -> Bool { + return ch == UInt8(ascii: "-") || ch == UInt8(ascii: "r") + || ch == UInt8(ascii: "w") || ch == UInt8(ascii: "x") + || ch == UInt8(ascii: "s") || ch == UInt8(ascii: "p") + } + + func isDecimal(_ ch: UInt8) -> Bool { + return ch >= UInt8(ascii: "0") && ch <= UInt8(ascii: "9") + } + + func isHex(_ ch: UInt8) -> Bool { + return ch >= UInt8(ascii: "A") && ch <= UInt8(ascii: "F") + || ch >= UInt8(ascii: "a") && ch <= UInt8(ascii: "f") + || ch >= UInt8(ascii: "0") && ch <= UInt8(ascii: "9") + } + + func isWhitespace(_ ch: UInt8) -> Bool { + return ch == UInt8(ascii: " ") || ch == UInt8(ascii: "\t") + } + + while ndx < procMapsUTF8.endIndex { + let ch = procMapsUTF8[ndx] + let next = procMapsUTF8.index(after: ndx) + + switch state { + case .start: + if !isHex(ch) { + return nil + } + state = .scanningStart + case .scanningStart: + if ch == UInt8(ascii: "-") { + match.start = procMaps[currentChunk.. Match? { + while ndx != procMapsUTF8.endIndex { + if let match = scanMatch() { + return match + } + + // If we failed to match, skip to end of line and retry + while ndx != procMapsUTF8.endIndex { + let ch = procMapsUTF8[ndx] + ndx = procMapsUTF8.index(after: ndx) + if ch == 0x0a { + break + } + } + } + + return nil + } +} + +#endif // os(Linux) diff --git a/stdlib/public/Backtracing/Shims.cpp b/stdlib/public/Backtracing/Shims.cpp deleted file mode 100644 index 751581687a133..0000000000000 --- a/stdlib/public/Backtracing/Shims.cpp +++ /dev/null @@ -1,45 +0,0 @@ -//===--- Shims.cpp - Operating system shims ---------------------*- C++ -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2023 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 -// -//===----------------------------------------------------------------------===// -// -// Defines operating system shims. -// -//===----------------------------------------------------------------------===// - -// We do this here because we can safely include dlfcn.h from this file -// without worrying about it trying to drag in the Darwin module. -// -// If we did this in _SwiftBacktracing.h as an inline, we'd need to know -// what RTLD_LAZY was for every platform we support. - -#if __has_include() -#include - -#ifdef __cplusplus -namespace swift { -extern "C" { -#endif - -void *_swift_backtrace_dlopen_lazy(const char *path) { - return dlopen(path, RTLD_LAZY); -} - -void *_swift_backtrace_dlsym(void *handle, const char *name) { - return dlsym(handle, name); -} - -#ifdef __cplusplus -} // extern "C" -} // namespace swift -#endif - -#endif - diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index 72d827c156744..2e7979e426234 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -17,12 +17,12 @@ import Swift -@_implementationOnly import _SwiftBacktracingShims +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import OS.Darwin +#endif -@_silgen_name("_swift_isThunkFunction") -func _swift_isThunkFunction( - _ rawName: UnsafePointer? -) -> CBool +@_implementationOnly import OS.Libc +@_implementationOnly import Runtime /// A symbolicated backtrace public struct SymbolicatedBacktrace: CustomStringConvertible { @@ -84,15 +84,20 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } /// A textual description of this frame. - public var description: String { + public func description(width: Int) -> String { if let symbol = symbol { let isInlined = inlined ? " [inlined]" : "" let isThunk = isSwiftThunk ? " [thunk]" : "" - return "\(captured)\(isInlined)\(isThunk) \(symbol)" + return "\(captured.description(width: width))\(isInlined)\(isThunk) \(symbol)" } else { - return captured.description + return captured.description(width: width) } } + + /// A textual description of this frame. + public var description: String { + return description(width: MemoryLayout.size * 2) + } } /// Represents a symbol we've located @@ -136,7 +141,23 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// True if this symbol is a Swift thunk function. public var isSwiftThunk: Bool { - return _swift_isThunkFunction(rawName) + return _swift_backtrace_isThunkFunction(rawName) + } + + private func maybeUnderscore(_ sym: String) -> String { + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + return "_" + sym + #else + return sym + #endif + } + + private func dylibName(_ dylib: String) -> String { + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + return dylib + ".dylib" + #else + return dylib + ".so" + #endif } /// True if this symbol represents a system function. @@ -148,20 +169,21 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { if rawName == "start" && imageName == "dyld" { return true } + #endif if rawName.hasSuffix("5$mainyyFZ") || rawName.hasSuffix("5$mainyyYaFZTQ0_") - || rawName == "_async_MainTQ0_" { + || rawName == maybeUnderscore("async_MainTQ0_") { return true } - if rawName == "__ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE" && imageName == "libswift_Concurrency.dylib" { + if rawName == maybeUnderscore("_ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE") && imageName == dylibName("libswift_Concurrency") { return true } if let location = sourceLocation, - location.line == 0 && location.column == 0 - && !_swift_isThunkFunction(rawName) { + ((location.line == 0 && location.column == 0) + || location.path.hasSuffix("")) + && !_swift_backtrace_isThunkFunction(rawName) { return true } - #endif return false } @@ -177,9 +199,23 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// Demangle the raw name, if possible. private func demangleRawName() -> String { - // We don't actually need this function on macOS because we're using - // CoreSymbolication, which demangles the name when it does the lookup - // anyway. We will need it for Linux and Windows though. + var length: size_t = 0 + if let demangled = _swift_backtrace_demangle(rawName, rawName.utf8.count, + nil, &length) { + defer { free(demangled) } + + // length is the size of the buffer that was allocated, *not* the + // length of the string. + let stringLen = strlen(demangled) + if stringLen > 0 { + return demangled.withMemoryRebound(to: UInt8.self, + capacity: stringLen) { + let demangledBytes = UnsafeBufferPointer(start: $0, + count: stringLen) + return String(decoding: demangledBytes, as: UTF8.self) + } + } + } return rawName } @@ -206,6 +242,11 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } } + /// The width, in bits, of an address in this backtrace. + public var addressWidth: Int { + return backtrace.addressWidth + } + /// A list of captured frame information. public var frames: [Frame] @@ -246,8 +287,8 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) /// Convert a build ID to a CFUUIDBytes. private static func uuidBytesFromBuildID(_ buildID: [UInt8]) - -> __swift_backtrace_CFUUIDBytes { - return withUnsafeTemporaryAllocation(of: __swift_backtrace_CFUUIDBytes.self, + -> CFUUIDBytes { + return withUnsafeTemporaryAllocation(of: CFUUIDBytes.self, capacity: 1) { buf in buf.withMemoryRebound(to: UInt8.self) { _ = $0.initialize(from: buildID) @@ -263,15 +304,15 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { fn: (CSSymbolicatorRef) throws -> T) rethrows -> T { let binaryImageList = images.map{ image in BinaryImageInformation( - base: __swift_vm_address_t(image.baseAddress), - extent: __swift_vm_address_t(image.endOfText), + base: vm_address_t(image.baseAddress), + extent: vm_address_t(image.endOfText), uuid: uuidBytesFromBuildID(image.buildID!), arch: HostContext.coreSymbolicationArchitecture, path: image.path, relocations: [ BinaryRelocationInformation( - base: __swift_vm_address_t(image.baseAddress), - extent: __swift_vm_address_t(image.endOfText), + base: vm_address_t(image.baseAddress), + extent: vm_address_t(image.endOfText), name: "__TEXT" ) ], @@ -336,7 +377,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { let theSymbol = Symbol(imageIndex: imageIndex, imageName: imageName, rawName: rawName, - offset: Int(address - UInt(range.location)), + offset: Int(address - UInt64(range.location)), sourceLocation: location) theSymbol.name = name @@ -381,7 +422,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { case .omittedFrames(_), .truncated: frames.append(Frame(captured: frame, symbol: nil)) default: - let address = __swift_vm_address_t(frame.adjustedProgramCounter) + let address = vm_address_t(frame.adjustedProgramCounter) let owner = CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, address, @@ -423,6 +464,101 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } } } + #elseif os(Linux) + var elf32Cache: [Int:Elf32Image] = [:] + var elf64Cache: [Int:Elf64Image] = [:] + + // This could be more efficient; at the moment we execute the line + // number programs once per frame, whereas we could just run them once + // for all the addresses we're interested in. + + for frame in backtrace.frames { + let address = FileImageSource.Address(frame.adjustedProgramCounter) + if let imageNdx = theImages.firstIndex( + where: { address >= $0.baseAddress + && address < $0.endOfText } + ) { + let relativeAddress = address - FileImageSource.Address(theImages[imageNdx].baseAddress) + var symbol: Symbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: "", + offset: 0, + sourceLocation: nil) + var elf32Image = elf32Cache[imageNdx] + var elf64Image = elf64Cache[imageNdx] + + if elf32Image == nil && elf64Image == nil { + if let source = try? FileImageSource(path: theImages[imageNdx].path) { + if let elfImage = try? Elf32Image(source: source) { + elf32Image = elfImage + elf32Cache[imageNdx] = elfImage + } else if let elfImage = try? Elf64Image(source: source) { + elf64Image = elfImage + elf64Cache[imageNdx] = elfImage + } + } + } + + if let theSymbol = elf32Image?.lookupSymbol(address: relativeAddress) { + var location = try? elf32Image!.sourceLocation(for: relativeAddress) + + for inline in elf32Image!.inlineCallSites(at: relativeAddress) { + let fakeSymbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: inline.rawName ?? "", + offset: 0, + sourceLocation: location) + frames.append(Frame(captured: frame, + symbol: fakeSymbol, + inlined: true)) + + location = SourceLocation(path: inline.filename, + line: inline.line, + column: inline.column) + } + + symbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: theSymbol.name, + offset: theSymbol.offset, + sourceLocation: location) + } else if let theSymbol = elf64Image?.lookupSymbol(address: relativeAddress) { + var location = try? elf64Image!.sourceLocation(for: relativeAddress) + + for inline in elf64Image!.inlineCallSites(at: relativeAddress) { + let fakeSymbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: inline.rawName ?? "", + offset: 0, + sourceLocation: location) + frames.append(Frame(captured: frame, + symbol: fakeSymbol, + inlined: true)) + + location = SourceLocation(path: inline.filename, + line: inline.line, + column: inline.column) + } + + symbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: theSymbol.name, + offset: theSymbol.offset, + sourceLocation: location) + } else { + symbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: "", + offset: 0, + sourceLocation: nil) + } + + frames.append(Frame(captured: frame, symbol: symbol)) + continue + } + + frames.append(Frame(captured: frame, symbol: nil)) + } #else frames = backtrace.frames.map{ Frame(captured: $0, symbol: nil) } #endif @@ -436,10 +572,11 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// Provide a textual version of the backtrace. public var description: String { var lines: [String] = [] + let addressChars = (backtrace.addressWidth + 3) / 4 var n = 0 for frame in frames { - lines.append("\(n)\t\(frame)") + lines.append("\(n)\t\(frame.description(width: addressChars))") switch frame.captured { case let .omittedFrames(count): n += count @@ -452,7 +589,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { lines.append("Images:") lines.append("") for (n, image) in images.enumerated() { - lines.append("\(n)\t\(image)") + lines.append("\(n)\t\(image.description(width: addressChars))") } if let sharedCacheInfo = sharedCacheInfo { @@ -460,7 +597,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { lines.append("Shared Cache:") lines.append("") lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") - lines.append(" Base: \(hex(sharedCacheInfo.baseAddress))") + lines.append(" Base: \(hex(sharedCacheInfo.baseAddress, width: addressChars))") lines.append(" Active: \(!sharedCacheInfo.noCache)") } diff --git a/stdlib/public/Backtracing/Utils.swift b/stdlib/public/Backtracing/Utils.swift index 5913f047c3843..7a5f033a45e0c 100644 --- a/stdlib/public/Backtracing/Utils.swift +++ b/stdlib/public/Backtracing/Utils.swift @@ -16,17 +16,74 @@ import Swift +@_implementationOnly import OS.Libc + internal func hex(_ value: T, - withPrefix: Bool = true) -> String { + prefix shouldPrefix: Bool = true, + width: Int = MemoryLayout.size * 2) + -> String { let digits = String(value, radix: 16) - let padTo = value.bitWidth / 4 - let padding = digits.count >= padTo ? "" : String(repeating: "0", - count: padTo - digits.count) - let prefix = withPrefix ? "0x" : "" + let padding = digits.count >= width ? "" : String(repeating: "0", + count: width - digits.count) + let prefix = shouldPrefix ? "0x" : "" return "\(prefix)\(padding)\(digits)" } internal func hex(_ bytes: [UInt8]) -> String { - return bytes.map{ hex($0, withPrefix: false) }.joined(separator: "") + return bytes.map{ hex($0, prefix: false) }.joined(separator: "") +} + +enum PadAlignment { + case left + case right +} + +func pad(_ value: T, _ width: Int, align: PadAlignment = .left) -> String { + let string = String(describing: value) + let padding = string.count >= width ? "" : String(repeating: " ", + count: width - string.count) + switch align { + case .left: + return string + padding + case .right: + return padding + string + } +} + +@_spi(Utils) +public func readString(from file: String) -> String? { + let fd = _swift_open(file, O_RDONLY, 0) + if fd < 0 { + return nil + } + defer { close(fd) } + + // Files in /proc are awkward; you can't get their length and then + // read the data in one chunk, because they have zero length and don't + // support seeking. + var bytes: [UInt8] = [] + withUnsafeTemporaryAllocation(of: UInt8.self, capacity: 4096) { buffer in + while true { + let bytesRead = read(fd, buffer.baseAddress, buffer.count) + if bytesRead <= 0 { + break + } + + bytes.append(contentsOf: buffer[0..(_ s: S) + -> S.SubSequence { + guard let firstNonWhitespace = s.firstIndex(where: { !$0.isWhitespace }) + else { + return "" + } + let lastNonWhitespace = s.lastIndex(where: { !$0.isWhitespace })! + return s[firstNonWhitespace...lastNonWhitespace] } diff --git a/stdlib/public/Backtracing/Win32Extras.cpp b/stdlib/public/Backtracing/Win32Extras.cpp new file mode 100644 index 0000000000000..3c45650d791b8 --- /dev/null +++ b/stdlib/public/Backtracing/Win32Extras.cpp @@ -0,0 +1,57 @@ +//===--- Win32Extras.cpp - Windows support functions ------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines some extra functions that aren't available in the OS or C library +// on Windows. +// +//===----------------------------------------------------------------------===// + +#ifdef _WIN32 + +#include + +#include "modules/OS/Libc.h" + +extern "C" ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset) { + HANDLE hFile = _get_osfhandle(fd); + OVERLAPPED ovl = {0}; + DWORD dwBytesRead = 0; + + ovl.Offset = (DWORD)offset; + ovl.OffsetHigh = (DWORD)(offset >> 32); + + if (!ReadFile(hFile, buf, (DWORD)count, &dwBytesRead, &ovl)) { + errno = EIO; + return -1; + } + + return dwBytesRead; +} + +extern "C" ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset) { + HANDLE hFile = _get_osfhandle(fd); + OVERLAPPED ovl = {0}; + DWORD dwBytesRead = 0; + + ovl.Offset = (DWORD)offset; + ovl.OffsetHigh = (DWORD)(offset >> 32); + + if (!WriteFile(hFile, buf, (DWORD)count, &dwBytesRead, &ovl)) { + errno = EIO; + return -1; + } + + return dwBytesRead; +} + +#endif // _WIN32 + diff --git a/stdlib/public/Backtracing/modules/Compression.h b/stdlib/public/Backtracing/modules/Compression.h new file mode 100644 index 0000000000000..872d8701af26f --- /dev/null +++ b/stdlib/public/Backtracing/modules/Compression.h @@ -0,0 +1,168 @@ +//===--- Compression.h - C decls for compression libraries ------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Includes and definitions to allow us to use the compression libraries +// (zlib, zstd and liblzma) in the backtracing module. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_COMPRESSION_H +#define SWIFT_BACKTRACING_COMPRESSION_H + +#include +#include + +// Right now, we're soft linking to zlib/zstd/liblzma, so that users don't +// need it installed (but if they try to do something that requires it, +// they'll see an error message). +// +// As a result, we've grabbed copies of the relevant definitions here so +// that we don't need to install the -dev packages in order to build Swift. + +#if SWIFT_BACKTRACE_STATIC_ZLIB +#include "zlib.h" +#else +// This is the version we took the z_stream structure from +#define ZLIB_VERSION "1.2.11" + +#define Z_OK 0 +#define Z_STREAM_END 1 + +#define Z_NO_FLUSH 0 + +typedef struct z_stream_s { + uint8_t *next_in; + unsigned avail_in; + unsigned long total_in; + + uint8_t *next_out; + unsigned avail_out; + unsigned long total_out; + + const char *msg; + struct internal_state *state; + + void (*zalloc)(void *, unsigned, unsigned); + void (*zfree)(void *, void *); + void *opaque; + + int data_type; + + unsigned long adler; + unsigned long reserved; +} z_stream; + +typedef z_stream *z_streamp; +#endif + +#if SWIFT_BACKTRACE_STATIC_ZSTD +#include "zstd.h" +#else +typedef struct ZSTD_inBuffer_s { + const void *src; + size_t size; + size_t pos; +} ZSTD_inBuffer; + +typedef struct ZSTD_outBuffer_s { + void *dst; + size_t size; + size_t pos; +} ZSTD_outBuffer; +#endif + +#if SWIFT_BACKTRACE_STATIC_LIBLZMA +#include "lzma.h" +#else +typedef enum { + LZMA_OK = 0, + LZMA_STREAM_END = 1, + LZMA_NO_CHECK = 2, + LZMA_UNSUPPORTED_CHECK = 3, + LZMA_GET_CHECK = 4, + LZMA_MEM_ERROR = 5, + LZMA_MEMLIMIT_ERROR = 6, + LZMA_FORMAT_ERROR = 7, + LZMA_OPTIONS_ERROR = 8, + LZMA_DATA_ERROR = 9, + LZMA_BUF_ERROR = 10, + LZMA_PROG_ERROR = 11, +} lzma_ret; + +typedef enum { + LZMA_RUN = 0, + LZMA_SYNC_FLUSH = 1, + LZMA_FULL_FLUSH = 2, + LZMA_FULL_BARRIER = 4, + LZMA_FINISH = 3 +} lzma_action; + +typedef enum { + LZMA_RESERVED_ENUM = 0, +} lzma_reserved_enum; + +typedef struct { + void *(*alloc)(void *, size_t, size_t); + void (*free)(void *, void *); + void *opaque; +} lzma_allocator; + +typedef struct lzma_internal_s lzma_internal; + +typedef struct { + const uint8_t *next_in; + size_t avail_in; + uint64_t total_in; + + uint8_t *next_out; + size_t avail_out; + uint64_t total_out; + + const lzma_allocator *allocator; + + lzma_internal *internal; + + void *reserved_ptr1; + void *reserved_ptr2; + void *reserved_ptr3; + void *reserved_ptr4; + uint64_t reserved_int1; + uint64_t reserved_int2; + size_t reserved_int3; + size_t reserved_int4; + lzma_reserved_enum reserved_enum1; + lzma_reserved_enum reserved_enum2; +} lzma_stream; + +#define LZMA_STREAM_INIT {0} +#endif + +#ifdef __cplusplus +namespace swift { +extern "C" { +#endif + +// The Swift importer can't cope with complex macros; it will do inline +// functions, however. +static inline lzma_stream lzma_stream_init() { + return (lzma_stream)LZMA_STREAM_INIT; +} +static inline z_stream zlib_stream_init() { + return (z_stream){ 0 }; +} + +#ifdef __cplusplus +} // extern "C" +} // namespace swift +#endif + +#endif // SWIFT_BACKTRACING_COMPRESSION_H diff --git a/stdlib/public/Backtracing/modules/FixedLayout.h b/stdlib/public/Backtracing/modules/FixedLayout.h new file mode 100644 index 0000000000000..e35022f9aca98 --- /dev/null +++ b/stdlib/public/Backtracing/modules/FixedLayout.h @@ -0,0 +1,60 @@ +//===--- FixedLayout.h - Types whose layout must be fixed -------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines types whose in-memory layout must be fixed, which therefore have +// to be defined using C code. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_FIXED_LAYOUT_H +#define SWIFT_BACKTRACING_FIXED_LAYOUT_H + +#ifdef __cplusplus +namespace swift { +extern "C" { +#endif + +#include + +struct x86_64_gprs { + uint64_t _r[16]; + uint64_t rflags; + uint16_t cs, fs, gs, _pad0; + uint64_t rip; + uint64_t valid; +}; + +struct i386_gprs { + uint32_t _r[8]; + uint32_t eflags; + uint16_t segreg[6]; + uint32_t eip; + uint32_t valid; +}; + +struct arm64_gprs { + uint64_t _x[32]; + uint64_t pc; + uint64_t valid; +}; + +struct arm_gprs { + uint32_t _r[16]; + uint32_t valid; +}; + +#ifdef __cpluspus +} // extern "C" +} // namespace swift +#endif + +#endif // SWIFT_BACKTRACING_FIXED_LAYOUT_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h new file mode 100644 index 0000000000000..015f1a66034c9 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h @@ -0,0 +1,737 @@ +//===--- dwarf.h - Definitions of DWARF structures for import into Swift --===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Definitions of DWARF structures for import into Swift code +// +// The types here are taken from the DWARF 5 specification, from +// +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DWARF_H +#define SWIFT_DWARF_H + +#include + +#pragma pack(push, 1) + +/* .. Useful macros ......................................................... */ + +#define DWARF_EXTENSIBLE_ENUM __attribute__((enum_extensibility(open))) +#define DWARF_ENUM(t,n) \ + enum DWARF_EXTENSIBLE_ENUM n: t +#define DWARF_BYTECODE \ + DWARF_EXTENSIBLE_ENUM : Dwarf_Byte + +/* .. Data Representation ................................................... */ + +// Common sizes (these don't change between 32-bit and 64-bit) +typedef uint8_t Dwarf_Byte; +typedef uint16_t Dwarf_Half; +typedef uint32_t Dwarf_Word; +typedef uint64_t Dwarf_Xword; +typedef int8_t Dwarf_Sbyte; +typedef int32_t Dwarf_Sword; +typedef int64_t Dwarf_Sxword; + +// 32-bit sizes +typedef uint32_t Dwarf32_Offset; +typedef uint32_t Dwarf32_Size; +typedef uint32_t Dwarf32_Length; // 0xfffffff0 and above are reserved + +// 64-bit sizes +typedef uint64_t Dwarf64_Offset; +typedef uint64_t Dwarf64_Size; +typedef struct { + uint32_t magic; // 0xffffffff means we're using 64-bit + uint64_t length; +} Dwarf64_Length; + +/* .. Unit Types ............................................................ */ + +// Table 7.2 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_UnitType) { + DW_UT_unknown = 0x00, + DW_UT_compile = 0x01, + DW_UT_type = 0x02, + DW_UT_partial = 0x03, + DW_UT_skeleton = 0x04, + DW_UT_split_compile = 0x05, + DW_UT_split_type = 0x06, + DW_UT_lo_user = 0x80, + DW_UT_hi_user = 0xff +} Dwarf_UnitType; + +/* .. Tags .................................................................. */ + +// Table 7.3 +typedef DWARF_ENUM(Dwarf_Xword, Dwarf_Tag) { + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parmeter = 0x05, + // Reserved = 0x06, + // Reserved = 0x07, + DW_TAG_imported_declaration = 0x08, + // Reserved = 0x09, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + // Reserved = 0x0c, + DW_TAG_member = 0x0d, + // Reserved = 0x0e, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + // Reserved = 0x14, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_parameter = 0x2f, + DW_TAG_template_value_parameter = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + // Reserved = 0x3e, + DW_TAG_condition = 0x3f, + DW_TAG_shared_type = 0x40, + DW_TAG_type_unit = 0x41, + DW_TAG_rvalue_reference_type = 0x42, + DW_TAG_template_alias = 0x43, + DW_TAG_coarray_type = 0x44, + DW_TAG_generic_subrange = 0x45, + DW_TAG_dynamic_type = 0x46, + DW_TAG_atomic_type = 0x47, + DW_TAG_call_site = 0x48, + DW_TAG_call_site_parameter = 0x49, + DW_TAG_skeleton_unit = 0x4a, + DW_TAG_immutable_type = 0x4b, + DW_TAG_lo_user = 0x4080, + DW_TAG_hi_user = 0xffff, +} Dwarf_Tag; + +/* .. Child Determination Encodings ......................................... */ + +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_ChildDetermination) { + DW_CHILDREN_no = 0x00, + DW_CHILDREN_yes = 0x01, +} Dwarf_ChildDetermination; + +/* .. Attribute Encodings ................................................... */ + +// Table 7.5 +typedef enum DWARF_EXTENSIBLE_ENUM Dwarf_Attribute { + DW_AT_sibling = 0x01, // reference + DW_AT_location = 0x02, // exprloc, loclist + DW_AT_name = 0x03, // string + // Reserved = 0x04, // not applicable + // Reserved = 0x05, // not applicable + // Reserved = 0x06, // not applicable + // Reserved = 0x07, // not applicable + // Reserved = 0x08, // not applicable + DW_AT_ordering = 0x09, // constant + // Reserved = 0x0a, // not applicable + DW_AT_byte_size = 0x0b, // constant, exprloc, reference + // Reserved = 0x0c2, // constant, exprloc, reference + DW_AT_bit_size = 0x0d, // constant, exprloc, reference + // Reserved = 0x0e, // not applicable + // Reserved = 0x0f, // not applicable + DW_AT_stmt_list = 0x10, // lineptr + DW_AT_low_pc = 0x11, // address + DW_AT_high_pc = 0x12, // address, constant + DW_AT_language = 0x13, // constant + // Reserved = 0x14, // not applicable + DW_AT_discr = 0x15, // reference + DW_AT_discr_value = 0x16, // constant + DW_AT_visibility = 0x17, // constant + DW_AT_import = 0x18, // reference + DW_AT_string_length = 0x19, // exprloc, loclist, reference + DW_AT_common_reference = 0x1a, // reference + DW_AT_comp_dir = 0x1b, // string + DW_AT_const_value = 0x1c, // block, constant, string + DW_AT_containing_type = 0x1d, // reference + DW_AT_default_value = 0x1e, // constant, reference, flag + // Reserved = 0x1f, // not applicable + DW_AT_inline = 0x20, // constant + DW_AT_is_optional = 0x21, // flag + DW_AT_lower_bound = 0x22, // constant, exprloc, reference + // Reserved = 0x23, // not applicable + // Reserved = 0x24, // not applicable + DW_AT_producer = 0x25, // string + // Reserved = 0x26, // not applicable + DW_AT_prototyped = 0x27, // flag + // Reserved = 0x28, // not applicable + // Reserved = 0x29, // not applicable + DW_AT_return_addr = 0x2a, // exprloc, loclist + // Reserved = 0x2b, // not applicable + DW_AT_start_scope = 0x2c, // constant, rnglist + // Reserved = 0x2d, // not applicable + DW_AT_bit_stride = 0x2e, // constant, exprloc, reference + DW_AT_upper_bound = 0x2f, // constant, exprloc, reference + // Reserved = 0x30, // not applicable + DW_AT_abstract_origin = 0x31, // reference + DW_AT_accessibility = 0x32, // constant + DW_AT_address_class = 0x33, // constant + DW_AT_artificial = 0x34, // flag + DW_AT_base_types = 0x35, // reference + DW_AT_calling_convention = 0x36, // constant + DW_AT_count = 0x37, // constant, exprloc, reference + DW_AT_data_member_location = 0x38, // constant, exprloc, loclist + DW_AT_decl_column = 0x39, // constant + DW_AT_decl_file = 0x3a, // constant + DW_AT_decl_line = 0x3b, // constant + DW_AT_declaration = 0x3c, // flag + DW_AT_discr_list = 0x3d, // block + DW_AT_encoding = 0x3e, // constant + DW_AT_external = 0x3f, // flag + DW_AT_frame_base = 0x40, // exprloc, loclist + DW_AT_friend = 0x41, // reference + DW_AT_identifier_case = 0x42, // constant + // Reserved = 0x43, // macptr + DW_AT_namelist_item = 0x44, // reference + DW_AT_priority = 0x45, // reference + DW_AT_segment = 0x46, // exprloc, loclist + DW_AT_specification = 0x47, // reference + DW_AT_static_link = 0x48, // exprloc, loclist + DW_AT_type = 0x49, // reference + DW_AT_use_location = 0x4a, // exprloc, loclist + DW_AT_variable_parameter = 0x4b, // flag + DW_AT_virtuality = 0x4c, // constant + DW_AT_vtable_elem_location = 0x4d, // exprloc, loclist + DW_AT_allocated = 0x4e, // constant, exprloc, reference + DW_AT_associated = 0x4f, // constant, exprloc, reference + DW_AT_data_location = 0x50, // exprloc + DW_AT_byte_stride = 0x51, // constant, exprloc, reference + DW_AT_entry_pc = 0x52, // address, constant + DW_AT_use_UTF8 = 0x53, // flag + DW_AT_extension = 0x54, // reference + DW_AT_ranges = 0x55, // rnglist + DW_AT_trampoline = 0x56, // address, flag, reference, string + DW_AT_call_column = 0x57, // constant + DW_AT_call_file = 0x58, // constant + DW_AT_call_line = 0x59, // constant + DW_AT_description = 0x5a, // string + DW_AT_binary_scale = 0x5b, // constant + DW_AT_decimal_scale = 0x5c, // constant + DW_AT_small = 0x5d, // reference + DW_AT_decimal_sign = 0x5e, // constant + DW_AT_digit_count = 0x5f, // constant + DW_AT_picture_string = 0x60, // string + DW_AT_mutable = 0x61, // flag + DW_AT_threads_scaled = 0x62, // flag + DW_AT_explicit = 0x63, // flag + DW_AT_object_pointer = 0x64, // reference + DW_AT_endianity = 0x65, // constant + DW_AT_elemental = 0x66, // flag + DW_AT_pure = 0x67, // flag + DW_AT_recursive = 0x68, // flag + DW_AT_signature = 0x69, // reference + DW_AT_main_subprogram = 0x6a, // flag + DW_AT_data_bit_offset = 0x6b, // constant + DW_AT_const_expr = 0x6c, // flag + DW_AT_enum_class = 0x6d, // flag + DW_AT_linkage_name = 0x6e, // string + DW_AT_string_length_bit_size = 0x6f, // constant + DW_AT_string_length_byte_size = 0x70, // constant + DW_AT_rank = 0x71, // constant, exprloc + DW_AT_str_offsets_base = 0x72, // stroffsetsptr + DW_AT_addr_base = 0x73, // addrptr + DW_AT_rnglists_base = 0x74, // rnglistsptr + // Reserved = 0x75, // Unused + DW_AT_dwo_name = 0x76, // string + DW_AT_reference = 0x77, // flag + DW_AT_rvalue_reference = 0x78, // flag + DW_AT_macros = 0x79, // macptr + DW_AT_call_all_calls = 0x7a, // flag + DW_AT_call_all_source_calls = 0x7b, // flag + DW_AT_call_all_tail_calls = 0x7c, // flag + DW_AT_call_return_pc = 0x7d, // address + DW_AT_call_value = 0x7e, // exprloc + DW_AT_call_origin = 0x7f, // exprloc + DW_AT_call_parameter = 0x80, // reference + DW_AT_call_pc = 0x81, // address + DW_AT_call_tail_call = 0x82, // flag + DW_AT_call_target = 0x83, // exprloc + DW_AT_call_target_clobbered = 0x84, // exprloc + DW_AT_call_data_location = 0x85, // exprloc + DW_AT_call_data_value = 0x86, // exprloc + DW_AT_noreturn = 0x87, // flag + DW_AT_alignment = 0x88, // constant + DW_AT_export_symbols = 0x89, // flag + DW_AT_deleted = 0x8a, // flag + DW_AT_defaulted = 0x8b, // constant + DW_AT_loclists_base = 0x8c, // loclistsptr + DW_AT_lo_user = 0x2000, // — + DW_AT_hi_user = 0x3fff, // — +} Dwarf_Attribute; + +/* .. Form Encodings ........................................................ */ + +// Table 7.6 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_Form) { + DW_FORM_addr = 0x01, + // Reserved = 0x02, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_strx = 0x1a, + DW_FORM_addrx = 0x1b, + DW_FORM_ref_sup4 = 0x1c, + DW_FORM_strp_sup = 0x1d, + DW_FORM_data16 = 0x1e, + DW_FORM_line_strp = 0x1f, + DW_FORM_ref_sig8 = 0x20, + DW_FORM_implicit_const = 0x21, + DW_FORM_loclistx = 0x22, + DW_FORM_rnglistx = 0x23, + DW_FORM_ref_sup8 = 0x24, + DW_FORM_strx1 = 0x25, + DW_FORM_strx2 = 0x26, + DW_FORM_strx3 = 0x27, + DW_FORM_strx4 = 0x28, + DW_FORM_addrx1 = 0x29, + DW_FORM_addrx2 = 0x2a, + DW_FORM_addrx3 = 0x2b, + DW_FORM_addrx4 = 0x2c, +} Dwarf_Form; + +/* .. DWARF Expressions ..................................................... */ + +// Table 7.9 +enum DWARF_BYTECODE { + // Reserved = 0x01, + // Reserved = 0x02, + DW_OP_addr = 0x03, + // Reserved = 0x04, + // Reserved = 0x05, + DW_OP_deref = 0x06, + // Reserved = 0x07, + DW_OP_const1u = 0x08, + DW_OP_const1s = 0x09, + DW_OP_const2u = 0x0a, + DW_OP_const2s = 0x0b, + DW_OP_const4u = 0x0c, + DW_OP_const4s = 0x0d, + DW_OP_const8u = 0x0e, + DW_OP_const8s = 0x0f, + DW_OP_constu = 0x10, + DW_OP_consts = 0x11, + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1a, + DW_OP_div = 0x1b, + DW_OP_minus = 0x1c, + DW_OP_mod = 0x1d, + DW_OP_mul = 0x1e, + DW_OP_neg = 0x1f, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_bra = 0x28, + DW_OP_eq = 0x29, + DW_OP_ge = 0x2a, + DW_OP_gt = 0x2b, + DW_OP_le = 0x2c, + DW_OP_lt = 0x2d, + DW_OP_ne = 0x2e, + DW_OP_skip = 0x2f, + + DW_OP_lit0 = 0x30, + DW_OP_lit1 = 0x31, + DW_OP_lit2 = 0x32, + DW_OP_lit3 = 0x33, + DW_OP_lit4 = 0x34, + DW_OP_lit5 = 0x35, + DW_OP_lit6 = 0x36, + DW_OP_lit7 = 0x37, + DW_OP_lit8 = 0x38, + DW_OP_lit9 = 0x39, + DW_OP_lit10 = 0x3a, + DW_OP_lit11 = 0x3b, + DW_OP_lit12 = 0x3c, + DW_OP_lit13 = 0x3d, + DW_OP_lit14 = 0x3e, + DW_OP_lit15 = 0x3f, + DW_OP_lit16 = 0x40, + DW_OP_lit17 = 0x41, + DW_OP_lit18 = 0x42, + DW_OP_lit19 = 0x43, + DW_OP_lit20 = 0x44, + DW_OP_lit21 = 0x45, + DW_OP_lit22 = 0x46, + DW_OP_lit23 = 0x47, + DW_OP_lit24 = 0x48, + DW_OP_lit25 = 0x49, + DW_OP_lit26 = 0x4a, + DW_OP_lit27 = 0x4b, + DW_OP_lit28 = 0x4c, + DW_OP_lit29 = 0x4d, + DW_OP_lit30 = 0x4e, + DW_OP_lit31 = 0x4f, + + DW_OP_reg0 = 0x50, + DW_OP_reg1 = 0x51, + DW_OP_reg2 = 0x52, + DW_OP_reg3 = 0x53, + DW_OP_reg4 = 0x54, + DW_OP_reg5 = 0x55, + DW_OP_reg6 = 0x56, + DW_OP_reg7 = 0x57, + DW_OP_reg8 = 0x58, + DW_OP_reg9 = 0x59, + DW_OP_reg10 = 0x5a, + DW_OP_reg11 = 0x5b, + DW_OP_reg12 = 0x5c, + DW_OP_reg13 = 0x5d, + DW_OP_reg14 = 0x5e, + DW_OP_reg15 = 0x5f, + DW_OP_reg16 = 0x60, + DW_OP_reg17 = 0x61, + DW_OP_reg18 = 0x62, + DW_OP_reg19 = 0x63, + DW_OP_reg20 = 0x64, + DW_OP_reg21 = 0x65, + DW_OP_reg22 = 0x66, + DW_OP_reg23 = 0x67, + DW_OP_reg24 = 0x68, + DW_OP_reg25 = 0x69, + DW_OP_reg26 = 0x6a, + DW_OP_reg27 = 0x6b, + DW_OP_reg28 = 0x6c, + DW_OP_reg29 = 0x6d, + DW_OP_reg30 = 0x6e, + DW_OP_reg31 = 0x6f, + + DW_OP_breg0 = 0x70, + DW_OP_breg1 = 0x71, + DW_OP_breg2 = 0x72, + DW_OP_breg3 = 0x73, + DW_OP_breg4 = 0x74, + DW_OP_breg5 = 0x75, + DW_OP_breg6 = 0x76, + DW_OP_breg7 = 0x77, + DW_OP_breg8 = 0x78, + DW_OP_breg9 = 0x79, + DW_OP_breg10 = 0x7a, + DW_OP_breg11 = 0x7b, + DW_OP_breg12 = 0x7c, + DW_OP_breg13 = 0x7d, + DW_OP_breg14 = 0x7e, + DW_OP_breg15 = 0x7f, + DW_OP_breg16 = 0x80, + DW_OP_breg17 = 0x81, + DW_OP_breg18 = 0x82, + DW_OP_breg19 = 0x83, + DW_OP_breg20 = 0x84, + DW_OP_breg21 = 0x85, + DW_OP_breg22 = 0x86, + DW_OP_breg23 = 0x87, + DW_OP_breg24 = 0x88, + DW_OP_breg25 = 0x89, + DW_OP_breg26 = 0x8a, + DW_OP_breg27 = 0x8b, + DW_OP_breg28 = 0x8c, + DW_OP_breg29 = 0x8d, + DW_OP_breg30 = 0x8e, + DW_OP_breg31 = 0x8f, + + DW_OP_regx = 0x90, + DW_OP_fbreg = 0x91, + DW_OP_bregx = 0x92, + + DW_OP_piece = 0x93, + DW_OP_deref_size = 0x94, + DW_OP_xderef_size = 0x95, + DW_OP_nop = 0x96, + DW_OP_push_object_address = 0x97, + DW_OP_call2 = 0x98, + DW_OP_call4 = 0x99, + DW_OP_call_ref = 0x9a, + DW_OP_form_tls_address = 0x9b, + DW_OP_call_frame_cfa = 0x9c, + DW_OP_bit_piece = 0x9d, + DW_OP_implicit_value = 0x9e, + DW_OP_stack_value = 0x9f, + DW_OP_implicit_pointer = 0xa0, + DW_OP_addrx = 0xa1, + DW_OP_constx = 0xa2, + DW_OP_entry_value = 0xa3, + DW_OP_const_type = 0xa4, + DW_OP_regval_type = 0xa5, + DW_OP_deref_type = 0xa6, + DW_OP_xderef_type = 0xa7, + DW_OP_convert = 0xa8, + DW_OP_reinterpret = 0xa9, + DW_OP_lo_user = 0xe0, + DW_OP_hi_user = 0xff, +}; + +/* .. Line Number Information ............................................... */ + +// Table 7.25 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_LNS_Opcode) { + DW_LNS_extended = 0x00, + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, + DW_LNS_set_prologue_end = 0x0a, + DW_LNS_set_epilogue_begin = 0x0b, + DW_LNS_set_isa = 0x0c, +} Dwarf_LNS_Opcode; + +// Table 7.26 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_LNE_Opcode) { + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + DW_LNE_define_file = 0x03, + DW_LNE_set_discriminator = 0x04, + DW_LNE_lo_user = 0x80, + DW_LNE_hi_user = 0xff, +} Dwarf_LNE_Opcode; + +// Table 7.27 +typedef DWARF_ENUM(Dwarf_Half, Dwarf_Lhdr_Format) { + DW_LNCT_path = 0x0001, + DW_LNCT_directory_index = 0x0002, + DW_LNCT_timestamp = 0x0003, + DW_LNCT_size = 0x0004, + DW_LNCT_MD5 = 0x0005, + DW_LNCT_lo_user = 0x2000, + DW_LNCT_hi_user = 0x3fff, +} Dwarf_Lhdr_Format; + +// 6.2.4 The Line Number Program Header +typedef struct { + Dwarf32_Length length; + Dwarf_Half version; + Dwarf_Byte address_size; + Dwarf_Byte segment_selector_size; + Dwarf32_Size header_length; + Dwarf_Byte minimum_instruction_length; + Dwarf_Byte maximum_operations_per_instruction; + Dwarf_Byte default_is_stmt; + Dwarf_Sbyte line_base; + Dwarf_Byte line_range; + Dwarf_Byte opcode_base; + + // Variable length fields: + // Dwarf_Byte standard_opcode_lengths[]; + // Dwarf_Byte directory_entry_format_count; + // Dwarf_ULEB128 directory_entry_format[]; + // Dwarf_ULEB128 directories_count; + // encoded directories[]; + // Dwarf_Byte file_name_entry_format_count; + // Dwarf_ULEB128 file_name_entry_format; + // Dwarf_ULEB128 file_names_count; + // encoded file_names[]; +} DWARF32_Lhdr; + +typedef struct { + Dwarf64_Length length; + Dwarf_Half version; + Dwarf_Byte address_size; + Dwarf_Byte segment_selector_size; + Dwarf64_Size header_length; + Dwarf_Byte minimum_instruction_length; + Dwarf_Byte maximum_operations_per_instruction; + Dwarf_Byte default_is_stmt; + Dwarf_Sbyte line_base; + Dwarf_Byte line_range; + Dwarf_Byte opcode_base; + + // Variable length fields: + // Dwarf_Byte standard_opcode_lengths[]; + // Dwarf_Byte directory_entry_format_count; + // Dwarf_ULEB128 directory_entry_format[]; + // Dwarf_ULEB128 directories_count; + // encoded directories[]; + // Dwarf_Byte file_name_entry_format_count; + // Dwarf_ULEB128 file_name_entry_format; + // Dwarf_ULEB128 file_names_count; + // encoded file_names[]; +} DWARF64_Lhdr; + +/* .. Call frame instruction encodings ...................................... */ + +// Table 7.29 +enum DWARF_BYTECODE { + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80, + DW_CFA_restore = 0xc0, + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f +}; + +/* .. Range list encodings .................................................. */ + +// Table 7.30 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_RLE_Entry) { + DW_RLE_end_of_list = 0x00, + DW_RLE_base_addressx = 0x01, + DW_RLE_startx_endx = 0x02, + DW_RLE_startx_length = 0x03, + DW_RLE_offset_pair = 0x04, + DW_RLE_base_address = 0x05, + DW_RLE_start_end = 0x06, + DW_RLE_start_length = 0x07 +} Dwarf_RLE_Entry; + +/* .. Common Information Entry .............................................. */ + +// Table 4.5: Common Information Entry (CIE) +typedef struct { + Dwarf32_Length length; + Dwarf32_Offset CIE_id; + Dwarf_Byte version; + + // Followed by variable length fields as follows: + // + // Dwarf_Byte augmentation[]; // NUL terminated string + // Dwarf_Byte address_size; + // Dwarf_Byte segment_selector_size; + // Dwarf_ULEB128 code_alignment_factor; + // Dwarf_SLEB128 data_alignment_factor; + // Dwarf_ULEB128 return_address_register; + // Dwarf_Byte initial_instructions[]; + // Dwarf_Byte padding[] +} Dwarf32_CIEHdr; + +typedef struct { + Dwarf64_Length length; + Dwarf64_Offset CIE_id; + Dwarf_Byte version; + + // Followed by variable length fields as follows: + // + // Dwarf_Byte augmentation[]; // NUL terminated string + // Dwarf_Byte address_size; + // Dwarf_Byte segment_selector_size; + // Dwarf_ULEB128 code_alignment_factor; + // Dwarf_SLEB128 data_alignment_factor; + // Dwarf_ULEB128 return_address_register; + // Dwarf_Byte initial_instructions[]; + // Dwarf_Byte padding[] +} Dwarf64_CIEHdr; + +/* .. Frame Descriptor Entry ................................................ */ + +// Table 4.7: Frame Descriptor Entry (FDE) +typedef struct { + Dwarf32_Length length; + Dwarf32_Offset CIE_pointer; // Offset into the section that points at CIE + + // Followed by variable fields as follows: + // + // Dwarf_FarAddr initial_location; // May include segment selector and address + // Dwarf_Addr address_range; // Number of bytes of instructions + // Dwarf_Byte instructions[]; + // Dwarf_Byte padding[]; +} Dwarf32_FDEHdr; + +#pragma pack(pop) + +#endif // SWIFT_DWARF_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/eh_frame_hdr.h b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/eh_frame_hdr.h new file mode 100644 index 0000000000000..87988bea54781 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/eh_frame_hdr.h @@ -0,0 +1,61 @@ +//===--- eh_frame_hdr.h - DWARF EH frame header definitions ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Defines the format of the .eh_frame_hdr section, which isn't part of the +// DWARF specification (it's a GNU extension). +// +// https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_EH_FRAME_HDR_H +#define SWIFT_EH_FRAME_HDR_H + +#include + +/* .. Useful macros ......................................................... */ + +#define EH_FRAME_OPTIONS(t,n) \ + t n; \ + enum __attribute__((flag_enum,enum_extensibility(open))): t + +/* .. .eh_frame_hdr header .................................................. */ + +struct EHFrameHdr { + uint8_t version; + uint8_t eh_frame_ptr_enc; + uint8_t fde_count_enc; + uint8_t table_enc; + // encoded eh_frame_ptr; + // encoded fde_count; + // encoded binary_search_table[fde_count]; +}; + +/* .. Constants ............................................................. */ + +typedef EH_FRAME_OPTIONS(uint8_t, EHFrameEncoding) { + DW_EH_PE_omit = 0xff, // No value is present + DW_EH_PE_uleb128 = 0x01, // Unsigned value encoded using LEB128 + DW_EH_PE_udata2 = 0x02, // A 2-byte unsigned value + DW_EH_PE_udata4 = 0x03, // A 4-byte unsigned value + DW_EH_PE_udata8 = 0x04, // An 8-byte unsigned value + DW_EH_PE_sleb128 = 0x09, // Signed value encoded using LEB128 + DW_EH_PE_sdata2 = 0x0a, // A 2-byte signed value + DW_EH_PE_sdata4 = 0x0b, // A 4-byte signed value + DW_EH_PE_sdata8 = 0x0c, // An 8-byte signed value + + DW_EH_PE_absptr = 0x00, // Absolute, used with no modification + DW_EH_PE_pcrel = 0x10, // Relative to the current program counter + DW_EH_PE_datarel = 0x30, // Relative to the beginning of the .eh_frame_hdr +}; + +#endif // SWIFT_EH_FRAME_HDR_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h b/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h new file mode 100644 index 0000000000000..ed129dac2b350 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h @@ -0,0 +1,787 @@ +//===--- elf.h - Definitions of ELF structures for import into Swift ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Definitions of ELF structures for import into Swift code +// +// The types here are taken from the System V ABI update, here: +// +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_ELF_H +#define SWIFT_ELF_H + +#include + +/* .. Useful macros ......................................................... */ + +#define ELF_ENUM(t,n) \ + enum __attribute__((enum_extensibility(open))) n: t +#define ELF_OPTIONS(t,n) \ + t n; \ + enum __attribute__((flag_enum,enum_extensibility(open))): t + +/* .. Data Representation ................................................... */ + +// Common sizes (these don't change between 32-bit and 64-bit) +typedef uint8_t Elf_Byte; +typedef uint16_t Elf_Half; +typedef uint32_t Elf_Word; +typedef uint64_t Elf_Xword; +typedef int32_t Elf_Sword; +typedef int64_t Elf_Sxword; + +// 32-bit sizes (includes some aliases of the above, for compatibility) +typedef Elf_Byte Elf32_Byte; +typedef uint32_t Elf32_Addr; +typedef Elf_Half Elf32_Half; +typedef uint32_t Elf32_Off; +typedef Elf_Sword Elf32_Sword; +typedef Elf_Word Elf32_Word; + +// 64-bit sizes (includes some aliases of the above, for compatibility) +typedef Elf_Byte Elf64_Byte; +typedef uint64_t Elf64_Addr; +typedef uint64_t Elf64_Off; +typedef Elf_Half Elf64_Half; +typedef Elf_Word Elf64_Word; +typedef Elf_Sword Elf64_Sword; +typedef Elf_Xword Elf64_Xword; +typedef Elf_Sxword Elf64_Sxword; + +/* .. Constants ............................................................. */ + +// e_type values +typedef ELF_ENUM(Elf_Half, Elf_Ehdr_Type) { + ET_NONE = 0, // No file type + ET_REL = 1, // Relocatable file + ET_EXEC = 2, // Executable file + ET_DYN = 3, // Shared object file + ET_CORE = 4, // Core file + ET_LOOS = 0xfe00, // Operating system specific + ET_HIOS = 0xfeff, // Operating system specific + ET_LOPROC = 0xff00, // Processor specific + ET_HIPROC = 0xffff, // Processor specific +} Elf_Ehdr_Type; + +// e_machine values +typedef ELF_ENUM(Elf_Half, Elf_Ehdr_Machine) { + EM_NONE = 0, // No machine + EM_M32 = 1, // AT&T WE 32100 + EM_SPARC = 2, // SPARC + EM_386 = 3, // Intel 80386 + EM_68K = 4, // Motorola 68000 + EM_88K = 5, // Motorola 88000 + + EM_860 = 7, // Intel 80860 + EM_MIPS = 8, // MIPS I Architecture + EM_S370 = 9, // IBM System/370 Processor + EM_MIPS_RS3_LE = 10, // MIPS RS3000 Little-endian + + EM_PARISC = 15, // Hewlett-Packard PA-RISC + + EM_VPP500 = 17, // Fujitsu VPP500 + EM_SPARC32PLUS = 18, // Enhanced instruction set SPARC + EM_960 = 19, // Intel 80960 + EM_PPC = 20, // PowerPC + EM_PPC64 = 21, // 64-bit PowerPC + EM_S390 = 22, // IBM System/390 Processor + EM_SPU = 23, // IBM SPU/SPC + + EM_V800 = 36, // NEC V800 + EM_FR20 = 37, // Fujitsu FR20 + EM_RH32 = 38, // TRW RH-32 + EM_RCE = 39, // Motorola RCE + EM_ARM = 40, // ARM 32-bit architecture (AARCH32) + EM_ALPHA = 41, // Digital Alpha + EM_SH = 42, // Hitachi SH + EM_SPARCV9 = 43, // SPARC Version 9 + EM_TRICORE = 44, // Siemens TriCore embedded processor + EM_ARC = 45, // Argonaut RISC Core, Argonaut Technologies Inc. + EM_H8_300 = 46, // Hitachi H8/300 + EM_H8_300H = 47, // Hitachi H8/300H + EM_H8S = 48, // Hitachi H8S + EM_H8_500 = 49, // Hitachi H8/500 + EM_IA_64 = 50, // Intel IA-64 processor architecture + EM_MIPS_X = 51, // Stanford MIPS-X + EM_COLDFIRE = 52, // Motorola ColdFire + EM_68HC12 = 53, // Motorola M68HC12 + EM_MMA = 54, // Fujitsu MMA Multimedia Accelerator + EM_PCP = 55, // Siemens PCP + EM_NCPU = 56, // Sony nCPU embedded RISC processor + EM_NDR1 = 57, // Denso NDR1 microprocessor + EM_STARCORE = 58, // Motorola Star*Core processor + EM_ME16 = 59, // Toyota ME16 processor + EM_ST100 = 60, // STMicroelectronics ST100 processor + EM_TINYJ = 61, // Advanced Logic Corp. TinyJ embedded processor family + EM_X86_64 = 62, // AMD x86-64 architecture + EM_PDSP = 63, // Sony DSP Processor + EM_PDP10 = 64, // Digital Equipment Corp. PDP-10 + EM_PDP11 = 65, // Digital Equipment Corp. PDP-11 + EM_FX66 = 66, // Siemens FX66 microcontroller + EM_ST9PLUS = 67, // STMicroelectronics ST9+ 8/16 bit microcontroller + EM_ST7 = 68, // STMicroelectronics ST7 8-bit microcontroller + EM_68HC16 = 69, // Motorola MC68HC16 Microcontroller + EM_68HC11 = 70, // Motorola MC68HC11 Microcontroller + EM_68HC08 = 71, // Motorola MC68HC08 Microcontroller + EM_68HC05 = 72, // Motorola MC68HC05 Microcontroller + EM_SVX = 73, // Silicon Graphics SVx + EM_ST19 = 74, // STMicroelectronics ST19 8-bit microcontroller + EM_VAX = 75, // Digital VAX + EM_CRIS = 76, // Axis Communications 32-bit embedded processor + EM_JAVELIN = 77, // Infineon Technologies 32-bit embedded processor + EM_FIREPATH = 78, // Element 14 64-bit DSP Processor + EM_ZSP = 79, // LSI Logic 16-bit DSP Processor + EM_MMIX = 80, // Donald Knuth's educational 64-bit processor + EM_HUANY = 81, // Harvard University machine-independent object files + EM_PRISM = 82, // SiTera Prism + EM_AVR = 83, // Atmel AVR 8-bit microcontroller + EM_FR30 = 84, // Fujitsu FR30 + EM_D10V = 85, // Mitsubishi D10V + EM_D30V = 86, // Mitsubishi D30V + EM_V850 = 87, // NEC v850 + EM_M32R = 88, // Mitsubishi M32R + EM_MN10300 = 89, // Matsushita MN10300 + EM_MN10200 = 90, // Matsushita MN10200 + EM_PJ = 91, // picoJava + EM_OPENRISC = 92, // OpenRISC 32-bit embedded processor + EM_ARC_COMPACT = 93, // ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5) + EM_XTENSA = 94, // Tensilica Xtensa Architecture + EM_VIDEOCORE = 95, // Alphamosaic VideoCore processor + EM_TMM_GPP = 96, // Thompson Multimedia General Purpose Processor + EM_NS32K = 97, // National Semiconductor 32000 series + EM_TPC = 98, // Tenor Network TPC processor + EM_SNP1K = 99, // Trebia SNP 1000 processor + EM_ST200 = 100, // STMicroelectronics (www.st.com) ST200 microcontroller + EM_IP2K = 101, // Ubicom IP2xxx microcontroller family + EM_MAX = 102, // MAX Processor + EM_CR = 103, // National Semiconductor CompactRISC microprocessor + EM_F2MC16 = 104, // Fujitsu F2MC16 + EM_MSP430 = 105, // Texas Instruments embedded microcontroller msp430 + EM_BLACKFIN = 106, // Analog Devices Blackfin (DSP) processor + EM_SE_C33 = 107, // S1C33 Family of Seiko Epson processors + EM_SEP = 108, // Sharp embedded microprocessor + EM_ARCA = 109, // Arca RISC Microprocessor + EM_UNICORE = 110, // Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University + EM_EXCESS = 111, // eXcess: 16/32/64-bit configurable embedded CPU + EM_DXP = 112, // Icera Semiconductor Inc. Deep Execution Processor + EM_ALTERA_NIOS2 = 113, // Altera Nios II soft-core processor + EM_CRX = 114, // National Semiconductor CompactRISC CRX microprocessor + EM_XGATE = 115, // Motorola XGATE embedded processor + EM_C166 = 116, // Infineon C16x/XC16x processor + EM_M16C = 117, // Renesas M16C series microprocessors + EM_DSPIC30F = 118, // Microchip Technology dsPIC30F Digital Signal Controller + EM_CE = 119, // Freescale Communication Engine RISC core + EM_M32C = 120, // Renesas M32C series microprocessors + + EM_TSK3000 = 131, // Altium TSK3000 core + EM_RS08 = 132, // Freescale RS08 embedded processor + EM_SHARC = 133, // Analog Devices SHARC family of 32-bit DSP processors + EM_ECOG2 = 134, // Cyan Technology eCOG2 microprocessor + EM_SCORE7 = 135, // Sunplus S+core7 RISC processor + EM_DSP24 = 136, // New Japan Radio (NJR) 24-bit DSP Processor + EM_VIDEOCORE3 = 137, // Broadcom VideoCore III processor + EM_LATTICEMICO32 = 138, // RISC processor for Lattice FPGA architecture + EM_SE_C17 = 139, // Seiko Epson C17 family + EM_TI_C6000 = 140, // The Texas Instruments TMS320C6000 DSP family + EM_TI_C2000 = 141, // The Texas Instruments TMS320C2000 DSP family + EM_TI_C5500 = 142, // The Texas Instruments TMS320C55x DSP family + + EM_MMDSP_PLUS = 160, // STMicroelectronics 64bit VLIW Data Signal Processor + EM_CYPRESS_M8C = 161, // Cypress M8C microprocessor + EM_R32C = 162, // Renesas R32C series microprocessors + EM_TRIMEDIA = 163, // NXP Semiconductors TriMedia architecture family + EM_QDSP6 = 164, // QUALCOMM DSP6 Processor + EM_8051 = 165, // Intel 8051 and variants + EM_STXP7X = 166, // STMicroelectronics STxP7x family of configurable and extensible RISC processors + EM_NDS32 = 167, // Andes Technology compact code size embedded RISC processor family + EM_ECOG1 = 168, // Cyan Technology eCOG1X family + EM_ECOG1X = 168, // Cyan Technology eCOG1X family + EM_MAXQ30 = 169, // Dallas Semiconductor MAXQ30 Core Micro-controllers + EM_XIMO16 = 170, // New Japan Radio (NJR) 16-bit DSP Processor + EM_MANIK = 171, // M2000 Reconfigurable RISC Microprocessor + EM_CRAYNV2 = 172, // Cray Inc. NV2 vector architecture + EM_RX = 173, // Renesas RX family + EM_METAG = 174, // Imagination Technologies META processor architecture + EM_MCST_ELBRUS = 175, // MCST Elbrus general purpose hardware architecture + EM_ECOG16 = 176, // Cyan Technology eCOG16 family + EM_CR16 = 177, // National Semiconductor CompactRISC CR16 16-bit microprocessor + EM_ETPU = 178, // Freescale Extended Time Processing Unit + EM_SLE9X = 179, // Infineon Technologies SLE9X core + EM_L10M = 180, // Intel L10M + EM_K10M = 181, // Intel K10M + + EM_AARCH64 = 183, // ARM 64-bit architecture (AARCH64) + + EM_AVR32 = 185, // Atmel Corporation 32-bit microprocessor family + EM_STM8 = 186, // STMicroeletronics STM8 8-bit microcontroller + EM_TILE64 = 187, // Tilera TILE64 multicore architecture family + EM_TILEPRO = 188, // Tilera TILEPro multicore architecture family + EM_MICROBLAZE = 189, // Xilinx MicroBlaze 32-bit RISC soft processor core + EM_CUDA = 190, // NVIDIA CUDA architecture + EM_TILEGX = 191, // Tilera TILE-Gx multicore architecture family + EM_CLOUDSHIELD = 192, // CloudShield architecture family + EM_COREA_1ST = 193, // KIPO-KAIST Core-A 1st generation processor family + EM_COREA_2ND = 194, // KIPO-KAIST Core-A 2nd generation processor family + EM_ARC_COMPACT2 = 195, // Synopsys ARCompact V2 + EM_OPEN8 = 196, // Open8 8-bit RISC soft processor core + EM_RL78 = 197, // Renesas RL78 family + EM_VIDEOCORE5 = 198, // Broadcom VideoCore V processor + EM_78KOR = 199, // Renesas 78KOR family + EM_56800EX = 200, // Freescale 56800EX Digital Signal Controller (DSC) + EM_BA1 = 201, // Beyond BA1 CPU architecture + EM_BA2 = 202, // Beyond BA2 CPU architecture + EM_XCORE = 203, // XMOS xCORE processor family + EM_MCHP_PIC = 204, // Microchip 8-bit PIC(r) family +} Elf_Ehdr_Machine; + +// e_version values +typedef ELF_ENUM(Elf_Word, Elf_Ehdr_Version) { + EV_NONE = 0, // Invalid version + EV_CURRENT = 1, // Current version +} Elf_Ehdr_Version; + +// e_ident[] identification indices +enum { + EI_MAG0 = 0, // File identification = 0x7f + EI_MAG1 = 1, // File identification = 'E' 0x45 + EI_MAG2 = 2, // File identification = 'L' 0x4c + EI_MAG3 = 3, // File identification = 'F' 0x46 + EI_CLASS = 4, // File class + EI_DATA = 5, // Data encoding + EI_VERSION = 6, // File version + EI_OSABI = 7, // Operating system/ABI identification + EI_ABIVERSION = 8, // ABI version + EI_PAD = 9, // Start of padding bytes +}; + +// Magic number +enum : uint8_t { + ELFMAG0 = 0x7f, + ELFMAG1 = 'E', + ELFMAG2 = 'L', + ELFMAG3 = 'F', +}; + +// File class +typedef ELF_ENUM(Elf_Byte, Elf_Ehdr_Class) { + ELFCLASSNONE = 0, // Invalid class + ELFCLASS32 = 1, // 32-bit objects + ELFCLASS64 = 2, // 64-bit objects +} Elf_Ehdr_Class; + +// Data encoding +typedef ELF_ENUM(Elf_Byte, Elf_Ehdr_Data) { + ELFDATANONE = 0, // Invalid data encoding + ELFDATA2LSB = 1, // 2's complement Little Endian + ELFDATA2MSB = 2, // 2's complement Big Endian +} Elk_Ehdr_Data; + +// OS/ABI identification +typedef ELF_ENUM(Elf_Byte, Elf_Ehdr_OsAbi) { + ELFOSABI_NONE = 0, // No extensions or unspecified + ELFOSABI_HPUX = 1, // Hewlett-Packard HP-UX + ELFOSABI_NETBSD = 2, // NetBSD + ELFOSABI_GNU = 3, // GNU + ELFOSABI_LINUX = 3, // Linux (historical - alias for ELFOSABI_GNU) + ELFOSABI_SOLARIS = 6, // Sun Solaris + ELFOSABI_AIX = 7, // AIX + ELFOSABI_IRIX = 8, // IRIX + ELFOSABI_FREEBSD = 9, // FreeBSD + ELFOSABI_TRU64 = 10, // Compaq TRU64 UNIX + ELFOSABI_MODESTO = 11, // Novell Modesto + ELFOSABI_OPENBSD = 12, // Open BSD + ELFOSABI_OPENVMS = 13, // Open VMS + ELFOSABI_NSK = 14, // Hewlett-Packard Non-Stop Kernel + ELFOSABI_AROS = 15, // Amiga Research OS + ELFOSABI_FENIXOS = 16, // The FenixOS highly scalable multi-core OS +} Elf_Ehdr_OsAbi; + +// Special Section Indices +enum { + SHN_UNDEF = 0, // Undefined, missing, irrelevant or meaningless + + SHN_LORESERVE = 0xff00, // Lower bound of reserved indices + + SHN_LOPROC = 0xff00, // Processor specific + SHN_HIPROC = 0xff1f, + + SHN_LOOS = 0xff20, // OS specific + SHN_HIOS = 0xff3f, + + SHN_ABS = 0xfff1, // Absolute (symbols are not relocated) + SHN_COMMON = 0xfff2, // Common + SHN_XINDEX = 0xffff, // Indicates section header index is elsewhere + + SHN_HIRESERVE = 0xffff, +}; + +// Section types +typedef ELF_ENUM(Elf_Word, Elf_Shdr_Type) { + SHT_NULL = 0, // Inactive + SHT_PROGBITS = 1, // Program-defined information + SHT_SYMTAB = 2, // Symbol table + SHT_STRTAB = 3, // String table + SHT_RELA = 4, // Relocation entries with explicit addends + SHT_HASH = 5, // Symbol hash table + SHT_DYNAMIC = 6, // Information for dynamic linking + SHT_NOTE = 7, // Notes + SHT_NOBITS = 8, // Program-defined empty space (bss) + SHT_REL = 9, // Relocation entries without explicit addents + SHT_SHLIB = 10, // Reserved + SHT_DYNSYM = 11, + SHT_INIT_ARRAY = 14, // Pointers to initialization functions + SHT_FINI_ARRAY = 15, // Pointers to termination functions + SHT_PREINIT_ARRAY = 16, // Pointers to pre-initialization functions + SHT_GROUP = 17, // Defines a section group + SHT_SYMTAB_SHNDX = 18, // Section header indices for symtab + + SHT_LOOS = 0x60000000, // OS specific + SHT_GNU_ATTRIBUTES = 0x6ffffff5, // Object attributes + SHT_GNU_HASH = 0x6ffffff6, // GNU-style hash table + SHT_GNU_LIBLIST = 0x6ffffff7, // Prelink library list + SHT_CHECKSUM = 0x6ffffff8, // Checksum for DSK content + + SHT_LOSUNW = 0x6ffffffa, // Sun-specific + SHT_SUNW_move = 0x6ffffffa, + SHT_SUNW_COMDAT = 0x6ffffffb, + SHT_SUNW_syminfo = 0x6ffffffc, + + SHT_GNU_verdef = 0x6ffffffd, + SHT_GNU_verneed = 0x6ffffffe, + SHT_GNU_versym = 0x6fffffff, + + SHT_HISUNW = 0x6fffffff, + SHT_HIOS = 0x6fffffff, + + SHT_LOPROC = 0x70000000, // Processor specific + SHT_HIPROC = 0x7fffffff, + + SHT_LOUSER = 0x80000000, // Application specific + SHT_HIUSER = 0xffffffff, +} Elf_Shdr_Type; + +// Section attribute flags (we can't have a type for these because the +// 64-bit section header defines them as 64-bit) +enum { + SHF_WRITE = 0x1, // Writable + SHF_ALLOC = 0x2, // Mapped + SHF_EXECINSTR = 0x4, // Executable instructions + SHF_MERGE = 0x10, // Mergeable elements + SHF_STRINGS = 0x20, // NUL-terminated strings + SHF_INFO_LINK = 0x40, // Section header table index + SHF_LINK_ORDER = 0x80, // Special ordering requirement + SHF_OS_NONCONFORMING = 0x100, // OS-specific processing + SHF_GROUP = 0x200, // Section group member + SHF_TLS = 0x400, // Thread Local Storage + SHF_COMPRESSED = 0x800, // Compressed + SHF_MASKOS = 0x0ff00000, // Operating system specific flags + SHF_MASKPROC = 0xf0000000, // Processor specific flags +}; + +// Section group flags +enum : Elf_Word { + GRP_COMDAT = 0x1, // COMDAT group + GRP_MASKOS = 0x0ff00000, // Operating system specific flags + GRP_MASKPROC = 0xf0000000, // Processof specific flags +}; + +// Compression type +typedef ELF_ENUM(Elf_Word, Elf_Chdr_Type) { + ELFCOMPRESS_ZLIB = 1, // DEFLATE algorithm + ELFCOMPRESS_ZSTD = 2, // zstd algorithm + + ELFCOMPRESS_LOOS = 0x60000000, // Operating system specific + ELFCOMPRESS_HIOS = 0x6fffffff, + + ELFCOMPRESS_LOPROC = 0x70000000, // Processor specific + ELFCOMPRESS_HIPROC = 0x7fffffff +} Elf_Chdr_Type; + +// Symbol table entry +enum : Elf_Word { + STN_UNDEF = 0 +}; + +typedef ELF_ENUM(Elf_Byte, Elf_Sym_Binding) { + STB_LOCAL = 0, + STB_GLOBAL = 1, + STB_WEAK = 2, + + STB_LOOS = 10, // Operating system specific + STB_HIOS = 12, + + STB_LOPROC = 13, // Processor specific + STB_HIPROC = 15 +} Elf_Sym_Binding; + +typedef ELF_ENUM(Elf_Byte, Elf_Sym_Type) { + STT_NOTYPE = 0, // Unspecified + STT_OBJECT = 1, // Data object (variable, array, &c) + STT_FUNC = 2, // Function or other executable code + STT_SECTION = 3, // A section + STT_FILE = 4, // Source file name + STT_COMMON = 5, // Uninitialized common block + STT_TLS = 6, // Thread Local Storage + + STT_LOOS = 10, // Operating system specific + STT_HIOS = 12, + + STT_LOPROC = 13, // Processor specific + STT_HIPROC = 15, +} Elf_Sym_Type; + +typedef ELF_ENUM(Elf_Byte, Elf_Sym_Visibility) { + STV_DEFAULT = 0, + STV_INTERNAL = 1, // Processor specific but like hidden + STV_HIDDEN = 2, // Not visible from other components + STV_PROTECTED = 3, // Visible but cannot be preempted +} Elf_Sym_Visibility; + +// Program header types +typedef ELF_ENUM(Elf_Word, Elf_Phdr_Type) { + PT_NULL = 0, // Element unused + PT_LOAD = 1, // Loadable segment + PT_DYNAMIC = 2, // Dynamic linking information + PT_INTERP = 3, // Interpreter + PT_NOTE = 4, // Auxiliary information + PT_SHLIB = 5, // Reserved + PT_PHDR = 6, // Program header table + PT_TLS = 7, // Thread Local Storage + + PT_LOOS = 0x60000000, // Operating system specific + PT_GNU_EH_FRAME = 0x6474e550, // GNU .eh_frame_hdr segment + PT_GNU_STACK = 0x6474e551, // Indicates stack executability + PT_GNU_RELRO = 0x6474e552, // Read-only after relocation + + PT_LOSUNW = 0x6ffffffa, + PT_SUNWBSS = 0x6ffffffa, + PT_SUNWSTACK = 0x6ffffffb, + PT_HISUNW = 0x6fffffff, + PT_HIOS = 0x6fffffff, + + PT_LOPROC = 0x70000000, // Processor specific + PT_HIPROC = 0x7fffffff, +} Elf_Phdr_Type; + +// Program header flags +typedef ELF_OPTIONS(Elf_Word, Elf_Phdr_Flags) { + PF_X = 0x1, // Execute + PF_W = 0x2, // Write + PF_R = 0x4, // Read, + + PF_MASKOS = 0x0ff00000, // Operating system specific + PF_MASKPROC = 0xf0000000, // Processor specific +}; + +// Dynamic linking tags +enum { + DT_NULL = 0, // Marks the end of the _DYNAMIC array + DT_NEEDED = 1, // String table offset of name of needed library + DT_PLTRELSZ = 2, // Total size of relocation entries for PLT + DT_PLTGOT = 3, // Address of PLT/GOT + DT_HASH = 4, // Address of symbol hash table + DT_STRTAB = 5, // Address of string table + DT_SYMTAB = 6, // Address of symbol table + DT_RELA = 7, // Address of DT_RELA relocation table + DT_RELASZ = 8, // Size of DT_RELA table + DT_RELAENT = 9, // Size of DT_RELA entry + DT_STRSZ = 10, // Size of string table + DT_SYMENT = 11, // Size of symbol table entry + DT_INIT = 12, // Address of initialization function + DT_FINI = 13, // Address of termination function + DT_SONAME = 14, // String table offset of name of shared object + DT_RPATH = 15, // String table offset of search path + DT_SYMBOLIC = 16, // Means to search from shared object first + DT_REL = 17, // Address of DT_REL relocation table + DT_RELSZ = 18, // Size of DT_REL table + DT_RELENT = 19, // Size of DT_REL entry + DT_PLTREL = 20, // Type of PLT relocation entry (DT_REL/DT_RELA) + DT_DEBUG = 21, // Used for debugging + DT_TEXTREL = 22, // Means relocations might write to read-only segment + DT_JMPREL = 23, // Address of relocation entries for PLT + DT_BIND_NOW = 24, // Means linker should not lazily bind + DT_INIT_ARRAY = 25, // Address of pointers to initialization functions + DT_FINI_ARRAY = 26, // Address of pointers to termination functions + DT_INIT_ARRAYSZ = 27, // Size in bytes of initialization function array + DT_FINI_ARRAYSZ = 28, // Size in bytes of termination function array + DT_RUNPATH = 29, // String table offset of search path + DT_FLAGS = 30, // Flags + + DT_ENCODING = 32, // Tags equal to or above this follow encoding rules + + DT_PREINIT_ARRAY = 32, // Address of pre-initialization function array + DT_PREINIT_ARRAYSZ = 33, // Size in bytes of pre-initialization fn array + + DT_LOOS = 0x6000000D, // Operating system specific + DT_HIOS = 0x6ffff000, + + DT_LOPROC = 0x70000000, // Processor specific + DT_HIPROC = 0x7fffffff, +}; + +// Dynamic linking flags +enum { + DF_ORIGIN = 0x1, // Uses $ORIGIN substitution string + DF_SYMBOLIC = 0x2, // Search shared object first before usual search + DF_TEXTREL = 0x4, // Relocations may modify read-only segments + DF_BIND_NOW = 0x8, // Linker should not lazily bind + DF_STATIC_TLS = 0x10, // Uses static TLS - must not be dynamically loaded +}; + +// GNU note types +enum { + NT_GNU_ABI_TAG = 1, // ABI information + NT_GNU_HWCAP = 2, // Synthetic hwcap information + NT_GNU_BUILD_ID = 3, // Build ID + NT_GNU_GOLD_VERSION = 4, // Generated by GNU gold + NT_GNU_PROPERTY_TYPE_0 = 5, // Program property +}; + +/* .. ELF Header ............................................................ */ + +#define EI_NIDENT 16 + +typedef struct { + Elf32_Byte e_ident[EI_NIDENT]; + Elf_Ehdr_Type e_type; + Elf_Ehdr_Machine e_machine; + Elf_Ehdr_Version e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +typedef struct { + Elf64_Byte e_ident[EI_NIDENT]; + Elf_Ehdr_Type e_type; + Elf_Ehdr_Machine e_machine; + Elf_Ehdr_Version e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +/* .. Section Header ........................................................ */ + +typedef struct { + Elf32_Word sh_name; + Elf_Shdr_Type sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +typedef struct { + Elf64_Word sh_name; + Elf_Shdr_Type sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_Shdr; + +/* .. Compression Header .................................................... */ + +typedef struct { + Elf_Chdr_Type ch_type; + Elf32_Word ch_size; + Elf32_Word ch_addralign; +} Elf32_Chdr; + +typedef struct { + Elf_Chdr_Type ch_type; + Elf64_Word ch_reserved; + Elf64_Xword ch_size; + Elf64_Xword ch_addralign; +} Elf64_Chdr; + +/* .. Symbol Table .......................................................... */ + +typedef struct { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + Elf32_Byte st_info; + Elf32_Byte st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +typedef struct { + Elf64_Word st_name; + Elf64_Byte st_info; + Elf64_Byte st_other; + Elf64_Half st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + +static inline Elf_Sym_Binding ELF32_ST_BIND(Elf_Byte i) { return i >> 4; } +static inline Elf_Sym_Type ELF32_ST_TYPE(Elf_Byte i) { return i & 0xf; } +static inline Elf_Byte ELF32_ST_INFO(Elf_Sym_Binding b, Elf_Sym_Type t) { + return (b << 4) | (t & 0xf); +} + +static inline Elf_Sym_Binding ELF64_ST_BIND(Elf_Byte i) { return i >> 4; } +static inline Elf_Sym_Type ELF64_ST_TYPE(Elf_Byte i) { return i & 0xf; } +static inline Elf_Byte ELF64_ST_INFO(Elf_Sym_Binding b, Elf_Sym_Type t) { + return (b << 4) | (t & 0xf); +} + +static inline Elf_Sym_Visibility ELF32_ST_VISIBILITY(Elf_Byte o) { + return o & 3; +} +static inline Elf_Sym_Visibility ELF64_ST_VISIBILITY(Elf_Byte o) { + return o & 3; +} + +/* .. Relocation ............................................................ */ + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; +} Elf64_Rel; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; + Elf64_Sxword r_addend; +} Elf64_Rela; + +static inline Elf32_Byte ELF32_R_SYM(Elf32_Word i) { return i >> 8; } +static inline Elf32_Byte ELF32_R_TYPE(Elf32_Word i) { return i & 0xff; } +static inline Elf32_Word ELF32_R_INFO(Elf32_Byte s, Elf32_Byte t) { + return (s << 8) | t; +} + +static inline Elf64_Word ELF64_R_SYM(Elf64_Xword i) { return i >> 32; } +static inline Elf64_Word ELF64_R_TYPE(Elf64_Xword i) { return i & 0xffffffff; } +static inline Elf64_Xword ELF64_R_INFO(Elf64_Word s, Elf64_Word t) { + return (((Elf64_Xword)s) << 32) | t; +} + +/* .. Program Header ........................................................ */ + +typedef struct { + Elf_Phdr_Type p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf_Phdr_Flags p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +typedef struct { + Elf_Phdr_Type p_type; + Elf_Phdr_Flags p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + +/* .. Note Header ........................................................... */ + +typedef struct { + Elf32_Word n_namesz; + Elf32_Word n_descsz; + Elf32_Word n_type; +} Elf32_Nhdr; + +typedef struct { + Elf64_Word n_namesz; + Elf64_Word n_descsz; + Elf64_Word n_type; +} Elf64_Nhdr; + +/* .. Dynamic Linking ....................................................... */ + +typedef struct { + Elf32_Sword d_tag; + union { + Elf32_Word d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +typedef struct { + Elf64_Sxword d_tag; + union { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; + +/* .. Hash Table ............................................................ */ + +typedef struct { + Elf32_Word h_nbucket; + Elf32_Word h_nchain; +} Elf32_Hash; + +typedef struct { + Elf64_Word h_nbucket; + Elf64_Word h_nchain; +} Elf64_Hash; + +static inline unsigned long +elf_hash(const unsigned char *name) +{ + unsigned long h = 0, g; + while (*name) { + h = (h << 4) + *name++; + if ((g = h & 0xf0000000)) + h ^= g >> 24; + h &= ~g; + } + return h; +} + +#endif // ELF_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/ImageFormats.modulemap b/stdlib/public/Backtracing/modules/ImageFormats/ImageFormats.modulemap new file mode 100644 index 0000000000000..94ebbd2651c64 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/ImageFormats.modulemap @@ -0,0 +1,12 @@ +module ImageFormats { + module Elf { + header "Elf/elf.h" + export * + } + module Dwarf { + header "Dwarf/dwarf.h" + header "Dwarf/eh_frame_hdr.h" + export * + } + export * +} diff --git a/stdlib/public/Backtracing/modules/OS/Darwin.h b/stdlib/public/Backtracing/modules/OS/Darwin.h new file mode 100644 index 0000000000000..7d6a4183930da --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/Darwin.h @@ -0,0 +1,253 @@ +//===--- Darwin.h - Darwin specifics ----------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Darwin specifics. +// +// WARNING: Some of the things in this file are SPI. If you use them in +// your own code, we will mercilessly break your program for you and hand +// you the pieces :-) +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_DARWIN_H +#define SWIFT_BACKTRACING_DARWIN_H +#ifdef __APPLE__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include + +#include + +// .. Mach fixes ............................................................... + +// Use an inline function for mach_task_self() or it won't import into Swift +#undef mach_task_self +static inline task_t mach_task_self() { return mach_task_self_; } + +// .. Thread states ............................................................ + +/* We can't import these from the system header, because it uses all kinds of + macros and the Swift importer can't cope with that. So declare them here + in a form it can understand. */ +#define ARM_THREAD_STATE64 6 +struct darwin_arm64_thread_state { + uint64_t _x[29]; + uint64_t fp; + uint64_t lr; + uint64_t sp; + uint64_t pc; + uint32_t cpsr; + uint32_t __pad; +}; + +struct darwin_arm64_exception_state { + uint64_t far; + uint32_t esr; + uint32_t exception; +}; + +struct darwin_arm64_mcontext { + struct darwin_arm64_exception_state es; + struct darwin_arm64_thread_state ss; + // followed by NEON state (which we don't care about) +}; + +#define X86_THREAD_STATE64 4 +struct darwin_x86_64_thread_state { + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rsp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; + uint64_t rflags; + uint64_t cs; + uint64_t fs; + uint64_t gs; +}; + +struct darwin_x86_64_exception_state { + uint16_t trapno; + uint16_t cpu; + uint32_t err; + uint64_t faultvaddr; +}; + +struct darwin_x86_64_mcontext { + struct darwin_x86_64_exception_state es; + struct darwin_x86_64_thread_state ss; + // followed by FP/AVX/AVX512 state (which we don't care about) +}; + +// .. libproc SPI .............................................................. + +int proc_name(int pid, void * buffer, uint32_t buffersize); + +// .. Mach SPI ................................................................. + +extern kern_return_t task_read_for_pid(task_t task, int pid, task_t *ptask); + +// .. dyld SPI ................................................................. + +struct dyld_process_cache_info { + uuid_t cacheUUID; + uint64_t cacheBaseAddress; + bool noCache; + bool privateCache; +}; +typedef struct dyld_process_cache_info dyld_process_cache_info; +typedef const struct dyld_process_info_base* dyld_process_info; + +extern dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kernelError); +extern void _dyld_process_info_release(dyld_process_info info); +extern void _dyld_process_info_retain(dyld_process_info info); +extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo); +extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)); +extern void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)); + +// .. Code Signing SPI ......................................................... + +#define CS_OPS_STATUS 0 +#define CS_PLATFORM_BINARY 0x04000000 +#define CS_PLATFORM_PATH 0x08000000 +extern int csops(int, unsigned int, void *, size_t); + +// .. CoreSymbolication SPI .................................................... + +typedef int32_t cpu_type_t; +typedef int32_t cpu_subtype_t; + +struct _CSArchitecture { + cpu_type_t cpu_type; + cpu_subtype_t cpu_subtype; +}; + +typedef struct _CSArchitecture CSArchitecture; + +static const CSArchitecture kCSArchitectureI386 = { + CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL +}; +static const CSArchitecture kCSArchitectureX86_64 = { + CPU_TYPE_X86_64, CPU_SUBTYPE_I386_ALL +}; +static const CSArchitecture kCSArchitectureArm64 = { + CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL +}; +static const CSArchitecture kCSArchitectureArm64_32 = { + CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_ALL +}; +static const CSArchitecture kCSArchitectureArmV7K = { + CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K +}; + +typedef struct _CSBinaryRelocationInformation { + vm_address_t base; + vm_address_t extent; + char name[17]; +} CSBinaryRelocationInformation; + +typedef struct _CSBinaryImageInformation { + vm_address_t base; + vm_address_t extent; + CFUUIDBytes uuid; + CSArchitecture arch; + const char *path; + CSBinaryRelocationInformation *relocations; + uint32_t relocationCount; + uint32_t flags; +} CSBinaryImageInformation; + +typedef uint64_t CSMachineTime; + +static const CSMachineTime kCSBeginningOfTime = 0; +static const CSMachineTime kCSEndOfTime = (1ull<<63) - 1; +static const CSMachineTime kCSNow = (1ull<<63); +static const CSMachineTime kCSAllTimes = (1ull<<63) + 1; + +struct _CSTypeRef { + uintptr_t _opaque_1; + uintptr_t _opaque_2; +}; + +typedef struct _CSTypeRef CSTypeRef; + +typedef CSTypeRef CSNullRef; +typedef CSTypeRef CSSymbolicatorRef; +typedef CSTypeRef CSSymbolOwnerRef; +typedef CSTypeRef CSSymbolRef; +typedef CSTypeRef CSSourceInfoRef; + +static const CSNullRef kCSNull = { 0, 0 }; + +typedef void (^CSSymbolOwnerIterator)(CSSymbolOwnerRef owner); +typedef void (^CSStackFrameIterator)(CSSymbolRef symbol, CSSourceInfoRef info); + +typedef struct _CSNotificationData { + CSSymbolicatorRef symbolicator; + union { + struct Ping { + uint32_t value; + } ping; + + struct DyldLoad { + CSSymbolOwnerRef symbolOwner; + } dyldLoad; + + struct DyldUnload { + CSSymbolOwnerRef symbolOwner; + } dyldUnload; + } u; +} CSNotificationData; + +typedef void (^CSNotificationBlock)(uint32_t type, CSNotificationData data); + +struct _CSRange { + vm_address_t location; + vm_size_t length; +}; + +typedef struct _CSRange CSRange; + +enum { + kCRSanitizePathGlobLocalHomeDirectories = 1, + kCRSanitizePathGlobLocalVolumes = 2, + kCRSanitizePathGlobAllTypes = 0xff, + + kCRSanitizePathNormalize = 0x100 << 0, + kCRSanitizePathKeepFile = 0x100 << 1, +}; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __APPLE__ +#endif // SWIFT_BACKTRACING_DARWIN_H + diff --git a/stdlib/public/Backtracing/modules/OS/Libc.h b/stdlib/public/Backtracing/modules/OS/Libc.h new file mode 100644 index 0000000000000..17893a89c9da1 --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/Libc.h @@ -0,0 +1,128 @@ +//===--- Libc.h - Imports from the C library --------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Imported functions from the C library. We can't use Darwin, Glibc or +// MSVCRT from here because that would create dependency issues. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_LIBC_H +#define SWIFT_BACKTRACING_LIBC_H + +#include +#include + +#if __has_include() +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#endif + +#ifdef _WIN32 +#include "swift/Runtime/Win32.h" + +#include +#include + +// Work around the fact that MSVCRT renamed all the POSIX functions and +// doesn't actually implement all of them anyway. +#ifdef __cplusplus +extern "C" { +#endif + +typedef __int64 off_t; +typedef int ssize_t; + +#define O_APPEND _O_APPEND +#define O_BINARY _O_BINARY +#define O_CREAT _O_CREAT +#define O_EXCL _O_EXCL +#define O_RDONLY _O_RDONLY +#define O_RDWR _O_RDWR +#define O_TEXT _O_TEXT +#define O_TRUNC _O_TRUNC +#define O_WRONLY _O_WRONLY + +static inline int open(const char *filename, int oflag, ...) { + wchar_t *wide = _swift_win32_copyWideFromUTF8(path); + int pmode = 0; + if (oflag & O_CREAT) { + va_list val; + va_start(val, oflag); + pmode = va_arg(val, int); + va_end(val); + } + int fd = _wopen(wpath, oflag, pmode); + free(wide); + return fd; +} + +static inline int close(int fd) { + return _close(fd); +} + +static inline off_t lseek(int fd, off_t offset, int whence) { + return _lseeki64(fd, offset, whence); +} + +static inline ssize_t read(int fd, void *buf, size_t nbyte) { + return _read(fd, buf, nbyte); +} + +static inline ssize_t write(int fd, void *buf, size_t nbyte) { + return _write(fd, buf, nbyte); +} + +ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset); +ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset); + +#ifdef __cplusplus +} +#endif + +#else +#include +#endif + +// .. Swift affordances ........................................................ + +#ifdef __cplusplus +extern "C" { +#endif + +/* open() is usually declared as a variadic function; these don't import into + Swift. */ +static inline int _swift_open(const char *filename, int oflag, int mode) { + return open(filename, oflag, mode); +} + +/* errno is typically not going to be easily accessible (it's often a macro), + so add a get_errno() function to do that. */ +static inline int _swift_get_errno() { return errno; } +static void _swift_set_errno(int err) { errno = err; } + +#ifdef __cplusplus +} +#endif + +#endif // SWIFT_BACKTRACING_LIBC_H diff --git a/stdlib/public/Backtracing/modules/OS/Linux.h b/stdlib/public/Backtracing/modules/OS/Linux.h new file mode 100644 index 0000000000000..716bc9635fafd --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/Linux.h @@ -0,0 +1,42 @@ +//===--- Linux.h - Linux specifics ------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Linux specific includes and declarations. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_LINUX_H +#define SWIFT_BACKTRACING_LINUX_H +#ifdef __linux__ + +#define _GNU_SOURCE +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +ssize_t process_vm_readv(pid_t pid, + const struct iovec *local_iov, + unsigned long liovcnt, + const struct iovec *remote_iov, + unsigned long riovcnt, + unsigned long flags); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __linux__ +#endif // SWIFT_BACKTRACING_LINUX_H + diff --git a/stdlib/public/Backtracing/modules/OS/OS.modulemap b/stdlib/public/Backtracing/modules/OS/OS.modulemap new file mode 100644 index 0000000000000..6d27426e635fd --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/OS.modulemap @@ -0,0 +1,17 @@ +module OS { + module Libc { + header "Libc.h" + } + + module Darwin { + header "Darwin.h" + } + + module Linux { + header "Linux.h" + } + + module Windows { + header "Windows.h" + } +} diff --git a/stdlib/public/SwiftShims/swift/shims/CrashInfo.h b/stdlib/public/Backtracing/modules/OS/Windows.h similarity index 58% rename from stdlib/public/SwiftShims/swift/shims/CrashInfo.h rename to stdlib/public/Backtracing/modules/OS/Windows.h index 21feb458c88b5..e67f4e0c55d71 100644 --- a/stdlib/public/SwiftShims/swift/shims/CrashInfo.h +++ b/stdlib/public/Backtracing/modules/OS/Windows.h @@ -1,4 +1,4 @@ -//===--- CrashInfo.h - Swift Backtracing Crash Information ------*- C++ -*-===// +//===--- Windows.h - Windows specifics --------------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -10,31 +10,24 @@ // //===----------------------------------------------------------------------===// // -// Defines the CrashInfo type that holds information about why the program -// crashed. +// Windows specific includes and declarations. // //===----------------------------------------------------------------------===// -#ifndef SWIFT_CRASHINFO_H -#define SWIFT_CRASHINFO_H - -#include "SwiftStdint.h" +#ifndef SWIFT_BACKTRACING_WINDOWS_H +#define SWIFT_BACKTRACING_WINDOWS_H +#ifdef _WIN32 #ifdef __cplusplus -namespace swift { extern "C" { #endif -struct CrashInfo { - __swift_uint64_t crashing_thread; - __swift_uint64_t signal; - __swift_uint64_t fault_address; - __swift_uint64_t mctx; -}; + // Nothing yet #ifdef __cplusplus } // extern "C" -} // namespace swift #endif -#endif // SWIFT_CRASHINFO_H +#endif // _WIN32 +#endif // SWIFT_BACKTRACING_WINDOWS_H + diff --git a/stdlib/public/Backtracing/modules/Runtime/Runtime.h b/stdlib/public/Backtracing/modules/Runtime/Runtime.h new file mode 100644 index 0000000000000..6cd602419efdd --- /dev/null +++ b/stdlib/public/Backtracing/modules/Runtime/Runtime.h @@ -0,0 +1,37 @@ +//===--- Runtime.h - Swift runtime imports ----------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Things to drag in from the Swift runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_RUNTIME_H +#define SWIFT_BACKTRACING_RUNTIME_H + +#include +#include + +#include "swift/Runtime/CrashInfo.h" + +// Can't import swift/Runtime/Debug.h because it assumes C++ +void swift_reportWarning(uint32_t flags, const char *message); + +// Returns true if the given function is a thunk function +bool _swift_backtrace_isThunkFunction(const char *rawName); + +// Demangle the given raw name (supports Swift and C++) +char *_swift_backtrace_demangle(const char *rawName, + size_t rawNameLength, + char *outputBuffer, + size_t *outputBufferSize); + +#endif // SWIFT_BACKTRACING_RUNTIME_H diff --git a/stdlib/public/Backtracing/modules/module.modulemap b/stdlib/public/Backtracing/modules/module.modulemap new file mode 100644 index 0000000000000..4c9121ec6f49a --- /dev/null +++ b/stdlib/public/Backtracing/modules/module.modulemap @@ -0,0 +1,14 @@ +extern module ImageFormats "ImageFormats/ImageFormats.modulemap" +extern module OS "OS/OS.modulemap" + +module Runtime { + header "Runtime/Runtime.h" +} + +module FixedLayout { + header "FixedLayout.h" +} + +module CompressionLibs { + header "Compression.h" +} diff --git a/stdlib/public/Cxx/std/CMakeLists.txt b/stdlib/public/Cxx/std/CMakeLists.txt index 2dc1ac80ec00a..20b4a6599dce0 100644 --- a/stdlib/public/Cxx/std/CMakeLists.txt +++ b/stdlib/public/Cxx/std/CMakeLists.txt @@ -1,3 +1,12 @@ +# Swift compiler currently assumes that the Darwin overlay is a dependency of +# CxxStdlib, and fails to build CxxStdlib if Darwin.swiftmodule in build dir +# is built with a different (older) version of the compiler. To workaround this, +# declare the Darwin overlay as a dependency of CxxStdlib. +set(swift_cxxstdlib_darwin_dependencies) +if(SWIFT_BUILD_STDLIB AND (SWIFT_BUILD_SDK_OVERLAY OR SWIFT_BUILD_TEST_SUPPORT_MODULES)) + set(swift_cxxstdlib_darwin_dependencies Darwin) +endif() + # # C++ Standard Library Overlay. # @@ -6,6 +15,11 @@ add_swift_target_library(swiftCxxStdlib STATIC NO_LINK_NAME IS_STDLIB IS_SWIFT_O String.swift SWIFT_MODULE_DEPENDS Cxx + SWIFT_MODULE_DEPENDS_IOS ${swift_cxxstdlib_darwin_dependencies} + SWIFT_MODULE_DEPENDS_OSX ${swift_cxxstdlib_darwin_dependencies} + SWIFT_MODULE_DEPENDS_TVOS ${swift_cxxstdlib_darwin_dependencies} + SWIFT_MODULE_DEPENDS_WATCHOS ${swift_cxxstdlib_darwin_dependencies} + SWIFT_MODULE_DEPENDS_MACCATALYST ${swift_cxxstdlib_darwin_dependencies} SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} -Xfrontend -enable-experimental-cxx-interop diff --git a/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt b/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt index 92c481d05b701..c54b2cf84b272 100644 --- a/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt +++ b/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt @@ -1,7 +1,6 @@ set(sources AssertionReporting.h CoreFoundationShims.h - CrashInfo.h FoundationShims.h GlobalObjects.h HeapObject.h @@ -22,7 +21,6 @@ set(sources ThreadLocalStorage.h UnicodeData.h Visibility.h - _SwiftBacktracing.h _SwiftConcurrency.h _SwiftDistributed.h diff --git a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h deleted file mode 100644 index b2d86f199363d..0000000000000 --- a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h +++ /dev/null @@ -1,396 +0,0 @@ -//===--- _SwiftBacktracing.h - Swift Backtracing Support --------*- C++ -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2023 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 -// -//===----------------------------------------------------------------------===// -// -// Defines types and support functions for the Swift backtracing code. -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_BACKTRACING_H -#define SWIFT_BACKTRACING_H - -#include "SwiftStdbool.h" -#include "SwiftStddef.h" -#include "SwiftStdint.h" -#include "Target.h" - -#include "CrashInfo.h" - -#ifdef __cplusplus -namespace swift { -extern "C" { -#endif - -// .. C library ................................................................ - -static inline __swift_size_t _swift_backtrace_strlen(const char *s) { - extern __swift_size_t strlen(const char *); - return strlen(s); -} - -void *_swift_backtrace_dlopen_lazy(const char *path); -void *_swift_backtrace_dlsym(void *handle, const char *name); - -typedef struct __swift_backtrace_FILE __swift_backtrace_FILE; - -static inline __swift_backtrace_FILE *_swift_backtrace_fopen(const char *fname, - const char *mode) { - extern __swift_backtrace_FILE *fopen(const char *, const char *); - return fopen(fname, mode); -} -static inline int _swift_backtrace_fclose(__swift_backtrace_FILE *fp) { - extern int fclose(__swift_backtrace_FILE *); - return fclose(fp); -} -static inline int _swift_backtrace_feof(__swift_backtrace_FILE *fp) { - extern int feof(__swift_backtrace_FILE *); - return feof(fp); -} -static inline int _swift_backtrace_ferror(__swift_backtrace_FILE *fp) { - extern int ferror(__swift_backtrace_FILE *); - return ferror(fp); -} -static inline char *_swift_backtrace_fgets(char *buffer, int size, - __swift_backtrace_FILE *fp) { - extern char *fgets(char *, int, __swift_backtrace_FILE *); - return fgets(buffer, size, fp); -} - -// .. Core Foundation .......................................................... - -#if SWIFT_TARGET_OS_DARWIN - -#if __LLP64__ -typedef long long __swift_backtrace_CFIndex; -#else -typedef long __swift_backtrace_CFIndex; -#endif - -struct __swift_backtrace_CFRange { - __swift_backtrace_CFIndex location; - __swift_backtrace_CFIndex length; - }; - -typedef struct { - __swift_uint8_t byte0; - __swift_uint8_t byte1; - __swift_uint8_t byte2; - __swift_uint8_t byte3; - __swift_uint8_t byte4; - __swift_uint8_t byte5; - __swift_uint8_t byte6; - __swift_uint8_t byte7; - __swift_uint8_t byte8; - __swift_uint8_t byte9; - __swift_uint8_t byte10; - __swift_uint8_t byte11; - __swift_uint8_t byte12; - __swift_uint8_t byte13; - __swift_uint8_t byte14; - __swift_uint8_t byte15; -} __swift_backtrace_CFUUIDBytes; - -#endif // SWIFT_TARGET_OS_DARWIN - -// .. Processor specifics ...................................................... - -struct x86_64_gprs { - __swift_uint64_t _r[16]; - __swift_uint64_t rflags; - __swift_uint16_t cs, fs, gs, _pad0; - __swift_uint64_t rip; - __swift_uint64_t valid; -}; - -struct i386_gprs { - __swift_uint32_t _r[8]; - __swift_uint32_t eflags; - __swift_uint16_t segreg[6]; - __swift_uint32_t eip; - __swift_uint32_t valid; -}; - -struct arm64_gprs { - __swift_uint64_t _x[32]; - __swift_uint64_t pc; - __swift_uint64_t valid; -}; - -struct arm_gprs { - __swift_uint32_t _r[16]; - __swift_uint32_t valid; -}; - -// .. Darwin specifics ......................................................... - -#if SWIFT_TARGET_OS_DARWIN - -// From libproc -int proc_name(int pid, void * buffer, __swift_uint32_t buffersize); - -/* Darwin thread states. We can't import these from the system header because - it uses all kinds of macros and the Swift importer can't cope with that. - So declare them here in a form it can understand. */ -#define ARM_THREAD_STATE64 6 -struct darwin_arm64_thread_state { - __swift_uint64_t _x[29]; - __swift_uint64_t fp; - __swift_uint64_t lr; - __swift_uint64_t sp; - __swift_uint64_t pc; - __swift_uint32_t cpsr; - __swift_uint32_t __pad; -}; - -struct darwin_arm64_exception_state { - __swift_uint64_t far; - __swift_uint32_t esr; - __swift_uint32_t exception; -}; - -struct darwin_arm64_mcontext { - struct darwin_arm64_exception_state es; - struct darwin_arm64_thread_state ss; - // followed by NEON state (which we don't care about) -}; - -#define x86_THREAD_STATE64 4 -struct darwin_x86_64_thread_state { - __swift_uint64_t rax; - __swift_uint64_t rbx; - __swift_uint64_t rcx; - __swift_uint64_t rdx; - __swift_uint64_t rdi; - __swift_uint64_t rsi; - __swift_uint64_t rbp; - __swift_uint64_t rsp; - __swift_uint64_t r8; - __swift_uint64_t r9; - __swift_uint64_t r10; - __swift_uint64_t r11; - __swift_uint64_t r12; - __swift_uint64_t r13; - __swift_uint64_t r14; - __swift_uint64_t r15; - __swift_uint64_t rip; - __swift_uint64_t rflags; - __swift_uint64_t cs; - __swift_uint64_t fs; - __swift_uint64_t gs; -}; - -struct darwin_x86_64_exception_state { - __swift_uint16_t trapno; - __swift_uint16_t cpu; - __swift_uint32_t err; - __swift_uint64_t faultvaddr; -}; - -struct darwin_x86_64_mcontext { - struct darwin_x86_64_exception_state es; - struct darwin_x86_64_thread_state ss; - // followed by FP/AVX/AVX512 state (which we don't care about) -}; - -typedef unsigned int __swift_task_t; -typedef unsigned int __swift_thread_t; -typedef unsigned int __swift_kern_return_t; -typedef unsigned char __swift_uuid_t[16]; -typedef __swift_uint64_t __swift_vm_address_t; -typedef __swift_uint64_t __swift_vm_size_t; -typedef int __swift_thread_state_flavor_t; -typedef unsigned int __swift_natural_t; -typedef __swift_natural_t __swift_msg_type_number_t; -typedef __swift_natural_t *__swift_thread_state_t; - -#define _SWIFT_KERN_SUCCESS 0 - -static inline __swift_task_t -_swift_backtrace_task_self() { - extern __swift_task_t mach_task_self_; - return mach_task_self_; -} - -static inline __swift_kern_return_t -_swift_backtrace_vm_read(__swift_task_t task, - __swift_vm_address_t address, - __swift_vm_size_t size, - void *buffer, - __swift_vm_size_t *length) { - extern __swift_kern_return_t mach_vm_read_overwrite(__swift_task_t, - __swift_vm_address_t, - __swift_vm_size_t, - __swift_vm_address_t, - __swift_vm_size_t *); - return mach_vm_read_overwrite(task, - address, - size, - (__swift_vm_address_t)buffer, - (__swift_vm_size_t *)length); -} - -#define _SWIFT_X86_THREAD_STATE64 6 -#define _SWIFT_ARM_THREAD_STATE64 6 - -static inline __swift_kern_return_t -_swift_backtrace_thread_get_state(__swift_thread_t target_act, - __swift_thread_state_flavor_t flavor, - __swift_thread_state_t old_state, - __swift_msg_type_number_t *old_stateCnt) { - extern __swift_kern_return_t thread_get_state(__swift_thread_t, - __swift_thread_state_flavor_t, - __swift_thread_state_t, - __swift_msg_type_number_t *); - return thread_get_state(target_act, flavor, old_state, old_stateCnt); -} - -extern __swift_kern_return_t task_read_for_pid(__swift_task_t task, int pid, __swift_task_t *ptask); - -/* DANGER! These are SPI. They may change (or vanish) at short notice, may - not work how you expect, and are generally dangerous to use. */ -struct dyld_process_cache_info { - __swift_uuid_t cacheUUID; - __swift_uint64_t cacheBaseAddress; - __swift_bool noCache; - __swift_bool privateCache; -}; -typedef struct dyld_process_cache_info dyld_process_cache_info; -typedef const struct dyld_process_info_base* dyld_process_info; - -extern dyld_process_info _dyld_process_info_create(__swift_task_t task, __swift_uint64_t timestamp, __swift_kern_return_t* kernelError); -extern void _dyld_process_info_release(dyld_process_info info); -extern void _dyld_process_info_retain(dyld_process_info info); -extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo); -extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(__swift_uint64_t machHeaderAddress, const __swift_uuid_t uuid, const char* path)); -extern void _dyld_process_info_for_each_segment(dyld_process_info info, __swift_uint64_t machHeaderAddress, void (^callback)(__swift_uint64_t segmentAddress, __swift_uint64_t segmentSize, const char* segmentName)); - -#define CS_OPS_STATUS 0 -#define CS_PLATFORM_BINARY 0x04000000 -#define CS_PLATFORM_PATH 0x08000000 -extern int csops(int, unsigned int, void *, __swift_size_t); - -/* DANGER! CoreSymbolication is a private framework. This is all SPI. */ -typedef __swift_int32_t cpu_type_t; -typedef __swift_int32_t cpu_subtype_t; - -struct _CSArchitecture { - cpu_type_t cpu_type; - cpu_subtype_t cpu_subtype; -}; - -typedef struct _CSArchitecture CSArchitecture; - -#define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */ -#define CPU_ARCH_ABI64_32 0x02000000 /* ABI for 64-bit hardware with 32-bit types; LP32 */ - -#define CPU_TYPE_X86 ((cpu_type_t) 7) -#define CPU_TYPE_I386 CPU_TYPE_X86 /* compatibility */ - -#define CPU_SUBTYPE_INTEL(f, m) ((cpu_subtype_t) (f) + ((m) << 4)) -#define CPU_SUBTYPE_I386_ALL CPU_SUBTYPE_INTEL(3, 0) - -#define CPU_TYPE_ARM ((cpu_type_t) 12) - -#define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0) -#define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) - -static const CSArchitecture kCSArchitectureI386 = { CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL }; -static const CSArchitecture kCSArchitectureX86_64 = { CPU_TYPE_I386|CPU_ARCH_ABI64, CPU_SUBTYPE_I386_ALL }; -static const CSArchitecture kCSArchitectureArm64 = { CPU_TYPE_ARM | CPU_ARCH_ABI64, CPU_SUBTYPE_ARM64_ALL }; -static const CSArchitecture kCSArchitectureArm64_32 = { CPU_TYPE_ARM | CPU_ARCH_ABI64_32, CPU_SUBTYPE_ARM64_ALL }; -static const CSArchitecture kCSArchitectureArmV7K = { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K }; - -typedef struct _CSBinaryRelocationInformation { - __swift_vm_address_t base; - __swift_vm_address_t extent; - char name[17]; -} CSBinaryRelocationInformation; - -typedef struct _CSBinaryImageInformation { - __swift_vm_address_t base; - __swift_vm_address_t extent; - __swift_backtrace_CFUUIDBytes uuid; - CSArchitecture arch; - const char *path; - CSBinaryRelocationInformation *relocations; - __swift_uint32_t relocationCount; - __swift_uint32_t flags; -} CSBinaryImageInformation; - -typedef __swift_uint64_t CSMachineTime; - -static const CSMachineTime kCSBeginningOfTime = 0; -static const CSMachineTime kCSEndOfTime = (1ull<<63) - 1; -static const CSMachineTime kCSNow = (1ull<<63); -static const CSMachineTime kCSAllTimes = (1ull<<63) + 1; - -struct _CSTypeRef { - __swift_uintptr_t _opaque_1; - __swift_uintptr_t _opaque_2; -}; - -typedef struct _CSTypeRef CSTypeRef; - -typedef CSTypeRef CSNullRef; -typedef CSTypeRef CSSymbolicatorRef; -typedef CSTypeRef CSSymbolOwnerRef; -typedef CSTypeRef CSSymbolRef; -typedef CSTypeRef CSSourceInfoRef; - -static const CSNullRef kCSNull = { 0, 0 }; - -typedef void (^CSSymbolOwnerIterator)(CSSymbolOwnerRef owner); -typedef void (^CSStackFrameIterator)(CSSymbolRef symbol, CSSourceInfoRef info); - -typedef struct _CSNotificationData { - CSSymbolicatorRef symbolicator; - union { - struct Ping { - __swift_uint32_t value; - } ping; - - struct DyldLoad { - CSSymbolOwnerRef symbolOwner; - } dyldLoad; - - struct DyldUnload { - CSSymbolOwnerRef symbolOwner; - } dyldUnload; - } u; -} CSNotificationData; - -typedef void (^CSNotificationBlock)(__swift_uint32_t type, CSNotificationData data); - -struct _CSRange { - __swift_vm_address_t location; - __swift_vm_size_t length; -}; - -typedef struct _CSRange CSRange; - -/* DANGER! This is also SPI */ -enum { - kCRSanitizePathGlobLocalHomeDirectories = 1, - kCRSanitizePathGlobLocalVolumes = 2, - kCRSanitizePathGlobAllTypes = 0xff, - - kCRSanitizePathNormalize = 0x100 << 0, - kCRSanitizePathKeepFile = 0x100 << 1, -}; - -#endif // SWIFT_TARGET_OS_DARWIN - -#ifdef __cplusplus -} // extern "C" -} // namespace swift -#endif - -#endif // SWIFT_BACKTRACING_H diff --git a/stdlib/public/SwiftShims/swift/shims/module.modulemap b/stdlib/public/SwiftShims/swift/shims/module.modulemap index 5da2bd8f7d94f..36d7dd8be0d2d 100644 --- a/stdlib/public/SwiftShims/swift/shims/module.modulemap +++ b/stdlib/public/SwiftShims/swift/shims/module.modulemap @@ -26,10 +26,6 @@ module _SwiftConcurrencyShims { header "_SwiftConcurrency.h" } -module _SwiftBacktracingShims { - header "_SwiftBacktracing.h" -} - module SwiftOverlayShims { header "LibcOverlayShims.h" export * diff --git a/stdlib/public/core/Macros.swift b/stdlib/public/core/Macros.swift index 1aaf41446e8cc..a947ae3980c0f 100644 --- a/stdlib/public/core/Macros.swift +++ b/stdlib/public/core/Macros.swift @@ -63,36 +63,6 @@ public macro column() -> T = @freestanding(expression) public macro dsohandle() -> UnsafeRawPointer = Builtin.DSOHandleMacro -/// Create an option set from a struct that contains a nested `Options` enum. -/// -/// Attach this macro to a struct that contains a nested `Options` enum -/// with an integer raw value. The struct will be transformed to conform to -/// `OptionSet` by -/// 1. Introducing a `rawValue` stored property to track which options are set, -/// along with the necessary `RawType` typealias and initializers to satisfy -/// the `OptionSet` protocol. The raw type is specified after `@OptionSet`, -/// e.g., `@OptionSet`. -/// 2. Introducing static properties for each of the cases within the `Options` -/// enum, of the type of the struct. -/// -/// The `Options` enum must have a raw value, where its case elements -/// each indicate a different option in the resulting option set. For example, -/// the struct and its nested `Options` enum could look like this: -/// -/// @OptionSet -/// struct ShippingOptions { -/// private enum Options: Int { -/// case nextDay -/// case secondDay -/// case priority -/// case standard -/// } -/// } -@attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) -@attached(conformance) -public macro OptionSet() = - #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") - /// Produce the given warning message during compilation. @freestanding(declaration) public macro warning(_ message: String) = Builtin.WarningMacro diff --git a/stdlib/public/libexec/swift-backtrace/CMakeLists.txt b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt index e1dc26aa68f50..10c8d6a9d3d70 100644 --- a/stdlib/public/libexec/swift-backtrace/CMakeLists.txt +++ b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt @@ -21,10 +21,16 @@ if(NOT SWIFT_BUILD_STDLIB) set(BUILD_STANDALONE TRUE) endif() +set(BACKTRACING_COMPILE_FLAGS + "-I${SWIFT_STDLIB_SOURCE_DIR}/public/Backtracing/modules" + "-Xcc;-I${SWIFT_SOURCE_DIR}/include" + "-Xcc;-I${CMAKE_BINARY_DIR}/include") + add_swift_target_executable(swift-backtrace BUILD_WITH_LIBEXEC main.swift AnsiColor.swift - Target.swift + TargetMacOS.swift + TargetLinux.swift Themes.swift Utils.swift @@ -35,7 +41,9 @@ add_swift_target_executable(swift-backtrace BUILD_WITH_LIBEXEC SWIFT_MODULE_DEPENDS_LINUX ${glibc} INSTALL_IN_COMPONENT libexec - COMPILE_FLAGS -parse-as-library + COMPILE_FLAGS + ${BACKTRACING_COMPILE_FLAGS} + -parse-as-library - TARGET_SDKS OSX) + TARGET_SDKS OSX LINUX) diff --git a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift new file mode 100644 index 0000000000000..f6dda1b37d895 --- /dev/null +++ b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift @@ -0,0 +1,232 @@ +//===--- TargetLinux.swift - Represents a process we are inspecting -------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Defines `Target`, which represents the process we are inspecting. +// This is the Linux version. +// +//===----------------------------------------------------------------------===// + +#if os(Linux) + +import Glibc + +import _Backtracing +@_spi(Internal) import _Backtracing +@_spi(Contexts) import _Backtracing +@_spi(MemoryReaders) import _Backtracing +@_spi(Utils) import _Backtracing + +@_implementationOnly import Runtime + +struct TargetThread { + typealias ThreadID = pid_t + + var id: ThreadID + var context: HostContext? + var name: String + var backtrace: SymbolicatedBacktrace +} + +class Target { + typealias Address = UInt64 + + var pid: pid_t + var name: String + var signal: UInt64 + var faultAddress: Address + var crashingThread: TargetThread.ThreadID + + var images: [Backtrace.Image] = [] + + var threads: [TargetThread] = [] + var crashingThreadNdx: Int = -1 + + var signalName: String { + switch signal { + case UInt64(SIGQUIT): return "SIGQUIT" + case UInt64(SIGABRT): return "SIGABRT" + case UInt64(SIGBUS): return "SIGBUS" + case UInt64(SIGFPE): return "SIGFPE" + case UInt64(SIGILL): return "SIGILL" + case UInt64(SIGSEGV): return "SIGSEGV" + case UInt64(SIGTRAP): return "SIGTRAP" + default: return "\(signal)" + } + } + + var signalDescription: String { + switch signal { + case UInt64(SIGQUIT): return "Terminated" + case UInt64(SIGABRT): return "Aborted" + case UInt64(SIGBUS): return "Bus error" + case UInt64(SIGFPE): return "Floating point exception" + case UInt64(SIGILL): return "Illegal instruction" + case UInt64(SIGSEGV): return "Bad pointer dereference" + case UInt64(SIGTRAP): return "System trap" + default: + return "Signal \(signal)" + } + } + + var reader: MemserverMemoryReader + + // Get the name of a process + private static func getProcessName(pid: pid_t) -> String { + let path = "/proc/\(pid)/comm" + guard let name = readString(from: path) else { + return "" + } + return String(stripWhitespace(name)) + } + + /// Get the name of a thread + private func getThreadName(tid: Int64) -> String { + let path = "/proc/\(pid)/task/\(tid)/comm" + guard let name = readString(from: path) else { + return "" + } + let trimmed = String(stripWhitespace(name)) + + // Allow the main thread to use the process' name, but other + // threads will have an empty name unless they've set the name + // explicitly + if trimmed == self.name && pid != tid { + return "" + } + return trimmed + } + + init(crashInfoAddr: UInt64, limit: Int?, top: Int, cache: Bool) { + // fd #4 is reserved for the memory server + let memserverFd: CInt = 4 + + pid = getppid() + reader = MemserverMemoryReader(fd: memserverFd) + name = Self.getProcessName(pid: pid) + + let crashInfo: CrashInfo + do { + crashInfo = try reader.fetch(from: crashInfoAddr, as: CrashInfo.self) + } catch { + print("swift-backtrace: unable to fetch crash info.") + exit(1) + } + + crashingThread = TargetThread.ThreadID(crashInfo.crashing_thread) + signal = crashInfo.signal + faultAddress = crashInfo.fault_address + + images = Backtrace.captureImages(using: reader, + forProcess: Int(pid)) + + do { + try fetchThreads(threadListHead: Address(crashInfo.thread_list), + limit: limit, top: top, cache: cache) + } catch { + print("swift-backtrace: failed to fetch thread information: \(error)") + exit(1) + } + } + + /// Fetch information about all of the process's threads; the crash_info + /// structure contains a linked list of thread ucontexts, which may not + /// include every thread. In particular, if a thread was stuck in an + /// uninterruptible wait, we won't have a ucontext for it. + func fetchThreads( + threadListHead: Address, + limit: Int?, top: Int, cache: Bool + ) throws { + var next = threadListHead + + while next != 0 { + let t = try reader.fetch(from: next, as: thread.self) + + next = t.next + + guard let ucontext + = try? reader.fetch(from: t.uctx, as: ucontext_t.self) else { + // This can happen if a thread is in an uninterruptible wait + continue + } + + let context = HostContext.fromHostMContext(ucontext.uc_mcontext) + let backtrace = try Backtrace.capture(from: context, + using: reader, + images: images, + limit: limit, + top: top) + guard let symbolicated + = backtrace.symbolicated(with: images, + sharedCacheInfo: nil, + useSymbolCache: cache) else { + print("unable to symbolicate backtrace for thread \(t.tid)") + exit(1) + } + + threads.append(TargetThread(id: TargetThread.ThreadID(t.tid), + context: context, + name: getThreadName(tid: t.tid), + backtrace: symbolicated)) + } + + // Sort the threads by thread ID; the main thread always sorts + // lower than any other. + threads.sort { + return $0.id == pid || ($1.id != pid && $0.id < $1.id) + } + + // Find the crashing thread index + if let ndx = threads.firstIndex(where: { $0.id == crashingThread }) { + crashingThreadNdx = ndx + } else { + print("unable to find the crashing thread") + exit(1) + } + } + + func redoBacktraces(limit: Int?, top: Int, cache: Bool) { + for (ndx, thread) in threads.enumerated() { + guard let context = thread.context else { + continue + } + + guard let backtrace = try? Backtrace.capture(from: context, + using: reader, + images: images, + limit: limit, + top: top) else { + print("unable to capture backtrace from context for thread \(ndx)") + continue + } + + guard let symbolicated = backtrace.symbolicated(with: images, + sharedCacheInfo: nil, + useSymbolCache: cache) else { + print("unable to symbolicate backtrace from context for thread \(ndx)") + continue + } + + threads[ndx].backtrace = symbolicated + } + } + + func withDebugger(_ body: () -> ()) throws { + print(""" + From another shell, please run + + lldb --attach-pid \(pid) -o c + """) + body() + } +} + +#endif // os(Linux) diff --git a/stdlib/public/libexec/swift-backtrace/Target.swift b/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift similarity index 96% rename from stdlib/public/libexec/swift-backtrace/Target.swift rename to stdlib/public/libexec/swift-backtrace/TargetMacOS.swift index 2a0e454f9174c..32bbc7bc26683 100644 --- a/stdlib/public/libexec/swift-backtrace/Target.swift +++ b/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift @@ -1,4 +1,4 @@ -//===--- Target.swift - Represents a process we are inspecting ------------===// +//===--- TargetMacOS.swift - Represents a process we are inspecting -------===// // // This source file is part of the Swift.org open source project // @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// // // Defines `Target`, which represents the process we are inspecting. -// There are a lot of system specifics in this file! +// This is the macOS version. // //===----------------------------------------------------------------------===// @@ -25,7 +25,8 @@ import _Backtracing @_spi(Contexts) import _Backtracing @_spi(MemoryReaders) import _Backtracing -import _SwiftBacktracingShims +@_implementationOnly import Runtime +@_implementationOnly import OS.Darwin #if arch(x86_64) typealias MContext = darwin_x86_64_mcontext @@ -157,7 +158,7 @@ class Target { task = parentTask - reader = RemoteMemoryReader(task: __swift_task_t(task)) + reader = RemoteMemoryReader(task: task_t(task)) name = Self.getProcessName(pid: pid) @@ -251,6 +252,7 @@ class Target { guard let backtrace = try? Backtrace.capture(from: ctx, using: reader, + images: nil, limit: limit, top: top) else { print("swift-backtrace: unable to capture backtrace from context for thread \(ndx)", @@ -283,6 +285,7 @@ class Target { guard let backtrace = try? Backtrace.capture(from: context, using: reader, + images: nil, limit: limit, top: top) else { print("swift-backtrace: unable to capture backtrace from context for thread \(ndx)", diff --git a/stdlib/public/libexec/swift-backtrace/Utils.swift b/stdlib/public/libexec/swift-backtrace/Utils.swift index a17b765df16a5..9d104a857f174 100644 --- a/stdlib/public/libexec/swift-backtrace/Utils.swift +++ b/stdlib/public/libexec/swift-backtrace/Utils.swift @@ -51,7 +51,7 @@ internal func parseUInt64(_ s: S) -> UInt64? { } } -#if os(macOS) +#if os(macOS) || os(Linux) struct PosixError: Error { var errno: Int32 @@ -139,6 +139,8 @@ internal func spawn(_ path: String, args: [String]) throws { } } +#endif // os(macOS) + struct CFileStream: TextOutputStream { var fp: UnsafeMutablePointer @@ -153,5 +155,3 @@ struct CFileStream: TextOutputStream { var standardOutput = CFileStream(fp: stdout) var standardError = CFileStream(fp: stderr) - -#endif // os(macOS) diff --git a/stdlib/public/libexec/swift-backtrace/main.swift b/stdlib/public/libexec/swift-backtrace/main.swift index f31576ab42f86..038c44552e72c 100644 --- a/stdlib/public/libexec/swift-backtrace/main.swift +++ b/stdlib/public/libexec/swift-backtrace/main.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if os(macOS) +#if (os(macOS) || os(Linux)) && (arch(x86_64) || arch(arm64)) #if canImport(Darwin) import Darwin.C @@ -524,7 +524,13 @@ Generate a backtrace for the parent process. tcgetattr(0, &oldAttrs) var newAttrs = oldAttrs - newAttrs.c_lflag &= ~(UInt(ICANON) | UInt(ECHO)) + + #if os(Linux) + newAttrs.c_lflag &= ~UInt32(ICANON | ECHO) + #else + newAttrs.c_lflag &= ~UInt(ICANON | ECHO) + #endif + tcsetattr(0, TCSANOW, &newAttrs) return oldAttrs @@ -581,7 +587,7 @@ Generate a backtrace for the parent process. static func backtraceFormatter() -> BacktraceFormatter { var terminalSize = winsize(ws_row: 24, ws_col: 80, ws_xpixel: 1024, ws_ypixel: 768) - _ = ioctl(0, TIOCGWINSZ, &terminalSize) + _ = ioctl(0, CUnsignedLong(TIOCGWINSZ), &terminalSize) return BacktraceFormatter(formattingOptions .theme(theme) @@ -607,7 +613,6 @@ Generate a backtrace for the parent process. writeln("") writeln(theme.crashReason(description)) - writeln("") var mentionedImages = Set() let formatter = backtraceFormatter() @@ -615,7 +620,7 @@ Generate a backtrace for the parent process. func dump(ndx: Int, thread: TargetThread) { let crashed = thread.id == target.crashingThread ? " crashed" : "" let name = !thread.name.isEmpty ? " \"\(thread.name)\"" : "" - writeln("Thread \(ndx)\(name)\(crashed):\n") + writeln("\nThread \(ndx)\(name)\(crashed):\n") if args.registers! == .all { if let context = thread.context { @@ -660,6 +665,7 @@ Generate a backtrace for the parent process. } } + let addressWidthInChars = (crashingThread.backtrace.addressWidth + 3) / 4 switch args.showImages! { case .none: break @@ -671,10 +677,12 @@ Generate a backtrace for the parent process. } else { writeln("\n\nImages:\n") } - writeln(formatter.format(images: images)) + writeln(formatter.format(images: images, + addressWidth: addressWidthInChars)) case .all: writeln("\n\nImages:\n") - writeln(formatter.format(images: target.images)) + writeln(formatter.format(images: target.images, + addressWidth: addressWidthInChars)) } } @@ -750,11 +758,14 @@ Generate a backtrace for the parent process. let name = thread.name.isEmpty ? "" : " \(thread.name)" writeln("Thread \(currentThread) id=\(thread.id)\(name)\(crashed)\n") + let addressWidthInChars = (backtrace.addressWidth + 3) / 4 + if let frame = backtrace.frames.drop(while: { $0.isSwiftRuntimeFailure }).first { let formatter = backtraceFormatter() - let formatted = formatter.format(frame: frame) + let formatted = formatter.format(frame: frame, + addressWidth: addressWidthInChars) writeln("\(formatted)") } break @@ -809,6 +820,7 @@ Generate a backtrace for the parent process. var rows: [BacktraceFormatter.TableRow] = [] for (n, thread) in target.threads.enumerated() { let backtrace = thread.backtrace + let addressWidthInChars = (backtrace.addressWidth + 3) / 4 let crashed: String if n == target.crashingThreadNdx { @@ -827,7 +839,10 @@ Generate a backtrace for the parent process. $0.isSwiftRuntimeFailure }).first { - rows += formatter.formatRows(frame: frame).map{ row in + rows += formatter.formatRows( + frame: frame, + addressWidth: addressWidthInChars).map{ row in + switch row { case let .columns(columns): return .columns([ "", "" ] + columns) @@ -846,8 +861,11 @@ Generate a backtrace for the parent process. writeln(output) case "images": let formatter = backtraceFormatter() - let images = target.threads[currentThread].backtrace.images - let output = formatter.format(images: images) + let backtrace = target.threads[currentThread].backtrace + let images = backtrace.images + let addressWidthInChars = (backtrace.addressWidth + 3) / 4 + let output = formatter.format(images: images, + addressWidth: addressWidthInChars) writeln(output) case "set": diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index c5eb53813dcf8..b30a8866ad171 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -52,6 +52,12 @@ #include #include +#ifdef _WIN32 +// We'll probably want dbghelp.h here +#else +#include +#endif + #define DEBUG_BACKTRACING_SETTINGS 0 #ifndef lengthof @@ -70,7 +76,7 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = { // enabled #if TARGET_OS_OSX OnOffTty::TTY, -#elif 0 // defined(__linux__) || defined(_WIN32) +#elif defined(__linux__) // || defined(_WIN32) OnOffTty::On, #else OnOffTty::Off, @@ -80,7 +86,7 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = { true, // interactive -#if TARGET_OS_OSX // || defined(__linux__) || defined(_WIN32) +#if TARGET_OS_OSX || defined(__linux__) // || defined(_WIN32) OnOffTty::TTY, #else OnOffTty::Off, @@ -117,7 +123,7 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = { true, // outputTo, - OutputTo::Stdout, + OutputTo::Auto, // swiftBacktracePath NULL, @@ -316,6 +322,13 @@ BacktraceInitializer::BacktraceInitializer() { _swift_backtraceSettings.preset = Preset::Full; } + if (_swift_backtraceSettings.outputTo == OutputTo::Auto) { + if (_swift_backtraceSettings.interactive == OnOffTty::On) + _swift_backtraceSettings.outputTo = OutputTo::Stdout; + else + _swift_backtraceSettings.outputTo = OutputTo::Stderr; + } + #if !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH) if (_swift_backtraceSettings.enabled == OnOffTty::On && !_swift_backtraceSettings.swiftBacktracePath) { @@ -642,7 +655,9 @@ _swift_processBacktracingSetting(llvm::StringRef key, } else if (key.equals_insensitive("cache")) { _swift_backtraceSettings.cache = parseBoolean(value); } else if (key.equals_insensitive("output-to")) { - if (value.equals_insensitive("stdout")) + if (value.equals_insensitive("auto")) + _swift_backtraceSettings.outputTo = OutputTo::Auto; + else if (value.equals_insensitive("stdout")) _swift_backtraceSettings.outputTo = OutputTo::Stdout; else if (value.equals_insensitive("stderr")) _swift_backtraceSettings.outputTo = OutputTo::Stderr; @@ -773,6 +788,54 @@ _swift_backtraceSetupEnvironment() *penv = 0; } +#ifdef __linux__ +struct spawn_info { + const char *path; + char * const *argv; + char * const *envp; + int memserver; +}; + +uint8_t spawn_stack[4096]; + +int +do_spawn(void *ptr) { + struct spawn_info *pinfo = (struct spawn_info *)ptr; + + /* Ensure that the memory server is always on fd 4 */ + if (pinfo->memserver != 4) { + dup2(pinfo->memserver, 4); + close(pinfo->memserver); + } + + /* Clear the signal mask */ + sigset_t mask; + sigfillset(&mask); + sigprocmask(SIG_UNBLOCK, &mask, NULL); + + return execvpe(pinfo->path, pinfo->argv, pinfo->envp); +} + +int +safe_spawn(pid_t *ppid, const char *path, int memserver, + char * const argv[], char * const envp[]) +{ + struct spawn_info info = { path, argv, envp, memserver }; + + /* The CLONE_VFORK is *required* because info is on the stack; we don't + want to return until *after* the subprocess has called execvpe(). */ + int ret = clone(do_spawn, spawn_stack + sizeof(spawn_stack), + CLONE_VFORK|CLONE_VM, &info); + if (ret < 0) + return ret; + + close(memserver); + + *ppid = ret; + return 0; +} +#endif // defined(__linux__) + #endif // SWIFT_BACKTRACE_ON_CRASH_SUPPORTED } // namespace @@ -789,20 +852,99 @@ namespace backtrace { /// @param mangledName is the symbol name to be tested. /// /// @returns `true` if `mangledName` represents a thunk function. -SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool -_swift_isThunkFunction(const char *mangledName) { +SWIFT_RUNTIME_STDLIB_SPI bool +_swift_backtrace_isThunkFunction(const char *mangledName) { swift::Demangle::Context ctx; return ctx.isThunkSymbol(mangledName); } +// Try to demangle a symbol. +SWIFT_RUNTIME_STDLIB_SPI char * +_swift_backtrace_demangle(const char *mangledName, + size_t mangledNameLength, + char *outputBuffer, + size_t *outputBufferSize) { + llvm::StringRef name = llvm::StringRef(mangledName, mangledNameLength); + + // You must provide buffer size if you're providing your own output buffer + if (outputBuffer && !outputBufferSize) { + return nullptr; + } + + if (Demangle::isSwiftSymbol(name)) { + // This is a Swift mangling + auto options = DemangleOptions::SimplifiedUIDemangleOptions(); + auto result = Demangle::demangleSymbolAsString(name, options); + size_t bufferSize; + + if (outputBufferSize) { + bufferSize = *outputBufferSize; + *outputBufferSize = result.length() + 1; + } + + if (outputBuffer == nullptr) { + outputBuffer = (char *)::malloc(result.length() + 1); + bufferSize = result.length() + 1; + } + + size_t toCopy = std::min(bufferSize - 1, result.length()); + ::memcpy(outputBuffer, result.data(), toCopy); + outputBuffer[toCopy] = '\0'; + + return outputBuffer; +#ifndef _WIN32 + } else if (name.startswith("_Z")) { + // Try C++; note that we don't want to force callers to use malloc() to + // allocate their buffer, which is a requirement for __cxa_demangle + // because it may call realloc() on the incoming pointer. As a result, + // we never pass the caller's buffer to __cxa_demangle. + size_t resultLen; + int status = 0; + char *result = abi::__cxa_demangle(mangledName, nullptr, &resultLen, &status); + + if (result) { + size_t bufferSize; + + if (outputBufferSize) { + bufferSize = *outputBufferSize; + *outputBufferSize = resultLen; + } + + if (outputBuffer == nullptr) { + return result; + } + + size_t toCopy = std::min(bufferSize - 1, resultLen - 1); + ::memcpy(outputBuffer, result, toCopy); + outputBuffer[toCopy] = '\0'; + + free(result); + + return outputBuffer; + } +#else + // On Windows, the mangling is different. + // ###TODO: Call __unDName() +#endif + } + + return nullptr; +} + // N.B. THIS FUNCTION MUST BE SAFE TO USE FROM A CRASH HANDLER. On Linux // and macOS, that means it must be async-signal-safe. On Windows, there // isn't an equivalent notion but a similar restriction applies. SWIFT_RUNTIME_STDLIB_INTERNAL bool +#ifdef __linux__ +_swift_spawnBacktracer(const ArgChar * const *argv, int memserver_fd) +#else _swift_spawnBacktracer(const ArgChar * const *argv) +#endif { -#if TARGET_OS_OSX || TARGET_OS_MACCATALYST +#if !SWIFT_BACKTRACE_ON_CRASH_SUPPORTED + return false; +#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST || defined(__linux__) pid_t child; const char *env[BACKTRACE_MAX_ENV_VARS + 1]; @@ -817,10 +959,16 @@ _swift_spawnBacktracer(const ArgChar * const *argv) // SUSv3 says argv and envp are "completely constant" and that the reason // posix_spawn() et al use char * const * is for compatibility. +#ifdef __linux__ + int ret = safe_spawn(&child, swiftBacktracePath, memserver_fd, + const_cast(argv), + const_cast(env)); +#else int ret = posix_spawn(&child, swiftBacktracePath, &backtraceFileActions, &backtraceSpawnAttrs, const_cast(argv), const_cast(env)); +#endif if (ret < 0) return false; @@ -835,10 +983,7 @@ _swift_spawnBacktracer(const ArgChar * const *argv) return false; - // ###TODO: Linux // ###TODO: Windows -#else - return false; #endif } diff --git a/stdlib/public/runtime/BytecodeLayouts.cpp b/stdlib/public/runtime/BytecodeLayouts.cpp index 9f72acd42f93c..0917e98331e58 100644 --- a/stdlib/public/runtime/BytecodeLayouts.cpp +++ b/stdlib/public/runtime/BytecodeLayouts.cpp @@ -109,8 +109,10 @@ static void existential_destroy(OpaqueValue* object) { } } -template -inline static bool handleNextRefCount(const Metadata *metadata, const uint8_t *typeLayout, size_t &offset, uintptr_t &addrOffset, Params... params) { +template +inline static bool handleNextRefCount(const Metadata *metadata, + const uint8_t *typeLayout, size_t &offset, + uintptr_t &addrOffset, Params... params) { uint64_t skip = readBytes(typeLayout, offset); auto tag = static_cast(skip >> 56); skip &= ~(0xffULL << 56); @@ -126,7 +128,8 @@ inline static bool handleNextRefCount(const Metadata *metadata, const uint8_t *t Handler::handleMetatype(type, addrOffset, std::forward(params)...); } else if (SWIFT_UNLIKELY(tag == RefCountingKind::SinglePayloadEnumSimple)) { - Handler::handleSinglePayloadEnumSimple(typeLayout, offset, addrOffset, std::forward(params)...); + Handler::handleSinglePayloadEnumSimple(typeLayout, offset, addrOffset, + std::forward(params)...); } else if (SWIFT_UNLIKELY(tag == RefCountingKind::SinglePayloadEnumFN)) { Handler::handleSinglePayloadEnumFN(typeLayout, offset, false, addrOffset, std::forward(params)...); @@ -134,6 +137,15 @@ inline static bool handleNextRefCount(const Metadata *metadata, const uint8_t *t RefCountingKind::SinglePayloadEnumFNResolved)) { Handler::handleSinglePayloadEnumFN(typeLayout, offset, true, addrOffset, std::forward(params)...); + } else if (SWIFT_UNLIKELY(tag == RefCountingKind::MultiPayloadEnumFN)) { + Handler::handleMultiPayloadEnumFN(metadata, typeLayout, offset, false, + addrOffset, + std::forward(params)...); + } else if (SWIFT_UNLIKELY(tag == + RefCountingKind::MultiPayloadEnumFNResolved)) { + Handler::handleMultiPayloadEnumFN(metadata, typeLayout, offset, true, + addrOffset, + std::forward(params)...); } else { Handler::handleReference(tag, addrOffset, std::forward(params)...); } @@ -141,21 +153,31 @@ inline static bool handleNextRefCount(const Metadata *metadata, const uint8_t *t return true; } -template -inline static void handleRefCounts(const Metadata *metadata, Params... params) { - const uint8_t *typeLayout = metadata->getLayoutString(); - size_t offset = layoutStringHeaderSize; - uintptr_t addrOffset = 0; - +template +inline static void handleRefCounts(const Metadata *metadata, + const uint8_t *typeLayout, size_t &offset, + uintptr_t &addrOffset, Params... params) { if (N == 0) { - while (handleNextRefCount(metadata, typeLayout, offset, addrOffset, std::forward(params)...)) {} + while (handleNextRefCount(metadata, typeLayout, offset, addrOffset, + std::forward(params)...)) { + } } else { for (int i = 0; i < N; i++) { - handleNextRefCount(metadata, typeLayout, offset, addrOffset, std::forward(params)...); + handleNextRefCount(metadata, typeLayout, offset, addrOffset, + std::forward(params)...); } } } +template +inline static void handleRefCounts(const Metadata *metadata, Params... params) { + const uint8_t *typeLayout = metadata->getLayoutString(); + size_t offset = layoutStringHeaderSize; + uintptr_t addrOffset = 0; + handleRefCounts(metadata, typeLayout, offset, addrOffset, + std::forward(params)...); +} + static uint64_t readTagBytes(uint8_t *addr, uint8_t byteCount) { switch (byteCount) { case 1: @@ -173,7 +195,7 @@ static uint64_t readTagBytes(uint8_t *addr, uint8_t byteCount) { static void handleSinglePayloadEnumSimple(const uint8_t *typeLayout, size_t &offset, uint8_t *addr, - size_t &addrOffset) { + uintptr_t &addrOffset) { auto byteCountsAndOffset = readBytes(typeLayout, offset); auto extraTagBytesPattern = (uint8_t)(byteCountsAndOffset >> 62); auto xiTagBytesPattern = ((uint8_t)(byteCountsAndOffset >> 59)) & 0x7; @@ -220,7 +242,7 @@ typedef unsigned (*GetEnumTagFn)(const uint8_t *); static void handleSinglePayloadEnumFN(const uint8_t *typeLayout, size_t &offset, bool resolved, uint8_t *addr, - size_t &addrOffset) { + uintptr_t &addrOffset) { GetEnumTagFn getEnumTag; if (resolved) { getEnumTag = readBytes(typeLayout, offset); @@ -240,6 +262,39 @@ static void handleSinglePayloadEnumFN(const uint8_t *typeLayout, size_t &offset, } } +template +static void handleMultiPayloadEnumFN(const Metadata *metadata, + const uint8_t *typeLayout, size_t &offset, + bool resolved, uintptr_t &addrOffset, + uint8_t *addr, Params... params) { + GetEnumTagFn getEnumTag; + if (resolved) { + getEnumTag = readBytes(typeLayout, offset); + } else { + getEnumTag = readRelativeFunctionPointer(typeLayout, offset); + } + + size_t numCases = readBytes(typeLayout, offset); + size_t refCountBytes = readBytes(typeLayout, offset); + size_t enumSize = readBytes(typeLayout, offset); + + unsigned enumTag = getEnumTag(addr + addrOffset); + + if (enumTag < numCases) { + size_t nestedOffset = offset + (enumTag * sizeof(size_t)); + size_t refCountOffset = readBytes(typeLayout, nestedOffset); + nestedOffset = offset + (numCases * sizeof(size_t)) + refCountOffset; + + uintptr_t nestedAddrOffset = addrOffset; + handleRefCounts<0, Handler>(metadata, typeLayout, nestedOffset, + nestedAddrOffset, addr, + std::forward(params)...); + } + + offset += refCountBytes + (numCases * sizeof(size_t)); + addrOffset += enumSize; +} + const DestroyFuncAndMask destroyTable[] = { {(DestrFn)&skipDestroy, false}, {(DestrFn)&swift_errorRelease, true}, @@ -265,23 +320,36 @@ const DestroyFuncAndMask destroyTable[] = { }; struct DestroyHandler { - static inline void handleMetatype(const Metadata *type, uintptr_t addrOffset, uint8_t *addr) { + static inline void handleMetatype(const Metadata *type, uintptr_t addrOffset, + uint8_t *addr) { type->vw_destroy((OpaqueValue *)(addr + addrOffset)); } - static inline void handleSinglePayloadEnumSimple(const uint8_t *typeLayout, size_t &offset, - size_t &addrOffset, uint8_t *addr) { + static inline void handleSinglePayloadEnumSimple(const uint8_t *typeLayout, + size_t &offset, + uintptr_t &addrOffset, + uint8_t *addr) { ::handleSinglePayloadEnumSimple(typeLayout, offset, addr, addrOffset); } static inline void handleSinglePayloadEnumFN(const uint8_t *typeLayout, size_t &offset, bool resolved, - size_t &addrOffset, + uintptr_t &addrOffset, uint8_t *addr) { ::handleSinglePayloadEnumFN(typeLayout, offset, resolved, addr, addrOffset); } - static inline void handleReference(RefCountingKind tag, uintptr_t addrOffset, uint8_t *addr) { + static inline void handleMultiPayloadEnumFN(const Metadata *metadata, + const uint8_t *typeLayout, + size_t &offset, bool resolved, + uintptr_t &addrOffset, + uint8_t *addr) { + ::handleMultiPayloadEnumFN(metadata, typeLayout, offset, + resolved, addrOffset, addr); + } + + static inline void handleReference(RefCountingKind tag, uintptr_t addrOffset, + uint8_t *addr) { const auto &destroyFunc = destroyTable[static_cast(tag)]; if (SWIFT_LIKELY(destroyFunc.isIndirect)) { destroyFunc.fn( @@ -344,25 +412,38 @@ const RetainFuncAndMask retainTable[] = { }; struct CopyHandler { - static inline void handleMetatype(const Metadata *type, uintptr_t addrOffset, uint8_t *dest, uint8_t *src) { + static inline void handleMetatype(const Metadata *type, uintptr_t addrOffset, + uint8_t *dest, uint8_t *src) { type->vw_initializeWithCopy((OpaqueValue*)((uintptr_t)dest + addrOffset), (OpaqueValue*)((uintptr_t)src + addrOffset)); } - static inline void handleSinglePayloadEnumSimple(const uint8_t *typeLayout, size_t &offset, - size_t &addrOffset, uint8_t *dest, uint8_t *src) { - ::handleSinglePayloadEnumSimple(typeLayout, offset, (uint8_t *)src, addrOffset); + static inline void handleSinglePayloadEnumSimple(const uint8_t *typeLayout, + size_t &offset, + uintptr_t &addrOffset, + uint8_t *dest, + uint8_t *src) { + ::handleSinglePayloadEnumSimple(typeLayout, offset, src, addrOffset); } static inline void handleSinglePayloadEnumFN(const uint8_t *typeLayout, size_t &offset, bool resolved, - size_t &addrOffset, + uintptr_t &addrOffset, uint8_t *dest, uint8_t *src) { - ::handleSinglePayloadEnumFN(typeLayout, offset, resolved, (uint8_t *)src, - addrOffset); + ::handleSinglePayloadEnumFN(typeLayout, offset, resolved, src, addrOffset); + } + + static inline void handleMultiPayloadEnumFN(const Metadata *metadata, + const uint8_t *typeLayout, + size_t &offset, bool resolved, + uintptr_t &addrOffset, + uint8_t *dest, uint8_t *src) { + ::handleMultiPayloadEnumFN(metadata, typeLayout, offset, + resolved, addrOffset, dest, src); } - static inline void handleReference(RefCountingKind tag, uintptr_t addrOffset, uint8_t *dest, uint8_t *src) { + static inline void handleReference(RefCountingKind tag, uintptr_t addrOffset, + uint8_t *dest, uint8_t *src) { const auto &retainFunc = retainTable[static_cast(tag)]; if (SWIFT_LIKELY(retainFunc.isSingle)) { ((RetainFn)retainFunc.fn)(*(void**)(((uintptr_t)dest + addrOffset))); @@ -409,6 +490,15 @@ struct TakeHandler { ::handleSinglePayloadEnumFN(typeLayout, offset, resolved, src, addrOffset); } + static inline void handleMultiPayloadEnumFN(const Metadata *metadata, + const uint8_t *typeLayout, + size_t &offset, bool resolved, + uintptr_t &addrOffset, + uint8_t *dest, uint8_t *src) { + ::handleMultiPayloadEnumFN(metadata, typeLayout, offset, + resolved, addrOffset, dest, src); + } + static inline void handleReference(RefCountingKind tag, uintptr_t addrOffset, uint8_t *dest, uint8_t *src) { if (tag == RefCountingKind::UnknownWeak) { @@ -420,8 +510,8 @@ struct TakeHandler { (OpaqueValue*)((uintptr_t)src + addrOffset)); if (SWIFT_UNLIKELY(!type->getValueWitnesses()->isBitwiseTakable())) { type->vw_initializeWithTake( - (OpaqueValue*)((uintptr_t)dest + addrOffset), - (OpaqueValue*)((uintptr_t)src + addrOffset)); + (OpaqueValue *)((uintptr_t)dest + addrOffset), + (OpaqueValue *)((uintptr_t)src + addrOffset)); } } } @@ -457,17 +547,20 @@ swift_generic_assignWithTake(swift::OpaqueValue *dest, swift::OpaqueValue *src, return swift_generic_initWithTake(dest, src, metadata); } -void swift::swift_resolve_resilientAccessors( - uint8_t *layoutStr, size_t layoutStrOffset, const uint8_t *fieldLayoutStr, - size_t refCountBytes, const Metadata *fieldType) { +void swift::swift_resolve_resilientAccessors(uint8_t *layoutStr, + size_t layoutStrOffset, + const uint8_t *fieldLayoutStr, + const Metadata *fieldType) { size_t i = layoutStringHeaderSize; - while (i < (layoutStringHeaderSize + refCountBytes)) { + while (true) { size_t currentOffset = i; uint64_t size = readBytes(fieldLayoutStr, i); RefCountingKind tag = (RefCountingKind)(size >> 56); size &= ~(0xffULL << 56); switch (tag) { + case RefCountingKind::End: + return; case RefCountingKind::Resilient: { auto *type = getResilientTypeMetadata(fieldType, fieldLayoutStr, i); size_t writeOffset = layoutStrOffset + currentOffset - @@ -503,6 +596,44 @@ void swift::swift_resolve_resilientAccessors( i += 3 * sizeof(size_t); break; + case RefCountingKind::MultiPayloadEnumFN: { + auto getEnumTag = + readRelativeFunctionPointer(fieldLayoutStr, i); + size_t writeOffset = + layoutStrOffset + currentOffset - layoutStringHeaderSize; + uint64_t tagAndOffset = + (((uint64_t)RefCountingKind::MultiPayloadEnumFNResolved) << 56) | + size; + writeBytes(layoutStr, writeOffset, tagAndOffset); + writeBytes(layoutStr, writeOffset, getEnumTag); + + size_t numCases = readBytes(fieldLayoutStr, i); + size_t refCountBytes = readBytes(fieldLayoutStr, i); + + size_t casesBeginOffset = + layoutStrOffset + i + (numCases * sizeof(size_t)); + + for (size_t j = 0; j < numCases; j++) { + size_t caseOffset = readBytes(fieldLayoutStr, i); + const uint8_t *caseLayoutString = + fieldLayoutStr + i + (numCases * sizeof(size_t)) + caseOffset; + swift_resolve_resilientAccessors(layoutStr, + casesBeginOffset + caseOffset, + caseLayoutString, fieldType); + } + break; + } + + case RefCountingKind::MultiPayloadEnumFNResolved: { + // skip function pointer + i += sizeof(uintptr_t); + size_t numCases = readBytes(fieldLayoutStr, i); + size_t refCountBytes = readBytes(fieldLayoutStr, i); + // skip enum size, offsets and ref counts + i += sizeof(size_t) + (numCases * sizeof(size_t)) + refCountBytes; + break; + } + default: break; } diff --git a/stdlib/public/runtime/BytecodeLayouts.h b/stdlib/public/runtime/BytecodeLayouts.h index 02bf2e06ceb38..f589352c25a17 100644 --- a/stdlib/public/runtime/BytecodeLayouts.h +++ b/stdlib/public/runtime/BytecodeLayouts.h @@ -47,6 +47,9 @@ enum class RefCountingKind : uint8_t { SinglePayloadEnumFN = 0x11, SinglePayloadEnumFNResolved = 0x12, + MultiPayloadEnumFN = 0x13, + MultiPayloadEnumFNResolved = 0x14, + Skip = 0x80, // We may use the MSB as flag that a count follows, // so all following values are reserved @@ -66,7 +69,10 @@ swift::OpaqueValue *swift_generic_initWithTake(swift::OpaqueValue *dest, swift:: SWIFT_RUNTIME_EXPORT void swift_generic_instantiateLayoutString(const uint8_t *layoutStr, Metadata *type); -void swift_resolve_resilientAccessors(uint8_t *layoutStr, size_t layoutStrOffset, const uint8_t *fieldLayoutStr, size_t refCountBytes, const Metadata *fieldType); +void swift_resolve_resilientAccessors(uint8_t *layoutStr, + size_t layoutStrOffset, + const uint8_t *fieldLayoutStr, + const Metadata *fieldType); } // namespace swift #endif // SWIFT_BYTECODE_LAYOUTS_H diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index e5adea1fa1a8a..379f72b010a72 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -81,7 +81,8 @@ set(swift_runtime_sources set(swift_runtime_backtracing_sources Backtrace.cpp - CrashHandlerMacOS.cpp) + CrashHandlerMacOS.cpp + CrashHandlerLinux.cpp) # Acknowledge that the following sources are known. set(LLVM_OPTIONAL_SOURCES diff --git a/stdlib/public/runtime/CrashHandlerLinux.cpp b/stdlib/public/runtime/CrashHandlerLinux.cpp new file mode 100644 index 0000000000000..2913245b97566 --- /dev/null +++ b/stdlib/public/runtime/CrashHandlerLinux.cpp @@ -0,0 +1,815 @@ +//===--- CrashHandlerLinux.cpp - Swift crash handler for Linux ----------- ===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// The Linux crash handler implementation. +// +//===----------------------------------------------------------------------===// + +#ifdef __linux__ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "swift/Runtime/Backtrace.h" + +#include + +// Run the memserver in a thread (0) or separate process (1) +#define MEMSERVER_USE_PROCESS 0 + +#ifndef lengthof +#define lengthof(x) (sizeof(x) / sizeof(x[0])) +#endif + +using namespace swift::runtime::backtrace; + +namespace { + +void handle_fatal_signal(int signum, siginfo_t *pinfo, void *uctx); +void suspend_other_threads(struct thread *self); +void resume_other_threads(); +void take_thread_lock(); +void release_thread_lock(); +void notify_paused(); +void wait_paused(uint32_t expected, const struct timespec *timeout); +int memserver_start(); +int memserver_entry(void *); +bool run_backtracer(int fd); + +ssize_t safe_read(int fd, void *buf, size_t len) { + uint8_t *ptr = (uint8_t *)buf; + uint8_t *end = ptr + len; + ssize_t total = 0; + + while (ptr < end) { + ssize_t ret; + do { + ret = read(fd, buf, len); + } while (ret < 0 && errno == EINTR); + if (ret < 0) + return ret; + total += ret; + ptr += ret; + len -= ret; + } + + return total; +} + +ssize_t safe_write(int fd, const void *buf, size_t len) { + const uint8_t *ptr = (uint8_t *)buf; + const uint8_t *end = ptr + len; + ssize_t total = 0; + + while (ptr < end) { + ssize_t ret; + do { + ret = write(fd, buf, len); + } while (ret < 0 && errno == EINTR); + if (ret < 0) + return ret; + total += ret; + ptr += ret; + len -= ret; + } + + return total; +} + +CrashInfo crashInfo; + +const int signalsToHandle[] = { + SIGQUIT, + SIGABRT, + SIGBUS, + SIGFPE, + SIGILL, + SIGSEGV, + SIGTRAP +}; + +} // namespace + +namespace swift { +namespace runtime { +namespace backtrace { + +SWIFT_RUNTIME_STDLIB_INTERNAL int +_swift_installCrashHandler() +{ + stack_t ss; + + // See if an alternate signal stack already exists + if (sigaltstack(NULL, &ss) < 0) + return errno; + + if (ss.ss_sp == 0) { + /* No, so set one up; note that if we end up having to do a PLT lookup + for a function we call from the signal handler, we need additional + stack space for the dynamic linker, or we'll just explode. That's + what the extra 16KB is for here. */ + ss.ss_flags = 0; + ss.ss_size = SIGSTKSZ + 16384; + ss.ss_sp = mmap(0, ss.ss_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ss.ss_sp == MAP_FAILED) + return errno; + + if (sigaltstack(&ss, NULL) < 0) + return errno; + } + + // Now register signal handlers + struct sigaction sa; + sigfillset(&sa.sa_mask); + for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) { + sigdelset(&sa.sa_mask, signalsToHandle[n]); + } + + sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_NODEFER; + sa.sa_sigaction = handle_fatal_signal; + + for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) { + struct sigaction osa; + + // See if a signal handler for this signal is already installed + if (sigaction(signalsToHandle[n], NULL, &osa) < 0) + return errno; + + if (osa.sa_handler == SIG_DFL) { + // No, so install ours + if (sigaction(signalsToHandle[n], &sa, NULL) < 0) + return errno; + } + } + + return 0; +} + +} // namespace backtrace +} // namespace runtime +} // namespace swift + +namespace { + +// Older glibc and musl don't have these two syscalls +pid_t +gettid() +{ + return (pid_t)syscall(SYS_gettid); +} + +int +tgkill(int tgid, int tid, int sig) { + return syscall(SYS_tgkill, tgid, tid, sig); +} + +void +reset_signal(int signum) +{ + struct sigaction sa; + sa.sa_handler = SIG_DFL; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + + sigaction(signum, &sa, NULL); +} + +void +handle_fatal_signal(int signum, + siginfo_t *pinfo, + void *uctx) +{ + int old_err = errno; + struct thread self = { 0, (int64_t)gettid(), (uint64_t)uctx }; + + // Prevent this from exploding if more than one thread gets here at once + suspend_other_threads(&self); + + // Remove our signal handlers; crashes should kill us here + for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) + reset_signal(signalsToHandle[n]); + + // Fill in crash info + crashInfo.crashing_thread = self.tid; + crashInfo.signal = signum; + crashInfo.fault_address = (uint64_t)pinfo->si_addr; + + // Start the memory server + int fd = memserver_start(); + + // Actually start the backtracer + run_backtracer(fd); + +#if !MEMSERVER_USE_PROCESS + /* If the memserver is in-process, it may have set signal handlers, + so reset SIGSEGV and SIGBUS again */ + reset_signal(SIGSEGV); + reset_signal(SIGBUS); +#endif + + // Restart the other threads + resume_other_threads(); + + // Restore errno and exit (to crash) + errno = old_err; +} + +// .. Thread handling .......................................................... + +void +reset_threads(struct thread *first) { + __atomic_store_n(&crashInfo.thread_list, (uint64_t)first, __ATOMIC_RELEASE); +} + +void +add_thread(struct thread *thread) { + uint64_t next = __atomic_load_n(&crashInfo.thread_list, __ATOMIC_ACQUIRE); + do { + thread->next = next; + } while (!__atomic_compare_exchange_n(&crashInfo.thread_list, &next, + (uint64_t)thread, + false, + __ATOMIC_RELEASE, __ATOMIC_ACQUIRE)); +} + +bool +seen_thread(pid_t tid) { + uint64_t next = __atomic_load_n(&crashInfo.thread_list, __ATOMIC_ACQUIRE); + while (next) { + struct thread *pthread = (struct thread *)next; + if (pthread->tid == tid) + return true; + next = pthread->next; + } + return false; +} + +void +pause_thread(int signum __attribute__((unused)), + siginfo_t *pinfo __attribute__((unused)), + void *uctx) +{ + int old_err = errno; + struct thread self = { 0, (int64_t)gettid(), (uint64_t)uctx }; + + add_thread(&self); + + notify_paused(); + + take_thread_lock(); + release_thread_lock(); + + errno = old_err; +} + +struct linux_dirent64 { + ino64_t d_ino; + off64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +int +getdents(int fd, void *buf, size_t bufsiz) +{ + return syscall(SYS_getdents64, fd, buf, bufsiz); +} + +/* Stop all other threads in this process; we do this by establishing a + signal handler for SIGPROF, then iterating through the threads sending + SIGPROF. + + Finding the other threads is a pain, because Linux has no actual API + for that; instead, you have to read /proc. Unfortunately, opendir() + and readdir() are not async signal safe, so we get to do this with + the getdents system call instead. + + The SIGPROF signals also serve to build the thread list. */ +void +suspend_other_threads(struct thread *self) +{ + struct sigaction sa, sa_old; + + // Take the lock + take_thread_lock(); + + // Start the thread list with this thread + reset_threads(self); + + // Swap out the SIGPROF signal handler first + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_NODEFER; + sa.sa_handler = NULL; + sa.sa_sigaction = pause_thread; + + sigaction(SIGPROF, &sa, &sa_old); + + /* Now scan /proc/self/task to get the tids of the threads in this + process. We need to ignore our own thread. */ + int fd = open("/proc/self/task", + O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC); + int our_pid = getpid(); + char buffer[4096]; + size_t offset = 0; + size_t count = 0; + + uint32_t thread_count = 0; + uint32_t old_thread_count; + + do { + old_thread_count = thread_count; + lseek(fd, 0, SEEK_SET); + + for (;;) { + if (offset >= count) { + ssize_t bytes = getdents(fd, buffer, sizeof(buffer)); + if (bytes <= 0) + break; + count = (size_t)bytes; + offset = 0; + } + + struct linux_dirent64 *dp = (struct linux_dirent64 *)&buffer[offset]; + offset += dp->d_reclen; + + if (strcmp(dp->d_name, ".") == 0 + || strcmp(dp->d_name, "..") == 0) + continue; + + int tid = atoi(dp->d_name); + + if ((int64_t)tid != self->tid && !seen_thread(tid)) { + tgkill(our_pid, tid, SIGPROF); + ++thread_count; + } + } + + // Wait up to 5 seconds for the threads to pause + struct timespec timeout = { 5, 0 }; + wait_paused(thread_count, &timeout); + } while (old_thread_count != thread_count); + + // Close the directory + close(fd); + + // Finally, reset the signal handler + sigaction(SIGPROF, &sa_old, NULL); +} + +void +resume_other_threads() +{ + // All we need to do here is release the lock. + release_thread_lock(); +} + +// .. Locking .................................................................. + +/* We use a futex to block the threads; we also use one to let us work out + when all the threads we've asked to pause have actually paused. */ +int +futex(uint32_t *uaddr, int futex_op, uint32_t val, + const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3) +{ + return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3); +} + +uint32_t thread_lock = 0; + +void +take_thread_lock() +{ + do { + uint32_t zero = 0; + if (__atomic_compare_exchange_n(&thread_lock, + &zero, + 1, + true, + __ATOMIC_ACQUIRE, + __ATOMIC_RELAXED)) + return; + } while (!futex(&thread_lock, FUTEX_WAIT, 1, NULL, NULL, 0) + || errno == EAGAIN); +} + +void +release_thread_lock() +{ + __atomic_store_n(&thread_lock, 0, __ATOMIC_RELEASE); + futex(&thread_lock, FUTEX_WAKE, 1, NULL, NULL, 0); +} + +uint32_t threads_paused = 0; + +void +notify_paused() +{ + __atomic_fetch_add(&threads_paused, 1, __ATOMIC_RELEASE); + futex(&threads_paused, FUTEX_WAKE, 1, NULL, NULL, 0); +} + +void +wait_paused(uint32_t expected, const struct timespec *timeout) +{ + uint32_t current; + do { + current = __atomic_load_n(&threads_paused, __ATOMIC_ACQUIRE); + if (current == expected) + return; + } while (!futex(&threads_paused, FUTEX_WAIT, current, timeout, NULL, 0) + || errno == EAGAIN); +} + +// .. Memory server ............................................................ + +/* The memory server exists so that we can gain access to the crashing + process's memory space from the backtracer without having to use ptrace() + or process_vm_readv(), both of which need CAP_SYS_PTRACE. + + We don't want to require CAP_SYS_PTRACE because we're potentially being + used inside of a Docker container, which won't have that enabled. */ + +char memserver_stack[4096]; +char memserver_buffer[4096]; +int memserver_fd; +bool memserver_has_ptrace; +sigjmp_buf memserver_fault_buf; +pid_t memserver_pid; + +int +memserver_start() +{ + int ret; + int fds[2]; + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + if (ret < 0) + return ret; + + memserver_fd = fds[0]; + ret = clone(memserver_entry, memserver_stack + sizeof(memserver_stack), +#if MEMSERVER_USE_PROCESS + 0, +#else + CLONE_THREAD | CLONE_VM | CLONE_FILES + | CLONE_FS | CLONE_IO | CLONE_SIGHAND, +#endif + NULL); + if (ret < 0) + return ret; + +#if MEMSERVER_USE_PROCESS + memserver_pid = ret; + + /* Tell the Yama LSM module, if it's running, that it's OK for + the memserver to read process memory */ + prctl(PR_SET_PTRACER, ret); + + close(fds[0]); +#else + memserver_pid = getpid(); +#endif + + return fds[1]; +} + +void +memserver_fault(int sig) { + (void)sig; + siglongjmp(memserver_fault_buf, -1); +} + +ssize_t __attribute__((noinline)) +memserver_read(void *to, const void *from, size_t len) { + if (memserver_has_ptrace) { + struct iovec local = { to, len }; + struct iovec remote = { const_cast(from), len }; + return process_vm_readv(memserver_pid, &local, 1, &remote, 1, 0); + } else { + if (!sigsetjmp(memserver_fault_buf, 1)) { + memcpy(to, from, len); + return len; + } else { + return 1; + } + } +} + +int +memserver_entry(void *dummy __attribute__((unused))) { + int fd = memserver_fd; + int result = 1; + +#if MEMSERVER_USE_PROCESS + prctl(PR_SET_NAME, "[backtrace]"); +#endif + + memserver_has_ptrace = !!prctl(PR_CAPBSET_READ, CAP_SYS_PTRACE); + + if (!memserver_has_ptrace) { + struct sigaction sa; + sigfillset(&sa.sa_mask); + sa.sa_handler = memserver_fault; + sa.sa_flags = SA_NODEFER; + sigaction(SIGSEGV, &sa, NULL); + sigaction(SIGBUS, &sa, NULL); + } + + for (;;) { + struct memserver_req req; + ssize_t ret; + + ret = safe_read(fd, &req, sizeof(req)); + if (ret != sizeof(req)) + break; + + uint64_t addr = req.addr; + uint64_t bytes = req.len; + + while (bytes) { + uint64_t todo = (bytes < sizeof(memserver_buffer) + ? bytes : sizeof(memserver_buffer)); + + ret = memserver_read(memserver_buffer, (void *)addr, (size_t)todo); + + struct memserver_resp resp; + + resp.addr = addr; + resp.len = ret; + + ret = safe_write(fd, &resp, sizeof(resp)); + if (ret != sizeof(resp)) + goto fail; + + if (resp.len < 0) + break; + + ret = safe_write(fd, memserver_buffer, resp.len); + if (ret != resp.len) + goto fail; + + addr += resp.len; + bytes -= resp.len; + } + } + + result = 0; + + fail: + close(fd); + return result; +} + +// .. Starting the backtracer .................................................. + +char addr_buf[18]; +char timeout_buf[22]; +char limit_buf[22]; +char top_buf[22]; +const char *backtracer_argv[] = { + "swift-backtrace", // 0 + "--unwind", // 1 + "precise", // 2 + "--demangle", // 3 + "true", // 4 + "--interactive", // 5 + "true", // 6 + "--color", // 7 + "true", // 8 + "--timeout", // 9 + timeout_buf, // 10 + "--preset", // 11 + "friendly", // 12 + "--crashinfo", // 13 + addr_buf, // 14 + "--threads", // 15 + "preset", // 16 + "--registers", // 17 + "preset", // 18 + "--images", // 19 + "preset", // 20 + "--limit", // 21 + limit_buf, // 22 + "--top", // 23 + top_buf, // 24 + "--sanitize", // 25 + "preset", // 26 + "--cache", // 27 + "true", // 28 + "--output-to", // 29 + "stdout", // 30 + NULL +}; + +// We can't call sprintf() here because we're in a signal handler, +// so we need to be async-signal-safe. +void +format_address(uintptr_t addr, char buffer[18]) +{ + char *ptr = buffer + 18; + *--ptr = '\0'; + while (ptr > buffer) { + char digit = '0' + (addr & 0xf); + if (digit > '9') + digit += 'a' - '0' - 10; + *--ptr = digit; + addr >>= 4; + if (!addr) + break; + } + + // Left-justify in the buffer + if (ptr > buffer) { + char *pt2 = buffer; + while (*ptr) + *pt2++ = *ptr++; + *pt2++ = '\0'; + } +} +void +format_address(const void *ptr, char buffer[18]) +{ + format_address(reinterpret_cast(ptr), buffer); +} + +// See above; we can't use sprintf() here. +void +format_unsigned(unsigned u, char buffer[22]) +{ + char *ptr = buffer + 22; + *--ptr = '\0'; + while (ptr > buffer) { + char digit = '0' + (u % 10); + *--ptr = digit; + u /= 10; + if (!u) + break; + } + + // Left-justify in the buffer + if (ptr > buffer) { + char *pt2 = buffer; + while (*ptr) + *pt2++ = *ptr++; + *pt2++ = '\0'; + } +} + +const char * +trueOrFalse(bool b) { + return b ? "true" : "false"; +} + +const char * +trueOrFalse(OnOffTty oot) { + return trueOrFalse(oot == OnOffTty::On); +} + +bool +run_backtracer(int memserver_fd) +{ + // Set-up the backtracer's command line arguments + switch (_swift_backtraceSettings.algorithm) { + case UnwindAlgorithm::Fast: + backtracer_argv[2] = "fast"; + break; + default: + backtracer_argv[2] = "precise"; + break; + } + + // (The TTY option has already been handled at this point, so these are + // all either "On" or "Off".) + backtracer_argv[4] = trueOrFalse(_swift_backtraceSettings.demangle); + backtracer_argv[6] = trueOrFalse(_swift_backtraceSettings.interactive); + backtracer_argv[8] = trueOrFalse(_swift_backtraceSettings.color); + + switch (_swift_backtraceSettings.threads) { + case ThreadsToShow::Preset: + backtracer_argv[16] = "preset"; + break; + case ThreadsToShow::All: + backtracer_argv[16] = "all"; + break; + case ThreadsToShow::Crashed: + backtracer_argv[16] = "crashed"; + break; + } + + switch (_swift_backtraceSettings.registers) { + case RegistersToShow::Preset: + backtracer_argv[18] = "preset"; + break; + case RegistersToShow::None: + backtracer_argv[18] = "none"; + break; + case RegistersToShow::All: + backtracer_argv[18] = "all"; + break; + case RegistersToShow::Crashed: + backtracer_argv[18] = "crashed"; + break; + } + + switch (_swift_backtraceSettings.images) { + case ImagesToShow::Preset: + backtracer_argv[20] = "preset"; + break; + case ImagesToShow::None: + backtracer_argv[20] = "none"; + break; + case ImagesToShow::All: + backtracer_argv[20] = "all"; + break; + case ImagesToShow::Mentioned: + backtracer_argv[20] = "mentioned"; + break; + } + + switch (_swift_backtraceSettings.preset) { + case Preset::Friendly: + backtracer_argv[12] = "friendly"; + break; + case Preset::Medium: + backtracer_argv[12] = "medium"; + break; + default: + backtracer_argv[12] = "full"; + break; + } + + switch (_swift_backtraceSettings.sanitize) { + case SanitizePaths::Preset: + backtracer_argv[26] = "preset"; + break; + case SanitizePaths::Off: + backtracer_argv[26] = "false"; + break; + case SanitizePaths::On: + backtracer_argv[26] = "true"; + break; + } + + switch (_swift_backtraceSettings.outputTo) { + case OutputTo::Stdout: + backtracer_argv[30] = "stdout"; + break; + case OutputTo::Auto: // Shouldn't happen, but if it does pick stderr + case OutputTo::Stderr: + backtracer_argv[30] = "stderr"; + break; + } + + backtracer_argv[28] = trueOrFalse(_swift_backtraceSettings.cache); + + format_unsigned(_swift_backtraceSettings.timeout, timeout_buf); + + if (_swift_backtraceSettings.limit < 0) + std::strcpy(limit_buf, "none"); + else + format_unsigned(_swift_backtraceSettings.limit, limit_buf); + + format_unsigned(_swift_backtraceSettings.top, top_buf); + format_address(&crashInfo, addr_buf); + + // Actually execute it + return _swift_spawnBacktracer(backtracer_argv, memserver_fd); +} + +} // namespace + +#endif // __linux__ + diff --git a/stdlib/public/runtime/CrashHandlerMacOS.cpp b/stdlib/public/runtime/CrashHandlerMacOS.cpp index 81a6c072c8be0..cf3adad780b70 100644 --- a/stdlib/public/runtime/CrashHandlerMacOS.cpp +++ b/stdlib/public/runtime/CrashHandlerMacOS.cpp @@ -57,7 +57,7 @@ void suspend_other_threads(); void resume_other_threads(); bool run_backtracer(void); -swift::CrashInfo crashInfo; +CrashInfo crashInfo; os_unfair_lock crashLock = OS_UNFAIR_LOCK_INIT; @@ -428,6 +428,7 @@ run_backtracer() case OutputTo::Stdout: backtracer_argv[30] = "stdout"; break; + case OutputTo::Auto: // Shouldn't happen, but if it does pick stderr case OutputTo::Stderr: backtracer_argv[30] = "stderr"; break; diff --git a/stdlib/public/runtime/Errors.cpp b/stdlib/public/runtime/Errors.cpp index b126245d14bbe..040153e406f22 100644 --- a/stdlib/public/runtime/Errors.cpp +++ b/stdlib/public/runtime/Errors.cpp @@ -404,6 +404,12 @@ swift::warning(uint32_t flags, const char *format, ...) warningv(flags, format, args); } +/// Report a warning to the system console and stderr. This is exported, +/// unlike the swift::warning() function above. +void swift::swift_reportWarning(uint32_t flags, const char *message) { + warning(flags, "%s", message); +} + // Crash when a deleted method is called by accident. SWIFT_RUNTIME_EXPORT SWIFT_NORETURN void swift_deletedMethodError() { swift::fatalError(/* flags = */ 0, diff --git a/stdlib/public/runtime/Heap.cpp b/stdlib/public/runtime/Heap.cpp index c2d26dddad5ad..23e09ffa4eef4 100644 --- a/stdlib/public/runtime/Heap.cpp +++ b/stdlib/public/runtime/Heap.cpp @@ -60,13 +60,6 @@ static constexpr size_t MALLOC_ALIGN_MASK = alignof(std::max_align_t) - 1; static_assert(_swift_MinAllocationAlignment > MALLOC_ALIGN_MASK, "Swift's default alignment must exceed platform malloc mask."); -#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC -static inline malloc_zone_t *DEFAULT_ZONE() { - static malloc_zone_t *z = SWIFT_LAZY_CONSTANT(malloc_default_zone()); - return z; -} -#endif - // When alignMask == ~(size_t(0)), allocation uses the "default" // _swift_MinAllocationAlignment. This is different than calling swift_slowAlloc // with `alignMask == _swift_MinAllocationAlignment - 1` because it forces @@ -91,11 +84,7 @@ void *swift::swift_slowAlloc(size_t size, size_t alignMask) { void *p; // This check also forces "default" alignment to use AlignedAlloc. if (alignMask <= MALLOC_ALIGN_MASK) { -#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC - p = malloc_zone_malloc(DEFAULT_ZONE(), size); -#else p = malloc(size); -#endif } else { size_t alignment = computeAlignment(alignMask); p = AlignedAlloc(size, alignment); @@ -111,10 +100,10 @@ void *swift::swift_slowAllocTyped(size_t size, size_t alignMask, void *p; // This check also forces "default" alignment to use malloc_memalign(). if (alignMask <= MALLOC_ALIGN_MASK) { - p = malloc_type_zone_malloc(DEFAULT_ZONE(), size, typeId); + p = malloc_type_malloc(size, typeId); } else { size_t alignment = computeAlignment(alignMask); - p = malloc_type_zone_memalign(DEFAULT_ZONE(), alignment, size, typeId); + p = malloc_type_aligned_alloc(alignment, size, typeId); } if (!p) swift::crash("Could not allocate memory."); return p; @@ -141,11 +130,7 @@ void *swift::swift_slowAllocTyped(size_t size, size_t alignMask, // consistent with allocation with the same alignment. static void swift_slowDeallocImpl(void *ptr, size_t alignMask) { if (alignMask <= MALLOC_ALIGN_MASK) { -#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC - malloc_zone_free(DEFAULT_ZONE(), ptr); -#else free(ptr); -#endif } else { AlignedFree(ptr); } diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index f891026e580e8..cae6faac16b74 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -2803,8 +2803,7 @@ void swift::swift_initStructMetadataWithLayoutString( if (fieldFlags & LayoutStringFlags::HasRelativePointers) { swift_resolve_resilientAccessors(layoutStr, layoutStrOffset, - fieldLayoutStr, fieldRefCountBytes, - fieldType); + fieldLayoutStr, fieldType); } if (offset) { diff --git a/test/Backtracing/BacktraceWithLimit.swift b/test/Backtracing/BacktraceWithLimit.swift index 4d7371be88cc7..c91cd29d60c68 100644 --- a/test/Backtracing/BacktraceWithLimit.swift +++ b/test/Backtracing/BacktraceWithLimit.swift @@ -7,7 +7,7 @@ // UNSUPPORTED: back_deployment_runtime // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu import _Backtracing diff --git a/test/Backtracing/BacktraceWithLimitAndTop.swift b/test/Backtracing/BacktraceWithLimitAndTop.swift index a1a6fd277a5d3..ccd067d1346b9 100644 --- a/test/Backtracing/BacktraceWithLimitAndTop.swift +++ b/test/Backtracing/BacktraceWithLimitAndTop.swift @@ -7,7 +7,7 @@ // UNSUPPORTED: back_deployment_runtime // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu import _Backtracing diff --git a/test/Backtracing/Crash.swift b/test/Backtracing/Crash.swift index 4ef18f5a5ad52..b818a1cbba3c2 100644 --- a/test/Backtracing/Crash.swift +++ b/test/Backtracing/Crash.swift @@ -7,18 +7,18 @@ // RUN: %target-codesign %t/CrashNoDebug // RUN: %target-codesign %t/CrashOpt // RUN: %target-codesign %t/CrashOptNoDebug -// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/Crash || true) | %FileCheck %s -// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/Crash || true) | %FileCheck %s --check-prefix FRIENDLY -// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/CrashNoDebug || true) | %FileCheck %s --check-prefix NODEBUG -// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/CrashOpt || true) | %FileCheck %s --check-prefix OPTIMIZED -// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/CrashOptNoDebug || true) | %FileCheck %s --check-prefix OPTNODEBUG +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/Crash 2>&1 || true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/Crash 2>&1 || true) | %FileCheck %s --check-prefix FRIENDLY +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/CrashNoDebug 2>&1 || true) | %FileCheck %s --check-prefix NODEBUG +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/CrashOpt 2>&1 || true) | %FileCheck %s --check-prefix OPTIMIZED +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/CrashOptNoDebug 2>&1 || true) | %FileCheck %s --check-prefix OPTNODEBUG // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime // UNSUPPORTED: asan // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu func level1() { level2() @@ -51,7 +51,7 @@ struct Crash { // CHECK: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** -// CHECK: Thread 0 crashed: +// CHECK: Thread 0 {{(".*" )?}}crashed: // CHECK: 0 0x{{[0-9a-f]+}} level5() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:42:15 // CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} level4() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:36:3 @@ -66,11 +66,11 @@ struct Crash { // CHECK: Images ({{[0-9]+}} omitted): -// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}Crash{{ +}}{{.*}}/Crash +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{([0-9a-f]+|)}}{{ +}}Crash{{ +}}{{.*}}/Crash // FRIENDLY: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** -// FRIENDLY: Thread 0 crashed: +// FRIENDLY: Thread 0 {{(".*" )?}}crashed: // FRIENDLY: 0 level5() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:42:15 @@ -128,7 +128,7 @@ struct Crash { // NODEBUG: *** Program crashed: Bad pointer dereference at 0x{{0*}}4 *** -// NODEBUG: Thread 0 crashed: +// NODEBUG: Thread 0 {{(".*" )?}}crashed: // NODEBUG: 0 0x{{[0-9a-f]+}} level5() + {{[0-9]+}} in CrashNoDebug // NODEBUG: 1 [ra] 0x{{[0-9a-f]+}} level4() + {{[0-9]+}} in CrashNoDebug @@ -143,11 +143,11 @@ struct Crash { // NODEBUG: Images ({{[0-9]+}} omitted): -// NODEBUG: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}CrashNoDebug{{ +}}{{.*}}/CrashNoDebug +// NODEBUG: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{([0-9a-f]+|)}}{{ +}}CrashNoDebug{{ +}}{{.*}}/CrashNoDebug // OPTIMIZED: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** -// OPTIMIZED: Thread 0 crashed: +// OPTIMIZED: Thread 0 {{(".*" )?}}crashed: // OPTIMIZED: 0 [inlined] 0x{{[0-9a-f]+}} level5() in CrashOpt at {{.*}}/Crash.swift:42:15 // OPTIMIZED-NEXT: 1 [inlined] 0x{{[0-9a-f]+}} level4() in CrashOpt at {{.*}}/Crash.swift:36:3 @@ -155,18 +155,17 @@ struct Crash { // OPTIMIZED-NEXT: 3 [inlined] 0x{{[0-9a-f]+}} level2() in CrashOpt at {{.*}}/Crash.swift:28:3 // OPTIMIZED-NEXT: 4 [inlined] 0x{{[0-9a-f]+}} level1() in CrashOpt at {{.*}}/Crash.swift:24:3 // OPTIMIZED-NEXT: 5 [inlined] 0x{{[0-9a-f]+}} static Crash.main() in CrashOpt at {{.*}}/Crash.swift:48:5 -// OPTIMIZED-NEXT: 6 [inlined] [system] 0x{{[0-9a-f]+}} static Crash.$main() in CrashOpt at {{.*}}/ -// OPTIMIZED-NEXT: 7 [system] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in CrashOpt at {{.*}}/Crash.swift +// OPTIMIZED: {{6|7}} [system] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in CrashOpt at {{.*}} // OPTIMIZED: Registers: // OPTIMIZED: Images ({{[0-9]+}} omitted): -// OPTIMIZED: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}CrashOpt{{ +}}{{.*}}/CrashOpt +// OPTIMIZED: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{([0-9a-f]+|)}}{{ +}}CrashOpt{{ +}}{{.*}}/CrashOpt // OPTNODEBUG: *** Program crashed: Bad pointer dereference at 0x{{0*}}4 *** -// OPTNODEBUG: Thread 0 crashed: +// OPTNODEBUG: Thread 0 {{(".*" )?}}crashed: // OPTNODEBUG: 0 0x{{[0-9a-f]+}} main + {{[0-9]+}} in CrashOptNoDebug @@ -174,5 +173,5 @@ struct Crash { // OPTNODEBUG: Images ({{[0-9]+}} omitted): -// OPTNODEBUG: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}CrashOptNoDebug{{ +}}{{.*}}/CrashOptNoDebug +// OPTNODEBUG: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{([0-9a-f]+|)}}{{ +}}CrashOptNoDebug{{ +}}{{.*}}/CrashOptNoDebug diff --git a/test/Backtracing/CrashAsync.swift b/test/Backtracing/CrashAsync.swift index 873bbdd180686..e462129f9c25c 100644 --- a/test/Backtracing/CrashAsync.swift +++ b/test/Backtracing/CrashAsync.swift @@ -5,15 +5,15 @@ // Demangling is disabled for now because older macOS can't demangle async // function names. We test demangling elsewhere, so this is no big deal. -// RUN: (env SWIFT_BACKTRACE=enable=yes,demangle=no,cache=no %target-run %t/CrashAsync || true) | %FileCheck %s -// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,demangle=no,cache=no %target-run %t/CrashAsync || true) | %FileCheck %s --check-prefix FRIENDLY +// RUN: (env SWIFT_BACKTRACE=enable=yes,demangle=no,cache=no %target-run %t/CrashAsync 2>&1 || true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,demangle=no,cache=no %target-run %t/CrashAsync 2>&1 || true) | %FileCheck %s --check-prefix FRIENDLY // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime // UNSUPPORTED: asan // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu @available(SwiftStdlib 5.1, *) func crash() { @@ -42,24 +42,24 @@ struct CrashAsync { // CHECK: Thread {{[0-9]+}} crashed: -// CHECK: 0 0x{{[0-9a-f]+}} _$s10CrashAsync5crashyyF + {{[0-9]+}} in CrashAsync at {{.*}}/CrashAsync.swift:21:15 -// CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} _$s10CrashAsync5levelyySiYaFTY0_ + {{[0-9]+}} in CrashAsync at {{.*}}/CrashAsync.swift:29:5 -// CHECK-NEXT: 2 [async] 0x{{[0-9a-f]+}} _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 -// CHECK-NEXT: 3 [async] 0x{{[0-9a-f]+}} _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 -// CHECK-NEXT: 4 [async] 0x{{[0-9a-f]+}} _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 -// CHECK-NEXT: 5 [async] 0x{{[0-9a-f]+}} _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 -// CHECK-NEXT: 6 [async] 0x{{[0-9a-f]+}} _$s10CrashAsyncAAV4mainyyYaFZTQ0_ in CrashAsync at {{.*}}/CrashAsync.swift:37 -// CHECK-NEXT: 7 [async] [system] 0x{{[0-9a-f]+}} _$s10CrashAsyncAAV5$mainyyYaFZTQ0_ in CrashAsync at {{.*}}/ -// CHECK-NEXT: 8 [async] [system] 0x{{[0-9a-f]+}} _async_MainTQ0_ in CrashAsync at {{.*}}/ -// CHECK-NEXT: 9 [async] [thunk] 0x{{[0-9a-f]+}} _$sIetH_yts5Error_pIegHrzo_TRTQ0_ in CrashAsync at {{.*}}/ -// CHECK-NEXT: 10 [async] [thunk] 0x{{[0-9a-f]+}} _$sIetH_yts5Error_pIegHrzo_TRTATQ0_ in CrashAsync at {{.*}}/ -// CHECK-NEXT: 11 [async] [system] 0x{{[0-9a-f]+}} __ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE in libswift_Concurrency.dylib +// CHECK: 0 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5crashyyF + {{[0-9]+}} in CrashAsync at {{.*}}/CrashAsync.swift:21:15 +// CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5levelyySiYaFTY0_ + {{[0-9]+}} in CrashAsync at {{.*}}/CrashAsync.swift:29:5 +// CHECK-NEXT: 2 [async] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 +// CHECK-NEXT: 3 [async] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 +// CHECK-NEXT: 4 [async] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 +// CHECK-NEXT: 5 [async] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 +// CHECK-NEXT: 6 [async] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsyncAAV4mainyyYaFZ{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/CrashAsync.swift:37 +// CHECK-NEXT: 7 [async] [system] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsyncAAV5$mainyyYaFZ{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/ +// CHECK-NEXT: 8 [async] [system] 0x{{[0-9a-f]+}} {{_?}}async_Main{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/ +// CHECK-NEXT: 9 [async] [thunk] 0x{{[0-9a-f]+}} {{_?}}$sIetH_yts5Error_pIegHrzo_TR{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/ +// CHECK-NEXT: 10 [async] [thunk] 0x{{[0-9a-f]+}} {{_?}}$sIetH_yts5Error_pIegHrzo_TRTA{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/ +// CHECK-NEXT: 11 [async] [system] 0x{{[0-9a-f]+}} {{_?}}_ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE in libswift_Concurrency.{{dylib|so}} // FRIENDLY: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** // FRIENDLY: Thread {{[0-9]+}} crashed: -// FRIENDLY: 0 _$s10CrashAsync5crashyyF + {{[0-9]+}} in CrashAsync at {{.*}}CrashAsync.swift:21:15 +// FRIENDLY: 0 {{_?}}$s10CrashAsync5crashyyF + {{[0-9]+}} in CrashAsync at {{.*}}CrashAsync.swift:21:15 // FRIENDLY: 19| func crash() { // FRIENDLY-NEXT: 20| let ptr = UnsafeMutablePointer(bitPattern: 4)! @@ -68,7 +68,7 @@ struct CrashAsync { // FRIENDLY-NEXT: 22| } // FRIENDLY-NEXT: 23| -// FRIENDLY: 1 _$s10CrashAsync5levelyySiYaFTY0_ + {{[0-9]+}} in CrashAsync at {{.*}}CrashAsync.swift:29:5 +// FRIENDLY: 1 {{_?}}$s10CrashAsync5levelyySiYaFTY0_ + {{[0-9]+}} in CrashAsync at {{.*}}CrashAsync.swift:29:5 // FRIENDLY: 27| await level(n + 1) // FRIENDLY-NEXT: 28| } else { @@ -77,7 +77,7 @@ struct CrashAsync { // FRIENDLY-NEXT: 30| } // FRIENDLY-NEXT: 31| } -// FRIENDLY:2 _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 +// FRIENDLY:2 {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 // FRIENDLY: 25| func level(_ n: Int) async { // FRIENDLY-NEXT: 26| if n < 5 { @@ -86,10 +86,10 @@ struct CrashAsync { // FRIENDLY-NEXT: 28| } else { // FRIENDLY-NEXT: 29| crash() -// FRIENDLY: 3 _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 -// FRIENDLY: 4 _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 -// FRIENDLY: 5 _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 -// FRIENDLY: 6 _$s10CrashAsyncAAV4mainyyYaFZTQ0_ in CrashAsync at {{.*}}CrashAsync.swift:37 +// FRIENDLY: 3 {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 +// FRIENDLY: 4 {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 +// FRIENDLY: 5 {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 +// FRIENDLY: 6 {{_?}}$s10CrashAsyncAAV4mainyyYaFZ{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}CrashAsync.swift:37 // FRIENDLY: 35| struct CrashAsync { // FRIENDLY-NEXT: 36| static func main() async { diff --git a/test/Backtracing/CrashWithThunk.swift b/test/Backtracing/CrashWithThunk.swift index cc663c48bb49d..b8beebf285dc9 100644 --- a/test/Backtracing/CrashWithThunk.swift +++ b/test/Backtracing/CrashWithThunk.swift @@ -1,15 +1,15 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift %s -parse-as-library -Onone -g -o %t/CrashWithThunk // RUN: %target-codesign %t/CrashWithThunk -// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/CrashWithThunk || true) | %FileCheck %s -// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/CrashWithThunk || true) | %FileCheck %s --check-prefix FRIENDLY +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/CrashWithThunk 2>&1 || true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/CrashWithThunk 2>&1 || true) | %FileCheck %s --check-prefix FRIENDLY // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime // UNSUPPORTED: asan // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu struct Foo { var value: T @@ -32,7 +32,7 @@ struct CrashWithThunk { // CHECK: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** -// CHECK: Thread 0 crashed: +// CHECK: Thread 0 {{(".*" )?}}crashed: // CHECK: 0 0x{{[0-9a-f]+}} crash() + {{[0-9]+}} in CrashWithThunk at {{.*}}/CrashWithThunk.swift:21:15 // CHECK-NEXT: 1 [ra] [thunk] 0x{{[0-9a-f]+}} thunk for @escaping @callee_guaranteed () -> () + {{[0-9]+}} in CrashWithThunk at {{.*}}/Backtracing/ @@ -44,11 +44,11 @@ struct CrashWithThunk { // CHECK: Images ({{[0-9]+}} omitted): -// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}CrashWithThunk{{ +}}{{.*}}/CrashWithThunk +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+|}}{{ +}}CrashWithThunk{{ +}}{{.*}}/CrashWithThunk // FRIENDLY: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** -// FRIENDLY: Thread 0 crashed: +// FRIENDLY: Thread 0 {{(".*" )?}}crashed: // FRIENDLY: 0 crash() + {{[0-9]+}} in CrashWithThunk at {{.*}}/CrashWithThunk.swift:21:15 diff --git a/test/Backtracing/DwarfReader.swift b/test/Backtracing/DwarfReader.swift new file mode 100644 index 0000000000000..813467b98d509 --- /dev/null +++ b/test/Backtracing/DwarfReader.swift @@ -0,0 +1,26 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %S/Inputs/Inlining.swift -parse-as-library -g -o %t/Inlining +// RUN: %target-build-swift %s -parse-as-library -g -o %t/DwarfReader +// RUN: %target-run %t/DwarfReader %t/Inlining | %FileCheck %s + +// REQUIRES: OS=linux-gnu + +@_spi(DwarfTest) import _Backtracing + +@main +struct DwarfReader { + static func main() { + if CommandLine.argc != 2 { + print("usage: DwarfReader ") + return + } + + // CHECK: {{.*}}/Inlining is a {{32|64}}-bit ELF image + // CHECK: Units: + // CHECK: Call Sites: + + if !testDwarfReaderFor(path: CommandLine.arguments[1]) { + exit(1) + } + } +} diff --git a/test/Backtracing/ElfReader.swift b/test/Backtracing/ElfReader.swift new file mode 100644 index 0000000000000..0b73581b92e06 --- /dev/null +++ b/test/Backtracing/ElfReader.swift @@ -0,0 +1,125 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang -x c -Wno-unused-command-line-argument -Wl,--build-id -g %S/Inputs/fib.c -o %t/fib +// RUN: %target-clang -x c -Wno-unused-command-line-argument -g %S/Inputs/fib.c -o %t/fib-no-uuid +// RUN: %target-clang -x c -Wno-unused-command-line-argument -Wl,--build-id -Wl,--compress-debug-sections=zlib-gnu -g %S/Inputs/fib.c -o %t/fib-compress-gnu +// RUN: %target-clang -x c -Wno-unused-command-line-argument -Wl,--build-id -Wl,--compress-debug-sections=zlib -g %S/Inputs/fib.c -o %t/fib-compress-zlib +// RUN: %target-build-swift %s -parse-as-library -g -o %t/ElfReader +// RUN: %target-run %t/ElfReader %t/fib | %FileCheck %s +// RUN: %target-run %t/ElfReader %t/fib-no-uuid | %FileCheck %s --check-prefix NOUUID +// RUN: %target-run %t/ElfReader %t/fib-compress-gnu | %FileCheck %s --check-prefix CMPGNU +// RUN: %target-run %t/ElfReader %t/fib-compress-zlib | %FileCheck %s --check-prefix CMPZLIB +// RUN: if %S/Inputs/make-minidebug %t/fib %t/fib-minidebug; then ( %target-run %t/ElfReader %t/fib-minidebug | %FileCheck %s --check-prefix MINIDEBUG ); else echo "warning: skipping minidebug test as we couldn't generate minidebug data"; fi +// RUN: libc=$(ldd %t/fib | awk '/libc\.so\.6/ { print $3 }'); if %S/Inputs/has-uuid-syms "$libc" >/dev/null; then %target-run %t/ElfReader "$libc" | %FileCheck %s --check-prefix LIBC; else echo "warning: skipping /usr/lib/debug test as libc symbols are not installed"; fi +// RUN: %S/Inputs/make-debuglink %t/fib %t/fib-stripped %t/fib.dbg && %target-run %t/ElfReader %t/fib-stripped | %FileCheck %s --check-prefix DBGLINK + +// REQUIRES: OS=linux-gnu + +@_spi(ElfTest) import _Backtracing + +@main +struct ElfReader { + + static func main() { + if CommandLine.argc != 2 { + print("usage: ElfReader ") + return + } + + // CHECK: {{.*}}/fib is a {{(32|64)}}-bit ELF image + // CHECK-NEXT: uuid: {{[0-9a-f]+}} + // CHECK-NEXT: debug image: + // CHECK-NEXT: .debug_info: found + // CHECK-NEXT: .debug_line: found + // CHECK-NEXT: .debug_abbrev: found + // CHECK-NEXT: .debug_ranges: not found + // CHECK-NEXT: .debug_str: found + // CHECK-NEXT: .debug_addr: found + // CHECK-NEXT: .debug_str_offsets: found + // CHECK-NEXT: .debug_line_str: found + // CHECK-NEXT: .debug_rnglists: not found + + // NOUUID: {{.*}}/fib-no-uuid is a {{(32|64)}}-bit ELF image + // NOUUID-NEXT: uuid: + // NOUUID-NEXT: debug image: + // NOUUID-NEXT: .debug_info: found + // NOUUID-NEXT: .debug_line: found + // NOUUID-NEXT: .debug_abbrev: found + // NOUUID-NEXT: .debug_ranges: not found + // NOUUID-NEXT: .debug_str: found + // NOUUID-NEXT: .debug_addr: found + // NOUUID-NEXT: .debug_str_offsets: found + // NOUUID-NEXT: .debug_line_str: found + // NOUUID-NEXT: .debug_rnglists: not found + + // CMPGNU: {{.*}}/fib-compress-gnu is a {{(32|64)}}-bit ELF image + // CMPGNU-NEXT: uuid: {{[0-9a-f]+}} + // CMPGNU-NEXT: debug image: + // CMPGNU-NEXT: .debug_info: found + // CMPGNU-NEXT: .debug_line: found + // CMPGNU-NEXT: .debug_abbrev: found + // CMPGNU-NEXT: .debug_ranges: not found + // CMPGNU-NEXT: .debug_str: found + // CMPGNU-NEXT: .debug_addr: found + // CMPGNU-NEXT: .debug_str_offsets: found + // CMPGNU-NEXT: .debug_line_str: found + // CMPGNU-NEXT: .debug_rnglists: not found + + // CMPZLIB: {{.*}}/fib-compress-zlib is a {{(32|64)}}-bit ELF image + // CMPZLIB-NEXT: uuid: {{[0-9a-f]+}} + // CMPZLIB-NEXT: debug image: + // CMPZLIB-NEXT: .debug_info: found + // CMPZLIB-NEXT: .debug_line: found + // CMPZLIB-NEXT: .debug_abbrev: found + // CMPZLIB-NEXT: .debug_ranges: not found + // CMPZLIB-NEXT: .debug_str: found + // CMPZLIB-NEXT: .debug_addr: found + // CMPZLIB-NEXT: .debug_str_offsets: found + // CMPZLIB-NEXT: .debug_line_str: found + // CMPZLIB-NEXT: .debug_rnglists: not found + + // MINIDEBUG: {{.*}}/fib-minidebug is a {{(32|64)}}-bit ELF image + // MINIDEBUG-NEXT: uuid: {{[0-9a-f]+}} + // MINIDEBUG-NEXT: debug image: image {{[0-9a-f]+}} + // MINIDEBUG-NEXT: .debug_info: found + // MINIDEBUG-NEXT: .debug_line: found + // MINIDEBUG-NEXT: .debug_abbrev: found + // MINIDEBUG-NEXT: .debug_ranges: not found + // MINIDEBUG-NEXT: .debug_str: found + // MINIDEBUG-NEXT: .debug_addr: found + // MINIDEBUG-NEXT: .debug_str_offsets: found + // MINIDEBUG-NEXT: .debug_line_str: found + // MINIDEBUG-NEXT: .debug_rnglists: not found + + // LIBC: {{.*}}/libc.so.6 is a {{32|64}}-bit ELF image + // LIBC-NEXT: uuid: [[PREFIX:[0-9a-f]{2}]][[SUFFIX:[0-9a-f]+]] + // LIBC-NEXT: debug image: /usr/lib/debug/.build-id/[[PREFIX]]/[[SUFFIX]].debug + // LIBC-NEXT: .debug_info: found + // LIBC-NEXT: .debug_line: found + // LIBC-NEXT: .debug_abbrev: found + // LIBC-NEXT: .debug_ranges: + // LIBC-NEXT: .debug_str: found + // LIBC-NEXT: .debug_addr: + // LIBC-NEXT: .debug_str_offsets: + // LIBC-NEXT: .debug_line_str: + // LIBC-NEXT: .debug_rnglists: + + // DBGLINK: {{.*}}/fib-stripped is a {{(32|64)}}-bit ELF image + // DBGLINK-NEXT: uuid: {{[0-9a-f]+}} + // DBGLINK-NEXT: debug image: {{.*}}/fib.dbg + // DBGLINK-NEXT: .debug_info: found + // DBGLINK-NEXT: .debug_line: found + // DBGLINK-NEXT: .debug_abbrev: found + // DBGLINK-NEXT: .debug_ranges: not found + // DBGLINK-NEXT: .debug_str: found + // DBGLINK-NEXT: .debug_addr: found + // DBGLINK-NEXT: .debug_str_offsets: found + // DBGLINK-NEXT: .debug_line_str: found + // DBGLINK-NEXT: .debug_rnglists: not found + + if !testElfImageAt(path: CommandLine.arguments[1]) { + exit(1) + } + } + +} + diff --git a/test/Backtracing/Inputs/Inlining.swift b/test/Backtracing/Inputs/Inlining.swift new file mode 100644 index 0000000000000..03e3585ea3428 --- /dev/null +++ b/test/Backtracing/Inputs/Inlining.swift @@ -0,0 +1,29 @@ +func square(_ x: Int) -> Int { + return x * x +} + +func euclid2(_ a: Int, _ b: Int) -> Int { + return square(a) + square(b) +} + +@main +struct Inlining { + static func main() { + if CommandLine.argc != 3 { + print("usage: Inlining ") + exit(1) + } + + guard let a = Int(CommandLine.arguments[1]) else { + print("Argument must be a number") + exit(1) + } + guard let b = Int(CommandLine.arguments[2]) else { + print("Argument must be a number") + exit(1) + } + let result = euclid2(a, b) + + print("\(a) * \(a) + \(b) * \(b) = \(result)") + } +} diff --git a/test/Backtracing/Inputs/fib.c b/test/Backtracing/Inputs/fib.c new file mode 100644 index 0000000000000..7a256b9ab14ef --- /dev/null +++ b/test/Backtracing/Inputs/fib.c @@ -0,0 +1,31 @@ +#include +#include + +int fib(int x) { + if (x < 2) { + if (x == 0) { + return 0; + } + return 1; + } + + return fib(x - 1) + fib(x - 2); +} + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, + "usage: fib [...]\n" + "\n" + "Return the Nth fibonacci number.\n"); + return 0; + } + + for (int n = 1; n < argc; ++n) { + int x = atoi(argv[n]); + + printf("%d: %d\n", x, fib(x)); + } + + return 0; +} diff --git a/test/Backtracing/Inputs/has-uuid-syms b/test/Backtracing/Inputs/has-uuid-syms new file mode 100755 index 0000000000000..5b658d1b1cd8c --- /dev/null +++ b/test/Backtracing/Inputs/has-uuid-syms @@ -0,0 +1,34 @@ +#!/bin/bash +# +# Test if the symbols for a given binary are installed, and if so return +# the path to them. +# + +set -e + +if [[ $# -ne 1 ]]; then + cat >2 < + +Check whether a given binary has symbols installed in /usr/lib/debug/.build-id, +and if it does, return the path to them. +EOF + exit 1 +fi + +input=$1 +if ! uuid=$(readelf -n "$input" | awk '/Build ID:/ { print $3 }'); then + echo "uuid-syms: $input has no uuid" + exit 1 +fi + +prefix=${uuid:0:2} +suffix=${uuid:2} +dbgpath="/usr/lib/debug/.build-id/$prefix/$suffix.debug" +if [[ -f "$dbgpath" ]]; then + echo "$dbgpath" + exit 0 +fi + +exit 1 + diff --git a/test/Backtracing/Inputs/make-debuglink b/test/Backtracing/Inputs/make-debuglink new file mode 100755 index 0000000000000..3dcf0ddc803a9 --- /dev/null +++ b/test/Backtracing/Inputs/make-debuglink @@ -0,0 +1,48 @@ +#!/bin/bash +# +# Strip a binary and generate a separate .dbg that gets referred to using +# .gnu_debuglink. +# + +set -e + +# Check we have the required arguments +if [[ $# -ne 3 ]]; then + cat >2 < + +Take , which contains symbols, strip it to create , and +extract the debug information into a separate file, adding a +'.gnu_debuglink' section to that points at it. +EOF + exit 1 +fi + +# Grab the arguments +original=$1 +stripped=$2 +debug=$3 + +# Create a temporary directory to work in +tempdir=$(mktemp -d -t tmp.XXXXXXXXXXX) +function cleanup { + rm -rf "$tempdir" +} +trap cleanup EXIT + +# Construct the debug file +nm -D "$original" --format=posix --defined-only \ + | awk '{ print $1 }' | sort > "$tempdir/dynsyms" +nm "$original" --format=posix --defined-only \ + | awk '$2 ~ /[TtD]/ { print $1 }' \ + | sort > "$tempdir/funcsyms" + +comm -13 "$tempdir/dynsyms" "$tempdir/funcsyms" > "$tempdir/keepsyms" + +objcopy --only-keep-debug \ + --remove-section .gdb_index \ + --remove-section .comment \ + --keep-symbols="$tempdir/keepsyms" \ + "$original" "$debug" + +objcopy -S --add-gnu-debuglink="$debug" "$original" "$stripped" diff --git a/test/Backtracing/Inputs/make-minidebug b/test/Backtracing/Inputs/make-minidebug new file mode 100755 index 0000000000000..ca3ad4ed4477f --- /dev/null +++ b/test/Backtracing/Inputs/make-minidebug @@ -0,0 +1,60 @@ +#!/bin/bash +# +# Generate MiniDebugInfo data (in the '.gnu_debugdata' section) +# + +set -e + +# Check we have the required arguments +if [[ $# -ne 2 ]]; then + cat >2 < + +Extract debug information from and generate a new version, , +containing a compressed '.gnu_debugdata' section. + +Requires xz be installed on your machine. +EOF + exit 1 +fi + +# Grab the arguments +input=$1 +output=$2 + +# Check for the tools we need +for tool in nm comm objcopy xz strip; do + if ! $(which $tool >/dev/null); then + echo "make-minidebug: $tool not installed." + exit 1 + fi +done + +# Create a temporary directory to work in +tempdir=$(mktemp -d -t tmp.XXXXXXXXXXX) +function cleanup { + rm -rf "$tempdir" +} +trap cleanup EXIT + +# Actually construct the MiniDebug section + +nm -D "$input" --format=posix --defined-only \ + | awk '{ print $1 }' | sort > "$tempdir/dynsyms" +nm "$input" --format=posix --defined-only \ + | awk '$2 ~ /[TtD]/ { print $1 }' \ + | sort > "$tempdir/funcsyms" + +comm -13 "$tempdir/dynsyms" "$tempdir/funcsyms" > "$tempdir/keepsyms" + +objcopy --only-keep-debug \ + --remove-section .gdb_index \ + --remove-section .comment \ + --keep-symbols="$tempdir/keepsyms" \ + "$input" "$tempdir/minidbginfo" + +xz "$tempdir/minidbginfo" + +objcopy -S --remove-section .comment \ + --add-section .gnu_debugdata="$tempdir/minidbginfo.xz" \ + "$input" "$output" diff --git a/test/Backtracing/Overflow.swift b/test/Backtracing/Overflow.swift index e9d04db9ca7ed..e9e5095274041 100644 --- a/test/Backtracing/Overflow.swift +++ b/test/Backtracing/Overflow.swift @@ -1,14 +1,14 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift %s -parse-as-library -Onone -g -o %t/Overflow // RUN: %target-codesign %t/Overflow -// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/Overflow || true) | %FileCheck %s -// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/Overflow || true) | %FileCheck %s --check-prefix FRIENDLY +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/Overflow 2>&1 || true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/Overflow 2>&1 || true) | %FileCheck %s --check-prefix FRIENDLY // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu var x: UInt = 0 func level1() { @@ -42,7 +42,7 @@ struct Overflow { // CHECK: *** Swift runtime failure: arithmetic overflow *** -// CHECK: Thread 0 crashed: +// CHECK: Thread 0 {{(".*" )?}}crashed: // CHECK: 0 [inlined] [system] 0x{{[0-9a-f]+}} Swift runtime failure: arithmetic overflow in Overflow at {{.*}}/ // CHECK-NEXT: 1 0x{{[0-9a-f]+}} level5() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:33:5 @@ -58,11 +58,11 @@ struct Overflow { // CHECK: Images ({{[0-9]+}} omitted): -// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}Overflow{{ +}}{{.*}}/Overflow +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+|}}{{ +}}Overflow{{ +}}{{.*}}/Overflow // FRIENDLY: *** Swift runtime failure: arithmetic overflow *** -// FRIENDLY: Thread 0 crashed: +// FRIENDLY: Thread 0 {{(".*" )?}}crashed: // FRIENDLY: 0 level5() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:33:5 diff --git a/test/Backtracing/SimpleAsyncBacktrace.swift b/test/Backtracing/SimpleAsyncBacktrace.swift index c4455b79da828..592a080fba9e6 100644 --- a/test/Backtracing/SimpleAsyncBacktrace.swift +++ b/test/Backtracing/SimpleAsyncBacktrace.swift @@ -7,7 +7,7 @@ // REQUIRES: concurrency_runtime // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime diff --git a/test/Backtracing/SimpleBacktrace.swift b/test/Backtracing/SimpleBacktrace.swift index ab8a5f09484ea..1695ff896b7f1 100644 --- a/test/Backtracing/SimpleBacktrace.swift +++ b/test/Backtracing/SimpleBacktrace.swift @@ -5,7 +5,7 @@ // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime diff --git a/test/Backtracing/StackOverflow.swift b/test/Backtracing/StackOverflow.swift index de4062393b9f5..5fa772d4c70a5 100644 --- a/test/Backtracing/StackOverflow.swift +++ b/test/Backtracing/StackOverflow.swift @@ -1,16 +1,16 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift %s -parse-as-library -Onone -g -o %t/StackOverflow // RUN: %target-codesign %t/StackOverflow -// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/StackOverflow || true) | %FileCheck %s -// RUN: (env SWIFT_BACKTRACE=limit=16,top=4,enable=yes,cache=no %target-run %t/StackOverflow || true) | %FileCheck %s --check-prefix LIMITED -// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/StackOverflow || true) | %FileCheck %s --check-prefix FRIENDLY +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/StackOverflow 2>&1|| true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=limit=17,top=5,enable=yes,cache=no %target-run %t/StackOverflow 2>&1 || true) | %FileCheck %s --check-prefix LIMITED +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/StackOverflow 2>&1 || true) | %FileCheck %s --check-prefix FRIENDLY // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime // UNSUPPORTED: asan // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu func recurse(_ level: Int) { if level % 100000 == 0 { @@ -32,9 +32,9 @@ struct StackOverflow { // CHECK: *** Program crashed: Bad pointer dereference at 0x{{[0-9a-f]+}} *** -// CHECK: Thread 0 crashed: +// CHECK: Thread 0 {{(".*" )?}}crashed: -// CHECK: 0 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{[0-9]+}} +// CHECK: 0 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift // CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // CHECK-NEXT: 2 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // CHECK-NEXT: 3 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 @@ -93,8 +93,12 @@ struct StackOverflow { // CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 + +// The exact number of recursion frames varies from platform to platform; +// on macOS, there is a hidden dyld frame at the very top, which takes up one +// of the 16 frames. On Linux, we may have a couple of libc frames as well. + +// CHECK: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 // CHECK-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} static StackOverflow.$main() + {{[0-9]+}} in StackOverflow at {{.*}}/ // CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift @@ -102,11 +106,11 @@ struct StackOverflow { // CHECK: Images ({{[0-9]+}} omitted): -// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}StackOverflow{{ +}}{{.*}}/StackOverflow +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+|}}{{ +}}StackOverflow{{ +}}{{.*}}/StackOverflow // LIMITED: *** Program crashed: Bad pointer dereference at 0x{{[0-9a-f]+}} *** -// LIMITED: Thread 0 crashed: +// LIMITED: Thread 0 {{(".*" )?}}crashed: // LIMITED: 0 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift // LIMITED-NEXT: 1 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 @@ -120,18 +124,21 @@ struct StackOverflow { // LIMITED-NEXT: 9 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // LIMITED-NEXT: 10 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // LIMITED-NEXT: ... -// LIMITED-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 + +// N.B. There can be platform differences surrounding the exact frame counts + +// LIMITED: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 // LIMITED-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} static StackOverflow.$main() + {{[0-9]+}} in StackOverflow at {{.*}}/ // LIMITED-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift // FRIENDLY: *** Program crashed: Bad pointer dereference at 0x{{[0-9a-f]+}} *** -// FRIENDLY: Thread 0 crashed: +// FRIENDLY: Thread 0 {{(".*" )?}}crashed: // FRIENDLY: {{[ ]}}0 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift // SKIP-FRIENDLY: 9│ // REQUIRES: executable_test -// SKIP-FRIENDLY-NEXT: 10│ // REQUIRES: OS=macosx +// SKIP-FRIENDLY-NEXT: 10│ // REQUIRES: OS=macosx || OS=linux-gnu // SKIP-FRIENDLY-NEXT: 11│ // SKIP-FRIENDLY-NEXT: 12│ func recurse(_ level: Int) { // SKIP-FRIENDLY-NEXT: │ ▲ @@ -203,8 +210,10 @@ struct StackOverflow { // FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 -// FRIENDLY-NEXT: {{[0-9]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 + +// N.B. There can be platform differences surrounding the exact frame counts + +// FRIENDLY: {{[0-9]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 // SKIP-FRIENDLY: 19│ @main // SKIP-FRIENDLY-NEXT: 20│ struct StackOverflow { diff --git a/test/Backtracing/SymbolicatedBacktrace.swift b/test/Backtracing/SymbolicatedBacktrace.swift index 4bd47a1b492cc..f1251b82df6ba 100644 --- a/test/Backtracing/SymbolicatedBacktrace.swift +++ b/test/Backtracing/SymbolicatedBacktrace.swift @@ -7,7 +7,7 @@ // UNSUPPORTED: back_deployment_runtime // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu import _Backtracing diff --git a/test/Backtracing/SymbolicatedBacktraceInline.swift b/test/Backtracing/SymbolicatedBacktraceInline.swift index cd97b6fa933b1..3f7065dd0e769 100644 --- a/test/Backtracing/SymbolicatedBacktraceInline.swift +++ b/test/Backtracing/SymbolicatedBacktraceInline.swift @@ -9,6 +9,10 @@ // REQUIRES: backtracing // REQUIRES: OS=macosx +// This currently doesn't work on Linux because the unwind finishes at pow(), +// which presumably doesn't have a frame pointer. When we add the Dwarf EH +// unwinder, we should be able to turn this test on. + import _Backtracing func kablam() { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0be9c9022c098..64dbbcaa0df45 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -407,7 +407,8 @@ foreach(SDK ${SWIFT_SDKS}) list(APPEND LIT_ARGS "--param" "threading=${SWIFT_SDK_${SDK}_THREADING_PACKAGE}") # Enable on-crash backtracing if supported - if("${SDK}" STREQUAL "OSX" AND NOT SWIFT_ASAN_BUILD) + if(("${SDK}" STREQUAL "OSX" OR "${SDK}" STREQUAL "LINUX") + AND NOT SWIFT_ASAN_BUILD) list(APPEND LIT_ARGS "--param" "backtrace_on_crash") endif() diff --git a/test/Constraints/patterns.swift b/test/Constraints/patterns.swift index 0104bbe045c16..7305d549e6fdf 100644 --- a/test/Constraints/patterns.swift +++ b/test/Constraints/patterns.swift @@ -120,7 +120,7 @@ case iPadHair.HairForceOne: () case iPadHair.HairForceOne: // expected-error{{generic enum type 'iPadHair' is ambiguous without explicit generic parameters when matching value of type 'any HairType'}} () -case Watch.Edition: // expected-warning {{cast from 'any HairType' to unrelated type 'Watch' always fails}} +case Watch.Edition: // expected-error {{pattern of type 'Watch' cannot match 'any HairType'}} () case .HairForceOne: // expected-error{{type 'any HairType' has no member 'HairForceOne'}} () @@ -564,3 +564,97 @@ struct TestIUOMatchOp { if case self = self {} } } + +struct TestRecursiveVarRef { + lazy var e: () -> Int = {e}() +} + +func testMultiStmtClosureExprPattern(_ x: Int) { + if case { (); return x }() = x {} +} + +func testExprPatternIsolation() { + // We type-check ExprPatterns separately, so these are illegal. + if case 0 = nil {} // expected-error {{'nil' requires a contextual type}} + let _ = { + if case 0 = nil {} // expected-error {{'nil' requires a contextual type}} + } + for case 0 in nil {} // expected-error {{'nil' requires a contextual type}} + for case 0 in [nil] {} + // expected-error@-1 {{type 'Any' cannot conform to 'Equatable'}} + // expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}} + // expected-note@-3 {{requirement from conditional conformance of 'Any?' to 'Equatable'}} + + // Though we will try Double for an integer literal... + let d: Double = 0 + if case d = 0 {} + let _ = { + if case d = 0 {} + } + for case d in [0] {} + + // But not Float + let f: Float = 0 + if case f = 0 {} // expected-error {{expression pattern of type 'Float' cannot match values of type 'Int'}} + let _ = { + if case f = 0 {} // expected-error {{expression pattern of type 'Float' cannot match values of type 'Int'}} + } + for case f in [0] {} // expected-error {{expression pattern of type 'Float' cannot match values of type 'Int'}} + + enum MultiPayload: Equatable { + case e(T, T) + static func f(_ x: T, _ y: T) -> Self { .e(x, y) } + } + enum E: Equatable { + case a, b + static var c: E { .a } + static var d: E { .b } + } + + func produceMultiPayload() -> MultiPayload { fatalError() } + + // We type-check ExprPatterns left to right, so only one of these works. + if case .e(0.0, 0) = produceMultiPayload() {} + if case .e(0, 0.0) = produceMultiPayload() {} // expected-error {{expression pattern of type 'Double' cannot match values of type 'Int'}} + + for case .e(0.0, 0) in [produceMultiPayload()] {} + for case .e(0, 0.0) in [produceMultiPayload()] {} // expected-error {{expression pattern of type 'Double' cannot match values of type 'Int'}} + + // Same, because although it's a top-level ExprPattern, we don't resolve + // that until during solving. + if case .f(0.0, 0) = produceMultiPayload() {} + if case .f(0, 0.0) = produceMultiPayload() {} // expected-error {{expression pattern of type 'Double' cannot match values of type 'Int'}} + + if case .e(5, nil) = produceMultiPayload() {} // expected-warning {{type 'Int' is not optional, value can never be nil; this is an error in Swift 6}} + + // FIXME: Bad error (https://github.com/apple/swift/issues/64279) + if case .e(nil, 0) = produceMultiPayload() {} + // expected-error@-1 {{expression pattern of type 'String' cannot match values of type 'Substring'}} + // expected-note@-2 {{overloads for '~=' exist with these partially matching parameter lists}} + + if case .e(5, nil) = produceMultiPayload() as MultiPayload {} + if case .e(nil, 0) = produceMultiPayload() as MultiPayload {} + + // Enum patterns are solved together. + if case .e(E.a, .b) = produceMultiPayload() {} + if case .e(.a, E.b) = produceMultiPayload() {} + + // These also work because they start life as EnumPatterns. + if case .e(E.c, .d) = produceMultiPayload() {} + if case .e(.c, E.d) = produceMultiPayload() {} + for case .e(E.c, .d) in [produceMultiPayload()] {} + for case .e(.c, E.d) in [produceMultiPayload()] {} + + // Silly, but allowed. + if case 0: Int? = 0 {} // expected-warning {{non-optional expression of type 'Int' used in a check for optionals}} + + var opt: Int? + if case opt = 0 {} +} + +enum LotsOfOptional { + case yup(Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?) +} +func testLotsOfNil(_ x: LotsOfOptional) { + if case .yup(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) = x {} +} diff --git a/test/Constraints/rdar105781521.swift b/test/Constraints/rdar105781521.swift index ec76bf061d662..6e5f6eebe3092 100644 --- a/test/Constraints/rdar105781521.swift +++ b/test/Constraints/rdar105781521.swift @@ -12,7 +12,6 @@ func test(value: MyEnum) { switch value { case .first(true): // expected-error@-1 {{expression pattern of type 'Bool' cannot match values of type 'String'}} - // expected-note@-2 {{overloads for '~=' exist with these partially matching parameter lists: (Substring, String)}} break default: break diff --git a/test/Constraints/rdar105782480.swift b/test/Constraints/rdar105782480.swift new file mode 100644 index 0000000000000..581f7b3d0db3b --- /dev/null +++ b/test/Constraints/rdar105782480.swift @@ -0,0 +1,18 @@ +// RUN: %target-typecheck-verify-swift + +// rdar://105782480 +enum MyEnum { + case second(Int?) +} + +func takeClosure(_ x: () -> Void) {} + +func foo(value: MyEnum) { + takeClosure { + switch value { + case .second(let drag).invalid: + // expected-error@-1 {{value of type 'MyEnum' has no member 'invalid'}} + break + } + } +} diff --git a/test/Constraints/rdar106598067.swift b/test/Constraints/rdar106598067.swift new file mode 100644 index 0000000000000..941bbae2f2a2e --- /dev/null +++ b/test/Constraints/rdar106598067.swift @@ -0,0 +1,11 @@ +// RUN: %target-typecheck-verify-swift + +enum E: Error { case e } + +// rdar://106598067 – Make sure we don't crash. +// FIXME: Bad diagnostic (the issue is that it should be written 'as', not 'as?') +let fn = { + // expected-error@-1 {{unable to infer closure type in the current context}} + do {} catch let x as? E {} + // expected-warning@-1 {{'catch' block is unreachable because no errors are thrown in 'do' block}} +} diff --git a/test/Constraints/rdar107420031.swift b/test/Constraints/rdar107420031.swift new file mode 100644 index 0000000000000..2f5687eb6fe31 --- /dev/null +++ b/test/Constraints/rdar107420031.swift @@ -0,0 +1,18 @@ +// RUN: %target-typecheck-verify-swift + +enum E { + case e +} + +func ~= (lhs: any Error, rhs: E) -> Bool { true } + +// rdar://107420031 – Make sure we don't crash. +// TODO: This ought to compile. +func foo(_ error: any Error) { + switch error { + case E.e: // expected-error {{pattern of type 'E' cannot match 'any Error'}} + break + default: + break + } +} diff --git a/test/Constraints/rdar107709341.swift b/test/Constraints/rdar107709341.swift new file mode 100644 index 0000000000000..cc38962ec4f12 --- /dev/null +++ b/test/Constraints/rdar107709341.swift @@ -0,0 +1,13 @@ +// RUN: %target-typecheck-verify-swift + +// rdar://107709341 – Make sure we don't crash. +func foo(_ x: Int) { + let _ = { + switch x { + case Optional.some(x): // expected-error {{pattern of type 'Optional' cannot match 'Int'}} {{none}} + break + default: + break + } + } +} diff --git a/test/Constraints/rdar109419240.swift b/test/Constraints/rdar109419240.swift new file mode 100644 index 0000000000000..9d78a4c6cf831 --- /dev/null +++ b/test/Constraints/rdar109419240.swift @@ -0,0 +1,15 @@ +// RUN: %target-typecheck-verify-swift + +// rdar://109419240 – Make sure we don't crash +enum E { // expected-note {{'E' declared here}} + case e(Int) +} + +func foo(_ arr: [E]) -> Int { + return arr.reduce(0) { (total, elem) -> Int in + switch elem { + case let e(x): // expected-error {{cannot find 'e' in scope; did you mean 'E'?}} + return total + x + } + } +} diff --git a/test/Constraints/result_builder_diags.swift b/test/Constraints/result_builder_diags.swift index 660e719a0e569..5cd49c0cc771a 100644 --- a/test/Constraints/result_builder_diags.swift +++ b/test/Constraints/result_builder_diags.swift @@ -659,8 +659,6 @@ struct MyView { } @TupleBuilder var invalidCaseWithoutDot: some P { - // expected-error@-1 {{return type of property 'invalidCaseWithoutDot' requires that 'Either' conform to 'P'}} - // expected-note@-2 {{opaque return type declared here}} switch Optional.some(1) { case none: 42 // expected-error {{cannot find 'none' in scope}} case .some(let x): diff --git a/test/Constraints/result_builder_invalid_stmts.swift b/test/Constraints/result_builder_invalid_stmts.swift index 5de3ccc668b1f..5f8dc47268ee4 100644 --- a/test/Constraints/result_builder_invalid_stmts.swift +++ b/test/Constraints/result_builder_invalid_stmts.swift @@ -15,7 +15,6 @@ func foo(_ x: String) -> Int { if .random() { switch x { case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} - // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists}} 0 default: 1 @@ -29,7 +28,6 @@ func bar(_ x: String) -> Int { case 0: switch x { case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} - // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists}} 0 default: 1 @@ -44,7 +42,6 @@ func baz(_ x: String) -> Int { do { switch x { case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} - // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists}} 0 default: 1 @@ -57,7 +54,6 @@ func qux(_ x: String) -> Int { for _ in 0 ... 0 { switch x { case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} - // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists}} 0 default: 1 diff --git a/test/DebugInfo/captures.swift b/test/DebugInfo/captures.swift new file mode 100644 index 0000000000000..984108ef95547 --- /dev/null +++ b/test/DebugInfo/captures.swift @@ -0,0 +1,26 @@ +// RUN: %target-swift-frontend %s -parse-as-library -module-name a -emit-sil -g -o - | %FileCheck %s +struct S {} +public class UIView {} +public protocol View {} +public final class Signal { + public func map(_ transform: @escaping (Value) -> U) -> Signal { + return Signal() + } +} +public final class C: UIView { + private let t1: C? = nil + private let t2: C? = nil + func foo() -> Signal<(S, UIView)> { + // CHECK: sil {{.*}}s1a1CC3foo + // CHECK: debug_value {{.*}} name "self" + // CHECK-NOT: debug_value {{.*}} name "view" + // CHECK: return % + return ( + Signal() + .map { [view = t1!] in ($0, view) }, + Signal() + .map { [view = t2!] in ($0, view) } + ).0 + } +} + diff --git a/test/Driver/Inputs/fake-resource-dir/lib/swift_static/wasi/static-executable-args.lnk b/test/Driver/Inputs/fake-resource-dir/lib/swift_static/wasi/static-executable-args.lnk new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/Driver/profiling.swift b/test/Driver/profiling.swift index f96cea46e33b6..45a9f84d12b69 100644 --- a/test/Driver/profiling.swift +++ b/test/Driver/profiling.swift @@ -20,7 +20,7 @@ // RUN: %swiftc_driver -sdk "" -driver-print-jobs -profile-generate -target x86_64-unknown-linux-gnu %s | %FileCheck -check-prefix=CHECK -check-prefix=LINUX %s // RUN: %swiftc_driver -sdk "" -driver-print-jobs -profile-generate -target x86_64-unknown-windows-msvc %s | %FileCheck -check-prefix=CHECK -check-prefix=WINDOWS %s -// RUN: %swiftc_driver -sdk "" -driver-print-jobs -profile-generate -target wasm32-unknown-wasi %s | %FileCheck -check-prefix CHECK -check-prefix WASI %s +// RUN: %swiftc_driver -sdk "" -driver-print-jobs -profile-generate -target wasm32-unknown-wasi -resource-dir %S/Inputs/fake-resource-dir/lib/swift_static %s | %FileCheck -check-prefix CHECK -check-prefix WASI %s // CHECK: swift // CHECK: -profile-generate @@ -55,7 +55,7 @@ // WINDOWS: -lclang_rt.profile // WASI: clang{{(\.exe)?"? }} -// WASI: lib{{(\\\\|/)}}swift{{(\\\\|/)}}clang{{(\\\\|/)}}lib{{(\\\\|/)}}wasi{{(\\\\|/)}}libclang_rt.profile-wasm32.a +// WASI: lib{{(\\\\|/)}}{{swift|swift_static}}{{(\\\\|/)}}clang{{(\\\\|/)}}lib{{(\\\\|/)}}wasi{{(\\\\|/)}}libclang_rt.profile-wasm32.a // WASI: -u__llvm_profile_runtime // RUN: not %swiftc_driver -sdk "" -driver-print-jobs -profile-generate -profile-use=/dev/null %s 2>&1 | %FileCheck -check-prefix=MIX_GEN_USE %s diff --git a/test/Driver/sdk.swift b/test/Driver/sdk.swift index 98cc05c55160c..8aba6e15f51f3 100644 --- a/test/Driver/sdk.swift +++ b/test/Driver/sdk.swift @@ -2,7 +2,7 @@ // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-linux-gnu -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix LINUX // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-freebsd -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix FREEBSD // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-windows-msvc -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix WINDOWS -// RUN: %swiftc_driver -driver-print-jobs -target wasm32-unknown-wasi -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix WASI +// RUN: %swiftc_driver -driver-print-jobs -target wasm32-unknown-wasi -g -sdk %S/../Inputs/clang-importer-sdk -resource-dir %S/Inputs/fake-resource-dir/lib/swift_static %s 2>&1 | %FileCheck %s --check-prefix WASI // RUN: env SDKROOT=%S/../Inputs/clang-importer-sdk %swiftc_driver_plain -target x86_64-apple-macosx10.9 -g -driver-print-jobs %s 2>&1 | %FileCheck %s --check-prefix OSX // RUN: env SDKROOT=%S/../Inputs/clang-importer-sdk %swiftc_driver_plain -target x86_64-unknown-linux-gnu -g -driver-print-jobs %s 2>&1 | %FileCheck %s --check-prefix LINUX diff --git a/test/Driver/wasm.swift b/test/Driver/wasm.swift index 80de3cea5c66b..04588954e851a 100644 --- a/test/Driver/wasm.swift +++ b/test/Driver/wasm.swift @@ -1,4 +1,4 @@ -// RUN: %swiftc_driver -driver-print-jobs -target wasm32-unknown-wasi -v %s 2>&1 | %FileCheck %s -check-prefix=CHECK-WASM +// RUN: %swiftc_driver -driver-print-jobs -target wasm32-unknown-wasi -resource-dir %S/Inputs/fake-resource-dir/lib/swift_static -v %s 2>&1 | %FileCheck %s -check-prefix=CHECK-WASM // CHECK-WASM: swift{{.*}} -frontend -c -primary-file {{.*}} -target wasm32-unknown-wasi -disable-objc-interop -// CHECK-WASM: clang{{.*}} -lswiftCore --target=wasm32-unknown-wasi -v {{.*}}-o +// CHECK-WASM: clang{{.*}} -target wasm32-unknown-wasi {{.*}}static-executable-args.lnk{{.*}}-v {{.*}}-o diff --git a/test/IDE/complete_optionset.swift b/test/IDE/complete_optionset.swift index d30be9b1e436d..33d0af9905844 100644 --- a/test/IDE/complete_optionset.swift +++ b/test/IDE/complete_optionset.swift @@ -16,6 +16,11 @@ func foo() { ShippingOptions.#^MEMBER_STATIC^# } +@attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) +@attached(conformance) +public macro OptionSet() = + #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") + // MEMBER_STATIC: Keyword[self]/CurrNominal: self[#ShippingOptions.Type#]; name=self // MEMBER_STATIC: Decl[TypeAlias]/CurrNominal: RawValue[#UInt8#]; name=RawValue // MEMBER_STATIC: Decl[Constructor]/CurrNominal: init({#rawValue: ShippingOptions.RawValue#})[#ShippingOptions#]; name=init(rawValue:) diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index 0e29f7935297e..f81f9a6c31ade 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -710,3 +710,38 @@ let _: DispatchTime = .#^UNRESOLVED_FUNCTION_CALL^#now() + 0.2 // UNRESOLVED_FUNCTION_CALL: Begin completions, 2 items // UNRESOLVED_FUNCTION_CALL-DAG: Decl[StaticMethod]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: now()[#DispatchTime#]; // UNRESOLVED_FUNCTION_CALL-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init()[#DispatchTime#]; + +func id(_ x: T) -> T { x } + +func testNestedExprPatternCompletion(_ x: SomeEnum1) { + // Multi-statement closures have different type-checking code paths, + // so we need to test both. + let fn = { + switch x { + case id(.#^UNRESOLVED_NESTED1^#): + // UNRESOLVED_NESTED1: Begin completions, 3 items + // UNRESOLVED_NESTED1: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#]; name=South + // UNRESOLVED_NESTED1: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#]; name=North + // UNRESOLVED_NESTED1: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(:) + break + } + if case id(.#^UNRESOLVED_NESTED2^#) = x {} + // UNRESOLVED_NESTED2: Begin completions, 3 items + // UNRESOLVED_NESTED2: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#]; name=South + // UNRESOLVED_NESTED2: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#]; name=North + // UNRESOLVED_NESTED2: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(:) + } + switch x { + case id(.#^UNRESOLVED_NESTED3^#): + // UNRESOLVED_NESTED3: Begin completions, 3 items + // UNRESOLVED_NESTED3: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#]; name=South + // UNRESOLVED_NESTED3: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#]; name=North + // UNRESOLVED_NESTED3: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(:) + break + } + if case id(.#^UNRESOLVED_NESTED4^#) = x {} + // UNRESOLVED_NESTED4: Begin completions, 3 items + // UNRESOLVED_NESTED4: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#]; name=South + // UNRESOLVED_NESTED4: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#]; name=North + // UNRESOLVED_NESTED4: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(:) +} diff --git a/test/IRGen/moveonly_split_module_source_deinit.swift b/test/IRGen/moveonly_split_module_source_deinit.swift index e98cd3e390533..7b45a3cd765de 100644 --- a/test/IRGen/moveonly_split_module_source_deinit.swift +++ b/test/IRGen/moveonly_split_module_source_deinit.swift @@ -5,7 +5,7 @@ // Make sure we call the deinit through the value witness table in the other module. -// REFERRING_MODULE-LABEL: define {{.*}}swiftcc void @"$s6serverAAV4mainyyKFZ"(%swift.refcounted* swiftself %0, %swift.error** noalias nocapture swifterror dereferenceable(8) %1) {{.*}}{ +// REFERRING_MODULE-LABEL: define {{.*}}swiftcc void @"$s6serverAAV4mainyyKFZ"(%swift.refcounted* swiftself %0, %swift.error** noalias nocapture swifterror dereferenceable({{4|8}}) %1) {{.*}}{ // REFERRING_MODULE: [[SERVER:%.*]] = alloca %T6server8MoveOnlyV // REFERRING_MODULE: [[VALUE_WITNESS_TABLE:%.*]] = getelementptr inbounds i8*, i8** %"$s6server8MoveOnlyVN.valueWitnesses" // REFERRING_MODULE: [[VALUE_WITNESS:%.*]] = load i8*, i8** [[VALUE_WITNESS_TABLE]] diff --git a/test/IRGen/pack_metadata_marker_inserter.sil b/test/IRGen/pack_metadata_marker_inserter.sil index 3c5d198336cd2..28dab57181efa 100644 --- a/test/IRGen/pack_metadata_marker_inserter.sil +++ b/test/IRGen/pack_metadata_marker_inserter.sil @@ -82,7 +82,7 @@ entry: // CHECK-LLVM-SAME: [[INT]] [[SHAPE:%[^,]+]], // CHECK-LLVM-SAME: %swift.type** [[PACK:%[^,]+]]) {{.*}} { // CHECK-LLVM: call swiftcc void @takeTypePack( -// CHECK-LLVM-SAME: i64 [[SHAPE]], +// CHECK-LLVM-SAME: [[INT]] [[SHAPE]], // CHECK-LLVM-SAME: %swift.type** [[PACK]]) // CHECK-LLVM: } sil @forward_type_pack : $() -> () { @@ -105,7 +105,6 @@ sil @forward_type_pack : $() -> () { // CHECK-LLVM: [[PACK_ADDR:%[^,]+]] = alloca [3 x %swift.type*] // CHECK-LLVM: [[LIFETIME_START_CAST:%[^,]+]] = bitcast [3 x %swift.type*]* [[PACK_ADDR]] to i8* // CHECK-LLVM: call void @llvm.lifetime.start.p0i8( -// CHECK-LLVM-SAME: [[INT]] 24, // CHECK-LLVM-SAME: i8* [[LIFETIME_START_CAST]]) // CHECK-LLVM: [[LIFETIME_END_CAST:%[^,]+]] = bitcast [3 x %swift.type*]* [[PACK_ADDR]] to i8* // CHECK-LLVM: call void @llvm.lifetime.end.p0i8( diff --git a/test/Index/index_macros.swift b/test/Index/index_macros.swift index 08644b8f79307..f20480556e50f 100644 --- a/test/Index/index_macros.swift +++ b/test/Index/index_macros.swift @@ -1,27 +1,284 @@ +// REQUIRES: swift_swift_parser + // RUN: %empty-directory(%t) -// RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %s | %FileCheck %s -// REQUIRES: OS=macosx +// RUN: split-file --leading-lines %s %t + +// Check that we index code expanded from macros, especially nested references +// (ie. calls within an added function). + +// Create the plugin with various macros for testing +// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(IndexMacros) -module-name=IndexMacros %t/IndexMacros.swift -g -no-toolchain-stdlib-rpath + +// Check indexed symbols +// RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %t/IndexTest.swift -load-plugin-library %t/%target-library-name(IndexMacros) -parse-as-library 2>&1 | tee %t/test.idx | %FileCheck %s + +//--- IndexTest.swift +@freestanding(expression) +macro freestandingExpr() = #externalMacro(module: "IndexMacros", type: "FreestandingExprMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | freestandingExpr() | [[EXPR_USR:.*]] | Def + +@freestanding(declaration, names: named(TestFree)) +macro freestandingDecl() = #externalMacro(module: "IndexMacros", type: "FreestandingDeclMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | freestandingDecl() | [[DECL_USR:.*]] | Def + +@attached(accessor) +macro Accessor() = #externalMacro(module: "IndexMacros", type: "SomeAccessorMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | Accessor() | [[ACCESSOR_USR:.*]] | Def + +@attached(conformance) +macro Conformance() = #externalMacro(module: "IndexMacros", type: "SomeConformanceMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | Conformance() | [[CONFORMANCE_USR:.*]] | Def + +@attached(member, names: named(memberFunc)) +macro Member() = #externalMacro(module: "IndexMacros", type: "SomeMemberMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | Member() | [[MEMBER_USR:.*]] | Def + +@attached(memberAttribute) +macro MemberAttribute() = #externalMacro(module: "IndexMacros", type: "SomeMemberAttributeMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | MemberAttribute() | [[MEMBER_ATTRIBUTE_USR:.*]] | Def + +@attached(peer, names: named(TestPeer)) +macro Peer() = #externalMacro(module: "IndexMacros", type: "SomePeerMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | Peer() | [[PEER_USR:.*]] | Def + +@attached(peer, names: named(peerMember)) +macro PeerMember() = #externalMacro(module: "IndexMacros", type: "SomePeerMemberMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | PeerMember() | [[PEER_MEMBER_USR:.*]] | Def + +protocol TestProto {} +// CHECK: [[@LINE-1]]:10 | protocol/Swift | TestProto | [[PROTO_USR:.*]] | Def + +func accessorLog() {} +// CHECK: [[@LINE-1]]:6 | function/Swift | accessorLog() | [[ACC_LOG_USR:.*]] | Def +func exprLog() {} +// CHECK: [[@LINE-1]]:6 | function/Swift | exprLog() | [[EXPR_LOG_USR:.*]] | Def +func freeLog() {} +// CHECK: [[@LINE-1]]:6 | function/Swift | freeLog() | [[FREE_LOG_USR:.*]] | Def +func memberLog() {} +// CHECK: [[@LINE-1]]:6 | function/Swift | memberLog() | [[MEMBER_LOG_USR:.*]] | Def +func peerLog() {} +// CHECK: [[@LINE-1]]:6 | function/Swift | peerLog() | [[PEER_LOG_USR:.*]] | Def + +// CHECK: [[@LINE+2]]:8 | struct/Swift | AddOne | [[ADD_ONE_USR:.*]] | Def +@propertyWrapper +struct AddOne { + var value: Int = 1 + var wrappedValue: Int { + get { value } + set { value = newValue + 1 } + } + init(wrappedValue: Int) { + self.wrappedValue = wrappedValue + } +} + +// Creates a `TestFree` struct with `freeFunc` calling `freeLog` +#freestandingDecl +// CHECK: [[@LINE-1]]:2 | macro/Swift | freestandingDecl() | [[DECL_USR]] | Ref +// CHECK: [[@LINE-2]]:1 | struct/Swift | TestFree | [[FREE_STRUCT_USR:.*]] | Def,Impl +// CHECK: [[@LINE-3]]:1 | instance-method/Swift | freeFunc() | [[FREE_FUNC_USR:.*]] | Def,Impl,RelChild +// CHECK-NEXT: RelChild | struct/Swift | TestFree | [[FREE_STRUCT_USR]] +// CHECK: [[@LINE-5]]:1 | function/Swift | freeLog() | [[FREE_LOG_USR]] | Ref,Call,Impl,RelCall,RelCont +// CHECK-NEXT: RelCall,RelCont | instance-method/Swift | freeFunc() | [[FREE_FUNC_USR]] + +// CHECK: [[@LINE+4]]:40 | macro/Swift | Peer() | [[PEER_USR]] | Ref +// CHECK: [[@LINE+3]]:23 | macro/Swift | MemberAttribute() | [[MEMBER_ATTRIBUTE_USR]] | Ref +// CHECK: [[@LINE+2]]:15 | macro/Swift | Member() | [[MEMBER_USR]] | Ref +// CHECK: [[@LINE+1]]:2 | macro/Swift | Conformance() | [[CONFORMANCE_USR]] | Ref +@Conformance @Member @MemberAttribute @Peer +struct TestAttached { + var attachedMember: Int + + @Accessor + var attachedMemberAccessors: Int +} +// `MemberAttribute` adds `@AddOne` to attachedMember +// CHECK: [[@LINE-8]]:22 | struct/Swift | AddOne | [[ADD_ONE_USR]] | Ref,Impl,RelCont +// CHECK-NEXT: RelCont | instance-property/Swift | attachedMember + +// `Accessor` adds getters/setters to `attachedMemberAccessors` that both call `accessorLog` +// CHECK: [[@LINE-8]]:3 | function/Swift | accessorLog() | [[ACC_LOG_USR]] | Ref,Call,Impl,RelCall,RelCont +// CHECK-NEXT: RelCall,RelCont | instance-method/acc-get/Swift | getter:attachedMemberAccessors +// `Member` adds a new member `memberFunc` that calls `memberLog` +// CHECK: [[@LINE-16]]:14 | instance-method/Swift | memberFunc() | [[MEMBER_FUNC_USR:.*]] | Def,Impl,RelChild +// CHECK: [[@LINE-17]]:14 | function/Swift | memberLog() | [[MEMBER_LOG_USR]] | Ref,Call,Impl,RelCall,RelCont +// CHECK-NEXT: RelCall,RelCont | instance-method/Swift | memberFunc() | [[MEMBER_FUNC_USR]] -@freestanding(expression) macro myLine() -> Int = #externalMacro(module: "MacroDefinition", type: "LineMacro") -@freestanding(expression) macro myFilename() -> T = #externalMacro(module: "MacroDefinition", type: "FileMacro") -@freestanding(expression) macro myStringify(_: T) -> (T, String) = #externalMacro(module: "MacroDefinition", type: "StringifyMacro") +// `Peer` adds a new inner type `TestPeer` that contains `peerFunc` with a call to `peerLog` +// CHECK: [[@LINE-21]]:39 | struct/Swift | TestPeer | [[PEER_STRUCT_USR:.*]] | Def,Impl +// CHECK: [[@LINE-22]]:39 | instance-method/Swift | peerFunc() | [[PEER_FUNC_USR:.*]] | Def,Impl,RelChild +// CHECK-NEXT: RelChild | struct/Swift | TestPeer | [[PEER_STRUCT_USR]] +// CHECK: [[@LINE-24]]:39 | function/Swift | peerLog() | [[PEER_LOG_USR]] | Ref,Call,Impl,RelCall,RelCont +// CHECK-NEXT: RelCall,RelCont | instance-method/Swift | peerFunc() | [[PEER_FUNC_USR]] -func test(x: Int) { - _ = #myLine - let _: String = #myFilename - _ = #myStringify(x + x) +// `Conformance` adds `TestProto` as a conformance on an extension of `TestAttached` +// CHECK: [[@LINE-28]]:1 | extension/ext-struct/Swift | TestAttached | {{.*}} | Def,Impl +// CHECK: [[@LINE-29]]:1 | protocol/Swift | TestProto | [[PROTO_USR]] | Ref,Impl,RelBase +// CHECK-NEXT: RelBase | extension/ext-struct/Swift | TestAttached + +// CHECK: [[@LINE+1]]:8 | struct/Swift | Outer | [[OUTER_USR:.*]] | Def +struct Outer { + // CHECK: [[@LINE+1]]:4 | macro/Swift | PeerMember() | [[PEER_MEMBER_USR]] | Ref + @PeerMember + var anyMember: Int + // `PeerMember` adds a new `peerMember` + // CHECK: [[@LINE-3]]:3 | instance-property/Swift | peerMember | {{.*}} | Def,Impl,RelChild + // CHECK-NEXT: RelChild | struct/Swift | Outer | [[OUTER_USR]] + + // CHECK: [[@LINE+2]]:17 | macro/Swift | Member() | [[MEMBER_USR]] | Ref + // CHECK: [[@LINE+1]]:4 | macro/Swift | Conformance() | [[CONFORMANCE_USR]] | Ref + @Conformance @Member + struct TestInner {} +} +// `Member` adds a new member `memberFunc` that calls `memberLog` +// CHECK: [[@LINE-4]]:16 | instance-method/Swift | memberFunc() | [[INNER_FUNC_USR:.*]] | Def,Impl +// CHECK-NEXT: RelChild | struct/Swift | TestInner +// CHECK: [[@LINE-6]]:16 | function/Swift | memberLog() | [[MEMBER_LOG_USR]] | Ref,Call,Impl,RelCall,RelCont +// CHECK-NEXT: RelCall,RelCont | instance-method/Swift | memberFunc() | [[INNER_FUNC_USR]] + +// `Conformance` adds `TestProto` as a conformance on an extension of `TestInner` +// CHECK: [[@LINE-10]]:3 | extension/ext-struct/Swift | TestInner | {{.*}} | Def,Impl +// CHECK: [[@LINE-11]]:3 | protocol/Swift | TestProto | [[PROTO_USR]] | Ref,Impl,RelBase +// CHECK-NEXT: RelBase | extension/ext-struct/Swift | TestInner + +func testExpr() { + #freestandingExpr + // CHECK: [[@LINE-1]]:3 | function/Swift | exprLog() | [[EXPR_LOG_USR]] | Ref,Call,Impl,RelCall,RelCont + // CHECK-NEXT: RelCall,RelCont | function/Swift | testExpr() } -// CHECK: 6:33 | macro/Swift | myLine() | s:14swift_ide_test6myLineSiycfm | Def | rel: 0 -// CHECK: 6:45 | struct/Swift | Int | s:Si | Ref | rel: 0 -// CHECK: 7:33 | macro/Swift | myFilename() | s:14swift_ide_test10myFilenamexycs26ExpressibleByStringLiteralRzlufm | Def | rel: 0 -// CHECK: 7:47 | protocol/Swift | ExpressibleByStringLiteral | s:s26ExpressibleByStringLiteralP | Ref | rel: 0 -// CHECK: 8:33 | macro/Swift | myStringify(_:) | s:14swift_ide_test11myStringifyyx_SStxclufm | Def | rel: 0 +//--- IndexMacros.swift +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +public struct FreestandingExprMacro: ExpressionMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + return "exprLog()" + } +} + +public struct FreestandingDeclMacro: DeclarationMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + return [""" + struct TestFree { + func freeFunc() { + freeLog() + } + } + """] + } +} + +public struct SomeAccessorMacro: AccessorMacro { + public static func expansion( + of node: AttributeSyntax, + providingAccessorsOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [AccessorDeclSyntax] { + return [ + """ + get { + accessorLog() + return 1 + } + """, + """ + set { + accessorLog() + } + """, + ] + } +} -// CHECK: 11:8 | macro/Swift | myLine() | s:14swift_ide_test6myLineSiycfm | Ref,RelCont | rel: 1 -// CHECK: 12:20 | macro/Swift | myFilename() | s:14swift_ide_test10myFilenamexycs26ExpressibleByStringLiteralRzlufm | Ref,RelCont | rel: 1 -// CHECK: 13:8 | macro/Swift | myStringify(_:) | s:14swift_ide_test11myStringifyyx_SStxclufm | Ref,RelCont | rel: 1 -// CHECK: 13:20 | param/Swift | x | s:14swift_ide_test0C01xySi_tFACL_Sivp | Ref,Read,RelCont | rel: 1 -// CHECK: 13:22 | static-method/infix-operator/Swift | +(_:_:) | s:Si1poiyS2i_SitFZ | Ref,Call,RelCall,RelCont | rel: 1 -// CHECK: 13:24 | param/Swift | x | s:14swift_ide_test0C01xySi_tFACL_Sivp | Ref,Read,RelCont | rel: 1 +public struct SomeConformanceMacro: ConformanceMacro { + public static func expansion( + of node: AttributeSyntax, + providingConformancesOf decl: some DeclGroupSyntax, + in context: some MacroExpansionContext + ) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] { + let protocolName: TypeSyntax = "TestProto" + return [(protocolName, nil)] + } +} + +public struct SomeMemberMacro: MemberMacro { + public static func expansion( + of node: AttributeSyntax, + providingMembersOf declaration: some DeclGroupSyntax, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + let newFunc: DeclSyntax = + """ + func memberFunc() { + memberLog() + } + """ + return [ + newFunc, + ] + } +} + +public struct SomeMemberAttributeMacro: MemberAttributeMacro { + public static func expansion( + of node: AttributeSyntax, + attachedTo parent: some DeclGroupSyntax, + providingAttributesFor member: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [AttributeSyntax] { + guard let varDecl = member.as(VariableDeclSyntax.self), + let binding = varDecl.bindings.first, + let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier.text, + identifier == "attachedMember" + else { + return [] + } + + return [AttributeSyntax( + attributeName: SimpleTypeIdentifierSyntax( + name: .identifier("AddOne") + ) + )] + } +} + +public struct SomePeerMacro: PeerMacro { + public static func expansion( + of node: AttributeSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + return [ + """ + struct TestPeer { + func peerFunc() { + peerLog() + } + } + """ + ] + } +} + +public struct SomePeerMemberMacro: PeerMacro { + public static func expansion( + of node: AttributeSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + return [ + """ + var peerMember: Int + """ + ] + } +} diff --git a/test/Interop/Cxx/enum/Inputs/c-enums-NS_OPTIONS.h b/test/Interop/Cxx/enum/Inputs/c-enums-NS_OPTIONS.h index 4400214581ae1..55c80c929b1e8 100644 --- a/test/Interop/Cxx/enum/Inputs/c-enums-NS_OPTIONS.h +++ b/test/Interop/Cxx/enum/Inputs/c-enums-NS_OPTIONS.h @@ -81,6 +81,10 @@ typedef NS_OPTIONS(NSUInteger, Bar) { typedef NS_OPTIONS(NSUInteger, Baz) { Baz1, Baz2 }; +struct HasNSOptionField { + Bar bar; +}; + Baz CFunctionReturningNSOption(); void CFunctionTakingNSOption(Baz); diff --git a/test/Interop/Cxx/enum/c-enums-NS_OPTIONS.swift b/test/Interop/Cxx/enum/c-enums-NS_OPTIONS.swift index 6b7efc355ceef..d454612f86143 100644 --- a/test/Interop/Cxx/enum/c-enums-NS_OPTIONS.swift +++ b/test/Interop/Cxx/enum/c-enums-NS_OPTIONS.swift @@ -20,3 +20,8 @@ import CenumsNSOptions // CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "insertionIndex") // CHECK-NEXT: static var InsertionIndex: NSBinarySearchingOptions { get } // CHECK-NEXT: } + +// CHECK: struct Bar : OptionSet, @unchecked Sendable +// CHECK: struct HasNSOptionField { +// CHECK: var bar: Bar +// CHECK: } diff --git a/test/Interop/Cxx/enum/ns-option-as-field.swift b/test/Interop/Cxx/enum/ns-option-as-field.swift new file mode 100644 index 0000000000000..aa1a66d2fad56 --- /dev/null +++ b/test/Interop/Cxx/enum/ns-option-as-field.swift @@ -0,0 +1,23 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-experimental-cxx-interop) + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import CenumsNSOptions +import StdlibUnittest + +var FieldTestSuite = TestSuite("NS_Option as field") + +struct HasNSOptionMember { + var member: Bar +} + +FieldTestSuite.test("NSOption as field") { + var parent = HasNSOptionMember(member: [.SwiftOptionOneApiNotes, .SwiftOptionTwoApiNotes]) + expectEqual(parent.member, [.SwiftOptionOneApiNotes, .SwiftOptionTwoApiNotes]) + + parent.member = [.SwiftOptionOneApiNotes] + expectNotEqual(parent.member, [.SwiftOptionOneApiNotes, .SwiftOptionTwoApiNotes]) +} + +runAllTests() diff --git a/test/Interop/Cxx/exceptions/trap-on-exception-irgen-itanium.swift b/test/Interop/Cxx/exceptions/trap-on-exception-irgen-itanium.swift index f7153f780ea34..19a2f24d8b26d 100644 --- a/test/Interop/Cxx/exceptions/trap-on-exception-irgen-itanium.swift +++ b/test/Interop/Cxx/exceptions/trap-on-exception-irgen-itanium.swift @@ -131,6 +131,33 @@ public: inline ClassWithNoThrowingConstructor() noexcept {} }; +struct StructWithDefaultConstructor { + StructWithDefaultConstructor() = default; + + int m = 0; +}; + + +struct NonTrivial { + ~NonTrivial() {} +}; + +struct StructWithDefaultCopyConstructor { + StructWithDefaultCopyConstructor() noexcept {} + StructWithDefaultCopyConstructor(const StructWithDefaultCopyConstructor &) = default; + + int m = 0; + NonTrivial _nonTrivialPoison; +}; + +struct StructWithDefaultDestructor { + StructWithDefaultDestructor() noexcept {} + ~StructWithDefaultDestructor() = default; + + int m = 0; + NonTrivial _nonTrivialPoison; +}; + //--- test.swift import CxxModule @@ -227,6 +254,22 @@ func testClassWithNoThrowingConstructor() -> CInt { return obj.m } +func testStructWithDefaultConstructor() -> StructWithDefaultConstructor { + return StructWithDefaultConstructor() +} + +func testStructWithDefaultCopyConstructor() -> CInt { + var s = StructWithDefaultCopyConstructor() + let copy = s + return s.m +} + +func testStructWithDefaultDestructor() -> CInt { + let s = StructWithDefaultDestructor() + let result = s.m + return result +} + let _ = testFreeFunctionNoThrowOnly() let _ = testFreeFunctionCalls() let _ = testMethodCalls() @@ -241,6 +284,9 @@ let _ = testClassWithCopyConstructor() let _ = testClassWithThrowingCopyConstructor() let _ = testClassWithThrowingConstructor() let _ = testClassWithNoThrowingConstructor() +let _ = testStructWithDefaultConstructor() +let _ = testStructWithDefaultCopyConstructor() +let _ = testStructWithDefaultDestructor() // CHECK: define {{.*}} @"$s4test0A23FreeFunctionNoThrowOnlys5Int32VyF"() #[[#SWIFTMETA:]] { // CHECK-NEXT: : @@ -393,6 +439,18 @@ let _ = testClassWithNoThrowingConstructor() // CHECK-NOT: invoke // CHECK: } +// CHECK: define {{.*}} @"$s4test0A28StructWithDefaultConstructorSo0bcdE0VyF"() #[[#SWIFTMETA]] { +// CHECK-NOT: invoke +// CHECK: } + +// CHECK: define {{.*}} @"$s4test0A32StructWithDefaultCopyConstructors5Int32VyF"() #[[#SWIFTMETA]] { +// CHECK-NOT: invoke +// CHECK: } + +// CHECK: define {{.*}} @"$s4test0A27StructWithDefaultDestructors5Int32VyF"() #[[#SWIFTMETA]] { +// CHECK-NOT: invoke +// CHECK: } + // CHECK: i32 @__gxx_personality_v0(...) // CHECK: attributes #[[#SWIFTMETA]] = { diff --git a/test/Interop/Cxx/stdlib/overlay/custom-convertible-to-collection.swift b/test/Interop/Cxx/stdlib/overlay/custom-convertible-to-collection.swift index bc77cacf78cab..7a3b5db9c8d01 100644 --- a/test/Interop/Cxx/stdlib/overlay/custom-convertible-to-collection.swift +++ b/test/Interop/Cxx/stdlib/overlay/custom-convertible-to-collection.swift @@ -20,13 +20,9 @@ CxxSequenceTestSuite.test("SimpleCopyAwareSequence to Swift.Array") { copiesCount = 0 let seq = SimpleCopyAwareSequence() - - let seqCopy = seq - expectEqual(1, copiesCount) // make sure our copy tracking mechanism works - let array = Array(seq) - expectEqual(1, copiesCount) // make sure we don't copy the C++ sequence value unnecessarily + expectEqual(0, copiesCount) // make sure we don't copy the C++ sequence value unnecessarily } #endif diff --git a/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift b/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift index a55c72bb170c5..6cac06d52f576 100644 --- a/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift +++ b/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift @@ -72,6 +72,9 @@ // CHECK: } // CHECK-NEXT: SWIFT_INLINE_THUNK String(const String &other) noexcept { // CHECK: } +// CHECK-NEXT: SWIFT_INLINE_THUNK String &operator =(const String &other) noexcept { +// CHECK: } +// CHECK-NEXT: SWIFT_INLINE_THUNK String &operator =(String &&other) = delete; // CHECK-NEXT: SWIFT_INLINE_PRIVATE_HELPER String(String &&) noexcept { // CHECK: } // CHECK-NEXT: static SWIFT_INLINE_THUNK String init() SWIFT_SYMBOL({{.*}}); diff --git a/test/Interop/SwiftToCxx/structs/struct-move-semantics-in-cxx.cpp b/test/Interop/SwiftToCxx/structs/struct-move-semantics-in-cxx.cpp index febfc3c7cb57d..1ebdaa7d7c600 100644 --- a/test/Interop/SwiftToCxx/structs/struct-move-semantics-in-cxx.cpp +++ b/test/Interop/SwiftToCxx/structs/struct-move-semantics-in-cxx.cpp @@ -9,6 +9,10 @@ // LINK: fatalError_Cxx_move_of_Swift_value_type_not_supported_yet +// Compile should fail by default when move assignment is attempted in C++: + +// RUN: not %target-interop-build-clangxx -c %s -I %t -o %t/swift-structs-execution.o -DMOVE_ASSIGN 2>&1 | %FileCheck --check-prefix=MOVEASSIGN %s + // Fallback to abort at runtime: // RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-structs-execution.o -DLINKS @@ -34,7 +38,13 @@ int main() { using namespace Structs; auto x = returnNewStructSeveralI64(42); +#ifdef MOVE_ASSIGN + auto y = returnNewStructSeveralI64(24); + x = std::move(y); +// MOVEASSIGN: deleted operator '=' +#else StructSeveralI64 x2 = std::move(x); +#endif return 0; } diff --git a/test/Interop/SwiftToCxx/structs/struct-with-refcounted-member-execution.cpp b/test/Interop/SwiftToCxx/structs/struct-with-refcounted-member-execution.cpp index 5b32f0e325995..b66a59465fbe0 100644 --- a/test/Interop/SwiftToCxx/structs/struct-with-refcounted-member-execution.cpp +++ b/test/Interop/SwiftToCxx/structs/struct-with-refcounted-member-execution.cpp @@ -44,5 +44,19 @@ int main() { // CHECK-NEXT: destroy RefCountedClass // CHECK-NEXT: destroy RefCountedClass // CHECK-NEXT: breakpoint 3 + + { + StructWithRefcountedMember value = returnNewStructWithRefcountedMember(); + StructWithRefcountedMember value2 = returnNewStructWithRefcountedMember(); + value = value2; + printBreak(4); + } + printBreak(5); +// CHECK-NEXT: create RefCountedClass +// CHECK-NEXT: create RefCountedClass +// CHECK-NEXT: destroy RefCountedClass +// CHECK-NEXT: breakpoint 4 +// CHECK-NEXT: destroy RefCountedClass +// CHECK-NEXT: breakpoint 5 return 0; } diff --git a/test/Interop/SwiftToCxx/structs/struct-with-refcounted-member.swift b/test/Interop/SwiftToCxx/structs/struct-with-refcounted-member.swift index c2564d24f6892..9a75b17b73e33 100644 --- a/test/Interop/SwiftToCxx/structs/struct-with-refcounted-member.swift +++ b/test/Interop/SwiftToCxx/structs/struct-with-refcounted-member.swift @@ -47,6 +47,18 @@ public func printBreak(_ x: Int) { // CHECK-NEXT: #endif // CHECK-NEXT: vwTable->initializeWithCopy(_getOpaquePointer(), const_cast(other._getOpaquePointer()), metadata._0); // CHECK-NEXT: } +// CHECK-NEXT: SWIFT_INLINE_THUNK StructWithRefcountedMember &operator =(const StructWithRefcountedMember &other) noexcept { +// CHECK-NEXT: auto metadata = _impl::$s7Structs26StructWithRefcountedMemberVMa(0); +// CHECK-NEXT: auto *vwTableAddr = reinterpret_cast(metadata._0) - 1; +// CHECK-NEXT: #ifdef __arm64e__ +// CHECK-NEXT: auto *vwTable = reinterpret_cast(ptrauth_auth_data(reinterpret_cast(*vwTableAddr), ptrauth_key_process_independent_data, ptrauth_blend_discriminator(vwTableAddr, 11839))); +// CHECK-NEXT: #else +// CHECK-NEXT: auto *vwTable = *vwTableAddr; +// CHECK-NEXT: #endif +// CHECK-NEXT: vwTable->assignWithCopy(_getOpaquePointer(), const_cast(other._getOpaquePointer()), metadata._0); +// CHECK-NEXT: return *this; +// CHECK-NEXT: } +// CHECK-NEXT: SWIFT_INLINE_THUNK StructWithRefcountedMember &operator =(StructWithRefcountedMember &&other) = delete; // CHECK-NEXT: SWIFT_INLINE_PRIVATE_HELPER StructWithRefcountedMember(StructWithRefcountedMember &&) noexcept { // CHECK-NEXT: swift::_impl::_fatalError_Cxx_move_of_Swift_value_type_not_supported_yet(); // CHECK-NEXT: swift::_impl::_swift_stdlib_reportFatalError("swift", 5, "C++ does not support moving a Swift value yet", 45, 0); diff --git a/test/Interop/SwiftToCxx/structs/swift-struct-in-cxx.swift b/test/Interop/SwiftToCxx/structs/swift-struct-in-cxx.swift index b23a67a8d78a2..5bb67a1b4e06e 100644 --- a/test/Interop/SwiftToCxx/structs/swift-struct-in-cxx.swift +++ b/test/Interop/SwiftToCxx/structs/swift-struct-in-cxx.swift @@ -38,6 +38,18 @@ // CHECK: } // CHECK-NEXT: SWIFT_INLINE_THUNK StructWithIntField(const StructWithIntField &other) noexcept { // CHECK: } +// CHECK: SWIFT_INLINE_THUNK StructWithIntField &operator =(const StructWithIntField &other) noexcept { +// CHECK-NEXT: auto metadata = _impl::$s7Structs18StructWithIntFieldVMa(0); +// CHECK-NEXT: auto *vwTableAddr = reinterpret_cast(metadata._0) - 1; +// CHECK-NEXT: #ifdef __arm64e__ +// CHECK-NEXT: auto *vwTable = reinterpret_cast(ptrauth_auth_data(reinterpret_cast(*vwTableAddr), ptrauth_key_process_independent_data, ptrauth_blend_discriminator(vwTableAddr, 11839))); +// CHECK-NEXT: #else +// CHECK-NEXT: auto *vwTable = *vwTableAddr; +// CHECK-NEXT: #endif +// CHECK-NEXT: vwTable->assignWithCopy(_getOpaquePointer(), const_cast(other._getOpaquePointer()), metadata._0); +// CHECK-NEXT: return *this; +// CHECK-NEXT: } +// CHECK-NEXT: SWIFT_INLINE_THUNK StructWithIntField &operator =(StructWithIntField &&other) = delete; // CHECK-NEXT: noreturn]] SWIFT_INLINE_PRIVATE_HELPER StructWithIntField(StructWithIntField &&) noexcept { // CHECK-NEXT: swift::_impl::_fatalError_Cxx_move_of_Swift_value_type_not_supported_yet(); // CHECK-NEXT: swift::_impl::_swift_stdlib_reportFatalError("swift", 5, "C++ does not support moving a Swift value yet", 45, 0); diff --git a/test/Interpreter/init_accessors.swift b/test/Interpreter/init_accessors.swift new file mode 100644 index 0000000000000..69e5f52ea99fa --- /dev/null +++ b/test/Interpreter/init_accessors.swift @@ -0,0 +1,334 @@ +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-feature -Xfrontend InitAccessors) | %FileCheck %s +// RUN: %target-run-simple-swift(-O -Xfrontend -enable-experimental-feature -Xfrontend InitAccessors) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: asserts + +struct TestInit { + var x: Int + var y: Int + var full: (Int, Int) + + var point: (Int, Int) { + init(initialValue) initializes(y, full) accesses(x) { + self.y = initialValue.1 + self.full = (self.x, self.y) + } + + get { full } + set { full = newValue } + } + + init(x: Int, y: Int) { + self.x = x + self.point = (x, y) + } +} + +do { + let test = TestInit(x: 0, y: -1) + print("test-init: \(test.point)") + // CHECK: test-init: (0, -1) +} + +struct TestSetter { + var x: Int + var y: Int + + var point: (Int, Int) { + init(initialValue) accesses(x, y) { + } + + get { (x, y) } + set { } + } + + init(x: Int, y: Int) { + self.x = x + self.y = y + self.point = (x, y) + } +} + +do { + let test = TestSetter(x: 0, y: -2) + print("test-setter: \(test.point)") + // CHECK: test-setter: (0, -2) +} + +struct TestInitThenSetter { + var x: Int + var y: Int + + var point: (Int, Int) { + init(initialValue) initializes(x, y) { + self.x = initialValue.0 + self.y = initialValue.1 + } + + get { (x, y) } + + set { + x = newValue.0 + y = newValue.1 + } + } + + init(x: Int, y: Int) { + self.point = (x, y) + + if x == 1 { + self.point = (0, 0) + } + } +} + +do { + let test = TestInitThenSetter(x: 1, y: 2) + print("test-init-then-setter: \(test.point)") + // CHECK: test-init-then-setter: (0, 0) +} + +struct TestPartialInt { + var x: Int + var y: Int + + var pointX: Int { + init(newValue) initializes(x) { + self.x = newValue + } + + get { x } + set { self.x = newValue } + } + + var pointY: Int { + init(newValue) initializes(y) { + self.y = newValue + } + + get { y } + set { self.y = newValue } + } + + init(x: Int, y: Int) { + // Init + self.pointX = x + // Init + self.pointY = y + + // Setter + self.pointX = 1 + // Setter + self.pointY = 2 + } +} + +do { + let test = TestPartialInt(x: 0, y: -1) + print("test-partial-init: (\(test.pointX), \(test.pointY))") + // CHECK: test-partial-init: (1, 2) +} + +struct TestNoInitAndInit { + var x: Int + var y: Int + + var pointX: Int { + init(initalValue) accesses(x) { + } + + get { x } + set { } + } + + var pointY: Int { + init(initialValue) initializes(y) { + self.y = initialValue + } + + get { y } + set { } + } + + init(x: Int, y: Int) { + self.x = x + self.pointX = x + self.pointY = y + print("TestNoInitAndInit(x: \(self.x), y: \(self.y))") + } +} + +do { + _ = TestNoInitAndInit(x: 10, y: -10) + // CHECK: TestNoInitAndInit(x: 10, y: -10) +} + +class TestClass { + var x: Int + var y: (Int, [String]) + + var data: (Int, (Int, [String])) { + init(initialValue) initializes(x, y) { + x = initialValue.0 + y = initialValue.1 + } + + get { (x, y) } + set { + x = newValue.0 + y = newValue.1 + } + } + + init(x: Int, y: (Int, [String])) { + self.data = (x, y) + } +} + +do { + let test = TestClass(x: 20, y: (0, ["a", "b"])) + print("test-class: \(test.data)") + // CHECK: test-class: (20, (0, ["a", "b"])) +} + +struct TestGeneric { + var a: T + var b: T + var c: U + + var data: (T, T) { + init(initialValue) initializes(a, b) accesses(c) { + a = initialValue.0 + b = initialValue.1 + print("TestGeneric(c: \(c))") + } + + get { (a, b) } + set { } + } + + init(a: T, b: T, c: U) { + self.c = c + self.data = (a, b) + self.data = (b, a) + } +} + +do { + let test = TestGeneric(a: 42, b: 0, c: [42, "a"] as [Any]) + print("test-generic: data = \(test.data)") + // CHECK: TestGeneric(c: [42, "a"]) + // CHECK-NEXT: test-generic: data = (42, 0) +} + +func test_local_with_memberwise() { + class MyValue {} + + struct TestMemberwiseConcrete { + var a: Int + var b: String + + var pair: (Int, String) { + init(initialValue) initializes(a, b) { + a = initialValue.0 + b = initialValue.1 + } + + get { (a, b) } + set { } + } + + var c: [MyValue] + } + + let concrete = TestMemberwiseConcrete(pair: (0, "a"), c: []) + print(concrete) + + struct TestMemberwiseGeneric where C: RangeReplaceableCollection, C.Element == T { + var _a: T + var _b: String + var _c: C + + var a: T { + init(initialValue) initializes(_a) { + _a = initialValue + } + + get { _a } + set { } + } + + var pair: (String, C) { + init(initialValue) initializes(_b, _c) accesses(_a) { + _b = initialValue.0 + _c = initialValue.1 + _c.append(_a) + } + + get { (_b, _c) } + set { } + } + } + + let generic = TestMemberwiseGeneric(a: 1, pair: ("a", [0])) + print(generic) +} + +test_local_with_memberwise() +// CHECK: TestMemberwiseConcrete(a: 0, b: "a", c: []) +// CHECK-NEXT: TestMemberwiseGeneric>(_a: 1, _b: "a", _c: [0, 1]) + +func test_assignments() { + struct Test { + var _a: Int + var _b: Int + + var a: Int { + init(initialValue) initializes(_a) { + self._a = initialValue + print("a-init-accessor: \(self._a)") + } + get { _a } + set { _a = newValue + 1 } + } + + var pair: (Int, Int) { + init(initialValue) initializes(_a, _b) { + _a = initialValue.0 + _b = initialValue.1 + } + + get { (_a, _b) } + set { } + } + + init(a: Int) { + // init + self.a = a + // re-assignment + self.a = a + 1 + self._b = 42 + // set + self.a = a + 2 + } + + init(a: Int, b: Int) { + self.a = a + self.pair = (0, b) + } + } + + let test1 = Test(a: 0) + print("test-assignments-1: \(test1.pair)") + + let test2 = Test(a: 0, b: 2) + print("test-assignments-2: \(test2.pair)") +} + +test_assignments() +// CHECK: a-init-accessor: 0 +// CHECK-NEXT: a-init-accessor: 1 +// CHECK-NEXT: test-assignments-1: (3, 42) +// CHECK-NEXT: a-init-accessor: 0 +// CHECK-NEXT: test-assignments-2: (0, 2) diff --git a/test/Macros/Inputs/syntax_macro_definitions.swift b/test/Macros/Inputs/syntax_macro_definitions.swift index f95ca9f6ec2b9..7eaa3505105c4 100644 --- a/test/Macros/Inputs/syntax_macro_definitions.swift +++ b/test/Macros/Inputs/syntax_macro_definitions.swift @@ -299,6 +299,25 @@ public struct DefineDeclsWithKnownNamesMacro: DeclarationMacro { } } +public struct VarDeclMacro: CodeItemMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) throws -> [CodeBlockItemSyntax] { + let name = context.makeUniqueName("fromMacro") + return [ + "let \(name) = 23", + "use(\(name))", + """ + if true { + let \(name) = "string" + use(\(name)) + } + """ + ] + } +} + public struct WarningMacro: ExpressionMacro { public static func expansion( of macro: some FreestandingMacroExpansionSyntax, @@ -1430,23 +1449,22 @@ public struct SimpleCodeItemMacro: CodeItemMacro { ) throws -> [CodeBlockItemSyntax] { [ .init(item: .decl(""" - struct \(context.makeUniqueName("foo")) { var x: Int } """)), .init(item: .stmt(""" - if true { print("from stmt") usedInExpandedStmt() } + """)), + .init(item: .stmt(""" if false { print("impossible") } """)), .init(item: .expr(""" - print("from expr") """)), ] diff --git a/test/Macros/macro_attribute_expansiondecl.swift b/test/Macros/macro_attribute_expansiondecl.swift index 00a6a536f545a..c26d8ac35473c 100644 --- a/test/Macros/macro_attribute_expansiondecl.swift +++ b/test/Macros/macro_attribute_expansiondecl.swift @@ -77,11 +77,24 @@ public struct FuncFromClosureMacro: DeclarationMacro { } } +public struct FuncFooBarNoAttrsMacro: DeclarationMacro { + public static var propagateFreestandingMacroAttributes: Bool { false } + public static var propagateFreestandingMacroModifiers: Bool { false } + + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + return ["func foo() -> Int { 1 }", "func bar() -> String { \"bar\" }"] + } +} + //--- test.swift @freestanding(declaration, names: named(globalFunc), named(globalVar)) macro globalDecls() = #externalMacro(module: "MacroDefinition", type: "GlobalFuncAndVarMacro") @freestanding(declaration, names: named(memberFunc), named(memberVar)) macro memberDecls() = #externalMacro(module: "MacroDefinition", type: "MemberFuncAndVarMacro") @freestanding(declaration, names: named(localFunc), named(localVar)) macro localDecls() = #externalMacro(module: "MacroDefinition", type: "LocalFuncAndVarMacro") +@freestanding(declaration, names: named(foo), named(bar)) macro funcFooBarNoAttrs() = #externalMacro(module: "MacroDefinition", type: "FuncFooBarNoAttrsMacro") @available(SwiftStdlib 9999, *) #globalDecls @@ -157,3 +170,12 @@ struct S2 { // expected-note 4 {{add @available attribute to enclosing struct}} } } } + +struct S3 { + @discardableResult private #funcFooBarNoAttrs() +} + +func testS3(value: S3) { + value.foo() // expected-warning {{result of call to 'foo()' is unused}} + value.bar() // expected-warning {{result of call to 'bar()' is unused}} +} diff --git a/test/Macros/macro_expand_codeitems.swift b/test/Macros/macro_expand_codeitems.swift index db27cc3397bc4..da2dd8e009428 100644 --- a/test/Macros/macro_expand_codeitems.swift +++ b/test/Macros/macro_expand_codeitems.swift @@ -33,3 +33,10 @@ func testFreestandingMacroExpansion() { #codeItems } testFreestandingMacroExpansion() + +@freestanding(codeItem) macro varDecl() = #externalMacro(module: "MacroDefinition", type: "VarDeclMacro") + +func testVarDecl() { + func use(_ t: T) {} + #varDecl() +} diff --git a/test/Macros/macros_diagnostics.swift b/test/Macros/macros_diagnostics.swift index 429ac87f77fc5..1955d9f6a76ec 100644 --- a/test/Macros/macros_diagnostics.swift +++ b/test/Macros/macros_diagnostics.swift @@ -46,7 +46,7 @@ internal struct X { } // expected-note{{type declared here}} // expected-warning@-1{{external macro implementation type}} struct ZZZ { - macro m5() -> Int = #externalMacro(module: "BuiltinMacros", type: "Blah") + macro m5() = #externalMacro(module: "BuiltinMacros", type: "Blah") // expected-error@-1{{macro 'm5()' can only be declared at file scope}} // expected-error@-2{{macro 'm5()' must declare its applicable roles}} // expected-warning@-3{{external macro implementation type}} @@ -200,3 +200,14 @@ struct SomeType { // expected-error@-2{{use of protocol 'Hashable' as a type must be written 'any Hashable'}} // expected-error@-3{{external macro implementation type}} } + + + +@freestanding(declaration) macro nonExpressionReturnsInt(_: T) -> Int = #externalMacro(module: "A", type: "B") +// expected-warning@-1{{external macro implementation type}} +// expected-error@-2{{only a freestanding expression macro can produce a result of type 'Int'}} +// expected-note@-3{{make this macro a freestanding expression macro}}{{1-1=@freestanding(expression)\n}} +// expected-note@-4{{remove the result type if the macro does not produce a value}}{{67-74=}} + +@freestanding(declaration) macro nonExpressionReturnsVoid(_: T) -> Void = #externalMacro(module: "A", type: "B") +// expected-warning@-1{{external macro implementation type}} diff --git a/test/Macros/option_set.swift b/test/Macros/option_set.swift index 0a13eb9f140cd..d3236c0eaa2b3 100644 --- a/test/Macros/option_set.swift +++ b/test/Macros/option_set.swift @@ -4,6 +4,11 @@ import Swift +@attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) +@attached(conformance) +public macro OptionSet() = + #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") + @OptionSet struct ShippingOptions { private enum Options: Int { diff --git a/test/Macros/top_level_freestanding.swift b/test/Macros/top_level_freestanding.swift index d0decd1d0c220..3fd9c07c1d6bd 100644 --- a/test/Macros/top_level_freestanding.swift +++ b/test/Macros/top_level_freestanding.swift @@ -50,7 +50,7 @@ func lookupGlobalFreestandingExpansion() { #anonymousTypes(public: true) { "hello" } -// CHECK-SIL: sil @$s9MacroUser03$s9A71User33_082AE7CFEFA6960C804A9FE7366EB5A0Ll14anonymousTypesfMf0_4namefMu_C5helloSSyF +// CHECK-SIL: sil @$s9MacroUser03$s9A70User33_082AE7CFEFA6960C804A9FE7366EB5A0Ll14anonymousTypesfMf_4namefMu_C5helloSSyF @main struct Main { diff --git a/test/ModuleInterface/moveonly_user.swift b/test/ModuleInterface/moveonly_user.swift index afcafa68ff0c6..5b60d7b87426b 100644 --- a/test/ModuleInterface/moveonly_user.swift +++ b/test/ModuleInterface/moveonly_user.swift @@ -5,7 +5,6 @@ // RUN: %target-swift-frontend -emit-sil -sil-verify-all -I %t %s > /dev/null // >> now again with library evolution; we expect the same result. -// FIXME: move checker doesn't like it when you specify library evolution // RUN: %target-swift-frontend -DSYNTHESIZE_ACCESSORS -enable-library-evolution -emit-module -o %t/Hello.swiftmodule %S/Inputs/moveonly_api.swift // RUN: %target-swift-frontend -emit-sil -sil-verify-all -I %t %s > /dev/null @@ -13,9 +12,6 @@ // FIXME: make this test work when we're not synthesizing the accessors -// rdar://106164128 -// XFAIL: * - import Hello func simpleTest() { diff --git a/test/Parse/matching_patterns.swift b/test/Parse/matching_patterns.swift index 932fb84e550c8..050916fac0574 100644 --- a/test/Parse/matching_patterns.swift +++ b/test/Parse/matching_patterns.swift @@ -122,7 +122,7 @@ if case let .Naught(value1, value2, value3) = n {} // expected-error{{pattern wi switch n { -case Foo.A: // expected-error{{enum case 'A' is not a member of type 'Voluntary'}} +case Foo.A: // expected-error{{pattern of type 'Foo' cannot match 'Voluntary'}} () case Voluntary.Naught, Voluntary.Naught(), // expected-error {{pattern with associated values does not match enum case 'Naught'}} @@ -307,6 +307,7 @@ do { while case let _ as [Derived] = arr {} // expected-warning@-1 {{'let' pattern has no effect; sub-pattern didn't bind any variables}} + // https://github.com/apple/swift/issues/61850 for case _ as [Derived] in [arr] {} if case is [Derived] = arr {} diff --git a/test/Parse/matching_patterns_reference_bindings.swift b/test/Parse/matching_patterns_reference_bindings.swift index 906bea74dfae0..fbf6b9840b1b2 100644 --- a/test/Parse/matching_patterns_reference_bindings.swift +++ b/test/Parse/matching_patterns_reference_bindings.swift @@ -140,7 +140,7 @@ if case inout .Naught(value1, value2, value3) = n {} // expected-error{{pattern switch n { -case Foo.A: // expected-error{{enum case 'A' is not a member of type 'Voluntary'}} +case Foo.A: // expected-error{{pattern of type 'Foo' cannot match 'Voluntary'}} () case Voluntary.Naught, Voluntary.Naught(), // expected-error {{pattern with associated values does not match enum case 'Naught'}} @@ -328,6 +328,7 @@ do { while case let _ as [Derived] = arr {} // expected-warning@-1 {{'let' pattern has no effect; sub-pattern didn't bind any variables}} + // https://github.com/apple/swift/issues/61850 for case _ as [Derived] in [arr] {} if case is [Derived] = arr {} diff --git a/test/SILGen/discard.swift b/test/SILGen/discard.swift index 2982767a79b7d..abb4ba5d8a5ad 100644 --- a/test/SILGen/discard.swift +++ b/test/SILGen/discard.swift @@ -1,11 +1,9 @@ // RUN: %target-swift-emit-silgen -enable-experimental-feature MoveOnlyEnumDeinits -module-name test %s | %FileCheck %s --enable-var-scope // RUN: %target-swift-emit-sil -enable-experimental-feature MoveOnlyEnumDeinits -module-name test -sil-verify-all %s | %FileCheck %s --check-prefix CHECK-SIL --enable-var-scope -// UNSUPPORTED: OS=windows-msvc -// -// On Windows, the struct_extract instructions are not fully cleaned up: -// CHECK-SIL-NOT: struct_extract -// It likely has to do with the lazy property. +// Swift sources are require to remove struct_extract so this check-not line passes: +// "CHECK-SIL-NOT: struct_extract" +// REQUIRES: swift_in_compiler func invokedDeinit() {} diff --git a/test/SILGen/moveonly_addressonly_computed_property.swift b/test/SILGen/moveonly_addressonly_computed_property.swift new file mode 100644 index 0000000000000..c84dd0a1876bc --- /dev/null +++ b/test/SILGen/moveonly_addressonly_computed_property.swift @@ -0,0 +1,80 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-sil -verify %s + +// rdar://109161396 + +public protocol P {} + +@_moveOnly +public struct M { + private var x: P + var other: CInt { 0 } + + var otherMoveOnly: M { + _read { + yield self + } + } + + @_silgen_name("no") + init() +} + +// CHECK-LABEL: sil [ossa] @${{.*}}4test3mut +// CHECK: [[CHECK:%.*]] = mark_must_check [consumable_and_assignable] %0 +// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[CHECK]] +// CHECK: [[RESULT:%.*]] = apply {{.*}}([[ACCESS]]) +// CHECK; end_access [[ACCESS]] +// CHECK: return [[RESULT]] +public func test(mut: inout M) -> CInt { + return mut.other +} + +// CHECK-LABEL: sil [ossa] @${{.*}}4test6borrow +// CHECK: [[CHECK:%.*]] = mark_must_check [no_consume_or_assign] %0 +// CHECK: [[RESULT:%.*]] = apply {{.*}}([[CHECK]]) +// CHECK: return [[RESULT]] +public func test(borrow: borrowing M) -> CInt { + return borrow.other +} + +// CHECK-LABEL: sil [ossa] @${{.*}}4test7consume +// CHECK: [[BOX:%.*]] = project_box +// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[BOX]] +// CHECK: [[CHECK:%.*]] = mark_must_check [no_consume_or_assign] [[ACCESS]] +// CHECK: [[RESULT:%.*]] = apply {{.*}}([[CHECK]]) +// CHECK; end_access [[ACCESS]] +// CHECK: return [[RESULT]] +public func test(consume: consuming M) -> CInt { + return consume.other +} + +// CHECK-LABEL: sil [ossa] @${{.*}}4test3own +// CHECK: [[CHECK:%.*]] = mark_must_check [consumable_and_assignable] %0 +// CHECK: [[RESULT:%.*]] = apply {{.*}}([[CHECK]]) +// CHECK: return [[RESULT]] +public func test(own: __owned M) -> CInt { + return own.other +} + +func use(_: CInt, andMutate _: inout M) {} +func use(_: CInt, andConsume _: consuming M) {} +func borrow(_: borrowing M, andMutate _: inout M) {} +func borrow(_: borrowing M, andConsume _: consuming M) {} + +public func testNoInterferenceGet(mut: inout M, extra: consuming M) { + // This should not cause exclusivity interference, since the result of + // the getter can have an independent lifetime from the borrow. + use(mut.other, andMutate: &mut) + use(mut.other, andConsume: mut) + mut = extra +} + +public func testInterferenceRead(mut: inout M, extra: consuming M) { + // This should cause exclusivity interference, since in order to borrow + // the yielded result from the `_read`, we need to keep the borrow of + // the base going. + borrow(mut.otherMoveOnly, andMutate: &mut) // expected-error{{}} expected-note{{}} + borrow(mut.otherMoveOnly, andConsume: mut) // expected-error{{}} expected-note{{}} + mut = extra +} diff --git a/test/SILGen/switch_expr.swift b/test/SILGen/switch_expr.swift index 7275aa1bb534d..310dc90329ab3 100644 --- a/test/SILGen/switch_expr.swift +++ b/test/SILGen/switch_expr.swift @@ -605,3 +605,14 @@ struct TestLValues { opt![keyPath: kp] = switch Bool.random() { case true: 1 case false: throw Err() } } } + +func exprPatternInClosure() { + let f: (Int) -> Void = { i in + switch i { + case i: + () + default: + () + } + } +} diff --git a/test/SILGen/variadic_generic_conformances.swift b/test/SILGen/variadic_generic_conformances.swift new file mode 100644 index 0000000000000..11411a1d34a10 --- /dev/null +++ b/test/SILGen/variadic_generic_conformances.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend -emit-silgen %s -disable-availability-checking + +protocol P { + associatedtype A: P +} + +struct S: P { + typealias A = S +} + +func f(_: repeat each T) -> (repeat each T.A.A.A.A) {} + +f(S(), S(), S()) \ No newline at end of file diff --git a/test/SILOptimizer/init_accessor_definite_init_diagnostics.swift b/test/SILOptimizer/init_accessor_definite_init_diagnostics.swift new file mode 100644 index 0000000000000..5198db703c0c8 --- /dev/null +++ b/test/SILOptimizer/init_accessor_definite_init_diagnostics.swift @@ -0,0 +1,141 @@ +// RUN: %target-swift-frontend -enable-experimental-feature InitAccessors -enable-copy-propagation=requested-passes-only -emit-sil -primary-file %s -o /dev/null -verify + +// REQUIRES: asserts + +struct Test1 { + var x: Int // expected-note {{variable defined here}} + var y: Int // expected-note {{variable defined here}} + var full: (Int, Int) + + var test1: (Int, Int) { + init(initialValue) initializes(y, full) accesses(x) { + self.full = (self.x, self.y) // expected-error {{variable 'y' used before being initialized}} + } + + get { full } + set { self.full = newValue } + } + + var pointY: Int { + init(initialValue) initializes(y) { + self.y = initialValue // Ok + } + + get { y } + set {} + } + + var errorPoint1: (Int, Int) { + init(initialValue) initializes(x, y) { + // expected-error@-1 {{property 'x' not initialized by init accessor}} + // expected-error@-2 {{property 'y' not initialized by init accessor}} + } + + get { (x, y) } + set { } + } + + var errorPoint2: (Int, Int) { + init(initialValue) initializes(x, y) { + // expected-error@-1 {{property 'y' not initialized by init accessor}} + self.x = initialValue.0 + } + + get { (x, y) } + set { } + } + + var errorPoint3: (Int, Int) { + init(initialValue) initializes(x, y) { + self.y = initialValue.1 + print(y) // Ok + print(x) // expected-error {{variable 'x' used before being initialized}} + } + + get { (x, y) } + set { } + } + + init(x: Int, y: Int) { + self.x = x + self.y = y + self.full = (x, y) + } +} + +struct TestPartial { + var x: Int + var y: Int + + var point: (Int, Int) { + init(initialValue) initializes(x, y) { + self.x = initialValue.0 + self.y = initialValue.1 + } + + get { (x, y) } + set { } + } + + init(x: Int, y: Int) { + self.x = x + self.point = (x, y) // Ok (x is going to get `destroy_addr`) + } + + init(_ x: Int, _ y: Int) { + self.x = x + self.y = y + self.point = (x, y) // Ok (calls a setter) + } +} + +struct TestDoubleInit1 { + let x: Int // expected-note {{change 'let' to 'var' to make it mutable}} + + var invalidPointX: Int { + init(initialValue) initializes(x) { + self.x = initialValue + self.x = 42 // expected-error {{immutable value 'x' may only be initialized once}} + } + + get { x } + set { } + } +} + +struct TestDoubleInit2 { + let x: Int // expected-note {{change 'let' to 'var' to make it mutable}} + + var pointX: Int { + init(initialValue) initializes(x) { + self.x = initialValue + } + + get { x } + set { } + } + + init(x: Int) { + self.pointX = x + self.x = 0 // expected-error {{immutable value 'self.x' may only be initialized once}} + } +} + +struct TestAccessBeforeInit { + var _x: Int + var x: Int { + init(initialValue) initializes(_x) accesses(y) { + _x = initialValue + } + + get { _x } + set {} + } + + var y: Int + + init(x: Int, y: Int) { + self.x = x // expected-error {{variable 'self.y' used before being initialized}} + self.y = y + } +} diff --git a/test/SILOptimizer/init_accessor_raw_sil_lowering.swift b/test/SILOptimizer/init_accessor_raw_sil_lowering.swift new file mode 100644 index 0000000000000..f12398a4d37d9 --- /dev/null +++ b/test/SILOptimizer/init_accessor_raw_sil_lowering.swift @@ -0,0 +1,81 @@ +// RUN: %target-swift-frontend -enable-experimental-feature InitAccessors -Xllvm -sil-print-after=definite-init -emit-sil -module-name assign_or_init_lowering %s -o /dev/null 2>&1 | %FileCheck %s + +// REQUIRES: asserts + +struct Test1 { + var _a: Int + var _b: String + + var a: Int { + init(initialValue) initializes(_a) { + _a = initialValue + } + + get { _a } + set { } + } + + var b: String { + init(initialValue) initializes(_a, _b) { + _a = 0 + _b = initialValue + } + + get { _b } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering5Test1V1aACSi_tcfC : $@convention(method) (Int, @thin Test1.Type) -> @owned Test1 + init(a: Int) { + // CHECK: assign_or_init [init] [[VALUE:%.*]] : $Int, init {{.*}} : $@convention(thin) (Int) -> @out Int, set {{.*}} : $@callee_guaranteed (Int) -> () + self.a = a + // CHECK: assign_or_init [init] [assign=0] [[VALUE:%.*]] : $String, init {{.*}} : $@convention(thin) (@owned String) -> (@out Int, @out String), set {{.*}} : $@callee_guaranteed (@owned String) -> () + self.a = -1 + self.b = "" + // CHECK: assign_or_init [set] [[VALUE:%.*]] : $Int, init {{.*}} : $@convention(thin) (Int) -> @out Int, set {{.*}} : $@callee_guaranteed (Int) -> () + self.a = a + } +} + +struct Test2 { + var _a: Int + var _b: T + var _c: String + + var pair: (Int, T) { + init(initialValue) initializes(_a, _b) { + _a = initialValue.0 + _b = initialValue.1 + } + + get { (_a, _b) } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering5Test2V1a1bACyxGSi_xtcfC : $@convention(method) (Int, @in T, @thin Test2.Type) -> @out Test2 + init(a: Int, b: T) { + // CHECK: assign_or_init [init] [[VALUE:%.*]] : $*(Int, T), init {{.*}} : $@convention(thin) <τ_0_0> (Int, @in τ_0_0) -> (@out Int, @out τ_0_0), set {{.*}} : $@callee_guaranteed (Int, @in T) -> () + self.pair = (a, b) + // CHECK: assign_or_init [init] [assign=0] [assign=1] [[VALUE:%.*]] : $*(Int, T), init {{.*}} : $@convention(thin) <τ_0_0> (Int, @in τ_0_0) -> (@out Int, @out τ_0_0), set {{.*}} : $@callee_guaranteed (Int, @in T) -> () + self.pair = (0, b) + self._c = "" + // CHECK: assign_or_init [set] [[VALUE:%.*]] : $*(Int, T), init {{.*}} : $@convention(thin) <τ_0_0> (Int, @in τ_0_0) -> (@out Int, @out τ_0_0), set {{.*}} : $@callee_guaranteed (Int, @in T) -> () + self.pair = (1, b) + } +} + +struct Test { + var test: Int { + init { + } + + get { 42 } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering4TestV1vACSi_tcfC : $@convention(method) (Int, @thin Test.Type) -> Test + init(v: Int) { + // CHECK: assign_or_init [set] %0 : $Int, init {{.*}} : $@convention(thin) (Int) -> (), set {{.*}} : $@callee_guaranteed (Int) -> () + self.test = v + } +} diff --git a/test/SILOptimizer/init_accessors.swift b/test/SILOptimizer/init_accessors.swift new file mode 100644 index 0000000000000..a67bbb376f49e --- /dev/null +++ b/test/SILOptimizer/init_accessors.swift @@ -0,0 +1,443 @@ +// RUN: %target-swift-frontend -enable-experimental-feature InitAccessors -primary-file %s -Onone -emit-sil \ +// RUN: -Xllvm -sil-print-after=raw-sil-inst-lowering \ +// RUN: -o /dev/null -module-name init_accessors 2>&1 | %FileCheck %s + +// REQUIRES: asserts + +struct TestInit { + var x: Int + var y: Int + var full: (Int, Int) + + var point: (Int, Int) { + // CHECK-LABEL: sil private [ossa] @$s14init_accessors8TestInitV5pointSi_Sitvi : $@convention(thin) (Int, Int, @inout Int) -> (@out Int, @out (Int, Int)) + // CHECK: bb0([[Y_REF:%.*]] : $*Int, [[FULL_REF:%.*]] : $*(Int, Int), [[X_VAL:%.*]] : $Int, [[Y_VAL:%.*]] : $Int, [[X_REF:%.*]] : $*Int): + // + // CHECK: [[INITIAL_VALUE:%.*]] = tuple ([[X_VAL]] : $Int, [[Y_VAL]] : $Int) + // CHECK: ([[X_VAL:%.*]], [[Y_VAL:%.*]]) = destructure_tuple [[INITIAL_VALUE]] : $(Int, Int) + // CHECK: [[Y_ACCESS:%.*]] = begin_access [modify] [static] [[Y_REF]] : $*Int + // CHECK-NEXT: store [[Y_VAL]] to [trivial] [[Y_ACCESS]] : $*Int + // CHECK-NEXT: end_access [[Y_ACCESS]] : $*Int + // + // CHECK-NEXT: [[X_ACCESS:%.*]] = begin_access [read] [static] [[X_REF]] : $*Int + // CHECK-NEXT: [[X_VAL:%.*]] = load [trivial] [[X_ACCESS]] : $*Int + // CHECK-NEXT: end_access [[X_ACCESS]] : $*Int + // + // CHECK-NEXT: [[Y_ACCESS:%.*]] = begin_access [read] [static] [[Y_REF]] : $*Int + // CHECK-NEXT: [[Y_VAL:%.*]] = load [trivial] [[Y_ACCESS]] : $*Int + // CHECK-NEXT: end_access [[Y_ACCESS]] : $*Int + // + // CHECK-NEXT: [[FULL_ACCESS:%.*]] = begin_access [modify] [static] [[FULL_REF]] : $*(Int, Int) + // CHECK-NEXT: [[FULL_ELT_0:%.*]] = tuple_element_addr [[FULL_ACCESS]] : $*(Int, Int), 0 + // CHECK-NEXT: store [[X_VAL]] to [trivial] [[FULL_ELT_0]] : $*Int + // CHECK-NEXT: [[FULL_ELT_1:%.*]] = tuple_element_addr [[FULL_ACCESS]] : $*(Int, Int), 1 + // CHECK-NEXT: store [[Y_VAL]] to [trivial] [[FULL_ELT_1]] : $*Int + // CHECK-NEXT: end_access [[FULL_ACCESS]] : $*(Int, Int) + init(initialValue) initializes(y, full) accesses(x) { + self.y = initialValue.1 + self.full = (self.x, self.y) + } + + get { full } + set { full = newValue } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors8TestInitV1x1yACSi_SitcfC : $@convention(method) (Int, Int, @thin TestInit.Type) -> TestInit + // CHECK: // function_ref TestInit.point.init + // CHECK-NEXT: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors8TestInitV5pointSi_Sitvi : $@convention(thin) (Int, Int, @inout Int) -> (@out Int, @out (Int, Int)) + // CHECK: [[SELF_VALUE:%.*]] = begin_access [modify] [dynamic] {{.*}} : $*TestInit + // CHECK: [[Y_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestInit, #TestInit.y + // CHECK-NEXT: [[FULL_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestInit, #TestInit.full + // CHECK-NEXT: ([[X_VAL:%.*]], [[Y_VAL:%.*]]) = destructure_tuple {{.*}} : $(Int, Int) + // CHECK-NEXT: [[X_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestInit, #TestInit.x + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[Y_REF]], [[FULL_REF]], [[X_VAL]], [[Y_VAL]], [[X_REF]]) : $@convention(thin) (Int, Int, @inout Int) -> (@out Int, @out (Int, Int)) + // CHECK-NEXT: end_access [[SELF_VALUE]] : $*TestInit + init(x: Int, y: Int) { + self.x = x + self.point = (x, y) + } +} + +struct TestSetter { + var x: Int + var y: Int + + var point: (Int, Int) { + init(initialValue) accesses(x, y) { + } + + get { (x, y) } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors10TestSetterV1x1yACSi_SitcfC : $@convention(method) (Int, Int, @thin TestSetter.Type) -> TestSetter + // CHECK: [[SETTER_REF:%.*]] = function_ref @$s14init_accessors10TestSetterV5pointSi_Sitvs : $@convention(method) (Int, Int, @inout TestSetter) -> () + // CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]([[SELF_VALUE:%.*]]) : $@convention(method) (Int, Int, @inout TestSetter) -> () + // CHECK: {{.*}} = apply [[SETTER_CLOSURE]]({{.*}}) : $@callee_guaranteed (Int, Int) -> () + init(x: Int, y: Int) { + self.x = x + self.y = y + self.point = (x, y) + } +} + +struct TestInitThenSetter { + var x: Int + var y: Int + + var point: (Int, Int) { + init(initialValue) initializes(x, y) { + self.x = initialValue.0 + self.y = initialValue.1 + } + + get { (x, y) } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors18TestInitThenSetterV1x1yACSi_SitcfC : $@convention(method) (Int, Int, @thin TestInitThenSetter.Type) -> TestInitThenSetter + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors18TestInitThenSetterV5pointSi_Sitvi : $@convention(thin) (Int, Int) -> (@out Int, @out Int) + // CHECK: [[X_REF:%.*]] = struct_element_addr {{.*}} : $*TestInitThenSetter, #TestInitThenSetter.x + // CHECK-NEXT: [[Y_REF:%.*]] = struct_element_addr {{.*}} : $*TestInitThenSetter, #TestInitThenSetter.y + // CHECK: {{.*}} = apply [[INIT_ACCESSOR]]([[X_REF]], [[Y_REF]], {{.*}}) : $@convention(thin) (Int, Int) -> (@out Int, @out Int) + // + // CHECK: [[SETTER_REF:%.*]] = function_ref @$s14init_accessors18TestInitThenSetterV5pointSi_Sitvs : $@convention(method) (Int, Int, @inout TestInitThenSetter) -> () + // CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]([[SELF_VALUE:%.*]]) : $@convention(method) (Int, Int, @inout TestInitThenSetter) -> () + // CHECK: ([[ZERO_X:%.*]], [[ZERO_Y:%.*]]) = destructure_tuple {{.*}} : $(Int, Int) + // CHECK: {{.*}} = apply [[SETTER_CLOSURE]]([[ZERO_X]], [[ZERO_Y]]) : $@callee_guaranteed (Int, Int) -> () + init(x: Int, y: Int) { + self.point = (x, y) + + if x == 1 { + self.point = (0, 0) + } + } +} + +struct TestPartialInt { + var x: Int + var y: Int + + var pointX: Int { + init(newValue) initializes(x) { + self.x = newValue + } + + get { x } + set { self.x = newValue } + } + + var pointY: Int { + init(newValue) initializes(y) { + self.y = newValue + } + + get { y } + set { self.y = newValue } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors14TestPartialIntV1x1yACSi_SitcfC : $@convention(method) (Int, Int, @thin TestPartialInt.Type) -> TestPartialInt + // + // CHECK: [[INIT_REF:%.*]] = function_ref @$s14init_accessors14TestPartialIntV6pointXSivi : $@convention(thin) (Int) -> @out Int + // CHECK: [[X_REF:%.*]] = struct_element_addr {{.*}} : $*TestPartialInt, #TestPartialInt.x + // CHECK-NEXT: {{.*}} = apply [[INIT_REF]]([[X_REF]], %0) : $@convention(thin) (Int) -> @out Int + // + // CHECK: [[INIT_REF:%.*]] = function_ref @$s14init_accessors14TestPartialIntV6pointYSivi : $@convention(thin) (Int) -> @out Int + // CHECK: [[Y_REF:%.*]] = struct_element_addr {{.*}} : $*TestPartialInt, #TestPartialInt.y + // CHECK-NEXT: {{.*}} = apply [[INIT_REF]]([[Y_REF]], %1) : $@convention(thin) (Int) -> @out Int + // + // CHECK: [[BUILTIN_ONE:%.*]] = integer_literal $Builtin.IntLiteral, 1 + // CHECK: [[SETTER_REF:%.*]] = function_ref @$s14init_accessors14TestPartialIntV6pointXSivs : $@convention(method) (Int, @inout TestPartialInt) -> () + // CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]({{.*}}) : $@convention(method) (Int, @inout TestPartialInt) -> () + // CHECK-NEXT: {{.*}} = apply [[SETTER_CLOSURE]]({{.*}}) : $@callee_guaranteed (Int) -> () + // + // CHECK: [[BUILTIN_TWO:%.*]] = integer_literal $Builtin.IntLiteral, 2 + // CHECK: [[SETTER_REF:%.*]] = function_ref @$s14init_accessors14TestPartialIntV6pointYSivs : $@convention(method) (Int, @inout TestPartialInt) -> () + // CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]({{.*}}) : $@convention(method) (Int, @inout TestPartialInt) -> () + // CHECK-NEXT: {{.*}} = apply [[SETTER_CLOSURE]]({{.*}}) : $@callee_guaranteed (Int) -> () + init(x: Int, y: Int) { + // Init + self.pointX = x + // Init + self.pointY = y + + // Setter + self.pointX = 1 + // Setter + self.pointY = 2 + + } +} + +struct TestNoInitAndInit { + var x: Int + var y: Int + + var pointX: Int { + init(initalValue) accesses(x) { + } + + get { x } + set { } + } + + var pointY: Int { + init(initialValue) initializes(y) { + self.y = initialValue + } + + get { y } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors013TestNoInitAndE0V1x1yACSi_SitcfC : $@convention(method) (Int, Int, @thin TestNoInitAndInit.Type) -> TestNoInitAndInit + // + // CHECK: [[INIT_REF:%.*]] = function_ref @$s14init_accessors013TestNoInitAndE0V6pointXSivi : $@convention(thin) (Int, @inout Int) -> () + // CHECK: [[SELF_REF:%.*]] = begin_access [modify] [dynamic] {{.*}} : $*TestNoInitAndInit + // CHECK-NEXT: [[X_REF:%.*]] = struct_element_addr [[SELF_REF]] : $*TestNoInitAndInit, #TestNoInitAndInit.x + // CHECK-NEXT: {{.*}} = apply [[INIT_REF]](%0, [[X_REF]]) : $@convention(thin) (Int, @inout Int) -> () + // CHECK-NEXT: end_access [[SELF_REF]] : $*TestNoInitAndInit + // + // CHECK: [[INIT_REF:%.*]] = function_ref @$s14init_accessors013TestNoInitAndE0V6pointYSivi : $@convention(thin) (Int) -> @out Int + // CHECK: [[SELF_REF:%.*]] = begin_access [modify] [dynamic] {{.*}} : $*TestNoInitAndInit + // CHECK-NEXT: [[Y_REF:%.*]] = struct_element_addr [[SELF_REF]] : $*TestNoInitAndInit, #TestNoInitAndInit.y + // CHECK-NEXT: {{.*}} = apply [[INIT_REF]]([[Y_REF]], %1) : $@convention(thin) (Int) -> @out Int + // CHECK-NEXT: end_access [[SELF_REF]] : $*TestNoInitAndInit + init(x: Int, y: Int) { + self.x = x + self.pointX = x + self.pointY = y + print("Point(x: \(self.x), y: \(self.y)") + } +} + +class TestClass { + var x: Int + var y: (Int, [String]) + + var data: (Int, (Int, [String])) { + // CHECK-LABEL: sil private [ossa] @$s14init_accessors9TestClassC4dataSi_Si_SaySSGttvi : $@convention(thin) (Int, Int, @owned Array) -> (@out Int, @out (Int, Array)) + // CHECK: bb0([[X_REF:%.*]] : $*Int, [[Y_REF:%.*]] : $*(Int, Array), [[X_VAL:%.*]] : $Int, [[Y_VAL_0:%.*]] : $Int, [[Y_VAL_1:%.*]] : @owned $Array): + // + // CHECK: ([[X_VAL:%.*]], [[Y_VAL:%.*]]) = destructure_tuple {{.*}} : $(Int, (Int, Array)) + // CHECK: [[X_ACCESS:%.*]] = begin_access [modify] [static] [[X_REF]] : $*Int + // CHECK-NEXT: store [[X_VAL]] to [trivial] [[X_ACCESS]] : $*Int + // CHECK-NEXT: end_access [[X_ACCESS]] : $*Int + // + // CHECK: ([[X_VAL:%.*]], [[Y_VAL:%.*]]) = destructure_tuple {{.*}} : $(Int, (Int, Array)) + // CHECK: ([[Y_VAL_0:%.*]], [[Y_VAL_1:%.*]]) = destructure_tuple {{.*}} : $(Int, Array) + // CHECK: [[Y_ACCESS:%.*]] = begin_access [modify] [static] [[Y_REF]] : $*(Int, Array) + // CHECK-NEXT: [[Y_ELT_0:%.*]] = tuple_element_addr [[Y_ACCESS]] : $*(Int, Array), 0 + // CHECK-NEXT: store [[Y_VAL_0]] to [trivial] [[Y_ELT_0]] : $*Int + // CHECK-NEXT: [[Y_ELT_1:%.*]] = tuple_element_addr [[Y_ACCESS]] : $*(Int, Array), 1 + // CHECK-NEXT: store [[Y_VAL_1]] to [init] [[Y_ELT_1]] : $*Array + // CHECK-NEXT: end_access [[Y_ACCESS]] : $*(Int, Array) + init(initialValue) initializes(x, y) { + x = initialValue.0 + y = initialValue.1 + } + + get { (x, y) } + set { + x = newValue.0 + y = newValue.1 + } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors9TestClassC1x1yACSi_Si_SaySSGttcfc : $@convention(method) (Int, Int, @owned Array, @owned TestClass) -> @owned TestClass + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors9TestClassC4dataSi_Si_SaySSGttvi : $@convention(thin) (Int, Int, @owned Array) -> (@out Int, @out (Int, Array)) + // CHECK: [[SELF_VALUE:%.*]] = copy_value %3 : $TestClass + // CHECK: [[SELF_REF:%.*]] = begin_borrow [[SELF_VALUE]] : $TestClass + // CHECK: [[X_REF:%.*]] = ref_element_addr [[SELF_REF]] : $TestClass, #TestClass.x + // CHECK-NEXT: [[Y_REF:%.*]] = ref_element_addr [[SELF_REF]] : $TestClass, #TestClass.y + // + // CHECK-NEXT: ([[X_VAL:%.*]], [[Y_VAL:%.*]]) = destructure_tuple {{.*}} : $(Int, (Int, Array)) + // CHECK-NEXT: ([[Y_VAL_0:%.*]], [[Y_VAL_1:%.*]]) = destructure_tuple [[Y_VAL]] : $(Int, Array) + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[X_REF]], [[Y_REF]], [[X_VAL]], [[Y_VAL_0]], [[Y_VAL_1]]) : $@convention(thin) (Int, Int, @owned Array) -> (@out Int, @out (Int, Array)) + init(x: Int, y: (Int, [String])) { + self.data = (x, y) + } +} + +struct TestGeneric { + var a: T + var b: T + var c: U + + // CHECK-LABEL: sil private [ossa] @$s14init_accessors11TestGenericV4datax_xtvi : $@convention(thin) (@in T, @in T, @inout U) -> (@out T, @out T) + // + // CHECK: bb0([[A_REF:%.*]] : $*T, [[B_REF:%.*]] : $*T, [[A_VALUE:%.*]] : $*T, [[B_VALUE:%.*]] : $*T, [[C_REF:%.*]] : $*U): + // + // CHECK: [[A_ACCESS:%.*]] = begin_access [modify] [static] [[A_REF]] : $*T + // CHECK-NEXT: copy_addr [take] {{.*}} to [init] [[A_ACCESS]] : $*T + // CHECK-NEXT: end_access [[A_ACCESS]] : $*T + // + // CHECK: [[B_ACCESS:%.*]] = begin_access [modify] [static] [[B_REF]] : $*T + // CHECK-NEXT: copy_addr [take] {{.*}} to [init] [[B_ACCESS]] : $*T + // CHECK-NEXT: end_access [[B_ACCESS]] : $*T + // + // CHECK: [[C_ACCESS:%.*]] = begin_access [read] [static] [[C_REF]] : $*U + // CHECK-NEXT: [[C_AS_ANY:%.*]] = init_existential_addr {{.*}} : $*Any, $U + // CHECK-NEXT: copy_addr [[C_ACCESS]] to [init] [[C_AS_ANY]] : $*U + // CHECK-NEXT: end_access [[C_ACCESS]] : $*U + var data: (T, T) { + init(initialValue) initializes(a, b) accesses(c) { + a = initialValue.0 + b = initialValue.1 + print(c) + } + + get { (a, b) } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors11TestGenericV1a1b1cACyxq_Gx_xq_tcfC : $@convention(method) (@in T, @in T, @in U, @thin TestGeneric.Type) -> @out TestGeneric + // + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors11TestGenericV4datax_xtvi : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_0, @inout τ_0_1) -> (@out τ_0_0, @out τ_0_0) + // CHECK: {{.*}} = apply [[INIT_ACCESSOR]]({{.*}}) : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_0, @inout τ_0_1) -> (@out τ_0_0, @out τ_0_0) + // + // CHECK: [[SETTER:%.*]] = function_ref @$s14init_accessors11TestGenericV4datax_xtvs : $@convention(method) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_0, @inout TestGeneric<τ_0_0, τ_0_1>) -> () + // CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER]]([[SELF_VALUE:%.*]]) : $@convention(method) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_0, @inout TestGeneric<τ_0_0, τ_0_1>) -> () + // CHECK: %55 = apply [[SETTER_CLOSURE]]({{.*}}) : $@callee_guaranteed (@in T, @in T) -> () + // CHECK-NEXT: end_access [[SELF_VALUE]] : $*TestGeneric + init(a: T, b: T, c: U) { + self.c = c + self.data = (a, b) + self.data = (b, a) + } +} + +func test_local_with_memberwise() { + class MyValue {} + + struct TestMemberwiseConcrete { + var a: Int + var b: String + + var pair: (Int, String) { + init(initialValue) initializes(a, b) { + a = initialValue.0 + b = initialValue.1 + } + + get { (a, b) } + set { } + } + + var c: [MyValue] + + // CHECK-LABEL: sil private [ossa] @$s14init_accessors26test_local_with_memberwiseyyF22TestMemberwiseConcreteL_V4pair1cADSi_SSt_SayAaByyF7MyValueL_CGtcfC : $@convention(method) (Int, @owned String, @owned Array, @thin TestMemberwiseConcrete.Type) -> @owned TestMemberwiseConcrete + // CHECK: [[SELF_VALUE:%.*]] = alloc_stack $TestMemberwiseConcrete + // CHECK-NEXT: [[A_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseConcrete, #TestMemberwiseConcrete.a + // CHECK-NEXT: [[B_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseConcrete, #TestMemberwiseConcrete.b + // CHECK: [[INIT_ACCESSOR_REF:%.*]] = function_ref @$s14init_accessors26test_local_with_memberwiseyyF22TestMemberwiseConcreteL_V4pairSi_SStvi : $@convention(thin) (Int, @owned String) -> (@out Int, @out String) + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR_REF]]([[A_REF]], [[B_REF]], %0, %1) : $@convention(thin) (Int, @owned String) -> (@out Int, @out String) + // CHECK-NEXT: [[C_REF:%.*]] = struct_element_addr %4 : $*TestMemberwiseConcrete, #TestMemberwiseConcrete.c + // CHECK-NEXT: store %2 to [init] [[C_REF]] : $*Array + // CHECK-NEXT: [[RESULT:%.*]] = load [take] [[SELF_VALUE]] : $*TestMemberwiseConcrete + // CHECK-NEXT: dealloc_stack [[SELF_VALUE]] : $*TestMemberwiseConcrete + // CHECK-NEXT: return [[RESULT]] : $TestMemberwiseConcrete + } + + _ = TestMemberwiseConcrete(pair: (0, "a"), c: []) + + struct TestMemberwiseGeneric where C: RangeReplaceableCollection, C.Element == T { + var _a: T + var _b: String + var _c: C + + var a: T { + init(initialValue) initializes(_a) { + _a = initialValue + } + + get { _a } + set { } + } + + var pair: (String, C) { + init(initialValue) initializes(_b, _c) accesses(_a) { + _b = initialValue.0 + _c = initialValue.1 + _c.append(_a) + } + + get { (_b, _c) } + set { } + } + + // CHECK-LABEL: sil private [ossa] @$s14init_accessors26test_local_with_memberwiseyyF21TestMemberwiseGenericL_V1a4pairADyxq_Gx_SS_q_ttcfC : $@convention(method) (@in T, @owned String, @in C, @thin TestMemberwiseGeneric.Type) -> @out TestMemberwiseGeneric + // CHECK: bb0([[SELF_VALUE:%.*]] : $*TestMemberwiseGeneric, [[A_VALUE:%*.]] : $*T, [[B_VALUE:%.*]] : @owned $String, [[C_VALUE:%.*]] : $*C, [[METATYPE:%.*]] : $@thin TestMemberwiseGeneric.Type): + // CHECK-NEXT: [[A_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseGeneric, #TestMemberwiseGeneric._a + // CHECK: [[INIT_ACCESSOR_REF:%.*]] = function_ref @$s14init_accessors26test_local_with_memberwiseyyF21TestMemberwiseGenericL_V1axvi : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == τ_0_1.Element, τ_0_1 : RangeReplaceableCollection> (@in τ_0_0) -> @out τ_0_0 // user: %7 + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR_REF]]([[A_REF]], [[A_VALUE]]) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == τ_0_1.Element, τ_0_1 : RangeReplaceableCollection> (@in τ_0_0) -> @out τ_0_0 + // CHECK-NEXT: [[B_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseGeneric, #TestMemberwiseGeneric._b + // CHECK-NEXT: [[C_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseGeneric, #TestMemberwiseGeneric._c + // CHECK-NEXT: [[A_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseGeneric, #TestMemberwiseGeneric._a + // CHECK: [[INIT_ACCESSOR_REF:%.*]] = function_ref @$s14init_accessors26test_local_with_memberwiseyyF21TestMemberwiseGenericL_V4pairSS_q_tvi : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == τ_0_1.Element, τ_0_1 : RangeReplaceableCollection> (@owned String, @in τ_0_1, @inout τ_0_0) -> (@out String, @out τ_0_1) + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR_REF]]([[B_REF]], [[C_REF]], [[B_VALUE]], [[C_VALUE]], [[A_REF]]) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == τ_0_1.Element, τ_0_1 : RangeReplaceableCollection> (@owned String, @in τ_0_1, @inout τ_0_0) -> (@out String, @out τ_0_1) + // CHECK-NEXT: [[VOID:%.*]] = tuple () + // CHECK-NEXT: return [[VOID]] : $() + } + + _ = TestMemberwiseGeneric(a: 1, pair: ("a", [0])) +} + +func test_assignments() { + struct Test { + var _a: Int + var _b: Int + + var a: Int { + init(initialValue) initializes(_a) { + self._a = initialValue + } + get { _a } + set { _a = newValue } + } + + var pair: (Int, Int) { + init(initialValue) initializes(_a, _b) { + _a = initialValue.0 + _b = initialValue.1 + } + + get { (_a, _b) } + set { } + } + + // CHECK-LABEL: sil private [ossa] @$s14init_accessors16test_assignmentsyyF4TestL_V1aADSi_tcfC : $@convention(method) (Int, @thin Test.Type) -> Test + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V1aSivi : $@convention(thin) (Int) -> @out Int + // CHECK: [[A_REF:%.*]] = struct_element_addr {{.*}} : $*Test, #Test._a + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[A_REF]], %0) : $@convention(thin) (Int) -> @out Int + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V1aSivi : $@convention(thin) (Int) -> @out Int + // CHECK: [[A_REF:%.*]] = struct_element_addr {{.*}} : $*Test, #Test._a + // CHECK-NEXT: destroy_addr [[A_REF]] : $*Int + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[A_REF]], %0) : $@convention(thin) (Int) -> @out Int + // CHECK: [[B_REF:%.*]] = struct_element_addr {{.*}} : $*Test, #Test._b + // CHECK-NEXT: store {{.*}} to [trivial] [[B_REF]] : $*Int + // CHECK: [[SETTER_REF:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V1aSivs : $@convention(method) (Int, @inout Test) -> () + // CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]([[SELF_VALUE:%.*]]) : $@convention(method) (Int, @inout Test) -> () + // CHECK: {{.*}} = apply [[SETTER_CLOSURE]](%0) : $@callee_guaranteed (Int) -> () + init(a: Int) { + self.a = a + self.a = a + self._b = 42 + self.a = a + } + + // CHECK-LABEL: sil private [ossa] @$s14init_accessors16test_assignmentsyyF4TestL_V1a1bADSi_SitcfC : $@convention(method) (Int, Int, @thin Test.Type) -> Test + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V1aSivi : $@convention(thin) (Int) -> @out Int + // CHECK: [[A_REF:%.*]] = struct_element_addr {{.*}} : $*Test, #Test._a + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[A_REF]], %0) : $@convention(thin) (Int) -> @out Int + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V4pairSi_Sitvi : $@convention(thin) (Int, Int) -> (@out Int, @out Int) + // CHECK: [[A_REF:%.*]] = struct_element_addr [[SELF_VALUE:%.*]] : $*Test, #Test._a + // CHECK-NEXT: destroy_addr [[A_REF]] : $*Int + // CHECK-NEXT: [[B_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*Test, #Test._b + // CHECK: {{.*}} = apply [[INIT_ACCESSOR]]([[A_REF]], [[B_REF]], {{.*}}) : $@convention(thin) (Int, Int) -> (@out Int, @out Int) + init(a: Int, b: Int) { + self.a = a + self.pair = (0, b) + } + } +} diff --git a/test/SILOptimizer/noimplicitcopy_borrowing_parameters.swift b/test/SILOptimizer/noimplicitcopy_borrowing_parameters.swift index f283399593d59..712f620f162fb 100644 --- a/test/SILOptimizer/noimplicitcopy_borrowing_parameters.swift +++ b/test/SILOptimizer/noimplicitcopy_borrowing_parameters.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil %s -verify +// RUN: %target-swift-frontend -emit-sil %s -verify -sil-verify-all //////////////////////// // MARK: Declarations // diff --git a/test/SILOptimizer/noimplicitcopy_consuming_parameters.swift b/test/SILOptimizer/noimplicitcopy_consuming_parameters.swift index 6668032bdae9c..e9a3f21e340db 100644 --- a/test/SILOptimizer/noimplicitcopy_consuming_parameters.swift +++ b/test/SILOptimizer/noimplicitcopy_consuming_parameters.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil %s -verify +// RUN: %target-swift-frontend -emit-sil %s -verify -sil-verify-all //////////////////////// // MARK: Declarations // diff --git a/test/SILOptimizer/outliner.swift b/test/SILOptimizer/outliner.swift index c43876f6d32be..1d1905925eb7e 100644 --- a/test/SILOptimizer/outliner.swift +++ b/test/SILOptimizer/outliner.swift @@ -3,6 +3,7 @@ // REQUIRES: objc_interop // REQUIRES: optimized_stdlib +// REQUIRES: swift_in_compiler import Foundation diff --git a/test/SILOptimizer/simplify_release_value.sil b/test/SILOptimizer/simplify_release_value.sil new file mode 100644 index 0000000000000..fbba4d60289dd --- /dev/null +++ b/test/SILOptimizer/simplify_release_value.sil @@ -0,0 +1,207 @@ +// RUN: %target-sil-opt -enable-experimental-feature MoveOnlyEnumDeinits -enable-sil-verify-all %s -simplification -simplify-instruction=release_value | %FileCheck %s + +// REQUIRES: swift_in_compiler + +import Swift +import Builtin + +class C {} + +enum U { + init() + case X + case Y(Builtin.Int64) + case Z(C) +} + +enum EnumWithPayload { + case A(UInt64) + case B(AnyObject) + case C +} + +struct WithDeinit : ~Copyable { + deinit +} + +struct Outer : ~Copyable { + var wd: WithDeinit +} + +enum EnumWithDeinit: ~Copyable { + case A(Int64) + case B + + deinit +} + +struct TwoEnums { + var u1: U + var u2: U +} + +struct MyStringObj { + @_hasStorage var a: UInt64 + @_hasStorage var b: Builtin.BridgeObject +} + +struct MyStringGuts { + @_hasStorage var o: MyStringObj +} + +struct MyString { + @_hasStorage var g: MyStringGuts +} + + +// CHECK-LABEL: sil @release_value_test +// CHECK: bb0({{%[0-9]+}} : $Builtin.Int8, [[RELEASE_TARGET:%[0-9]+]] : $Builtin.NativeObject): +// CHECK-NEXT: strong_release [[RELEASE_TARGET]] : $Builtin.NativeObject +// CHECK-NEXT: tuple () +// CHECK-NEXT: return +// CHECK: } // end sil function 'release_value_test' +sil @release_value_test : $@convention(thin) (Builtin.Int8, Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.Int8, %1 : $Builtin.NativeObject): + release_value %0 : $Builtin.Int8 + release_value %1 : $Builtin.NativeObject + %2 = tuple() + return %2 : $() +} + +// CHECK-LABEL: sil @dont_remove_struct_with_deinit_field +// CHECK: release_value %0 +// CHECK: } // end sil function 'dont_remove_struct_with_deinit_field' +sil @dont_remove_struct_with_deinit_field : $@convention(thin) (Outer) -> () { +bb0(%0 : $Outer): + release_value %0 : $Outer + %2 = tuple () + return %2 : $() +} + +// CHECK-LABEL: sil @struct_with_two_trivial_enums +// CHECK-NOT: release_value +// CHECK: } // end sil function 'struct_with_two_trivial_enums' +sil @struct_with_two_trivial_enums : $@convention(thin) (Builtin.Int64) -> () { +bb0(%0 : $Builtin.Int64): + %1 = enum $U, #U.X!enumelt + %2 = enum $U, #U.Y!enumelt, %0 : $Builtin.Int64 + %3 = struct $TwoEnums (%1 : $U, %2 : $U) + release_value %3 : $TwoEnums + %5 = tuple () + return %5 : $() +} + +// CHECK-LABEL: sil @struct_with_non_trivial_enums +// CHECK: release_value +// CHECK: } // end sil function 'struct_with_non_trivial_enums' +sil @struct_with_non_trivial_enums : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : $C): + %1 = enum $U, #U.X!enumelt + %2 = enum $U, #U.Z!enumelt, %0 : $C + %3 = struct $TwoEnums (%1 : $U, %2 : $U) + release_value %3 : $TwoEnums + %5 = tuple () + return %5 : $() +} + +// CHECK-LABEL: sil @remove_value_to_bridge_object +// CHECK: bb0(%0 : $UInt64): +// CHECK-NEXT: tuple +// CHECK-NEXT: return +// CHECK: } // end sil function 'remove_value_to_bridge_object' +sil @remove_value_to_bridge_object : $@convention(thin) (UInt64) -> () { +bb0(%0 : $UInt64): + %1 = integer_literal $Builtin.Int64, -1945555039024054272 + %2 = value_to_bridge_object %1 : $Builtin.Int64 + %3 = struct $MyStringObj (%0 : $UInt64, %2 : $Builtin.BridgeObject) + %4 = struct $MyStringGuts (%3 : $MyStringObj) + %5 = struct $MyString (%4 : $MyStringGuts) + release_value %5 : $MyString + %7 = tuple () + return %7 : $() +} + +// CHECK-LABEL: sil @sil_destroyvalue_of_enum +// CHECK: bb0([[TYPE_ARG:%[0-9]+]] : $@thin U.Type, [[CLASS_ARG:%[0-9]+]] : $C): +// CHECK-NEXT: enum $U, #U.X!enumelt +// CHECK-NEXT: integer_literal $Builtin.Int64, 32 +// CHECK-NEXT: enum $U, #U.Y!enumelt, {{%[0-9]+}} : $Builtin.Int64 +// CHECK-NEXT: [[INVALID_CASE:%[0-9]+]] = enum $U, #U.Z!enumelt, {{%[0-9]+}} : $C +// CHECK-NEXT: strong_release [[CLASS_ARG]] : $C +// CHECK-NEXT: tuple ({{%[0-9]+}} : $U, {{%[0-9]+}} : $U, [[INVALID_CASE]] : $U) +// CHECK-NEXT: return +// CHECK: } // end sil function 'sil_destroyvalue_of_enum' +sil @sil_destroyvalue_of_enum : $@convention(thin) (@thin U.Type, C) -> (U, U, U) { +bb0(%0 : $@thin U.Type, %1 : $C): + %2 = enum $U, #U.X!enumelt + release_value %2 : $U + %3 = integer_literal $Builtin.Int64, 32 + %4 = enum $U, #U.Y!enumelt, %3 : $Builtin.Int64 + release_value %4 : $U + %5 = enum $U, #U.Z!enumelt, %1 : $C + release_value %5 : $U + %6 = tuple(%2 : $U, %4 : $U, %5 : $U) + return %6 : $(U, U, U) +} + +// CHECK-LABEL: sil @ref_ops_of_enum_is_equivalent_to_ref_ops_of_payload : +// CHECK: bb0 +// CHECK-NEXT: enum +// CHECK-NEXT: strong_release %0 +// CHECK-NEXT: return +// CHECK: } // end sil function 'ref_ops_of_enum_is_equivalent_to_ref_ops_of_payload' +sil @ref_ops_of_enum_is_equivalent_to_ref_ops_of_payload : $@convention(thin) (Builtin.NativeObject) -> Optional { +bb0(%0 : $Builtin.NativeObject): + %1 = enum $Optional, #Optional.some!enumelt, %0 : $Builtin.NativeObject + release_value %1 : $Optional + return %1 : $Optional +} + +// CHECK-LABEL: sil @lower_retainrelease_value_of_unowned_to_unowned_retainrelease : +// CHECK: unowned_release +// CHECK: } // end sil function 'lower_retainrelease_value_of_unowned_to_unowned_retainrelease' +sil @lower_retainrelease_value_of_unowned_to_unowned_retainrelease : $@convention(thin) (@owned @sil_unowned C) -> () { +bb0(%0 : $@sil_unowned C): + release_value %0 : $@sil_unowned C + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil @optimize_arc_with_trivial_payload_enum +// CHECK: bb0(%0 : $UInt64): +// CHECK-NEXT: tuple +// CHECK-NEXT: return +// CHECK: } // end sil function 'optimize_arc_with_trivial_payload_enum' +sil @optimize_arc_with_trivial_payload_enum : $@convention(thin) (UInt64) -> () { +bb0(%0 : $UInt64): + %1 = enum $EnumWithPayload, #EnumWithPayload.A!enumelt, %0 : $UInt64 + release_value %1 : $EnumWithPayload + %7 = tuple () + return %7 : $() +} + +// CHECK-LABEL: sil @optimize_arc_with_trivial_enum +// CHECK: bb0: +// CHECK-NEXT: tuple +// CHECK-NEXT: return +// CHECK: } // end sil function 'optimize_arc_with_trivial_enum' +sil @optimize_arc_with_trivial_enum : $@convention(thin) () -> () { +bb0: + %1 = enum $EnumWithPayload, #EnumWithPayload.C!enumelt + release_value %1 : $EnumWithPayload + %7 = tuple () + return %7 : $() +} + +// CHECK-LABEL: sil @dont_remove_release_of_nontrivial_enum +// CHECK: strong_release %0 +// CHECK: } // end sil function 'dont_remove_release_of_nontrivial_enum' +sil @dont_remove_release_of_nontrivial_enum : $@convention(thin) (@guaranteed AnyObject) -> () { +bb0(%0 : $AnyObject): + %1 = enum $EnumWithPayload, #EnumWithPayload.B!enumelt, %0 : $AnyObject + release_value %1 : $EnumWithPayload + %7 = tuple () + return %7 : $() +} + + diff --git a/test/SILOptimizer/simplify_retain_value.sil b/test/SILOptimizer/simplify_retain_value.sil new file mode 100644 index 0000000000000..f345448f85d73 --- /dev/null +++ b/test/SILOptimizer/simplify_retain_value.sil @@ -0,0 +1,186 @@ +// RUN: %target-sil-opt -enable-experimental-feature MoveOnlyEnumDeinits -enable-sil-verify-all %s -simplification -simplify-instruction=retain_value | %FileCheck %s + +// REQUIRES: swift_in_compiler + +import Swift +import Builtin + +class C {} + +enum U { + init() + case X + case Y(Builtin.Int64) + case Z(C) +} + +struct WithDeinit : ~Copyable { + deinit +} + +struct Outer : ~Copyable { + var wd: WithDeinit +} + +enum EnumWithDeinit: ~Copyable { + case A(Int64) + case B + + deinit +} + +struct TwoEnums { + var u1: U + var u2: U +} + +struct MyStringObj { + @_hasStorage var a: UInt64 + @_hasStorage var b: Builtin.BridgeObject +} + +struct MyStringGuts { + @_hasStorage var o: MyStringObj +} + +struct MyString { + @_hasStorage var g: MyStringGuts +} + +// CHECK-LABEL: sil @retain_value_test +// CHECK: bb0([[TRIVIAL_TARGET:%[0-9]+]] : $Builtin.Int8, [[REFCOUNT_TARGET:%[0-9]+]] : $Builtin.NativeObject): +// CHECK-NEXT: strong_retain [[REFCOUNT_TARGET]] : $Builtin.NativeObject +// CHECK-NEXT: tuple ([[TRIVIAL_TARGET]] : $Builtin.Int8, [[REFCOUNT_TARGET]] : $Builtin.NativeObject) +// CHECK-NEXT: return +// CHECK: } // end sil function 'retain_value_test' +sil @retain_value_test : $@convention(thin) (Builtin.Int8, Builtin.NativeObject) -> (Builtin.Int8, Builtin.NativeObject) { +bb0(%0 : $Builtin.Int8, %1 : $Builtin.NativeObject): + retain_value %0 : $Builtin.Int8 + retain_value %1 : $Builtin.NativeObject + %4 = tuple(%0 : $Builtin.Int8, %1 : $Builtin.NativeObject) + return %4 : $(Builtin.Int8, Builtin.NativeObject) +} + +// CHECK-LABEL: sil @dont_remove_struct_with_deinit_field +// CHECK: retain_value %0 +// CHECK: } // end sil function 'dont_remove_struct_with_deinit_field' +sil @dont_remove_struct_with_deinit_field : $@convention(thin) (Outer) -> () { +bb0(%0 : $Outer): + retain_value %0 : $Outer + %2 = tuple () + return %2 : $() +} + +// CHECK-LABEL: sil @struct_with_two_trivial_enums +// CHECK-NOT: retain_value +// CHECK: } // end sil function 'struct_with_two_trivial_enums' +sil @struct_with_two_trivial_enums : $@convention(thin) (Builtin.Int64) -> () { +bb0(%0 : $Builtin.Int64): + %1 = enum $U, #U.X!enumelt + %2 = enum $U, #U.Y!enumelt, %0 : $Builtin.Int64 + %3 = struct $TwoEnums (%1 : $U, %2 : $U) + retain_value %3 : $TwoEnums + %5 = tuple () + return %5 : $() +} + +// CHECK-LABEL: sil @struct_with_non_trivial_enums +// CHECK: retain_value +// CHECK: } // end sil function 'struct_with_non_trivial_enums' +sil @struct_with_non_trivial_enums : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : $C): + %1 = enum $U, #U.X!enumelt + %2 = enum $U, #U.Z!enumelt, %0 : $C + %3 = struct $TwoEnums (%1 : $U, %2 : $U) + retain_value %3 : $TwoEnums + %5 = tuple () + return %5 : $() +} + +// CHECK-LABEL: sil @remove_value_to_bridge_object +// CHECK: bb0(%0 : $UInt64): +// CHECK-NEXT: tuple +// CHECK-NEXT: return +// CHECK: } // end sil function 'remove_value_to_bridge_object' +sil @remove_value_to_bridge_object : $@convention(thin) (UInt64) -> () { +bb0(%0 : $UInt64): + %1 = integer_literal $Builtin.Int64, -1945555039024054272 + %2 = value_to_bridge_object %1 : $Builtin.Int64 + %3 = struct $MyStringObj (%0 : $UInt64, %2 : $Builtin.BridgeObject) + %4 = struct $MyStringGuts (%3 : $MyStringObj) + %5 = struct $MyString (%4 : $MyStringGuts) + retain_value %5 : $MyString + %7 = tuple () + return %7 : $() +} + +// CHECK-LABEL: sil @sil_copyvalue_of_enum +// CHECK: bb0([[TYPE_ARG:%[0-9]+]] : $@thin U.Type, [[CLASS_ARG:%[0-9]+]] : $C): +// CHECK-NEXT: enum $U, #U.X!enumelt +// CHECK-NEXT: integer_literal $Builtin.Int64, 32 +// CHECK-NEXT: enum $U, #U.Y!enumelt, {{%[0-9]+}} : $Builtin.Int64 +// CHECK-NEXT: [[INVALID_CASE:%[0-9]+]] = enum $U, #U.Z!enumelt, {{%[0-9]+}} : $C +// CHECK-NEXT: strong_retain [[CLASS_ARG]] : $C +// CHECK-NEXT: tuple ({{%[0-9]+}} : $U, {{%[0-9]+}} : $U, [[INVALID_CASE]] : $U) +// CHECK-NEXT: return +// CHECK: } // end sil function 'sil_copyvalue_of_enum' +sil @sil_copyvalue_of_enum : $@convention(thin) (@thin U.Type, C) -> (U, U, U) { +bb0(%0 : $@thin U.Type, %1 : $C): + %2 = enum $U, #U.X!enumelt + retain_value %2 : $U + %3 = integer_literal $Builtin.Int64, 32 + %4 = enum $U, #U.Y!enumelt, %3 : $Builtin.Int64 + retain_value %4 : $U + %5 = enum $U, #U.Z!enumelt, %1 : $C + retain_value %5 : $U + %6 = tuple(%2 : $U, %4 : $U, %5 : $U) + return %6 : $(U, U, U) +} + +// CHECK-LABEL: sil @dont_replace_payload_of_enum_with_deinit +// CHECK: %1 = enum +// CHECK: retain_value %1 +// CHECK: } // end sil function 'dont_replace_payload_of_enum_with_deinit' +sil @dont_replace_payload_of_enum_with_deinit : $@convention(thin) (Int64) -> () { +bb0(%0 : $Int64): + %1 = enum $EnumWithDeinit, #EnumWithDeinit.A!enumelt, %0 : $Int64 + retain_value %1 : $EnumWithDeinit + %3 = tuple () + return %3 : $() +} + +// CHECK-LABEL: sil @release_then_retain_peephole +// CHECK: bb0 +// CHECK-NOT: retain_value +// CHECK-NOT: release_value +// CHECK-NEXT: return +// CHECK: } // end sil function 'release_then_retain_peephole' +sil @release_then_retain_peephole : $@convention(thin) (Optional) -> Optional { +bb0(%0 : $Optional): + release_value %0 : $Optional + retain_value %0 : $Optional + return %0 : $Optional +} + +// CHECK-LABEL: sil @ref_ops_of_enum_is_equivalent_to_ref_ops_of_payload : $@convention(thin) (Builtin.NativeObject) -> Optional { +// CHECK: bb0 +// CHECK-NEXT: enum +// CHECK-NEXT: strong_retain %0 +// CHECK-NEXT: return +// CHECK: } // end sil function 'ref_ops_of_enum_is_equivalent_to_ref_ops_of_payload' +sil @ref_ops_of_enum_is_equivalent_to_ref_ops_of_payload : $@convention(thin) (Builtin.NativeObject) -> Optional { +bb0(%0 : $Builtin.NativeObject): + %1 = enum $Optional, #Optional.some!enumelt, %0 : $Builtin.NativeObject + retain_value %1 : $Optional + return %1 : $Optional +} + +// CHECK-LABEL: sil @lower_retainrelease_value_of_unowned_to_unowned_retainrelease : +// CHECK: unowned_retain +// CHECK: } // end sil function 'lower_retainrelease_value_of_unowned_to_unowned_retainrelease' +sil @lower_retainrelease_value_of_unowned_to_unowned_retainrelease : $@convention(thin) (@owned @sil_unowned C) -> @owned @sil_unowned C { +bb0(%0 : $@sil_unowned C): + retain_value %0 : $@sil_unowned C + return %0 : $@sil_unowned C +} + diff --git a/test/Serialization/Recovery/module-recovery-remarks.swift b/test/Serialization/Recovery/module-recovery-remarks.swift index d5b05300e3434..e27b183de9baf 100644 --- a/test/Serialization/Recovery/module-recovery-remarks.swift +++ b/test/Serialization/Recovery/module-recovery-remarks.swift @@ -15,6 +15,43 @@ /// Main error downgraded to a remark. // CHECK-MOVED: LibWithXRef.swiftmodule:1:1: remark: reference to type 'MyType' broken by a context change; 'MyType' was expected to be in 'A', but now a candidate is found only in 'A_related' + +/// Contextual notes about the modules involved. +// CHECK-MOVED: :0: note: the type was expected to be found in module 'A' at ' +// CHECK-MOVED-SAME: A.swiftmodule' +// CHECK-MOVED: :0: note: or expected to be found in the underlying module 'A' defined at ' +// CHECK-MOVED-SAME: module.modulemap' +// CHECK-MOVED: :0: note: the type was actually found in module 'A_related' at ' +// CHECK-MOVED-SAME: A_related.swiftmodule' + +/// More notes depending on the context +// CHECK-MOVED: :0: note: the module 'LibWithXRef' was built with a Swift language version set to 5 +// CHECK-MOVED-SAME: while the current invocation uses 4 + +// CHECK-MOVED: :0: note: the module 'LibWithXRef' has enabled library-evolution; the following file may need to be deleted if the SDK was modified: ' +// CHECK-MOVED-SAME: LibWithXRef.swiftmodule' +// CHECK-MOVED: :0: note: declarations in the underlying clang module 'A' may be hidden by clang preprocessor macros +// CHECK-MOVED: :0: note: the distributed module 'LibWithXRef' refers to the local module 'A'; this may be caused by header maps or search paths +// CHECK-MOVED: :0: note: the type 'MyType' moved between related modules; clang preprocessor macros may affect headers shared between these modules +// CHECK-MOVED: LibWithXRef.swiftmodule:1:1: note: could not deserialize type for 'foo()' +// CHECK-MOVED: error: cannot find 'foo' in scope + +/// Move A to the SDK, triggering a different note about layering. +// RUN: mv %t/A.swiftmodule %t/sdk/A.swiftmodule +// RUN: not %target-swift-frontend -c -O %t/Client.swift -I %t -I %t/sdk -Rmodule-recovery -sdk %t/sdk 2>&1 \ +// RUN: | %FileCheck --check-prefixes CHECK-LAYERING-FOUND %s +// CHECK-LAYERING-FOUND: :0: note: the reference may break layering; the candidate was found in the local module 'A_related' for a reference from the distributed module 'LibWithXRef' +// CHECK-LAYERING-FOUND: error: cannot find 'foo' in scope + +/// Delete A, keep only the underlying clangmodule for notes about clang modules. +// RUN: rm %t/sdk/A.swiftmodule +// RUN: %target-swift-frontend %t/Empty.swift -emit-module-path %t/A_related.swiftmodule -module-name A_related +// RUN: not %target-swift-frontend -c -O %t/Client.swift -I %t -I %t/sdk -Rmodule-recovery -sdk %t/sdk 2>&1 \ +// RUN: | %FileCheck --check-prefixes CHECK-CLANG %s +// CHECK-CLANG: :0: note: declarations in the clang module 'A' may be hidden by clang preprocessor macros +// CHECK-CLANG: error: cannot find 'foo' in scope + + //--- module.modulemap module A { header "A.h" diff --git a/test/Serialization/modularization-error.swift b/test/Serialization/modularization-error.swift index 478b654a79469..4f58b44788c68 100644 --- a/test/Serialization/modularization-error.swift +++ b/test/Serialization/modularization-error.swift @@ -18,8 +18,14 @@ // RUN: %target-swift-frontend -emit-sil %t/LibWithXRef.swiftmodule -module-name LibWithXRef -I %t \ // RUN: -experimental-force-workaround-broken-modules 2>&1 \ // RUN: | %FileCheck --check-prefixes CHECK-WORKAROUND %s -// CHECK-WORKAROUND: warning: attempting forced recovery enabled by -experimental-force-workaround-broken-modules -// CHECK-WORKAROUND: note: reference to type 'MyType' broken by a context change; 'MyType' was expected to be in 'A', but now a candidate is found only in 'B' +// CHECK-WORKAROUND: LibWithXRef.swiftmodule:1:1: warning: reference to type 'MyType' broken by a context change; 'MyType' was expected to be in 'A', but now a candidate is found only in 'B' +// CHECK-WORKAROUND-NEXT: A.MyType +// CHECK-WORKAROUND-NEXT: ^ +// CHECK-WORKAROUND: :0: note: the type was expected to be found in module 'A' at ' +// CHECK-WORKAROUND-SAME: A.swiftmodule' +// CHECK-WORKAROUND: :0: note: the type was actually found in module 'B' at ' +// CHECK-WORKAROUND-SAME: B.swiftmodule' +// CHECK-WORKAROUND: :0: note: attempting forced recovery enabled by -experimental-force-workaround-broken-modules // CHECK-WORKAROUND: func foo() -> some Proto /// Change MyType into a function. @@ -34,7 +40,7 @@ // RUN: %target-swift-frontend %t/LibTypeChanged.swift -emit-module-path %t/B.swiftmodule -module-name B // RUN: not %target-swift-frontend -emit-sil %t/LibWithXRef.swiftmodule -module-name LibWithXRef -I %t 2>&1 \ // RUN: | %FileCheck --check-prefixes CHECK,CHECK-KIND-CHANGED-AND-MOVED %s -// CHECK-KIND-CHANGED-AND-MOVED: LibWithXRef.swiftmodule:1:1: error: reference to type 'MyType' broken by a context change; the declaration kind of 'MyType' changed since building 'LibWithXRef', it was in 'A' and is now found in 'B' +// CHECK-KIND-CHANGED-AND-MOVED: LibWithXRef.swiftmodule:1:1: error: reference to type 'MyType' broken by a context change; the declaration kind of 'MyType' changed since building 'LibWithXRef', it was in 'A' and is now a candidate is found only in 'B' /// Remove MyType from all imported modules. // RUN: %target-swift-frontend %t/Empty.swift -emit-module-path %t/A.swiftmodule -module-name A diff --git a/test/SourceKit/Macros/macro_basic.swift b/test/SourceKit/Macros/macro_basic.swift index f7c581b0066d8..9109b45f45980 100644 --- a/test/SourceKit/Macros/macro_basic.swift +++ b/test/SourceKit/Macros/macro_basic.swift @@ -131,7 +131,7 @@ macro anonymousTypes(_: () -> String) = #externalMacro(module: "MacroDefinition" // RUN: %sourcekitd-test -req=refactoring.expand.macro -pos=57:1 %s -- ${COMPILER_ARGS[@]} -parse-as-library | %FileCheck -check-prefix=EXPAND_MACRO_DECL %s // RUN: %sourcekitd-test -req=refactoring.expand.macro -pos=57:2 %s -- ${COMPILER_ARGS[@]} -parse-as-library | %FileCheck -check-prefix=EXPAND_MACRO_DECL %s // EXPAND_MACRO_DECL: source.edit.kind.active: -// EXPAND_MACRO_DECL-NEXT: 57:1-57:28 (@__swiftmacro_9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf0_.swift) "class $s9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf0_4namefMu_ { +// EXPAND_MACRO_DECL-NEXT: 57:1-57:28 (@__swiftmacro_9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf_.swift) "class $s9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf_4namefMu_ { // EXPAND_MACRO_DECL-NEXT: func hello() -> String { // EXPAND_MACRO_DECL-NEXT: "hello" // EXPAND_MACRO_DECL-NEXT: } @@ -140,7 +140,7 @@ macro anonymousTypes(_: () -> String) = #externalMacro(module: "MacroDefinition" // EXPAND_MACRO_DECL-NEXT: return Self.self // EXPAND_MACRO_DECL-NEXT: } // EXPAND_MACRO_DECL-NEXT: } -// EXPAND_MACRO_DECL-NEXT: enum $s9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf0_4namefMu0_ { +// EXPAND_MACRO_DECL-NEXT: enum $s9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf_4namefMu0_ { // EXPAND_MACRO_DECL-NEXT: case apple // EXPAND_MACRO_DECL-NEXT: case banana // EXPAND_MACRO_DECL-EMPTY: @@ -148,7 +148,7 @@ macro anonymousTypes(_: () -> String) = #externalMacro(module: "MacroDefinition" // EXPAND_MACRO_DECL-NEXT: "hello" // EXPAND_MACRO_DECL-NEXT: } // EXPAND_MACRO_DECL-NEXT: } -// EXPAND_MACRO_DECL-NEXT: struct $s9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf0_4namefMu1_: Equatable { +// EXPAND_MACRO_DECL-NEXT: struct $s9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf_4namefMu1_: Equatable { // EXPAND_MACRO_DECL-NEXT: static func == (lhs: Self, rhs: Self) -> Bool { // EXPAND_MACRO_DECL-NEXT: false // EXPAND_MACRO_DECL-NEXT: } diff --git a/test/SourceKit/Macros/macro_option_set.swift b/test/SourceKit/Macros/macro_option_set.swift index 30d57830a504e..da617ba8ba4a0 100644 --- a/test/SourceKit/Macros/macro_option_set.swift +++ b/test/SourceKit/Macros/macro_option_set.swift @@ -16,6 +16,11 @@ func test(opts: ShippingOptions) { let _ = ShippingOptions.nextDay } +@attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) +@attached(conformance) +public macro OptionSet() = + #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") + // REQUIRES: swift_swift_parser // RUN: %empty-directory(%t) diff --git a/test/SourceKit/Macros/syntactic_expansion.swift b/test/SourceKit/Macros/syntactic_expansion.swift new file mode 100644 index 0000000000000..17d3f7ae8b032 --- /dev/null +++ b/test/SourceKit/Macros/syntactic_expansion.swift @@ -0,0 +1,82 @@ +//--- test.swift +@DelegatedConformance +@wrapAllProperties +struct Generic { + + @myPropertyWrapper + @otherAttr + var value: Int + + func member() {} + var otherVal: Int = 1 + + #bitwidthNumberedStructs("blah") +} + +//--- DelegatedConformance.json +{ + key.macro_roles: [source.lang.swift.macro_role.conformance, source.lang.swift.macro_role.member], + key.modulename: "MacroDefinition", + key.typename: "DelegatedConformanceMacro", +} + +//--- myPropertyWrapper.json +{ + key.macro_roles: [source.lang.swift.macro_role.accessor, source.lang.swift.macro_role.peer], + key.modulename: "MacroDefinition", + key.typename: "PropertyWrapperMacro", +} + +//--- wrapAllProperties.json +{ + key.macro_roles: [source.lang.swift.macro_role.member_attribute], + key.modulename: "MacroDefinition", + key.typename: "WrapAllProperties", +} + +//--- bitwidthNumberedStructs.json +{ + key.macro_roles: [source.lang.swift.macro_role.declaration], + key.modulename: "MacroDefinition", + key.typename: "DefineBitwidthNumberedStructsMacro", +} + +//--- dummy.script +// REQUIRES: swift_swift_parser + +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/plugins +// RUN: split-file %s %t + +//##-- Prepare the macro plugin. +// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/plugins/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/../../Macros/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath + +// RUN: %sourcekitd-test \ +// RUN: -shell -- echo '### 1' \ +// RUN: == \ +// RUN: -req=syntactic-expandmacro \ +// RUN: -req-opts=1:1:%t/DelegatedConformance.json \ +// RUN: -req-opts=5:3:%t/myPropertyWrapper.json \ +// RUN: -req-opts=2:1:%t/wrapAllProperties.json \ +// RUN: -req-opts=12:3:%t/bitwidthNumberedStructs.json \ +// RUN: %t/test.swift \ +// RUN: -- \ +// RUN: %t/test.swift \ +// RUN: -plugin-path %t/plugins -Xfrontend -dump-macro-expansions \ +// RUN: -module-name TestModule \ +// RUN: == \ +// RUN: -shell -- echo '### 2' \ +// RUN: == \ +// RUN: -req=syntactic-expandmacro \ +// RUN: -req-opts=12:3:%t/bitwidthNumberedStructs.json \ +// RUN: -req-opts=2:1:%t/wrapAllProperties.json \ +// RUN: -req-opts=5:3:%t/myPropertyWrapper.json \ +// RUN: -req-opts=1:1:%t/DelegatedConformance.json \ +// RUN: %t/test.swift \ +// RUN: -- \ +// RUN: %t/test.swift \ +// RUN: -plugin-path %t/plugins -Xfrontend -dump-macro-expansions \ +// RUN: -module-name TestModule \ +// RUN: | tee %t.response + +// RUN: diff -u %s.expected %t.response diff --git a/test/SourceKit/Macros/syntactic_expansion.swift.expected b/test/SourceKit/Macros/syntactic_expansion.swift.expected new file mode 100644 index 0000000000000..90a0ccf5b0f65 --- /dev/null +++ b/test/SourceKit/Macros/syntactic_expansion.swift.expected @@ -0,0 +1,108 @@ +### 1 +source.edit.kind.active: + 1:1-1:22 "" +source.edit.kind.active: + 13:1-13:1 "static func requirement() where Element : P { + Element.requirement() +}" +source.edit.kind.active: + 13:2-13:2 "extension Generic : P where Element: P {}" +source.edit.kind.active: + 5:3-5:21 "" +source.edit.kind.active: + 7:17-7:17 "{ + get { + _value.wrappedValue + } + + set { + _value.wrappedValue = newValue + } +}" +source.edit.kind.active: + 7:12-7:12 "var _value: MyWrapperThingy" +source.edit.kind.active: + 2:1-2:19 "" +source.edit.kind.active: + 7:3-7:3 "@Wrapper" +source.edit.kind.active: + 10:3-10:3 "@Wrapper" +source.edit.kind.active: + 12:3-12:35 "struct blah8 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu0_() { + } +} +struct blah16 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu1_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu2_() { + } +} +struct blah32 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu3_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu4_() { + } +} +struct blah64 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu5_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu6_() { + } +}" +### 2 +source.edit.kind.active: + 12:3-12:35 "struct blah8 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu0_() { + } +} +struct blah16 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu1_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu2_() { + } +} +struct blah32 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu3_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu4_() { + } +} +struct blah64 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu5_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu6_() { + } +}" +source.edit.kind.active: + 2:1-2:19 "" +source.edit.kind.active: + 7:3-7:3 "@Wrapper" +source.edit.kind.active: + 10:3-10:3 "@Wrapper" +source.edit.kind.active: + 5:3-5:21 "" +source.edit.kind.active: + 7:17-7:17 "{ + get { + _value.wrappedValue + } + + set { + _value.wrappedValue = newValue + } +}" +source.edit.kind.active: + 7:12-7:12 "var _value: MyWrapperThingy" +source.edit.kind.active: + 1:1-1:22 "" +source.edit.kind.active: + 13:1-13:1 "static func requirement() where Element : P { + Element.requirement() +}" +source.edit.kind.active: + 13:2-13:2 "extension Generic : P where Element: P {}" diff --git a/test/SourceKit/SyntaxMapData/consuming_borrowing.swift b/test/SourceKit/SyntaxMapData/consuming_borrowing.swift new file mode 100644 index 0000000000000..7bcb31a396d25 --- /dev/null +++ b/test/SourceKit/SyntaxMapData/consuming_borrowing.swift @@ -0,0 +1,60 @@ +func foo(a: consuming Int, b: borrowing Int, c: _const Int) {} + +// RUN: %sourcekitd-test -req=syntax-map %s | %FileCheck %s + +// CHECK: key.syntaxmap: [ +// CHECK-NEXT: { +// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword, +// CHECK-NEXT: key.offset: 0, +// CHECK-NEXT: key.length: 4 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier, +// CHECK-NEXT: key.offset: 5, +// CHECK-NEXT: key.length: 3 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier, +// CHECK-NEXT: key.offset: 9, +// CHECK-NEXT: key.length: 1 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword, +// CHECK-NEXT: key.offset: 12, +// CHECK-NEXT: key.length: 9 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.typeidentifier, +// CHECK-NEXT: key.offset: 22, +// CHECK-NEXT: key.length: 3 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier, +// CHECK-NEXT: key.offset: 27, +// CHECK-NEXT: key.length: 1 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword, +// CHECK-NEXT: key.offset: 30, +// CHECK-NEXT: key.length: 9 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.typeidentifier, +// CHECK-NEXT: key.offset: 40, +// CHECK-NEXT: key.length: 3 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier, +// CHECK-NEXT: key.offset: 45, +// CHECK-NEXT: key.length: 1 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword, +// CHECK-NEXT: key.offset: 48, +// CHECK-NEXT: key.length: 6 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.typeidentifier, +// CHECK-NEXT: key.offset: 55, +// CHECK-NEXT: key.length: 3 +// CHECK-NEXT: }, diff --git a/test/decl/var/init_accessors.swift b/test/decl/var/init_accessors.swift new file mode 100644 index 0000000000000..93eeb37bdc5af --- /dev/null +++ b/test/decl/var/init_accessors.swift @@ -0,0 +1,138 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-feature InitAccessors + +// REQUIRES: asserts + +func test_empty_init_accessor() { + struct Test { + var empty: Int { + init { // Ok + } + + get { 42 } + set { } + } + + var noArgs: Int { + init(initialValue) { // Ok + } + + get { 42 } + set { } + } + } +} + +func test_invalid_init_accessor_use() { + var other: String = "" // expected-warning {{}} + var x: Int { + init(initialValue) initializes(other) {} + // expected-error@-1 {{init accessors could only be associated with properties}} + + get { 42 } + } + + struct X { + subscript(x: Int) -> Bool { + init(initialValue) {} // expected-error {{init accessors could only be associated with properties}} + + get { false } + } + } +} + +func test_use_of_initializes_accesses_on_non_inits() { + struct Test1 { + var x: Int + var y: String + + var _x: Int { + init(initialValue) initializes(x) accesses(y) { // Ok + } + + get { x } + } + + var _y: String { + get { y } + set(initialValue) initializes(y) {} + // expected-error@-1 {{initalizes(...) attribute could only be used with init accessors}} + } + + var _q: String { + get { y } + set(initialValue) accesses(x) {} + // expected-error@-1 {{accesses(...) attribute could only be used with init accessors}} + } + } +} + +func test_invalid_refs_in_init_attrs() { + struct Test { + var c: Int { get { 42 } } + var x: Int { + init(initialValue) initializes(a) accesses(b, c) {} + // expected-error@-1 {{find type 'a' in scope}} + // expected-error@-2 {{find type 'b' in scope}} + // expected-error@-3 {{init accessor cannot refer to property 'c'; init accessors can refer only to stored properties}} + } + + var y: String { + init(initialValue) initializes(test) {} + // expected-error@-1 {{ambiguous reference to member 'test'}} + } + + func test(_: Int) {} // expected-note {{'test' declared here}} + func test(_: String) -> Int { 42 } // expected-note {{'test' declared here}} + } +} + +func test_assignment_to_let_properties() { + struct Test { + let x: Int + let y: Int // expected-note {{change 'let' to 'var' to make it mutable}} + + var pointX: Int { + init(initialValue) initializes(x) accesses(y) { + self.x = initialValue // Ok + self.y = 42 // expected-error {{cannot assign to property: 'y' is a 'let' constant}} + } + } + + var point: (Int, Int) { + init(initialValue) initializes(x, y) { + self.x = initialValue.0 // Ok + self.y = initialValue.1 // Ok + } + } + } +} + +func test_duplicate_and_computed_lazy_properties() { + struct Test1 { + var _a: Int + var _b: Int + + var a: Int { + init(initialValue) initializes(_b, _a) accesses(_a) { + // expected-error@-1 {{property '_a' cannot be both initialized and accessed}} + } + } + } + + struct Test2 { + var _a: Int + + var a: Int { + init(initialValue) initializes(a, c) accesses(_a, b) {} + // expected-error@-1 {{init accessor cannot refer to property 'a'; init accessors can refer only to stored properties}} + // expected-error@-2 {{init accessor cannot refer to property 'b'; init accessors can refer only to stored properties}} + // expected-error@-3 {{init accessor cannot refer to property 'c'; init accessors can refer only to stored properties}} + } + + var b: Int { + get { 42 } + } + + lazy var c: Int = 42 + } +} diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index fa33d07393543..33acdbf13df9a 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -1338,7 +1338,7 @@ struct MissingPropertyWrapperUnwrap { struct InvalidPropertyDelegateUse { // TODO(diagnostics): We need to a tailored diagnostic for extraneous arguments in property delegate initialization - @Foo var x: Int = 42 // expected-error@:21 {{argument passed to call that takes no arguments}} + @Foo var x: Int = 42 // expected-error@:21 {{extra argument 'wrappedValue' in call}} func test() { self.x.foo() // expected-error {{value of type 'Int' has no member 'foo'}} diff --git a/test/lit.cfg b/test/lit.cfg index da873e4085de7..05bf54c07f73f 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -494,7 +494,7 @@ if backtracing is not None: backtrace_on_crash = lit_config.params.get('backtrace_on_crash', None) if backtrace_on_crash is not None: - config.environment['SWIFT_BACKTRACE'] = 'enable=on,output-to=stderr' + config.environment['SWIFT_BACKTRACE'] = 'enable=on' config.available_features.add('lld_lto') diff --git a/test/stdlib/StringDiagnostics.swift b/test/stdlib/StringDiagnostics.swift index c3fde8ff884f5..3a53a26c69ee0 100644 --- a/test/stdlib/StringDiagnostics.swift +++ b/test/stdlib/StringDiagnostics.swift @@ -48,7 +48,6 @@ func testAmbiguousStringComparisons(s: String) { // Shouldn't suggest 'as' in a pattern-matching context, as opposed to all these other situations if case nsString = "" {} // expected-error{{expression pattern of type 'NSString' cannot match values of type 'String'}} - // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists: (Substring, String)}} } func testStringDeprecation(hello: String) { diff --git a/test/stmt/errors.swift b/test/stmt/errors.swift index d71da201b4247..506b2230f775a 100644 --- a/test/stmt/errors.swift +++ b/test/stmt/errors.swift @@ -169,7 +169,8 @@ func thirteen() { thirteen_helper { (a) in // expected-error {{invalid conversion from throwing function of type '(Thirteen) throws -> ()' to non-throwing function type '(Thirteen) -> ()'}} do { try thrower() - } catch a { + // FIXME: Bad diagnostic (https://github.com/apple/swift/issues/63459) + } catch a { // expected-error {{binary operator '~=' cannot be applied to two 'any Error' operands}} } } } diff --git a/tools/SourceKit/include/SourceKit/Core/Context.h b/tools/SourceKit/include/SourceKit/Core/Context.h index 20d2d00714600..d4e9a234e3a5c 100644 --- a/tools/SourceKit/include/SourceKit/Core/Context.h +++ b/tools/SourceKit/include/SourceKit/Core/Context.h @@ -90,16 +90,6 @@ class RequestTracker { } public: - /// Returns \c true if the request with the given \p CancellationToken has - /// already been cancelled. - bool isCancelled(SourceKitCancellationToken CancellationToken) { - if (!CancellationToken) { - return false; - } - llvm::sys::ScopedLock L(RequestsMtx); - return isCancelledImpl(CancellationToken); - } - /// Adds a \p CancellationHandler that will be called when the request /// associated with the \p CancellationToken is cancelled. /// If that request has already been cancelled when this method is called, diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index 5dccd87dd59a0..4203393ca53b2 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -220,6 +220,64 @@ struct RawCharSourceRange { unsigned Length; }; +enum class MacroRole : uint8_t { + // This should align with 'swift::MacroRole'. + Expression = 0x01, + Declaration = 0x02, + Accessor = 0x04, + MemberAttribute = 0x08, + Member = 0x10, + Peer = 0x20, + Conformance = 0x40, + CodeItem = 0x80, +}; +using MacroRoles = swift::OptionSet; + +struct MacroExpansionInfo { + // See swift::ExternalMacroReference. + struct ExternalMacroReference { + std::string moduleName; + std::string typeName; + + ExternalMacroReference(StringRef moduleName, StringRef typeName) + : moduleName(moduleName), typeName(typeName){}; + }; + // See swift::ExpandedMacroDefinition. + struct ExpandedMacroDefinition { + // 'Replacement.range' references some part of code in 'expansionText'. + // 'expansionText' will be replaced by the 'parameterIndex'-th argument of + // the macro. + struct Replacement { + RawCharSourceRange range; + unsigned parameterIndex; + Replacement(RawCharSourceRange range, unsigned parameterIndex) + : range(range), parameterIndex(parameterIndex) {} + }; + std::string expansionText; + std::vector replacements; + + ExpandedMacroDefinition(StringRef expansionText) + : expansionText(expansionText), replacements(){}; + }; + + // Offset of the macro expansion syntax (i.e. attribute or #) from + // the start of the source file. + unsigned offset; + + // Macro roles. + MacroRoles roles; + + // Tagged union of macro definition. + std::variant macroDefinition; + + MacroExpansionInfo(unsigned offset, MacroRoles roles, + ExternalMacroReference macroRef) + : offset(offset), roles(roles), macroDefinition(macroRef) {} + MacroExpansionInfo(unsigned offset, MacroRoles roles, + ExpandedMacroDefinition definition) + : offset(offset), roles(roles), macroDefinition(definition) {} +}; + /// Stores information about a given buffer, including its name and, if /// generated, its source text and original location. struct BufferInfo { @@ -1128,6 +1186,11 @@ class LangSupport { ConformingMethodListConsumer &Consumer, Optional vfsOptions) = 0; + virtual void expandMacroSyntactically(llvm::MemoryBuffer *inputBuf, + ArrayRef args, + ArrayRef expansions, + CategorizedEditsReceiver receiver) = 0; + virtual void performCompile(StringRef Name, ArrayRef Args, Optional vfsOptions, diff --git a/tools/SourceKit/lib/SwiftLang/CMakeLists.txt b/tools/SourceKit/lib/SwiftLang/CMakeLists.txt index 38725d140d215..31f596e5a0019 100644 --- a/tools/SourceKit/lib/SwiftLang/CMakeLists.txt +++ b/tools/SourceKit/lib/SwiftLang/CMakeLists.txt @@ -11,6 +11,7 @@ add_sourcekit_library(SourceKitSwiftLang SwiftLangSupport.cpp SwiftMangling.cpp SwiftSourceDocInfo.cpp + SwiftSyntacticMacroExpansion.cpp SwiftTypeContextInfo.cpp LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} bitreader diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index a8d0d9266c513..6f8b019d2db92 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -29,6 +29,7 @@ #include "swift/IDE/SyntaxModel.h" #include "swift/IDE/Utils.h" #include "swift/IDETool/IDEInspectionInstance.h" +#include "swift/IDETool/SyntacticMacroExpansion.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" @@ -303,6 +304,9 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) // By default, just use the in-memory cache. CCCache->inMemory = std::make_unique(); + SyntacticMacroExpansions = + std::make_shared(SwiftExecutablePath, Plugins); + // Provide a default file system provider. setFileSystemProvider("in-memory-vfs", std::make_unique()); } @@ -369,6 +373,8 @@ UIdent SwiftLangSupport::getUIDForAccessor(const ValueDecl *D, return IsRef ? KindRefAccessorRead : KindDeclAccessorRead; case AccessorKind::Modify: return IsRef ? KindRefAccessorModify : KindDeclAccessorModify; + case AccessorKind::Init: + return IsRef ? KindRefAccessorInit : KindDeclAccessorInit; } llvm_unreachable("Unhandled AccessorKind in switch."); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 5fd15b589bd20..ad4a8a7fe5769 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -55,6 +55,7 @@ namespace ide { class IDEInspectionInstance; class OnDiskCodeCompletionCache; class SourceEditConsumer; + class SyntacticMacroExpansion; enum class CodeCompletionDeclKind : uint8_t; enum class SyntaxNodeKind : uint8_t; enum class SyntaxStructureKind : uint8_t; @@ -366,6 +367,7 @@ class SwiftLangSupport : public LangSupport { llvm::StringMap> FileSystemProviders; std::shared_ptr IDEInspectionInst; std::shared_ptr CompileManager; + std::shared_ptr SyntacticMacroExpansions; public: explicit SwiftLangSupport(SourceKit::Context &SKCtx); @@ -756,6 +758,11 @@ class SwiftLangSupport : public LangSupport { ConformingMethodListConsumer &Consumer, Optional vfsOptions) override; + void expandMacroSyntactically(llvm::MemoryBuffer *inputBuf, + ArrayRef args, + ArrayRef expansions, + CategorizedEditsReceiver receiver) override; + void performCompile(StringRef Name, ArrayRef Args, Optional vfsOptions, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSyntacticMacroExpansion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSyntacticMacroExpansion.cpp new file mode 100644 index 0000000000000..170ca176bc7f0 --- /dev/null +++ b/tools/SourceKit/lib/SwiftLang/SwiftSyntacticMacroExpansion.cpp @@ -0,0 +1,94 @@ +//===--- SwiftSyntaxMacro.cpp ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 "SwiftLangSupport.h" +#include "swift/AST/MacroDefinition.h" +#include "swift/Frontend/Frontend.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/IDE/TypeContextInfo.h" +#include "swift/IDETool/SyntacticMacroExpansion.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Comment.h" +#include "clang/AST/Decl.h" + +using namespace SourceKit; +using namespace swift; +using namespace ide; + +void SwiftLangSupport::expandMacroSyntactically( + llvm::MemoryBuffer *inputBuf, ArrayRef args, + ArrayRef reqExpansions, + CategorizedEditsReceiver receiver) { + + std::string error; + auto instance = SyntacticMacroExpansions->getInstance(args, error); + if (!instance) { + return receiver( + RequestResult>::fromError(error)); + } + auto &ctx = instance->getASTContext(); + + // Convert 'SourceKit::MacroExpansionInfo' to 'ide::MacroExpansionSpecifier'. + SmallVector expansions; + for (auto &req : reqExpansions) { + unsigned offset = req.offset; + + swift::MacroRoles macroRoles; + if (req.roles.contains(SourceKit::MacroRole::Expression)) + macroRoles |= swift::MacroRole::Expression; + if (req.roles.contains(SourceKit::MacroRole::Declaration)) + macroRoles |= swift::MacroRole::Declaration; + if (req.roles.contains(SourceKit::MacroRole::CodeItem)) + macroRoles |= swift::MacroRole::CodeItem; + if (req.roles.contains(SourceKit::MacroRole::Accessor)) + macroRoles |= swift::MacroRole::Accessor; + if (req.roles.contains(SourceKit::MacroRole::MemberAttribute)) + macroRoles |= swift::MacroRole::MemberAttribute; + if (req.roles.contains(SourceKit::MacroRole::Member)) + macroRoles |= swift::MacroRole::Member; + if (req.roles.contains(SourceKit::MacroRole::Peer)) + macroRoles |= swift::MacroRole::Peer; + if (req.roles.contains(SourceKit::MacroRole::Conformance)) + macroRoles |= swift::MacroRole::Conformance; + + MacroDefinition definition = [&] { + if (auto *expanded = + std::get_if( + &req.macroDefinition)) { + SmallVector replacements; + for (auto &reqReplacement : expanded->replacements) { + replacements.push_back( + {/*startOffset=*/reqReplacement.range.Offset, + /*endOffset=*/reqReplacement.range.Offset + + reqReplacement.range.Length, + /*parameterIndex=*/reqReplacement.parameterIndex}); + } + return MacroDefinition::forExpanded(ctx, expanded->expansionText, + replacements); + } else if (auto *externalRef = + std::get_if( + &req.macroDefinition)) { + return MacroDefinition::forExternal( + ctx.getIdentifier(externalRef->moduleName), + ctx.getIdentifier(externalRef->typeName)); + } else { + return MacroDefinition::forUndefined(); + } + }(); + + expansions.push_back({offset, macroRoles, definition}); + } + + RequestRefactoringEditConsumer consumer(receiver); + instance->expandAll(inputBuf, expansions, consumer); + // consumer automatically send the results on destruction. +} diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp index e92982d18ea0a..d6f97c5d446ef 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp @@ -153,6 +153,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { .Case("diags", SourceKitRequest::Diagnostics) .Case("compile", SourceKitRequest::Compile) .Case("compile.close", SourceKitRequest::CompileClose) + .Case("syntactic-expandmacro", SourceKitRequest::SyntacticMacroExpansion) #define SEMANTIC_REFACTORING(KIND, NAME, ID) .Case("refactoring." #ID, SourceKitRequest::KIND) #include "swift/Refactoring/RefactoringKinds.def" .Default(SourceKitRequest::None); @@ -203,6 +204,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { << "- collect-type\n" << "- global-config\n" << "- dependency-updated\n" + << "- syntactic-expandmacro\n" #define SEMANTIC_REFACTORING(KIND, NAME, ID) << "- refactoring." #ID "\n" #include "swift/Refactoring/RefactoringKinds.def" "\n"; diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h index d8b9868dc28c9..ace443a58fa86 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h @@ -70,6 +70,7 @@ enum class SourceKitRequest { Diagnostics, Compile, CompileClose, + SyntacticMacroExpansion, #define SEMANTIC_REFACTORING(KIND, NAME, ID) KIND, #include "swift/Refactoring/RefactoringKinds.def" }; diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 2db2dc7206c92..84f4ae8482940 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -416,6 +416,46 @@ static bool readPopularAPIList(StringRef filename, return false; } +/// Read '-req-opts' for syntactic macro expansion request and apply it to 'req' +/// object. +/// The format of the argument is '-req-opts={line}:{column}:{path}' +/// where {path} is a path to a JSON file that has macro roles and definition. +/// {line} and {column} is resolved to 'offset' using \p inputBuf . +static bool setSyntacticMacroExpansions(sourcekitd_object_t req, + TestOptions &opts, + llvm::MemoryBuffer *inputBuf) { + SmallVector expansions; + for (std::string &opt : opts.RequestOptions) { + SmallVector args; + StringRef(opt).split(args, ":"); + unsigned line, column; + + if (args.size() != 3 || args[0].getAsInteger(10, line) || + args[1].getAsInteger(10, column)) { + llvm::errs() << "-req-opts should be {line}:{column}:{json-path}"; + return true; + } + unsigned offset = resolveFromLineCol(line, column, inputBuf); + + auto Buffer = getBufferForFilename(args[2], opts.VFSFiles)->getBuffer(); + char *Err = nullptr; + auto expansion = sourcekitd_request_create_from_yaml(Buffer.data(), &Err); + if (!expansion) { + assert(Err); + llvm::errs() << Err; + free(Err); + return true; + } + sourcekitd_request_dictionary_set_int64(expansion, KeyOffset, + int64_t(offset)); + expansions.push_back(expansion); + } + sourcekitd_request_dictionary_set_value( + req, KeyExpansions, + sourcekitd_request_array_create(expansions.data(), expansions.size())); + return false; +} + namespace { class PrintingTimer { std::string desc; @@ -1107,6 +1147,12 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCompileClose); break; + + case SourceKitRequest::SyntacticMacroExpansion: + sourcekitd_request_dictionary_set_uid(Req, KeyRequest, + RequestSyntacticMacroExpansion); + setSyntacticMacroExpansions(Req, Opts, SourceBuf.get()); + break; } if (!Opts.SourceFile.empty()) { @@ -1521,6 +1567,7 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts, #define SEMANTIC_REFACTORING(KIND, NAME, ID) case SourceKitRequest::KIND: #include "swift/Refactoring/RefactoringKinds.def" case SourceKitRequest::SyntacticRename: + case SourceKitRequest::SyntacticMacroExpansion: printSyntacticRenameEdits(Info, llvm::outs()); break; case SourceKitRequest::FindRenameRanges: diff --git a/tools/SourceKit/tools/sourcekitd/bin/InProc/sourcekitdInProc-darwin.exports b/tools/SourceKit/tools/sourcekitd/bin/InProc/sourcekitdInProc-darwin.exports index 4f42446abd74a..fa724cc440c3e 100644 --- a/tools/SourceKit/tools/sourcekitd/bin/InProc/sourcekitdInProc-darwin.exports +++ b/tools/SourceKit/tools/sourcekitd/bin/InProc/sourcekitdInProc-darwin.exports @@ -70,6 +70,9 @@ sourcekitd_variant_uid_get_value _ZN10sourcekitd13cancelRequestEPKv _ZN10sourcekitd13enableLoggingEN4llvm9StringRefE _ZN10sourcekitd13handleRequestEPvPKvNSt3__18functionIFvS0_EEE +_ZN10sourcekitd16requestIsBarrierEPv +_ZN10sourcekitd23requestIsEnableBarriersEPv +_ZN10sourcekitd27sendBarriersEnabledResponseENSt3__18functionIFvPvEEE _ZN10sourcekitd17initializeServiceEN4llvm9StringRefES1_S1_NSt3__18functionIFvPvEEE _ZN10sourcekitd24createErrorRequestFailedEN4llvm9StringRefE _ZN10sourcekitd24disposeCancellationTokenEPKv diff --git a/tools/SourceKit/tools/sourcekitd/bin/InProc/sourcekitdInProc.cpp b/tools/SourceKit/tools/sourcekitd/bin/InProc/sourcekitdInProc.cpp index 5672cfb5bc8a5..879fb56e3689c 100644 --- a/tools/SourceKit/tools/sourcekitd/bin/InProc/sourcekitdInProc.cpp +++ b/tools/SourceKit/tools/sourcekitd/bin/InProc/sourcekitdInProc.cpp @@ -34,6 +34,15 @@ using namespace SourceKit; +/// The queue on which the all incoming requests will be handled. If barriers +/// are enabled, open/edit/close requests will be dispatched as barriers on this +/// queue. +WorkQueue *msgHandlingQueue = nullptr; + +/// Whether request barriers have been enabled, i.e. whether open/edit/close +/// requests should be dispatched as barriers. +static bool RequestBarriersEnabled = false; + static void postNotification(sourcekitd_response_t Notification); static void getToolchainPrefixPath(llvm::SmallVectorImpl &Path) { @@ -96,6 +105,9 @@ static std::string getDiagnosticDocumentationPath() { } void sourcekitd_initialize(void) { + assert(msgHandlingQueue == nullptr && "Cannot initialize service twice"); + msgHandlingQueue = new WorkQueue(WorkQueue::Dequeuing::Concurrent, + "sourcekitdInProc.msgHandlingQueue"); if (sourcekitd::initializeClient()) { LOG_INFO_FUNC(High, "initializing"); sourcekitd::initializeService(getSwiftExecutablePath(), getRuntimeLibPath(), @@ -154,7 +166,7 @@ void sourcekitd_send_request(sourcekitd_object_t req, sourcekitd_request_retain(req); receiver = Block_copy(receiver); - WorkQueue::dispatchConcurrent([=] { + auto handler = [=] { sourcekitd::handleRequest(req, /*CancellationToken=*/request_handle, [=](sourcekitd_response_t resp) { // The receiver accepts ownership of the @@ -163,7 +175,20 @@ void sourcekitd_send_request(sourcekitd_object_t req, Block_release(receiver); }); sourcekitd_request_release(req); - }); + }; + + if (sourcekitd::requestIsEnableBarriers(req)) { + RequestBarriersEnabled = true; + sourcekitd::sendBarriersEnabledResponse([=](sourcekitd_response_t resp) { + // The receiver accepts ownership of the response. + receiver(resp); + Block_release(receiver); + }); + } else if (RequestBarriersEnabled && sourcekitd::requestIsBarrier(req)) { + msgHandlingQueue->dispatchBarrier(handler); + } else { + msgHandlingQueue->dispatchConcurrent(handler); + } } void sourcekitd_cancel_request(sourcekitd_request_handle_t handle) { diff --git a/tools/SourceKit/tools/sourcekitd/bin/XPC/Service/XPCService.cpp b/tools/SourceKit/tools/sourcekitd/bin/XPC/Service/XPCService.cpp index bf4a1d1df2062..7486a908ed927 100644 --- a/tools/SourceKit/tools/sourcekitd/bin/XPC/Service/XPCService.cpp +++ b/tools/SourceKit/tools/sourcekitd/bin/XPC/Service/XPCService.cpp @@ -32,6 +32,7 @@ using namespace SourceKit; using namespace sourcekitd; static xpc_connection_t MainConnection = nullptr; +static bool RequestBarriersEnabled = false; static void postNotification(sourcekitd_response_t Notification) { xpc_connection_t peer = MainConnection; @@ -231,6 +232,7 @@ static std::string getDiagnosticDocumentationPath() { } static dispatch_queue_t msgHandlingQueue; +static dispatch_queue_t requestQueue; static void sourcekitdServer_peer_event_handler(xpc_connection_t peer, xpc_object_t event) { @@ -258,18 +260,36 @@ static void sourcekitdServer_peer_event_handler(xpc_connection_t peer, dispatch_async(msgHandlingQueue, ^{ if (xpc_object_t contents = xpc_dictionary_get_value(event, xpc::KeyMsg)) { - SourceKitCancellationToken cancelToken = - reinterpret_cast( - xpc_dictionary_get_uint64(event, xpc::KeyCancelToken)); - auto Responder = std::make_shared(event, peer); - xpc_release(event); - assert(xpc_get_type(contents) == XPC_TYPE_ARRAY); sourcekitd_object_t req = xpc_array_get_value(contents, 0); - sourcekitd::handleRequest(req, /*CancellationToken=*/cancelToken, - [Responder](sourcekitd_response_t response) { - Responder->sendReply(response); - }); + + void (^handler)(void) = ^{ + SourceKitCancellationToken cancelToken = + reinterpret_cast( + xpc_dictionary_get_uint64(event, xpc::KeyCancelToken)); + auto Responder = std::make_shared(event, peer); + xpc_release(event); + + sourcekitd::handleRequest(req, /*CancellationToken=*/cancelToken, + [Responder](sourcekitd_response_t response) { + Responder->sendReply(response); + }); + }; + + if (sourcekitd::requestIsEnableBarriers(req)) { + dispatch_barrier_async(requestQueue, ^{ + auto Responder = std::make_shared(event, peer); + xpc_release(event); + RequestBarriersEnabled = true; + sourcekitd::sendBarriersEnabledResponse([Responder](sourcekitd_response_t response) { + Responder->sendReply(response); + }); + }); + } else if (RequestBarriersEnabled && sourcekitd::requestIsBarrier(req)) { + dispatch_barrier_async(requestQueue, handler); + } else { + dispatch_async(requestQueue, handler); + } } else if (xpc_object_t contents = xpc_dictionary_get_value(event, "ping")) { // Ping back. @@ -282,7 +302,11 @@ static void sourcekitdServer_peer_event_handler(xpc_connection_t peer, reinterpret_cast( xpc_dictionary_get_uint64(event, xpc::KeyCancelRequest))) { - sourcekitd::cancelRequest(/*CancellationToken=*/cancelToken); + // Execute cancellation on a queue other than `msgHandling` so that we + // don’t block the cancellation of a request with a barrier + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + sourcekitd::cancelRequest(/*CancellationToken=*/cancelToken); + }); } else if (SourceKitCancellationToken cancelToken = reinterpret_cast( xpc_dictionary_get_uint64( @@ -391,9 +415,13 @@ int main(int argc, const char *argv[]) { LOG_WARN_FUNC("getrlimit failed: " << llvm::sys::StrError()); } - auto attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, + auto msgHandlingQueueAttr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, + QOS_CLASS_DEFAULT, 0); + msgHandlingQueue = dispatch_queue_create("message-handling", msgHandlingQueueAttr); + + auto requestQueueAttr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_DEFAULT, 0); - msgHandlingQueue = dispatch_queue_create("request-handling", attr); + requestQueue = dispatch_queue_create("request-handling", requestQueueAttr); xpc_main(sourcekitdServer_event_handler); return 0; diff --git a/tools/SourceKit/tools/sourcekitd/include/sourcekitd/Service.h b/tools/SourceKit/tools/sourcekitd/include/sourcekitd/Service.h index b96689e4b7ef2..0e1e155cafcb8 100644 --- a/tools/SourceKit/tools/sourcekitd/include/sourcekitd/Service.h +++ b/tools/SourceKit/tools/sourcekitd/include/sourcekitd/Service.h @@ -45,6 +45,21 @@ void cancelRequest(SourceKitCancellationToken CancellationToken); void disposeCancellationToken(SourceKitCancellationToken CancellationToken); +/// Returns \c true if \p Request is of a request kind that should be issued as +/// a dispatch barrier of the message handling queue. In practice, this returns +/// \c true for open, edit and close requets. +/// +/// This does not check if dispatch barriers have been enabled by the sourckitd +/// client. +bool requestIsBarrier(sourcekitd_object_t Request); + +/// Returns \c true if this is a request to enable dispatch barriers in +/// sourcekitd. +bool requestIsEnableBarriers(sourcekitd_object_t Request); + +/// Send the response that request barriers have been enabled to \p Receiver. +void sendBarriersEnabledResponse(ResponseReceiver Receiver); + } // namespace sourcekitd #endif // LLVM_SOURCEKITD_SERVICE_H diff --git a/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp index e65d4203c43eb..d0ad86c6ed704 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp @@ -405,6 +405,26 @@ void sourcekitd::disposeCancellationToken( getGlobalContext().getRequestTracker()->stopTracking(CancellationToken); } +bool sourcekitd::requestIsBarrier(sourcekitd_object_t ReqObj) { + RequestDict Req(ReqObj); + sourcekitd_uid_t ReqUID = Req.getUID(KeyRequest); + return ReqUID == RequestEditorOpen || ReqUID == RequestEditorReplaceText || + ReqUID == RequestEditorClose; +} + +bool sourcekitd::requestIsEnableBarriers(sourcekitd_object_t ReqObj) { + RequestDict Req(ReqObj); + sourcekitd_uid_t ReqUID = Req.getUID(KeyRequest); + return ReqUID == RequestEnableRequestBarriers; +} + +void sourcekitd::sendBarriersEnabledResponse(ResponseReceiver Receiver) { + ResponseBuilder RespBuilder; + auto Elem = RespBuilder.getDictionary(); + Elem.setBool(KeyBarriersEnabled, true); + Receiver(RespBuilder.createResponse()); +} + static std::unique_ptr getInputBufForRequest( Optional SourceFile, Optional SourceText, const Optional &vfsOptions, llvm::SmallString<64> &ErrBuf) { @@ -1804,6 +1824,134 @@ handleRequestDiagnostics(const RequestDict &Req, }); } +/// Expand macros in the specified source file syntactically. +/// +/// Request would look like: +/// { +/// key.compilerargs: [] +/// key.sourcefile: +/// key.sourcetext: (optional) +/// key.expansions: [...] +/// } +/// 'compilerargs' is used for plugin search paths. +/// 'expansion specifier' is +/// { +/// key.offset: +/// key.modulename: +/// key.typename: +/// key.macro_roles: [...] +/// } +/// +/// Sends the results as a 'CategorizedEdits'. +/// Note that, unlike refactoring, each edit doesn't have 'key.buffer_name'. +/// FIXME: Support nested expansion. +static void handleRequestSyntacticMacroExpansion( + const RequestDict &req, SourceKitCancellationToken cancellationToken, + ResponseReceiver rec) { + + Optional vfsOptions = getVFSOptions(req); + std::unique_ptr inputBuf = + getInputBufForRequestOrEmitError(req, vfsOptions, rec); + if (!inputBuf) + return; + + SmallVector args; + if (getCompilerArgumentsForRequestOrEmitError(req, args, rec)) + return; + + // key.expansions: [ + // { key.offset: 42, + // key.macro_roles: [source.lang.swift.macrorole.conformance, + // source.lang.swift.macrorole.member], + // key.modulename: "MyMacroImpl", + // key.typename: "StringifyMacro"}, + // { key.offset: 132, + // key.sourceText: "foo(bar, baz)", + // key.macro_roles: [source.lang.swift.macrorole.conformance, + // source.lang.swift.macrorole.member], + // key.expandedmacro_replacements: [ + // {key.offset: 4, key.length: 3, key.argindex: 0}, + // {key.offset: 9, key.length: 3, key.argindex: 1}]} + // ] + std::vector expansions; + bool failed = req.dictionaryArrayApply(KeyExpansions, [&](RequestDict dict) { + // offset. + int64_t offset; + dict.getInt64(KeyOffset, offset, false); + + // macro roles. + SmallVector macroRoleUIDs; + if (dict.getUIDArray(KeyMacroRoles, macroRoleUIDs, false)) { + rec(createErrorRequestInvalid( + "missing 'key.macro_roles' for expansion specifier")); + return true; + } + MacroRoles macroRoles; + for (auto uid : macroRoleUIDs) { + if (uid == KindMacroRoleExpression) + macroRoles |= MacroRole::Expression; + if (uid == KindMacroRoleDeclaration) + macroRoles |= MacroRole::Declaration; + if (uid == KindMacroRoleCodeItem) + macroRoles |= MacroRole::CodeItem; + if (uid == KindMacroRoleAccessor) + macroRoles |= MacroRole::Accessor; + if (uid == KindMacroRoleMemberAttribute) + macroRoles |= MacroRole::MemberAttribute; + if (uid == KindMacroRoleMember) + macroRoles |= MacroRole::Member; + if (uid == KindMacroRolePeer) + macroRoles |= MacroRole::Peer; + if (uid == KindMacroRoleConformance) + macroRoles |= MacroRole::Conformance; + } + + // definition. + if (auto moduleName = dict.getString(KeyModuleName)) { + auto typeName = dict.getString(KeyTypeName); + if (!typeName) { + rec(createErrorRequestInvalid( + "missing 'key.typename' for external macro definition")); + return true; + } + MacroExpansionInfo::ExternalMacroReference definition(moduleName->str(), + typeName->str()); + expansions.emplace_back(offset, macroRoles, definition); + } else if (auto expandedText = dict.getString(KeySourceText)) { + MacroExpansionInfo::ExpandedMacroDefinition definition(*expandedText); + bool failed = dict.dictionaryArrayApply( + KeyExpandedMacroReplacements, [&](RequestDict dict) { + int64_t offset, length, paramIndex; + bool failed = false; + failed |= dict.getInt64(KeyOffset, offset, false); + failed |= dict.getInt64(KeyLength, length, false); + failed |= dict.getInt64(KeyArgIndex, paramIndex, false); + if (failed) { + rec(createErrorRequestInvalid( + "macro replacement should have key.offset, key.length, and " + "key.argindex")); + return true; + } + definition.replacements.emplace_back( + RawCharSourceRange{unsigned(offset), unsigned(length)}, + paramIndex); + return false; + }); + if (failed) + return true; + expansions.emplace_back(offset, macroRoles, definition); + } + return false; + }); + if (failed) + return; + + LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); + Lang.expandMacroSyntactically( + inputBuf.get(), args, expansions, + [&](const auto &Result) { rec(createCategorizedEditsResponse(Result)); }); +} + void handleRequestImpl(sourcekitd_object_t ReqObj, SourceKitCancellationToken CancellationToken, ResponseReceiver Rec) { @@ -1907,6 +2055,8 @@ void handleRequestImpl(sourcekitd_object_t ReqObj, HANDLE_REQUEST(RequestRelatedIdents, handleRequestRelatedIdents) HANDLE_REQUEST(RequestActiveRegions, handleRequestActiveRegions) HANDLE_REQUEST(RequestDiagnostics, handleRequestDiagnostics) + HANDLE_REQUEST(RequestSyntacticMacroExpansion, + handleRequestSyntacticMacroExpansion) { SmallString<64> ErrBuf; diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 985b40212da4e..463f801d3949d 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -3426,6 +3426,9 @@ class ASTCommentPrinter : public ASTWalker { case AccessorKind::Modify: OS << ""; diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 948dd5c94e409..745ef816f59fe 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -1223,6 +1223,8 @@ sourcekit-lsp=0 [preset: buildbot_incremental_linux,lsan,tools=RDA,stdlib=RDA,test=no] build-subdir=buildbot_incremental_lsan +swift-enable-backtracing=0 + # Reduce the size of the final toolchain, limit debug info extra-cmake-options= -DCMAKE_C_FLAGS="-gline-tables-only" @@ -1238,6 +1240,8 @@ reconfigure [preset: buildbot_incremental_linux,lsan,tools=RDA,stdlib=DA,test=no] build-subdir=buildbot_incremental_lsan +swift-enable-backtracing=0 + # Reduce the size of the final toolchain, limit debug info extra-cmake-options= -DCMAKE_C_FLAGS="-gline-tables-only" diff --git a/utils/build-script-impl b/utils/build-script-impl index 99302d005d42d..f3941c1427948 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -3314,6 +3314,8 @@ function build_and_test_installable_package() { COMPATIBILITY_VERSION=2 COMPATIBILITY_VERSION_DISPLAY_STRING="Xcode 8.0" DARWIN_TOOLCHAIN_CREATED_DATE="$(date -u +'%a %b %d %T GMT %Y')" + XCODE_DEFAULT_TOOLCHAIN_PLUGIN_SERVER_DESCRIPTOR='$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/host/plugins#$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-plugin-server' + XCODE_DEFAULT_TOOLCHAIN_LOCAL_PLUGIN_SERVER_DESCRIPTOR='$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/local/lib/swift/host/plugins#$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-plugin-server' SWIFT_USE_DEVELOPMENT_TOOLCHAIN_RUNTIME="YES" if [[ "${DARWIN_TOOLCHAIN_REQUIRE_USE_OS_RUNTIME}" -eq "1" ]]; then @@ -3349,6 +3351,8 @@ function build_and_test_installable_package() { call ${PLISTBUDDY_BIN} -c "Add OverrideBuildSettings:SWIFT_DEVELOPMENT_TOOLCHAIN string 'YES'" "${DARWIN_TOOLCHAIN_INFO_PLIST}" call ${PLISTBUDDY_BIN} -c "Add OverrideBuildSettings:SWIFT_USE_DEVELOPMENT_TOOLCHAIN_RUNTIME string '${SWIFT_USE_DEVELOPMENT_TOOLCHAIN_RUNTIME}'" "${DARWIN_TOOLCHAIN_INFO_PLIST}" + call ${PLISTBUDDY_BIN} -c "Add OverrideBuildSettings:OTHER_SWIFT_FLAGS string '\$(inherited) -Xfrontend -external-plugin-path -Xfrontend ${XCODE_DEFAULT_TOOLCHAIN_PLUGIN_SERVER_DESCRIPTOR} -Xfrontend -external-plugin-path -Xfrontend ${XCODE_DEFAULT_TOOLCHAIN_LOCAL_PLUGIN_SERVER_DESCRIPTOR}'" "${DARWIN_TOOLCHAIN_INFO_PLIST}" + call chmod a+r "${DARWIN_TOOLCHAIN_INFO_PLIST}" if [[ "${DARWIN_TOOLCHAIN_APPLICATION_CERT}" ]] ; then diff --git a/utils/gyb_sourcekit_support/UIDs.py b/utils/gyb_sourcekit_support/UIDs.py index fe3e2e72a178c..a660222a15665 100644 --- a/utils/gyb_sourcekit_support/UIDs.py +++ b/utils/gyb_sourcekit_support/UIDs.py @@ -207,7 +207,11 @@ def __init__(self, internal_name, external_name): # in this time. For cancellation testing purposes. KEY('SimulateLongRequest', 'key.simulate_long_request'), KEY('IsSynthesized', 'key.is_synthesized'), - KEY('BufferName', 'key.buffer_name') + KEY('BufferName', 'key.buffer_name'), + KEY('BarriersEnabled', 'key.barriers_enabled'), + KEY('Expansions', 'key.expansions'), + KEY('MacroRoles', 'key.macro_roles'), + KEY('ExpandedMacroReplacements', 'key.expanded_macro_replacements'), ] @@ -272,6 +276,9 @@ def __init__(self, internal_name, external_name): REQUEST('Diagnostics', 'source.request.diagnostics'), REQUEST('Compile', 'source.request.compile'), REQUEST('CompileClose', 'source.request.compile.close'), + REQUEST('EnableRequestBarriers', 'source.request.enable_request_barriers'), + REQUEST('SyntacticMacroExpansion', + 'source.request.syntactic_macro_expansion'), ] @@ -318,6 +325,10 @@ def __init__(self, internal_name, external_name): 'source.lang.swift.decl.function.accessor.modify'), KIND('RefAccessorModify', 'source.lang.swift.ref.function.accessor.modify'), + KIND('DeclAccessorInit', + 'source.lang.swift.decl.function.accessor.init'), + KIND('RefAccessorInit', + 'source.lang.swift.ref.function.accessor.init'), KIND('DeclConstructor', 'source.lang.swift.decl.function.constructor'), KIND('RefConstructor', 'source.lang.swift.ref.function.constructor'), KIND('DeclDestructor', 'source.lang.swift.decl.function.destructor'), @@ -485,4 +496,12 @@ def __init__(self, internal_name, external_name): KIND('StatInstructionCount', 'source.statistic.instruction-count'), KIND('Swift', 'source.lang.swift'), KIND('ObjC', 'source.lang.objc'), + KIND('MacroRoleExpression', 'source.lang.swift.macro_role.expression'), + KIND('MacroRoleDeclaration', 'source.lang.swift.macro_role.declaration'), + KIND('MacroRoleCodeItem', 'source.lang.swift.macro_role.codeitem'), + KIND('MacroRoleAccessor', 'source.lang.swift.macro_role.accessor'), + KIND('MacroRoleMemberAttribute', 'source.lang.swift.macro_role.member_attribute'), + KIND('MacroRoleMember', 'source.lang.swift.macro_role.member'), + KIND('MacroRolePeer', 'source.lang.swift.macro_role.peer'), + KIND('MacroRoleConformance', 'source.lang.swift.macro_role.conformance'), ] diff --git a/utils/gyb_syntax_support/AttributeKinds.py b/utils/gyb_syntax_support/AttributeKinds.py index 13f6001dc23ea..8322bb3a449c2 100644 --- a/utils/gyb_syntax_support/AttributeKinds.py +++ b/utils/gyb_syntax_support/AttributeKinds.py @@ -590,9 +590,17 @@ def __init__(self, name, swift_name=None): swift_name='atReasync', code=110), - # 111 was an experimental @completionHandlerAsync and is now unused - - # 113 was experimental _unsafeSendable and is now unused + DeclAttribute('initializes', 'Initializes', + OnAccessor, + ABIStableToAdd, ABIStableToRemove, + APIBreakingToAdd, APIBreakingToRemove, + code=111), + # Note: 112 is used by 'nonisolated' + DeclAttribute('accesses', 'Accesses', + OnAccessor, + ABIStableToAdd, ABIStableToRemove, + APIBreakingToAdd, APIBreakingToRemove, + code=113), SimpleDeclAttribute('_unsafeInheritExecutor', 'UnsafeInheritExecutor', OnFunc, UserInaccessible, diff --git a/validation-test/SIL/verify_all_overlays.py b/validation-test/SIL/verify_all_overlays.py index 3d68f7d6301d9..d4b6a15083db8 100755 --- a/validation-test/SIL/verify_all_overlays.py +++ b/validation-test/SIL/verify_all_overlays.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # RUN: ${python} %s %target-swiftmodule-name %platform-sdk-overlay-dir \ +# RUN: %swift_src_root \ # RUN: %target-sil-opt -sdk %sdk -enable-sil-verify-all \ # RUN: -F %sdk/System/Library/PrivateFrameworks \ # RUN: -F "%xcode-extra-frameworks-dir" @@ -14,9 +15,11 @@ target_swiftmodule_name = sys.argv[1] sdk_overlay_dir = sys.argv[2] -sil_opt_invocation = sys.argv[3:] +source_dir = sys.argv[3] +sil_opt_invocation = sys.argv[4:] for module_file in os.listdir(sdk_overlay_dir): + extra_args = [] module_name, ext = os.path.splitext(module_file) if ext != ".swiftmodule": continue @@ -33,6 +36,12 @@ # TODO: fix the DifferentiationUnittest module. if module_name == "DifferentiationUnittest": continue + # Backtracing needs its own additional modules in the module path + if module_name == "_Backtracing": + extra_args = ["-I", os.path.join(source_dir, "stdlib", + "public", "Backtracing", "modules"), + "-I", os.path.join(source_dir, "include")] + print("# " + module_name) module_path = os.path.join(sdk_overlay_dir, module_file) @@ -50,4 +59,5 @@ # We are deliberately discarding the output here; we're just making sure # it can be generated. subprocess.check_output(sil_opt_invocation + - [module_path, "-module-name", module_name]) + [module_path, "-module-name", module_name] + + extra_args) diff --git a/validation-test/Sema/SwiftUI/rdar70256351.swift b/validation-test/Sema/SwiftUI/rdar70256351.swift index 0881009bd7457..1aa7ad723e977 100644 --- a/validation-test/Sema/SwiftUI/rdar70256351.swift +++ b/validation-test/Sema/SwiftUI/rdar70256351.swift @@ -10,7 +10,6 @@ struct ContentView: View { var body: some View { switch currentPage { case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} - // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists: (Substring, String)}} Text("1") default: Text("default") diff --git a/validation-test/Sema/type_checker_crashers_fixed/issue-65360.swift b/validation-test/Sema/type_checker_crashers_fixed/issue-65360.swift index 2e7e10322c932..fd6652a85f4e4 100644 --- a/validation-test/Sema/type_checker_crashers_fixed/issue-65360.swift +++ b/validation-test/Sema/type_checker_crashers_fixed/issue-65360.swift @@ -9,12 +9,24 @@ let _: () -> Void = { let _: () -> Void = { for case (0)? in [a] {} + // expected-error@-1 {{pattern cannot match values of type 'Any?'}} if case (0, 0) = a {} - // expected-error@-1 {{cannot convert value of type 'Any?' to specified type '(_, _)}} } let _: () -> Void = { for case (0)? in [a] {} + // expected-error@-1 {{pattern cannot match values of type 'Any?'}} for case (0, 0) in [a] {} - // expected-error@-1 {{cannot convert value of type 'Any?' to expected element type '(_, _)'}} +} + +let _: () -> Void = { + if case (0, 0) = a {} + // expected-error@-1 {{cannot convert value of type 'Any?' to specified type '(Int, Int)'}} + for case (0)? in [a] {} +} + +let _: () -> Void = { + for case (0, 0) in [a] {} + // expected-error@-1 {{cannot convert value of type 'Any?' to expected element type '(Int, Int)'}} + for case (0)? in [a] {} } diff --git a/validation-test/compiler_crashers_2_fixed/rdar110363503.swift b/validation-test/compiler_crashers_2_fixed/rdar110363503.swift new file mode 100644 index 0000000000000..95c605602b149 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar110363503.swift @@ -0,0 +1,54 @@ +// RUN: %target-swift-frontend -emit-ir %s -disable-availability-checking + +struct ZipCollection { + let c: (repeat each C) +} + +extension ZipCollection: Collection { + struct Element { + var elt: (repeat each C.Element) + } + + struct Index { + let i: (repeat each C.Index) + } + + var startIndex: Index { + Index(i: (repeat (each c).startIndex)) + } + + var endIndex: Index { + Index(i: (repeat (each c).endIndex)) + } + + func index(after i: Index) -> Index { + Index(i: (repeat (each c).index(after: each i.i))) + } + + subscript(index: Index) -> Element { + Element(elt: (repeat (each c)[each index.i])) + } +} + +extension ZipCollection.Index: Equatable { + static func ==(lhs: Self, rhs: Self) -> Bool { + var result = true + repeat result = ((each lhs.i) == (each rhs.i)) && result + return result + } +} + +extension ZipCollection.Index: Comparable { + static func <(lhs: Self, rhs: Self) -> Bool { + var result: Bool? = nil + func check(_ x: T, _ y: T) { + if result == nil { + if x == y { return } + if x < y { result = true } + if x > y { result = false } + } + } + repeat check(each lhs.i, each rhs.i) + return result ?? false + } +}