Fully Qualify the Parent Type When Diagnosing Missing Member Types

Use the FullyQualified<Type> abstraction from the prior commit plus DescriptiveDeclKind to give a bit more information when issuing a missing member type diagnostic during type resolution.
This commit is contained in:
Robert Widmann
2020-11-09 15:05:43 -08:00
parent b3843afc3e
commit 363b66a7ad
19 changed files with 63 additions and 46 deletions

View File

@@ -802,7 +802,8 @@ NOTE(invalid_redecl_implicit_wrapper,none,
ERROR(ambiguous_type_base,none, ERROR(ambiguous_type_base,none,
"%0 is ambiguous for type lookup in this context", (DeclNameRef)) "%0 is ambiguous for type lookup in this context", (DeclNameRef))
ERROR(invalid_member_type,none, ERROR(invalid_member_type,none,
"%0 is not a member type of %1", (DeclNameRef, Type)) "%0 is not a member type of %1 %2",
(DeclNameRef, DescriptiveDeclKind, FullyQualified<Type>))
ERROR(invalid_member_type_suggest,none, ERROR(invalid_member_type_suggest,none,
"%0 does not have a member type named %1; did you mean %2?", "%0 does not have a member type named %1; did you mean %2?",
(Type, DeclNameRef, DeclName)) (Type, DeclNameRef, DeclName))

View File

@@ -153,6 +153,17 @@ Type TypeResolution::mapTypeIntoContext(Type type) const {
llvm_unreachable("unhandled stage"); llvm_unreachable("unhandled stage");
} }
// FIXME: It would be nice to have a 'DescriptiveTypeKind' abstraction instead.
static DescriptiveDeclKind describeDeclOfType(Type t) {
if (!t) {
return DescriptiveDeclKind::Type;
}
if (auto *NTD = t->getAnyNominal()) {
return NTD->getDescriptiveKind();
}
return DescriptiveDeclKind::Type;
}
Type TypeResolution::resolveDependentMemberType( Type TypeResolution::resolveDependentMemberType(
Type baseTy, DeclContext *DC, Type baseTy, DeclContext *DC,
SourceRange baseRange, SourceRange baseRange,
@@ -211,7 +222,8 @@ Type TypeResolution::resolveDependentMemberType(
if (!singleType) { if (!singleType) {
auto name = ref->getNameRef(); auto name = ref->getNameRef();
auto nameLoc = ref->getNameLoc(); auto nameLoc = ref->getNameLoc();
ctx.Diags.diagnose(nameLoc, diag::invalid_member_type, name, baseTy) const auto kind = describeDeclOfType(baseTy);
ctx.Diags.diagnose(nameLoc, diag::invalid_member_type, name, kind, baseTy)
.highlight(baseRange); .highlight(baseRange);
corrections.noteAllCandidates(); corrections.noteAllCandidates();
@@ -1160,8 +1172,10 @@ static Type diagnoseUnknownType(TypeResolution resolution,
// Qualified lookup case. // Qualified lookup case.
if (!parentType->mayHaveMembers()) { if (!parentType->mayHaveMembers()) {
diags.diagnose(comp->getNameLoc(), diag::invalid_member_type, const auto kind = describeDeclOfType(parentType);
comp->getNameRef(), parentType) diags
.diagnose(comp->getNameLoc(), diag::invalid_member_type,
comp->getNameRef(), kind, parentType)
.highlight(parentRange); .highlight(parentRange);
return ErrorType::get(ctx); return ErrorType::get(ctx);
} }
@@ -1214,8 +1228,10 @@ static Type diagnoseUnknownType(TypeResolution resolution,
parentType) parentType)
.highlight(parentRange); .highlight(parentRange);
} else { } else {
diags.diagnose(comp->getNameLoc(), diag::invalid_member_type, const auto kind = describeDeclOfType(parentType);
comp->getNameRef(), parentType) diags
.diagnose(comp->getNameLoc(), diag::invalid_member_type,
comp->getNameRef(), kind, parentType)
.highlight(parentRange); .highlight(parentRange);
// Note where the type was defined, this can help diagnose if the user // Note where the type was defined, this can help diagnose if the user
// expected name lookup to find a module when there's a conflicting type. // expected name lookup to find a module when there's a conflicting type.

View File

@@ -632,7 +632,7 @@ class Super: Differentiable {
// TODO(TF-632): Fix "'TangentVector' is not a member type of 'Self'" diagnostic. // TODO(TF-632): Fix "'TangentVector' is not a member type of 'Self'" diagnostic.
// The underlying error should appear instead: // The underlying error should appear instead:
// "covariant 'Self' can only appear at the top level of method result type". // "covariant 'Self' can only appear at the top level of method result type".
// expected-error @+1 2 {{'TangentVector' is not a member type of 'Self'}} // expected-error @+1 2 {{'TangentVector' is not a member type of type 'Self'}}
func vjpDynamicSelfResult() -> (Self, (Self.TangentVector) -> Self.TangentVector) { func vjpDynamicSelfResult() -> (Self, (Self.TangentVector) -> Self.TangentVector) {
return (self, { $0 }) return (self, { $0 })
} }

View File

@@ -19,6 +19,6 @@ struct A2<T> : P {
typealias Element = T typealias Element = T
} }
func toA<S: Empty, AT:P>(_ s: S) -> AT where AT.Element == S.Generator.Element { // expected-error{{'Generator' is not a member type of 'S'}} func toA<S: Empty, AT:P>(_ s: S) -> AT where AT.Element == S.Generator.Element { // expected-error{{'Generator' is not a member type of type 'S'}}
return AT() return AT()
} }

View File

@@ -25,6 +25,6 @@ protocol _Collection {
protocol Collection : _Collection, Sequence { protocol Collection : _Collection, Sequence {
subscript(i: Index) -> Iterator.Element {get set } subscript(i: Index) -> Iterator.Element {get set }
} }
func insertionSort<C: Mutable> (_ elements: inout C, i: C.Index) { // expected-error {{cannot find type 'Mutable' in scope}} expected-error {{'Index' is not a member type of 'C'}} func insertionSort<C: Mutable> (_ elements: inout C, i: C.Index) { // expected-error {{cannot find type 'Mutable' in scope}} expected-error {{'Index' is not a member type of type 'C'}}
var x: C.Iterator.Element = elements[i] // expected-error {{'Iterator' is not a member type of 'C'}} var x: C.Iterator.Element = elements[i] // expected-error {{'Iterator' is not a member type of type 'C'}}
} }

View File

@@ -141,7 +141,7 @@ func test8b<T: Barrable, U: Barrable>(_ t: T, u: U)
} }
// rdar://problem/19137463 // rdar://problem/19137463
func rdar19137463<T>(_ t: T) where T.a == T {} // expected-error{{'a' is not a member type of 'T'}} func rdar19137463<T>(_ t: T) where T.a == T {} // expected-error{{'a' is not a member type of type 'T'}}
rdar19137463(1) rdar19137463(1)

View File

@@ -79,4 +79,4 @@ class D {
typealias BElement = Int // expected-note{{did you mean 'BElement'?}} typealias BElement = Int // expected-note{{did you mean 'BElement'?}}
} }
func typoSuperclass2<T : D>(_: T, _: T.Element) { } // expected-error{{'Element' is not a member type of 'T'}} func typoSuperclass2<T : D>(_: T, _: T.Element) { } // expected-error{{'Element' is not a member type of type 'T'}}

View File

@@ -290,7 +290,7 @@ func sameTypeEq<T>(_: T) where T = T {} // expected-error{{use '==' for same-typ
func badTypeConformance1<T>(_: T) where Int : EqualComparable {} // expected-error{{type 'Int' in conformance requirement does not refer to a generic parameter or associated type}} func badTypeConformance1<T>(_: T) where Int : EqualComparable {} // expected-error{{type 'Int' in conformance requirement does not refer to a generic parameter or associated type}}
func badTypeConformance2<T>(_: T) where T.Blarg : EqualComparable { } // expected-error{{'Blarg' is not a member type of 'T'}} func badTypeConformance2<T>(_: T) where T.Blarg : EqualComparable { } // expected-error{{'Blarg' is not a member type of type 'T'}}
func badTypeConformance3<T>(_: T) where (T) -> () : EqualComparable { } func badTypeConformance3<T>(_: T) where (T) -> () : EqualComparable { }
// expected-error@-1{{type '(T) -> ()' in conformance requirement does not refer to a generic parameter or associated type}} // expected-error@-1{{type '(T) -> ()' in conformance requirement does not refer to a generic parameter or associated type}}

View File

@@ -199,7 +199,7 @@ struct XParam<T> { // expected-note{{'XParam' declared here}}
} }
} }
var xp : XParam<Int>.T = Int() // expected-error{{'T' is not a member type of 'XParam<Int>'}} var xp : XParam<Int>.T = Int() // expected-error{{'T' is not a member type of generic struct 'generic_types.XParam<Swift.Int>'}}
// Diagnose failure to meet a superclass requirement. // Diagnose failure to meet a superclass requirement.
class X1 { } class X1 { }
@@ -237,11 +237,11 @@ class Bottom<T : Bottom<Top>> {}
// Invalid inheritance clause // Invalid inheritance clause
struct UnsolvableInheritance1<T : T.A> {} struct UnsolvableInheritance1<T : T.A> {}
// expected-error@-1 {{'A' is not a member type of 'T'}} // expected-error@-1 {{'A' is not a member type of type 'T'}}
struct UnsolvableInheritance2<T : U.A, U : T.A> {} struct UnsolvableInheritance2<T : U.A, U : T.A> {}
// expected-error@-1 {{'A' is not a member type of 'U'}} // expected-error@-1 {{'A' is not a member type of type 'U'}}
// expected-error@-2 {{'A' is not a member type of 'T'}} // expected-error@-2 {{'A' is not a member type of type 'T'}}
enum X7<T> where X7.X : G { case X } // expected-error{{enum case 'X' is not a member type of 'X7<T>'}} enum X7<T> where X7.X : G { case X } // expected-error{{enum case 'X' is not a member type of 'X7<T>'}}
// expected-error@-1{{cannot find type 'G' in scope}} // expected-error@-1{{cannot find type 'G' in scope}}

View File

@@ -79,7 +79,7 @@ func badDiagnostic3() {
// Crash with missing nested type inside concrete type // Crash with missing nested type inside concrete type
class OuterGeneric<T> { class OuterGeneric<T> {
class InnerGeneric<U> where U:OuterGeneric<T.NoSuchType> { class InnerGeneric<U> where U:OuterGeneric<T.NoSuchType> {
// expected-error@-1 {{'NoSuchType' is not a member type of 'T'}} // expected-error@-1 {{'NoSuchType' is not a member type of type 'T'}}
func method() { func method() {
_ = method _ = method
} }
@@ -107,10 +107,10 @@ class P<N> {
// SR-5579 // SR-5579
protocol Foo { protocol Foo {
associatedtype Bar where Bar.Nonsense == Int // expected-error{{'Nonsense' is not a member type of 'Self.Bar'}} associatedtype Bar where Bar.Nonsense == Int // expected-error{{'Nonsense' is not a member type of type 'Self.Bar'}}
} }
protocol Wibble : Foo where Bar.EvenMoreNonsense == Int { } // expected-error{{'EvenMoreNonsense' is not a member type of 'Self.Bar'}} protocol Wibble : Foo where Bar.EvenMoreNonsense == Int { } // expected-error{{'EvenMoreNonsense' is not a member type of type 'Self.Bar'}}
// rdar://45271500 - failure to emit a diagnostic // rdar://45271500 - failure to emit a diagnostic
enum Cat<A> {} enum Cat<A> {}

View File

@@ -30,14 +30,14 @@ v.PrivateMemberVar = 1 // expected-error {{value of type 'PublicPrivate' has no
PublicPrivate.PrivateStaticMemberVar = 1 // expected-error {{'PublicPrivate' has no member 'PrivateStaticMemberVar'}} PublicPrivate.PrivateStaticMemberVar = 1 // expected-error {{'PublicPrivate' has no member 'PrivateStaticMemberVar'}}
v.privateMemberFunc() // expected-error {{value of type 'PublicPrivate' has no member 'privateMemberFunc'}} v.privateMemberFunc() // expected-error {{value of type 'PublicPrivate' has no member 'privateMemberFunc'}}
var privateTypedefVar: PublicPrivate.PrivateTypedef // expected-error {{'PrivateTypedef' is not a member type of 'PublicPrivate'}} var privateTypedefVar: PublicPrivate.PrivateTypedef // expected-error {{'PrivateTypedef' is not a member type of struct 'AccessSpecifiers.PublicPrivate'}}
var privateStructVar: PublicPrivate.PrivateStruct // expected-error {{'PrivateStruct' is not a member type of 'PublicPrivate'}} var privateStructVar: PublicPrivate.PrivateStruct // expected-error {{'PrivateStruct' is not a member type of struct 'AccessSpecifiers.PublicPrivate'}}
var privateEnumVar: PublicPrivate.PrivateEnum // expected-error {{'PrivateEnum' is not a member type of 'PublicPrivate'}} var privateEnumVar: PublicPrivate.PrivateEnum // expected-error {{'PrivateEnum' is not a member type of struct 'AccessSpecifiers.PublicPrivate'}}
// TODO: PrivateEnumValue1 and PrivateAnonymousEnumValue1 give the expected // TODO: PrivateEnumValue1 and PrivateAnonymousEnumValue1 give the expected
// error, but only because these types of enums (private or public) aren't // error, but only because these types of enums (private or public) aren't
// currently imported at all. Once that is fixed, remove this TODO. // currently imported at all. Once that is fixed, remove this TODO.
print(PublicPrivate.PrivateEnumValue1) // expected-error {{'PublicPrivate' has no member 'PrivateEnumValue1'}} print(PublicPrivate.PrivateEnumValue1) // expected-error {{'PublicPrivate' has no member 'PrivateEnumValue1'}}
print(PublicPrivate.PrivateAnonymousEnumValue1) // expected-error {{'PublicPrivate' has no member 'PrivateAnonymousEnumValue1'}} print(PublicPrivate.PrivateAnonymousEnumValue1) // expected-error {{'PublicPrivate' has no member 'PrivateAnonymousEnumValue1'}}
var privateClosedEnumVar: PublicPrivate.PrivateClosedEnum // expected-error {{'PrivateClosedEnum' is not a member type of 'PublicPrivate'}} var privateClosedEnumVar: PublicPrivate.PrivateClosedEnum // expected-error {{'PrivateClosedEnum' is not a member type of struct 'AccessSpecifiers.PublicPrivate'}}
var privateOpenEnumVar: PublicPrivate.PrivateOpenEnum // expected-error {{'PrivateOpenEnum' is not a member type of 'PublicPrivate'}} var privateOpenEnumVar: PublicPrivate.PrivateOpenEnum // expected-error {{'PrivateOpenEnum' is not a member type of struct 'AccessSpecifiers.PublicPrivate'}}
var privateFlagEnumVar: PublicPrivate.PrivateFlagEnum // expected-error {{'PrivateFlagEnum' is not a member type of 'PublicPrivate'}} var privateFlagEnumVar: PublicPrivate.PrivateFlagEnum // expected-error {{'PrivateFlagEnum' is not a member type of struct 'AccessSpecifiers.PublicPrivate'}}

View File

@@ -16,7 +16,7 @@ import SecondModule
public func f(_ x: ModuleType.MyStruct) {} public func f(_ x: ModuleType.MyStruct) {}
// CHECK: error: 'MyStruct' is not a member type of 'ModuleType' // CHECK: error: 'MyStruct' is not a member type of struct 'SecondModule.ModuleType'
// CHECK: SecondModule.ModuleType:1:15: note: 'ModuleType' declared here // CHECK: SecondModule.ModuleType:1:15: note: 'ModuleType' declared here
// CHECK: public struct ModuleType { // CHECK: public struct ModuleType {
// CHECK: ^ // CHECK: ^

View File

@@ -128,7 +128,7 @@ struct OuterGenericStruct<A> {
func genericFunction<T>(t: T) { func genericFunction<T>(t: T) {
class First : Second<T>.UnknownType { } class First : Second<T>.UnknownType { }
// expected-error@-1 {{type 'First' cannot be nested in generic function 'genericFunction(t:)'}} // expected-error@-1 {{type 'First' cannot be nested in generic function 'genericFunction(t:)'}}
// expected-error@-2 {{'UnknownType' is not a member type of 'Second<T>'}} // expected-error@-2 {{'UnknownType' is not a member type of generic class 'type_in_function.Second<T>'}}
class Second<T> : Second { } // expected-note{{'Second' declared here}} class Second<T> : Second { } // expected-note{{'Second' declared here}}
// expected-error@-1 {{type 'Second' cannot be nested in generic function 'genericFunction(t:)'}} // expected-error@-1 {{type 'Second' cannot be nested in generic function 'genericFunction(t:)'}}
// expected-error@-2 {{'Second' inherits from itself}} // expected-error@-2 {{'Second' inherits from itself}}

View File

@@ -82,8 +82,8 @@ struct Epsilon<T: Alpha, // expected-note{{conformance constraint 'U': 'Gamma' i
// expected-warning@-1{{redundant conformance constraint 'T': 'Alpha'}} // expected-warning@-1{{redundant conformance constraint 'T': 'Alpha'}}
U: Gamma> // expected-warning{{redundant conformance constraint 'U': 'Gamma'}} U: Gamma> // expected-warning{{redundant conformance constraint 'U': 'Gamma'}}
// expected-note@-1{{conformance constraint 'T': 'Alpha' implied here}} // expected-note@-1{{conformance constraint 'T': 'Alpha' implied here}}
where T.Beta == U, // expected-error{{'Beta' is not a member type of 'T'}} where T.Beta == U, // expected-error{{'Beta' is not a member type of type 'T'}}
U.Delta == T {} // expected-error{{'Delta' is not a member type of 'U'}} U.Delta == T {} // expected-error{{'Delta' is not a member type of type 'U'}}
// ----- // -----

View File

@@ -184,10 +184,10 @@ struct NonSynthesizedClass : Codable { // expected-note 4 {{'NonSynthesizedClass
// Qualified type lookup should clearly fail -- we shouldn't get a synthesized // Qualified type lookup should clearly fail -- we shouldn't get a synthesized
// type here. // type here.
public func qualifiedFoo(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedClass'}} public func qualifiedFoo(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'class_codable_member_type_lookup.NonSynthesizedClass'}}
internal func qualifiedBar(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedClass'}} internal func qualifiedBar(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'class_codable_member_type_lookup.NonSynthesizedClass'}}
fileprivate func qualfiedBaz(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedClass'}} fileprivate func qualfiedBaz(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'class_codable_member_type_lookup.NonSynthesizedClass'}}
private func qualifiedQux(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedClass'}} private func qualifiedQux(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'class_codable_member_type_lookup.NonSynthesizedClass'}}
// Unqualified lookups should find the public top-level CodingKeys type. // Unqualified lookups should find the public top-level CodingKeys type.
public func unqualifiedFoo(_ key: CodingKeys) { print(CodingKeys.topLevel) } public func unqualifiedFoo(_ key: CodingKeys) { print(CodingKeys.topLevel) }

View File

@@ -184,10 +184,10 @@ struct NonSynthesizedStruct : Codable { // expected-note 4 {{'NonSynthesizedStru
// Qualified type lookup should clearly fail -- we shouldn't get a synthesized // Qualified type lookup should clearly fail -- we shouldn't get a synthesized
// type here. // type here.
public func qualifiedFoo(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedStruct'}} public func qualifiedFoo(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'struct_codable_member_type_lookup.NonSynthesizedStruct'}}
internal func qualifiedBar(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedStruct'}} internal func qualifiedBar(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'struct_codable_member_type_lookup.NonSynthesizedStruct'}}
fileprivate func qualfiedBaz(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedStruct'}} fileprivate func qualfiedBaz(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'struct_codable_member_type_lookup.NonSynthesizedStruct'}}
private func qualifiedQux(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedStruct'}} private func qualifiedQux(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'struct_codable_member_type_lookup.NonSynthesizedStruct'}}
// Unqualified lookups should find the public top-level CodingKeys type. // Unqualified lookups should find the public top-level CodingKeys type.
public func unqualifiedFoo(_ key: CodingKeys) { print(CodingKeys.topLevel) } public func unqualifiedFoo(_ key: CodingKeys) { print(CodingKeys.topLevel) }

View File

@@ -34,14 +34,14 @@ struct GenericStruct<T> { // expected-note 3{{generic type 'GenericStruct' decla
func methodTwo() -> MetaAlias {} func methodTwo() -> MetaAlias {}
func methodOne() -> Alias.BadType {} func methodOne() -> Alias.BadType {}
// expected-error@-1 {{'BadType' is not a member type of 'GenericStruct<T>.Alias'}} // expected-error@-1 {{'BadType' is not a member type of type 'dependent_types.GenericStruct<T>.Alias'}}
func methodTwo() -> MetaAlias.BadType {} func methodTwo() -> MetaAlias.BadType {}
// expected-error@-1 {{'BadType' is not a member type of 'GenericStruct<T>.MetaAlias'}} // expected-error@-1 {{'BadType' is not a member type of type 'dependent_types.GenericStruct<T>.MetaAlias'}}
var propertyOne: Alias.BadType var propertyOne: Alias.BadType
// expected-error@-1 {{'BadType' is not a member type of 'GenericStruct<T>.Alias' (aka 'T')}} // expected-error@-1 {{'BadType' is not a member type of type 'dependent_types.GenericStruct<T>.Alias' (aka 'T')}}
var propertyTwo: MetaAlias.BadType var propertyTwo: MetaAlias.BadType
// expected-error@-1 {{'BadType' is not a member type of 'GenericStruct<T>.MetaAlias' (aka 'T.Type')}} // expected-error@-1 {{'BadType' is not a member type of type 'dependent_types.GenericStruct<T>.MetaAlias' (aka 'T.Type')}}
} }
// This was accepted in Swift 3.0 and sort of worked... but we can't // This was accepted in Swift 3.0 and sort of worked... but we can't

View File

@@ -2,7 +2,7 @@
protocol P { protocol P {
associatedtype A : P where A.X == Self associatedtype A : P where A.X == Self
// expected-error@-1{{'X' is not a member type of 'Self.A}} // expected-error@-1{{'X' is not a member type of type 'Self.A'}}
associatedtype X : P where P.A == Self associatedtype X : P where P.A == Self
// expected-error@-1{{associated type 'A' can only be used with a concrete type or generic parameter base}} // expected-error@-1{{associated type 'A' can only be used with a concrete type or generic parameter base}}
} }

View File

@@ -10,5 +10,5 @@
// Issue found by https://github.com/julasamer (julasamer) // Issue found by https://github.com/julasamer (julasamer)
struct c<d, e: b> where d.c == e { // expected-error {{cannot find type 'b' in scope}} expected-error {{'c' is not a member type of 'd'}} struct c<d, e: b> where d.c == e { // expected-error {{cannot find type 'b' in scope}} expected-error {{'c' is not a member type of type 'd'}}
} }