Skip to content

Fix bugs that caused IRGen to behave differently when debug info was … #16933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 71 additions & 58 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,18 @@ class IRGenSILFunction :
return VarDecl->getInterfaceType()->hasTypeParameter();
}

/// Force all archetypes referenced by the type to be bound by this point.
/// TODO: just make sure that we have a path to them that the debug info
/// can follow.
void bindArchetypes(swift::Type Ty) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if lldb only required type metadata for top-level generic arguments, and not any referenced associated types. Can you file a radar to fix that? It would be feasible to tackle this once you're able to use remote mirrors to calculate layouts (remote mirrors doesn't require metadata instantiation in the target).

auto runtimeTy = getRuntimeReifiedType(IGM, Ty->getCanonicalType());
if (!IGM.IRGen.Opts.shouldOptimize() && runtimeTy->hasArchetype())
runtimeTy.visit([&](CanType t) {
if (auto archetype = dyn_cast<ArchetypeType>(t))
emitTypeMetadataRef(archetype);
});
}

/// Emit debug info for a function argument or a local variable.
template <typename StorageType>
void emitDebugVariableDeclaration(StorageType Storage,
Expand All @@ -872,23 +884,11 @@ class IRGenSILFunction :
StringRef Name,
unsigned ArgNo = 0,
IndirectionKind Indirection = DirectValue) {
// Force all archetypes referenced by the type to be bound by this point.
// TODO: just make sure that we have a path to them that the debug info
// can follow.

// FIXME: The debug info type of all inlined instances of a variable must be
// the same as the type of the abstract variable.
if (isInlinedGeneric(VarDecl, DS))
return;

auto runtimeTy = getRuntimeReifiedType(IGM,
Ty.getType()->getCanonicalType());
if (!IGM.IRGen.Opts.shouldOptimize() && runtimeTy->hasArchetype())
runtimeTy.visit([&](CanType t) {
if (auto archetype = dyn_cast<ArchetypeType>(t))
emitTypeMetadataRef(archetype);
});

assert(IGM.DebugInfo && "debug info not enabled");
if (ArgNo) {
PrologueLocation AutoRestore(IGM.DebugInfo, Builder);
Expand Down Expand Up @@ -3636,6 +3636,8 @@ void IRGenSILFunction::emitErrorResultVar(SILResultInfo ErrorInfo,
auto Storage =
emitShadowCopyIfNeeded(ErrorResultSlot.getAddress(), getDebugScope(),
Var->Name, Var->ArgNo, false);
if (!IGM.DebugInfo)
return;
DebugTypeInfo DTI(nullptr, nullptr, ErrorInfo.getType(),
ErrorResultSlot->getType(), IGM.getPointerSize(),
IGM.getPointerAlignment(), true);
Expand All @@ -3645,9 +3647,6 @@ void IRGenSILFunction::emitErrorResultVar(SILResultInfo ErrorInfo,
}

void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) {
if (!IGM.DebugInfo)
return;

if (i->getDebugScope()->getInlinedFunction()->isTransparent())
return;

Expand Down Expand Up @@ -3686,14 +3685,15 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) {
llvm::SmallVector<llvm::Value *, 8> Copy;
emitShadowCopyIfNeeded(SILVal, i->getDebugScope(), Name, VarInfo->ArgNo,
IsAnonymous, Copy);
bindArchetypes(DbgTy.getType());
if (!IGM.DebugInfo)
return;

emitDebugVariableDeclaration(Copy, DbgTy, SILTy, i->getDebugScope(),
i->getDecl(), Name, VarInfo->ArgNo);
}

void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) {
if (!IGM.DebugInfo)
return;

if (i->getDebugScope()->getInlinedFunction()->isTransparent())
return;

Expand Down Expand Up @@ -3723,6 +3723,10 @@ void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) {
auto DbgTy = DebugTypeInfo::getLocalVariable(
CurSILFn->getDeclContext(), CurSILFn->getGenericEnvironment(), Decl,
RealType, getTypeInfo(SILVal->getType()), Unwrap);
bindArchetypes(DbgTy.getType());
if (!IGM.DebugInfo)
return;

// Put the value's address into a stack slot at -Onone and emit a debug
// intrinsic.
emitDebugVariableDeclaration(
Expand Down Expand Up @@ -3994,26 +3998,29 @@ void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i,
Indirection = IndirectValue;
}

if (IGM.DebugInfo && Decl) {
// Ignore compiler-generated patterns but not optional bindings.
if (auto *Pattern = Decl->getParentPattern())
if (Pattern->isImplicit() &&
Pattern->getKind() != PatternKind::OptionalSome)
return;
if (!Decl)
return;

SILType SILTy = i->getType();
auto RealType = SILTy.getASTType();
auto DbgTy = DebugTypeInfo::getLocalVariable(
CurSILFn->getDeclContext(), CurSILFn->getGenericEnvironment(), Decl,
RealType, type, false);
// Ignore compiler-generated patterns but not optional bindings.
if (auto *Pattern = Decl->getParentPattern())
if (Pattern->isImplicit() &&
Pattern->getKind() != PatternKind::OptionalSome)
return;

SILType SILTy = i->getType();
auto RealType = SILTy.getASTType();
auto DbgTy = DebugTypeInfo::getLocalVariable(
CurSILFn->getDeclContext(), CurSILFn->getGenericEnvironment(), Decl,
RealType, type, false);

// FIXME: This is working around the inverse special case in LLDB.
if (DbgTy.isImplicitlyIndirect())
Indirection = DirectValue;
// FIXME: This is working around the inverse special case in LLDB.
if (DbgTy.isImplicitlyIndirect())
Indirection = DirectValue;

bindArchetypes(DbgTy.getType());
if (IGM.DebugInfo)
emitDebugVariableDeclaration(addr, DbgTy, SILTy, DS, Decl, Name,
VarInfo->ArgNo, Indirection);
}
}

void IRGenSILFunction::visitAllocStackInst(swift::AllocStackInst *i) {
Expand Down Expand Up @@ -4184,34 +4191,39 @@ void IRGenSILFunction::visitAllocBoxInst(swift::AllocBoxInst *i) {

if (i->getDebugScope()->getInlinedFunction()->isTransparent())
return;

if (IGM.DebugInfo && Decl) {
// FIXME: This is a workaround to not produce local variables for
// capture list arguments like "[weak self]". The better solution
// would be to require all variables to be described with a
// SILDebugValue(Addr) and then not describe capture list
// arguments.
if (Name == IGM.Context.Id_self.str())
return;

assert(i->getBoxType()->getLayout()->getFields().size() == 1
&& "box for a local variable should only have one field");
auto SILTy = i->getBoxType()->getFieldType(IGM.getSILModule(), 0);
auto RealType = SILTy.getASTType();
auto DbgTy = DebugTypeInfo::getLocalVariable(
CurSILFn->getDeclContext(), CurSILFn->getGenericEnvironment(), Decl,
RealType, type, /*Unwrap=*/false);
if (!Decl)
return;
// FIXME: This is a workaround to not produce local variables for
// capture list arguments like "[weak self]". The better solution
// would be to require all variables to be described with a
// SILDebugValue(Addr) and then not describe capture list
// arguments.
if (Name == IGM.Context.Id_self.str())
return;

if (isInlinedGeneric(Decl, i->getDebugScope()))
return;
assert(i->getBoxType()->getLayout()->getFields().size() == 1 &&
"box for a local variable should only have one field");
auto SILTy = i->getBoxType()->getFieldType(IGM.getSILModule(), 0);
auto RealType = SILTy.getASTType();
auto DbgTy = DebugTypeInfo::getLocalVariable(
CurSILFn->getDeclContext(), CurSILFn->getGenericEnvironment(), Decl,
RealType, type, /*Unwrap=*/false);

IGM.DebugInfo->emitVariableDeclaration(
Builder,
emitShadowCopyIfNeeded(boxWithAddr.getAddress(), i->getDebugScope(),
Name, 0, IsAnonymous),
DbgTy, i->getDebugScope(), Decl, Name, 0,
DbgTy.isImplicitlyIndirect() ? DirectValue : IndirectValue);
}
if (isInlinedGeneric(Decl, i->getDebugScope()))
return;

auto Storage = emitShadowCopyIfNeeded(
boxWithAddr.getAddress(), i->getDebugScope(), Name, 0, IsAnonymous);

if (!IGM.DebugInfo)
return;

IGM.DebugInfo->emitVariableDeclaration(
Builder,
Storage,
DbgTy, i->getDebugScope(), Decl, Name, 0,
DbgTy.isImplicitlyIndirect() ? DirectValue : IndirectValue);
}

void IRGenSILFunction::visitProjectBoxInst(swift::ProjectBoxInst *i) {
Expand Down Expand Up @@ -4773,6 +4785,7 @@ void IRGenSILFunction::visitObjCExistentialMetatypeToObjectInst(
to.add(value);
setLoweredExplosion(i, to);
}

void IRGenSILFunction::visitObjCProtocolInst(ObjCProtocolInst *i) {
// Get the protocol reference.
llvm::Value *protoRef = emitReferenceToObjCProtocol(*this, i->getProtocol());
Expand Down
33 changes: 23 additions & 10 deletions lib/IRGen/LocalTypeData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,28 +317,41 @@ LocalTypeDataCache::AbstractCacheEntry::follow(IRGenFunction &IGF,
static void maybeEmitDebugInfoForLocalTypeData(IRGenFunction &IGF,
LocalTypeDataKey key,
MetadataResponse value) {
// Only if debug info is enabled.
if (!IGF.IGM.DebugInfo) return;

// FIXME: This check doesn't entirely behave correctly for non-transparent
// functions that were inlined into transparent functions. Correct would be to
// check which instruction requests the type metadata and see whether its
// inlined function is transparent.
auto * DS = IGF.getDebugScope();
if (DS && DS->getInlinedFunction() &&
DS->getInlinedFunction()->isTransparent())
return;

// Only for formal type metadata.
if (key.Kind != LocalTypeDataKind::forFormalTypeMetadata()) return;
if (key.Kind != LocalTypeDataKind::forFormalTypeMetadata())
return;

// Only for archetypes, and not for opened archetypes.
auto type = dyn_cast<ArchetypeType>(key.Type);
if (!type) return;
if (type->getOpenedExistentialType()) return;
if (!type)
return;
if (type->getOpenedExistentialType())
return;

llvm::Value *data = value.getMetadata();

// At -O0, create an alloca to keep the type alive.
auto name = type->getFullName();
if (!IGF.IGM.IRGen.Opts.shouldOptimize()) {
auto temp = IGF.createAlloca(data->getType(), IGF.IGM.getPointerAlignment(),
name);
IGF.Builder.CreateStore(data, temp);
data = temp.getAddress();
auto alloca =
IGF.createAlloca(data->getType(), IGF.IGM.getPointerAlignment(), name);
IGF.Builder.CreateStore(data, alloca);
data = alloca.getAddress();
}

// Only if debug info is enabled.
if (!IGF.IGM.DebugInfo)
return;

// Emit debug info for the metadata.
IGF.IGM.DebugInfo->emitTypeMetadata(IGF, data, name);
}
Expand Down
20 changes: 20 additions & 0 deletions test/DebugInfo/protocol-extension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: %target-swift-frontend -primary-file %s -emit-ir -g -o - | %FileCheck %s

public protocol P {
var v : Int32 { get };
}

public extension P {
// CHECK: define {{.*}}swiftcc i32 @"$S4main1PPAAE1fs5Int32VyF"
public func f() -> Int32 {
// CHECK-NEXT: entry:
// CHECK-NEXT: %[[ALLOCA:.*]] = alloca %swift.type*,
// CHECK-NEXT: @llvm.dbg.declare(metadata %swift.type** %[[ALLOCA]],
// CHECK-SAME: metadata ![[SELFMETA:.*]], metadata !DIExpression())
return v
}
}

// CHECK: ![[SELFMETA]] = !DILocalVariable(name: "$swift.type.Self",
// CHECK-SAME: type: ![[SELFTY:[0-9]+]], flags: DIFlagArtificial)
// CHECK: ![[SELFTY]] = !DIDerivedType(tag: DW_TAG_typedef, name: "$swift.type"
3 changes: 3 additions & 0 deletions test/IRGen/abitypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ class Foo {
// x86_64-macosx: define hidden swiftcc float @"$S8abitypes3FooC25getXFromRectIndirectSwift{{[_0-9a-zA-Z]*}}F"(i64, i64, %T8abitypes3FooC* swiftself) {{.*}} {
func getXFromRectIndirectSwift(_ r: MyRect) -> Float {
let f : Float = 1.0
// x86_64-macosx: alloca
// x86_64-macosx: alloca
// x86_64-macosx: alloca
// x86_64-macosx: [[TEMP:%.*]] = alloca [[TEMPTYPE:%.*]], align 8
// x86_64-macosx: [[RESULT:%.*]] = call float bitcast (void ()* @objc_msgSend to float (i8*, i8*, float, float, float, float, float, float, float, [[TEMPTYPE]]*)*)(i8* %{{.*}}, i8* %{{.*}}, float 1.000000e+00, float 1.000000e+00, float 1.000000e+00, float 1.000000e+00, float 1.000000e+00, float 1.000000e+00, float 1.000000e+00, [[TEMPTYPE]]* byval align 8 [[TEMP]])
// x86_64-macosx: ret float [[RESULT]]
Expand Down
1 change: 1 addition & 0 deletions test/IRGen/alloc_stack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Foobar {

// Make sure we are mis-initializing the alloca.
// CHECK-LABEL: define {{.*}}swiftcc %T11alloc_stack6FoobarC* @"$S11alloc_stack6FoobarCACycfc"(%T11alloc_stack6FoobarC* swiftself)
// CHECK: alloca %TSb, align 1
// CHECK-NOT: store{{.*}}opaque
// CHECK: ret {{.*}}%0
// CHECK:}
Expand Down
4 changes: 2 additions & 2 deletions test/IRGen/associated_type_witness.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ struct Fulfilled<T : P & Q> : Assocked {
// CHECK-NEXT: [[T3:%.*]] = call swiftcc %swift.metadata_response @swift_checkMetadataState(i64 %0, %swift.type* [[T2]])
// CHECK-NEXT: [[CHECKED:%.*]] = extractvalue %swift.metadata_response [[T3]], 0
// CHECK-NEXT: [[STATE:%.*]] = extractvalue %swift.metadata_response [[T3]], 1
// CHECK-NEXT: [[T3:%.*]] = insertvalue %swift.metadata_response undef, %swift.type* [[CHECKED]], 0
// CHECK: [[T3:%.*]] = insertvalue %swift.metadata_response undef, %swift.type* [[CHECKED]], 0
// CHECK-NEXT: [[T4:%.*]] = insertvalue %swift.metadata_response [[T3]], i64 [[STATE]], 1
// CHECK-NEXT: ret %swift.metadata_response [[T4]]

Expand Down Expand Up @@ -142,7 +142,7 @@ struct Computed<T, U> : Assocked {
// CHECK: [[T0:%.*]] = bitcast %swift.type* %"Computed<T, U>" to %swift.type**
// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[T0]], i64 3
// CHECK-NEXT: [[U:%.*]] = load %swift.type*, %swift.type** [[T1]], align 8, !invariant.load
// CHECK-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S23associated_type_witness4PairVMa"(i64 %0, %swift.type* [[T]], %swift.type* [[U]])
// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S23associated_type_witness4PairVMa"(i64 %0, %swift.type* [[T]], %swift.type* [[U]])
// CHECK-NEXT: [[FETCH_RESULT]] = extractvalue %swift.metadata_response [[T0]], 0
// CHECK-NEXT: [[FETCH_STATE]] = extractvalue %swift.metadata_response [[T0]], 1
// CHECK-NEXT: [[COMPLETE:%.*]] = icmp eq i64 [[FETCH_STATE]], 0
Expand Down
2 changes: 2 additions & 0 deletions test/IRGen/associated_types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func testFastRuncible<T: Runcible, U: FastRuncible>(_ t: T, u: U)
// CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to %swift.metadata_response ([[INT]], %swift.type*, i8**)*
// CHECK-NEXT: [[T2:%.*]] = call swiftcc %swift.metadata_response [[T1]]([[INT]] 0, %swift.type* %T, i8** %T.Runcible)
// CHECK-NEXT: %T.RuncerType = extractvalue %swift.metadata_response [[T2]], 0
// CHECK-NEXT: store %swift.type*
// 2. Get the witness table for U.RuncerType.Runcee : Speedy
// 2a. Get the protocol witness table for U.RuncerType : FastRuncer.
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8*, i8** %U.FastRuncible, i32 2
Expand All @@ -92,6 +93,7 @@ func testFastRuncible<T: Runcible, U: FastRuncible>(_ t: T, u: U)
// CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to %swift.metadata_response ([[INT]], %swift.type*, i8**)*
// CHECK-NEXT: [[T2:%.*]] = call swiftcc %swift.metadata_response [[T1]]([[INT]] 0, %swift.type* %T.RuncerType, i8** %T.RuncerType.FastRuncer)
// CHECK-NEXT: %T.RuncerType.Runcee = extractvalue %swift.metadata_response [[T2]], 0
// CHECK-NEXT: store %swift.type*
// 2b. Get the witness table for U.RuncerType.Runcee : Speedy.
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8*, i8** %T.RuncerType.FastRuncer, i32 2
// CHECK-NEXT: [[T1:%.*]] = load i8*, i8** [[T0]],
Expand Down
7 changes: 4 additions & 3 deletions test/IRGen/builtins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,8 @@ func unsafeGuaranteed_test(_ x: Builtin.NativeObject) -> Builtin.NativeObject {

// CHECK-LABEL: define {{.*}} @{{.*}}unsafeGuaranteedEnd_test
// CHECK-NEXT: {{.*}}:
// CHECK-NEXT: alloca
// CHECK-NEXT: store
// CHECK-NEXT: ret void
func unsafeGuaranteedEnd_test(_ x: Builtin.Int8) {
Builtin.unsafeGuaranteedEnd(x)
Expand Down Expand Up @@ -849,9 +851,8 @@ func atomicload(_ p: Builtin.RawPointer) {
}

// CHECK-LABEL: define {{.*}} @"$S8builtins14stringObjectOryS2u_SutF"(i64, i64)
// CHECK-NEXT: {{.*}}:
// CHECK-NEXT: %2 = or i64 %0, %1
// CHECK-NEXT: ret i64 %2
// CHECK: %4 = or i64 %0, %1
// CHECK-NEXT: ret i64 %4
func stringObjectOr(_ x: UInt, _ y: UInt) -> UInt {
return UInt(Builtin.stringObjectOr_Int64(
x._value, y._value))
Expand Down
10 changes: 6 additions & 4 deletions test/IRGen/class_bounded_generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,11 @@ func takes_metatype<T>(_: T.Type) {}
// CHECK-LABEL: define hidden swiftcc void @"$S22class_bounded_generics023archetype_with_generic_A11_constraint1tyx_tAA1ACyq_GRbzr0_lF"(%T22class_bounded_generics1AC.1*, %swift.type* %T)
// CHECK: [[ISA_ADDR:%.*]] = bitcast %T22class_bounded_generics1AC.1* %0 to %swift.type**
// CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
// CHECK-NEXT: call swiftcc void @"$S22class_bounded_generics14takes_metatypeyyxmlF"(%swift.type* %T, %swift.type* %T)
// CHECK: call swiftcc void @"$S22class_bounded_generics14takes_metatypeyyxmlF"(%swift.type* %T, %swift.type* %T)
// CHECK-NEXT: [[ISA_PTR:%.*]] = bitcast %swift.type* [[ISA]] to %swift.type**
// CHECK-NEXT: [[U_ADDR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[ISA_PTR]], i64 10
// CHECK-NEXT: [[U:%.*]] = load %swift.type*, %swift.type** [[U_ADDR]]
// CHECK-NEXT: call swiftcc void @"$S22class_bounded_generics14takes_metatypeyyxmlF"(%swift.type* %U, %swift.type* %U)
// CHECK: call swiftcc void @"$S22class_bounded_generics14takes_metatypeyyxmlF"(%swift.type* %U, %swift.type* %U)
// CHECK: ret void

func archetype_with_generic_class_constraint<T, U>(t: T) where T : A<U> {
Expand All @@ -297,12 +297,14 @@ func archetype_with_generic_class_constraint<T, U>(t: T) where T : A<U> {
}

// CHECK-LABEL: define hidden swiftcc void @"$S22class_bounded_generics029calls_archetype_with_generic_A11_constraint1ayAA1ACyxG_tlF"(%T22class_bounded_generics1AC*) #0 {
// CHECK: alloca
// CHECK: store
// CHECK: [[ISA_ADDR:%.*]] = getelementptr inbounds %T22class_bounded_generics1AC, %T22class_bounded_generics1AC* %0, i32 0, i32 0, i32 0
// CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
// CHECK: [[SELF:%.*]] = bitcast %T22class_bounded_generics1AC* %0 to %T22class_bounded_generics1AC.1*
// CHECK-NEXT: [[ISA_PTR:%.*]] = bitcast %swift.type* [[ISA]] to %swift.type**
// CHECK: [[ISA_PTR:%.*]] = bitcast %swift.type* [[ISA]] to %swift.type**
// CHECK-NEXT: [[T_ADDR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[ISA_PTR]], i64 10
// CHECK-NEXT: [[T:%.*]] = load %swift.type*, %swift.type** [[T_ADDR]]
// CHECK: [[SELF:%.*]] = bitcast %T22class_bounded_generics1AC* %0 to %T22class_bounded_generics1AC.1*
// CHECK-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S22class_bounded_generics1ACMa"([[INT]] 0, %swift.type* [[T]])
// CHECK-NEXT: [[A_OF_T:%.*]] = extractvalue %swift.metadata_response [[T0]], 0
// CHECK-NEXT: call swiftcc void @"$S22class_bounded_generics023archetype_with_generic_A11_constraint1tyx_tAA1ACyq_GRbzr0_lF"(%T22class_bounded_generics1AC.1* [[SELF]], %swift.type* [[A_OF_T]])
Expand Down
Loading