From 3b0cbcd4ef11b7f7e2a8e6723783baa3285af8ef Mon Sep 17 00:00:00 2001 From: Leonard Grey Date: Tue, 21 Apr 2026 17:37:07 -0400 Subject: [PATCH 1/3] [lldb][NativePDB] Recognize bound generic pattern Bound generic types are represented by nested structs arranged in a certain way (see https://github.com/swiftlang/swift/issues/87093 for details). Similar to DWARF, this change adds special handling to recognize this pattern and recover the mangled name of the actual type. --- .../NativePDB/PdbAstBuilderSwift.cpp | 78 ++++++++++++++++++- .../NativePDB/SymbolFileNativePDB.cpp | 7 +- .../SymbolFile/NativePDB/swift-frame-var.test | 26 ++++++- 3 files changed, 106 insertions(+), 5 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.cpp index 4669e7024d1b2..341be945f1d77 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.cpp @@ -17,7 +17,9 @@ #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Support/ErrorHandling.h" @@ -28,6 +30,51 @@ using namespace lldb_private::npdb; using namespace llvm::codeview; using namespace llvm::pdb; +namespace { +// To avoid issues with uniquing, bound generics are emitted as follows in DWARF: +// - A sized outer structure with no name or identifier +// - A sizeless inner structure with the mangled name as the name and no identifier. +// The latter is an unnamed member of the former. +// CodeView deviates in two major ways: +// - Unnamed types are emitted as module name + `::` instead of having an empty name. +// - Unnamed forward declared members are dropped entirely. To get around this, we name +// the member with the mangled name of the inner type, since this is illegal in a +// user-written type. See https://github.com/swiftlang/swift/issues/87093 +struct BoundGenericVisitor : public TypeVisitorCallbacks { + TpiStream &tpi; + llvm::StringRef outer_prefix; + llvm::StringRef mangled_name; + bool seen = false; + + BoundGenericVisitor(TpiStream &tpi, llvm::StringRef outer_prefix) + : tpi(tpi), outer_prefix(outer_prefix) {} + + llvm::Error visitKnownMember(CVMemberRecord &, + DataMemberRecord &member) override { + if (seen) { + // More than one member = doesn't match the pattern. + mangled_name = {}; + return llvm::Error::success(); + } + seen = true; + CVType inner_cvt = tpi.getType(member.Type); + if (!llvm::is_contained({LF_STRUCTURE, LF_CLASS}, inner_cvt.kind())) + return llvm::Error::success(); + ClassRecord inner; + if (llvm::Error err = + TypeDeserializer::deserializeAs(inner_cvt, inner)) + return err; + llvm::StringRef inner_mangled = inner.Name; + if (!inner_mangled.consume_front(outer_prefix) || + inner_mangled != member.Name || + !swift::Demangle::isSwiftSymbol(member.Name)) + return llvm::Error::success(); + mangled_name = member.Name; + return llvm::Error::success(); + } +}; +} // namespace + PdbAstBuilderSwift::PdbAstBuilderSwift(TypeSystemSwiftTypeRef &swift_ts) : m_swift_ts(swift_ts) {} @@ -48,9 +95,36 @@ CompilerType PdbAstBuilderSwift::CreateType(PdbTypeSymId type, "Failed to deserialize ClassRecord: {0}"); return {}; } - if (!cr.hasUniqueName()) + if (cr.hasUniqueName()) { + decorated = cr.UniqueName; + } else if (cr.Name.ends_with("::") && + !cr.FieldList.isNoneType()) { + // See comment at BoundGenericVisitor for details. + CVType field_list_cvt = tpi.getType(cr.FieldList); + if (field_list_cvt.kind() != LF_FIELDLIST) + return {}; + FieldListRecord field_list; + if (auto err = TypeDeserializer::deserializeAs( + field_list_cvt, field_list)) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Failed to deserialize FieldListRecord: {0}"); + return {}; + } + // Grab the outer prefix so we can make sure member name matches the inner type name. + llvm::StringRef outer_prefix = + cr.Name.drop_back(llvm::StringRef("").size()); + BoundGenericVisitor visitor(tpi, outer_prefix); + if (auto err = visitMemberRecordStream(field_list.Data, visitor)) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Failed to walk bound generic field list: {0}"); + return {}; + } + if (visitor.mangled_name.empty()) + return {}; + decorated = visitor.mangled_name; + } else { return {}; - decorated = cr.UniqueName; + } break; } case LF_ENUM: { diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp index 441bae3de4039..076fd3f631e09 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -305,7 +305,12 @@ static bool IsSwiftType(PdbTypeSymId type_id, PdbIndex& index) { return false; ClassRecord cr; llvm::cantFail(TypeDeserializer::deserializeAs(cvt, cr)); - return cr.hasUniqueName() && swift::Demangle::isSwiftSymbol(cr.UniqueName); + if (cr.hasUniqueName()) + return swift::Demangle::isSwiftSymbol(cr.UniqueName); + // https://github.com/swiftlang/swift/issues/87093 + // FIXME: This is more of a heuristic than a definitive check, but we would need to do a + // second field-list walk to be sure. + return cr.Name.ends_with("::"); #else return false; #endif diff --git a/lldb/test/Shell/SymbolFile/NativePDB/swift-frame-var.test b/lldb/test/Shell/SymbolFile/NativePDB/swift-frame-var.test index 5921244d4078f..6e8d83c923b93 100644 --- a/lldb/test/Shell/SymbolFile/NativePDB/swift-frame-var.test +++ b/lldb/test/Shell/SymbolFile/NativePDB/swift-frame-var.test @@ -18,6 +18,15 @@ enum Direction { case south } +struct Box { + var value: T +} + +enum Pair { + case first(A) + case second(B) +} + func main() { var myInt32: Int32 = 42 var myBool: Bool = true @@ -27,20 +36,27 @@ func main() { var myString: String = "hello" var myPoint: Point = Point(x: 10, y: 20) var myDirection: Direction = .north + var myOptional: Int32? = 7 + var myArray: [Int32] = [1, 2, 3] + var myOptionalArray: [Int32]? = [4, 5] + var myResult: Result = .success(42) + var myBox: Box = Box(value: 99) + var myPair: Pair = .first(7) let constInt: Int = 100 let constInt8: Int8 = -42 let constUInt16: UInt16 = 1000 let constBool: Bool = false let constString: String = "world" print(myInt32, myBool, myUInt64, myFloat, myDouble, - myString, myPoint, myDirection, + myString, myPoint, myDirection, myOptional, myArray, + myOptionalArray, myResult, myBox, myPair, constInt, constInt8, constUInt16, constBool, constString) } main() #--- commands.input -b main.swift:25 +b main.swift:40 run frame variable @@ -54,6 +70,12 @@ frame variable # CHECK: (String) myString = "hello" # CHECK: (main.Point) myPoint = (x = 10, y = 20) # CHECK: (main.Direction) myDirection = north +# CHECK: (Int32?) myOptional = 7 +# CHECK: ([Int32]) myArray = 3 values +# CHECK: ([Int32]?) myOptionalArray = 2 values +# CHECK: (Result) myResult = success +# CHECK: (main.Box) myBox = (value = 99) +# CHECK: (main.Pair) myPair = first # Local constants # CHECK: (Int) constInt = 100 # CHECK: (Int8) constInt8 = -42 From c0de2c651388b1ce8971bff60afd06ed424f78de Mon Sep 17 00:00:00 2001 From: Leonard Grey Date: Fri, 24 Apr 2026 13:26:28 -0400 Subject: [PATCH 2/3] Do the full check in IsSwiftType --- .../NativePDB/PdbAstBuilderSwift.cpp | 67 ++++++++++--------- .../SymbolFile/NativePDB/PdbAstBuilderSwift.h | 25 +++++++ .../NativePDB/SymbolFileNativePDB.cpp | 16 +++-- 3 files changed, 71 insertions(+), 37 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.cpp index 341be945f1d77..dfb95f5aa8d53 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.cpp @@ -31,15 +31,7 @@ using namespace llvm::codeview; using namespace llvm::pdb; namespace { -// To avoid issues with uniquing, bound generics are emitted as follows in DWARF: -// - A sized outer structure with no name or identifier -// - A sizeless inner structure with the mangled name as the name and no identifier. -// The latter is an unnamed member of the former. -// CodeView deviates in two major ways: -// - Unnamed types are emitted as module name + `::` instead of having an empty name. -// - Unnamed forward declared members are dropped entirely. To get around this, we name -// the member with the mangled name of the inner type, since this is illegal in a -// user-written type. See https://github.com/swiftlang/swift/issues/87093 +// Fills `mangled_name` if `cr` is shaped like a bound generic. struct BoundGenericVisitor : public TypeVisitorCallbacks { TpiStream &tpi; llvm::StringRef outer_prefix; @@ -75,6 +67,32 @@ struct BoundGenericVisitor : public TypeVisitorCallbacks { }; } // namespace +std::optional> +PdbAstBuilderSwift::MaybeUnwrapBoundGeneric(const ClassRecord &cr, + TpiStream &tpi) { + if (!cr.Name.ends_with("::") || cr.FieldList.isNoneType()) + return std::nullopt; + + CVType field_list_cvt = tpi.getType(cr.FieldList); + if (field_list_cvt.kind() != LF_FIELDLIST) + return std::nullopt; + + FieldListRecord field_list; + if (llvm::Error err = TypeDeserializer::deserializeAs( + field_list_cvt, field_list)) + return std::move(err); + + llvm::StringRef outer_prefix = + cr.Name.drop_back(llvm::StringRef("").size()); + BoundGenericVisitor visitor(tpi, outer_prefix); + if (llvm::Error err = visitMemberRecordStream(field_list.Data, visitor)) + return std::move(err); + + if (visitor.mangled_name.empty()) + return std::nullopt; + return visitor.mangled_name; +} + PdbAstBuilderSwift::PdbAstBuilderSwift(TypeSystemSwiftTypeRef &swift_ts) : m_swift_ts(swift_ts) {} @@ -97,33 +115,16 @@ CompilerType PdbAstBuilderSwift::CreateType(PdbTypeSymId type, } if (cr.hasUniqueName()) { decorated = cr.UniqueName; - } else if (cr.Name.ends_with("::") && - !cr.FieldList.isNoneType()) { - // See comment at BoundGenericVisitor for details. - CVType field_list_cvt = tpi.getType(cr.FieldList); - if (field_list_cvt.kind() != LF_FIELDLIST) + } else { + auto unwrapped = MaybeUnwrapBoundGeneric(cr, tpi); + if (!unwrapped) return {}; - FieldListRecord field_list; - if (auto err = TypeDeserializer::deserializeAs( - field_list_cvt, field_list)) { - LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), - "Failed to deserialize FieldListRecord: {0}"); + if (!*unwrapped) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), unwrapped->takeError(), + "Deserialization failure while checking for Swift bound generic: {0}"); return {}; } - // Grab the outer prefix so we can make sure member name matches the inner type name. - llvm::StringRef outer_prefix = - cr.Name.drop_back(llvm::StringRef("").size()); - BoundGenericVisitor visitor(tpi, outer_prefix); - if (auto err = visitMemberRecordStream(field_list.Data, visitor)) { - LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), - "Failed to walk bound generic field list: {0}"); - return {}; - } - if (visitor.mangled_name.empty()) - return {}; - decorated = visitor.mangled_name; - } else { - return {}; + decorated = **unwrapped; } break; } diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.h b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.h index b9fad01090e70..8dde666258e23 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.h @@ -12,6 +12,14 @@ #include "PdbAstBuilder.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +#include + +namespace llvm::codeview { +class ClassRecord; +} namespace llvm::pdb { class TpiStream; @@ -52,6 +60,23 @@ class PdbAstBuilderSwift : public PdbAstBuilder { void Dump(Stream &stream, llvm::StringRef filter) override; + // To avoid issues with uniquing, bound generics are emitted as follows in DWARF: + // - A sized outer structure with no name or identifier + // - A sizeless inner structure with the mangled name as the name and no identifier. + // The latter is an unnamed member of the former. + // CodeView deviates in two major ways: + // - Unnamed types are emitted as module name + `::` instead of having an empty name. + // - Unnamed forward declared members are dropped entirely. To get around this, we name + // the member with the mangled name of the inner type, since this is illegal in a + // user-written type. See https://github.com/swiftlang/swift/issues/87093 + // This function returns: + // - `std::nullopt` if `cr` is not shaped like a bound generic. + // - an error if deserialization fails while checking. + // - the mangled name of the inner type if `cr` is shaped like a bound generic. + static std::optional> + MaybeUnwrapBoundGeneric(const llvm::codeview::ClassRecord &cr, + llvm::pdb::TpiStream &tpi); + private: CompilerType CreateType(PdbTypeSymId type, llvm::pdb::TpiStream &tpi); diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp index 076fd3f631e09..9da27b58357ab 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -59,6 +59,7 @@ #include #if LLDB_ENABLE_SWIFT +#include "PdbAstBuilderSwift.h" #include "swift/Demangling/Demangle.h" #endif @@ -307,10 +308,17 @@ static bool IsSwiftType(PdbTypeSymId type_id, PdbIndex& index) { llvm::cantFail(TypeDeserializer::deserializeAs(cvt, cr)); if (cr.hasUniqueName()) return swift::Demangle::isSwiftSymbol(cr.UniqueName); - // https://github.com/swiftlang/swift/issues/87093 - // FIXME: This is more of a heuristic than a definitive check, but we would need to do a - // second field-list walk to be sure. - return cr.Name.ends_with("::"); + auto unwrapped = PdbAstBuilderSwift::MaybeUnwrapBoundGeneric(cr, index.tpi()); + // Doesn't match the shape. + if (!unwrapped) + return false; + // Deserialization failed. + if (!*unwrapped) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), unwrapped->takeError(), + "Deserialization failure while checking for Swift bound generic: {0}"); + return false; + } + return true; #else return false; #endif From 33687ab426bebd31aff6dcb87997754f630c95ee Mon Sep 17 00:00:00 2001 From: Leonard Grey Date: Tue, 28 Apr 2026 16:03:45 -0400 Subject: [PATCH 3/3] Change MaybeUnwrapBoundGeneric signature, add test case, fix comments --- .../NativePDB/PdbAstBuilderSwift.cpp | 20 ++++++------- .../SymbolFile/NativePDB/PdbAstBuilderSwift.h | 30 +++++++++---------- .../NativePDB/SymbolFileNativePDB.cpp | 10 +++---- .../SymbolFile/NativePDB/swift-frame-var.test | 6 ++-- 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.cpp index dfb95f5aa8d53..28793f808d7b5 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.cpp @@ -31,7 +31,7 @@ using namespace llvm::codeview; using namespace llvm::pdb; namespace { -// Fills `mangled_name` if `cr` is shaped like a bound generic. +/// Fills `mangled_name` if `cr` is shaped like a bound generic. struct BoundGenericVisitor : public TypeVisitorCallbacks { TpiStream &tpi; llvm::StringRef outer_prefix; @@ -67,15 +67,15 @@ struct BoundGenericVisitor : public TypeVisitorCallbacks { }; } // namespace -std::optional> +llvm::Expected PdbAstBuilderSwift::MaybeUnwrapBoundGeneric(const ClassRecord &cr, TpiStream &tpi) { if (!cr.Name.ends_with("::") || cr.FieldList.isNoneType()) - return std::nullopt; + return llvm::StringRef(); CVType field_list_cvt = tpi.getType(cr.FieldList); if (field_list_cvt.kind() != LF_FIELDLIST) - return std::nullopt; + return llvm::StringRef(); FieldListRecord field_list; if (llvm::Error err = TypeDeserializer::deserializeAs( @@ -88,8 +88,6 @@ PdbAstBuilderSwift::MaybeUnwrapBoundGeneric(const ClassRecord &cr, if (llvm::Error err = visitMemberRecordStream(field_list.Data, visitor)) return std::move(err); - if (visitor.mangled_name.empty()) - return std::nullopt; return visitor.mangled_name; } @@ -117,14 +115,14 @@ CompilerType PdbAstBuilderSwift::CreateType(PdbTypeSymId type, decorated = cr.UniqueName; } else { auto unwrapped = MaybeUnwrapBoundGeneric(cr, tpi); - if (!unwrapped) - return {}; - if (!*unwrapped) { - LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), unwrapped->takeError(), + if (!unwrapped) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), unwrapped.takeError(), "Deserialization failure while checking for Swift bound generic: {0}"); return {}; } - decorated = **unwrapped; + if (unwrapped->empty()) + return {}; + decorated = *unwrapped; } break; } diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.h b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.h index 8dde666258e23..95a56947aaca5 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderSwift.h @@ -15,8 +15,6 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" -#include - namespace llvm::codeview { class ClassRecord; } @@ -60,20 +58,20 @@ class PdbAstBuilderSwift : public PdbAstBuilder { void Dump(Stream &stream, llvm::StringRef filter) override; - // To avoid issues with uniquing, bound generics are emitted as follows in DWARF: - // - A sized outer structure with no name or identifier - // - A sizeless inner structure with the mangled name as the name and no identifier. - // The latter is an unnamed member of the former. - // CodeView deviates in two major ways: - // - Unnamed types are emitted as module name + `::` instead of having an empty name. - // - Unnamed forward declared members are dropped entirely. To get around this, we name - // the member with the mangled name of the inner type, since this is illegal in a - // user-written type. See https://github.com/swiftlang/swift/issues/87093 - // This function returns: - // - `std::nullopt` if `cr` is not shaped like a bound generic. - // - an error if deserialization fails while checking. - // - the mangled name of the inner type if `cr` is shaped like a bound generic. - static std::optional> + /// To avoid issues with uniquing, bound generics are emitted as follows in DWARF: + /// - A sized outer structure with no name or identifier + /// - A sizeless inner structure with the mangled name as the name and no identifier. + /// The latter is an unnamed member of the former. + /// CodeView deviates in two major ways: + /// - Unnamed types are emitted as module name + `::` instead of having an empty name. + /// - Unnamed forward declared members are dropped entirely. To get around this, we name + /// the member with the mangled name of the inner type, since this is illegal in a + /// user-written type. See https://github.com/swiftlang/swift/issues/87093 + /// This function returns: + /// - an error if deserialization fails while checking. + /// - an empty string if `cr` is not shaped like a bound generic. + /// - the mangled name of the inner type if `cr` is shaped like a bound generic. + static llvm::Expected MaybeUnwrapBoundGeneric(const llvm::codeview::ClassRecord &cr, llvm::pdb::TpiStream &tpi); diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp index 9da27b58357ab..3b516bd40e8e4 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -309,16 +309,14 @@ static bool IsSwiftType(PdbTypeSymId type_id, PdbIndex& index) { if (cr.hasUniqueName()) return swift::Demangle::isSwiftSymbol(cr.UniqueName); auto unwrapped = PdbAstBuilderSwift::MaybeUnwrapBoundGeneric(cr, index.tpi()); - // Doesn't match the shape. - if (!unwrapped) - return false; // Deserialization failed. - if (!*unwrapped) { - LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), unwrapped->takeError(), + if (!unwrapped) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), unwrapped.takeError(), "Deserialization failure while checking for Swift bound generic: {0}"); return false; } - return true; + // Doesn't match the shape. + return !unwrapped->empty(); #else return false; #endif diff --git a/lldb/test/Shell/SymbolFile/NativePDB/swift-frame-var.test b/lldb/test/Shell/SymbolFile/NativePDB/swift-frame-var.test index 6e8d83c923b93..213f5ad09396d 100644 --- a/lldb/test/Shell/SymbolFile/NativePDB/swift-frame-var.test +++ b/lldb/test/Shell/SymbolFile/NativePDB/swift-frame-var.test @@ -37,6 +37,7 @@ func main() { var myPoint: Point = Point(x: 10, y: 20) var myDirection: Direction = .north var myOptional: Int32? = 7 + var myNestedOptional: Int32?? = 8 var myArray: [Int32] = [1, 2, 3] var myOptionalArray: [Int32]? = [4, 5] var myResult: Result = .success(42) @@ -48,7 +49,7 @@ func main() { let constBool: Bool = false let constString: String = "world" print(myInt32, myBool, myUInt64, myFloat, myDouble, - myString, myPoint, myDirection, myOptional, myArray, + myString, myPoint, myDirection, myOptional, myNestedOptional, myArray, myOptionalArray, myResult, myBox, myPair, constInt, constInt8, constUInt16, constBool, constString) } @@ -56,7 +57,7 @@ func main() { main() #--- commands.input -b main.swift:40 +b main.swift:41 run frame variable @@ -71,6 +72,7 @@ frame variable # CHECK: (main.Point) myPoint = (x = 10, y = 20) # CHECK: (main.Direction) myDirection = north # CHECK: (Int32?) myOptional = 7 +# CHECK: (Int32??) myNestedOptional = 8 # CHECK: ([Int32]) myArray = 3 values # CHECK: ([Int32]?) myOptionalArray = 2 values # CHECK: (Result) myResult = success