Skip to content

New swift attributes + direct change #2189

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 5 commits into from
Dec 8, 2020
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
15 changes: 14 additions & 1 deletion clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -2106,6 +2106,19 @@ def Regparm : TypeAttr {
let ASTNode = 0;
}

def SwiftAsyncName : InheritableAttr {
let Spellings = [GNU<"swift_async_name">];
let Args = [StringArgument<"Name">];
let Subjects = SubjectList<[ObjCMethod, Function], ErrorDiag>;
let Documentation = [SwiftAsyncNameDocs];
}

def SwiftAttr : InheritableAttr {
let Spellings = [GNU<"swift_attr">];
let Args = [StringArgument<"Attribute">];
let Documentation = [SwiftAttrDocs];
}

def SwiftBridge : Attr {
let Spellings = [GNU<"swift_bridge">];
let Subjects = SubjectList<[Tag, TypedefName, ObjCInterface, ObjCProtocol],
Expand Down Expand Up @@ -2168,7 +2181,7 @@ def SwiftImportAsNonGeneric : InheritableAttr {
let Documentation = [Undocumented];
}

def SwiftImportPropertyAsAccessors : InheritableAttr {
def SwiftImportPropertyAsAccessors : InheritableAttr {
// This attribute has no spellings as it is only ever created implicitly
// from API notes.
let Spellings = [];
Expand Down
34 changes: 34 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -3546,6 +3546,40 @@ The ``ns_error_domain`` attribute indicates a global constant representing the e
}];
}

def SwiftAsyncNameDocs : Documentation {
let Category = SwiftDocs;
let Heading = "swift_async_name";
let Content = [{
The ``swift_async_name`` attribute provides the name of the ``async`` overload for
the given declaration in Swift. If this attribute is absent, the name is
transformed according to the algorithm built into the Swift compiler.

The argument is a string literal that contains the Swift name of the function or
method. The name may be a compound Swift name. The function or method with such
an attribute must have more than zero parameters, as its last parameter is
assumed to be a callback that's eliminated in the Swift ``async`` name.

.. code-block:: objc

@interface URL
+ (void) loadContentsFrom:(URL *)url callback:(void (^)(NSData *))data __attribute__((__swift_async_name__("URL.loadContentsFrom(_:)")))
@end
}];
}

def SwiftAttrDocs : Documentation {
let Category = SwiftDocs;
let Heading = "swift_attr";
let Content = [{
The ``swift_attr`` provides a Swift-specific annotation for the declaration
to which the attribute appertains to. It can be used on any declaration
in Clang. This kind of annotation is ignored by Clang as it doesn't have any
semantic meaning in languages supported by Clang. The Swift compiler can
interpret these annotations according to its own rules when importing C or
Objective-C declarations.
}];
}

def SwiftBridgeDocs : Documentation {
let Category = SwiftDocs;
let Content = [{
Expand Down
16 changes: 13 additions & 3 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,10 @@ def warn_objc_direct_property_ignored : Warning<
InGroup<IgnoredAttributes>;
def err_objc_direct_dynamic_property : Error<
"direct property cannot be @dynamic">;
def err_objc_direct_protocol_conformance : Error<
"%select{category %1|class extension}0 cannot conform to protocol %2 because "
"of direct members declared in interface %3">;
def note_direct_member_here : Note<"direct member declared here">;

def warn_conflicting_overriding_ret_types : Warning<
"conflicting return type in "
Expand Down Expand Up @@ -4062,9 +4066,15 @@ def warn_attr_swift_name_multiple_selfs : Warning<
def warn_attr_swift_name_static_subscript : Warning<
"%0 attribute for 'subscript' must take a 'self:' parameter">,
InGroup<SwiftNameAttribute>;
def warn_attr_swift_name_num_params : Warning<
"too %select{few|many}0 parameters in %1 attribute (expected %2; got %3)">,
InGroup<SwiftNameAttribute>;
def warn_attr_swift_name_num_params
: Warning<"too %select{few|many}0 parameters in the signature specified by "
"the %1 attribute (expected %2; got %3)">,
InGroup<SwiftNameAttribute>;
def warn_attr_swift_name_decl_missing_params
: Warning<"%0 attribute cannot be applied to a %select{function|method}1 "
"with no parameters">,
InGroup<SwiftNameAttribute>;

def err_attr_swift_error_no_error_parameter : Error<
"%0 attribute can only be applied to a %select{function|method}1 "
"with an error parameter">;
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1857,7 +1857,7 @@ class Sema final {
/// \returns true if the name is a valid swift name for \p D, false otherwise.
bool DiagnoseSwiftName(Decl *D, StringRef Name,
SourceLocation ArgLoc,
const IdentifierInfo *AttrName);
const IdentifierInfo *AttrName, bool IsAsync);

/// A derivative of BoundTypeDiagnoser for which the diagnostic's type
/// parameter is preceded by a 0/1 enum that is 1 if the type is sizeless.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaAPINotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ static void ProcessAPINotes(Sema &S, Decl *D,
auto &APINoteName = S.getASTContext().Idents.get("SwiftName API Note");

if (!S.DiagnoseSwiftName(D, info.SwiftName, D->getLocation(),
&APINoteName)) {
&APINoteName, /*IsAsync=*/false)) {
return nullptr;
}

Expand Down
48 changes: 44 additions & 4 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5553,6 +5553,16 @@ static void handleObjCPreciseLifetimeAttr(Sema &S, Decl *D,
D->addAttr(::new (S.Context) ObjCPreciseLifetimeAttr(S.Context, AL));
}

static void handleSwiftAttrAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
// Make sure that there is a string literal as the annotation's single
// argument.
StringRef Str;
if (!S.checkStringLiteralArgumentAttr(AL, 0, Str))
return;

D->addAttr(::new (S.Context) SwiftAttrAttr(S.Context, AL, Str));
}

static Optional<unsigned>
validateSwiftFunctionName(StringRef Name,
unsigned &SwiftParamCount,
Expand Down Expand Up @@ -5714,7 +5724,7 @@ validateSwiftFunctionName(StringRef Name,
/// \returns true if the name is a valid swift name for \p D, false otherwise.
bool Sema::DiagnoseSwiftName(Decl *D, StringRef Name,
SourceLocation ArgLoc,
const IdentifierInfo *AttrName) {
const IdentifierInfo *AttrName, bool IsAsync) {
if (isa<ObjCMethodDecl>(D) || isa<FunctionDecl>(D)) {
ArrayRef<ParmVarDecl*> Params;
unsigned ParamCount;
Expand All @@ -5734,6 +5744,16 @@ bool Sema::DiagnoseSwiftName(Decl *D, StringRef Name,
}
}

// The async name drops the last callback parameter.
if (IsAsync) {
if (ParamCount == 0) {
Diag(ArgLoc, diag::warn_attr_swift_name_decl_missing_params)
<< AttrName << isa<ObjCMethodDecl>(D);
return false;
}
ParamCount -= 1;
}

unsigned SwiftParamCount;
bool IsSingleParamInit;
if (auto diagID = validateSwiftFunctionName(Name, SwiftParamCount,
Expand Down Expand Up @@ -5769,10 +5789,10 @@ bool Sema::DiagnoseSwiftName(Decl *D, StringRef Name,
return false;
}

} else if (isa<EnumConstantDecl>(D) || isa<ObjCProtocolDecl>(D) ||
} else if ((isa<EnumConstantDecl>(D) || isa<ObjCProtocolDecl>(D) ||
isa<ObjCInterfaceDecl>(D) || isa<ObjCPropertyDecl>(D) ||
isa<VarDecl>(D) || isa<TypedefNameDecl>(D) || isa<TagDecl>(D) ||
isa<IndirectFieldDecl>(D) || isa<FieldDecl>(D)) {
isa<IndirectFieldDecl>(D) || isa<FieldDecl>(D)) && !IsAsync) {
StringRef ContextName, BaseName;
std::tie(ContextName, BaseName) = Name.split('.');
if (BaseName.empty()) {
Expand Down Expand Up @@ -5803,12 +5823,26 @@ static void handleSwiftName(Sema &S, Decl *D, const ParsedAttr &Attr) {
if (!S.checkStringLiteralArgumentAttr(Attr, 0, Name, &ArgLoc))
return;

if (!S.DiagnoseSwiftName(D, Name, ArgLoc, Attr.getAttrName()))
if (!S.DiagnoseSwiftName(D, Name, ArgLoc, Attr.getAttrName(),
/*IsAsync=*/false))
return;

D->addAttr(::new (S.Context) SwiftNameAttr(S.Context, Attr, Name));
}

static void handleSwiftAsyncName(Sema &S, Decl *D, const ParsedAttr &Attr) {
StringRef Name;
SourceLocation ArgLoc;
if (!S.checkStringLiteralArgumentAttr(Attr, 0, Name, &ArgLoc))
return;

if (!S.DiagnoseSwiftName(D, Name, ArgLoc, Attr.getAttrName(),
/*IsAsync=*/true))
return;

D->addAttr(::new (S.Context) SwiftAsyncNameAttr(S.Context, Attr, Name));
}

static bool isErrorParameter(Sema &S, QualType paramType) {
if (auto ptr = paramType->getAs<PointerType>()) {
auto outerPointee = ptr->getPointeeType();
Expand Down Expand Up @@ -7877,6 +7911,12 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
break;

// Swift attributes.
case ParsedAttr::AT_SwiftAsyncName:
handleSwiftAsyncName(S, D, AL);
break;
case ParsedAttr::AT_SwiftAttr:
handleSwiftAttrAttr(S, D, AL);
break;
case ParsedAttr::AT_SwiftPrivate:
handleSimpleAttribute<SwiftPrivateAttr>(S, D, AL);
break;
Expand Down
51 changes: 51 additions & 0 deletions clang/lib/Sema/SemaDeclObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3946,6 +3946,55 @@ static void DiagnoseVariableSizedIvars(Sema &S, ObjCContainerDecl *OCD) {
}
}

static void DiagnoseCategoryDirectMembersProtocolConformance(
Sema &S, ObjCProtocolDecl *PDecl, ObjCCategoryDecl *CDecl);

static void DiagnoseCategoryDirectMembersProtocolConformance(
Sema &S, ObjCCategoryDecl *CDecl,
const llvm::iterator_range<ObjCProtocolList::iterator> &Protocols) {
for (auto *PI : Protocols)
DiagnoseCategoryDirectMembersProtocolConformance(S, PI, CDecl);
}

static void DiagnoseCategoryDirectMembersProtocolConformance(
Sema &S, ObjCProtocolDecl *PDecl, ObjCCategoryDecl *CDecl) {
if (!PDecl->isThisDeclarationADefinition() && PDecl->getDefinition())
PDecl = PDecl->getDefinition();

llvm::SmallVector<const Decl *, 4> DirectMembers;
const auto *IDecl = CDecl->getClassInterface();
for (auto *MD : PDecl->methods()) {
if (!MD->isPropertyAccessor()) {
if (const auto *CMD =
IDecl->getMethod(MD->getSelector(), MD->isInstanceMethod())) {
if (CMD->isDirectMethod())
DirectMembers.push_back(CMD);
}
}
}
for (auto *PD : PDecl->properties()) {
if (const auto *CPD = IDecl->FindPropertyVisibleInPrimaryClass(
PD->getIdentifier(),
PD->isClassProperty()
? ObjCPropertyQueryKind::OBJC_PR_query_class
: ObjCPropertyQueryKind::OBJC_PR_query_instance)) {
if (CPD->isDirectProperty())
DirectMembers.push_back(CPD);
}
}
if (!DirectMembers.empty()) {
S.Diag(CDecl->getLocation(), diag::err_objc_direct_protocol_conformance)
<< CDecl->IsClassExtension() << CDecl << PDecl << IDecl;
for (const auto *MD : DirectMembers)
S.Diag(MD->getLocation(), diag::note_direct_member_here);
return;
}

// Check on this protocols's referenced protocols, recursively.
DiagnoseCategoryDirectMembersProtocolConformance(S, CDecl,
PDecl->protocols());
}

// Note: For class/category implementations, allMethods is always null.
Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods,
ArrayRef<DeclGroupPtrTy> allTUVars) {
Expand Down Expand Up @@ -4050,6 +4099,8 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods,
ObjCInterfaceDecl *CCPrimary = C->getClassInterface();
DiagnoseClassExtensionDupMethods(C, CCPrimary);
}

DiagnoseCategoryDirectMembersProtocolConformance(*this, C, C->protocols());
}
if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(ClassDecl)) {
if (CDecl->getIdentifier())
Expand Down
8 changes: 8 additions & 0 deletions clang/test/AST/attr-swift_attr.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// RUN: %clang_cc1 -fsyntax-only -ast-dump %s | FileCheck %s

__attribute__((swift_attr("@actor")))
@interface View
@end

// CHECK: InterfaceDecl {{.*}} View
// CHECK-NEXT: SwiftAttrAttr {{.*}} "@actor"
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
// CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property)
// CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member)
// CHECK-NEXT: SpeculativeLoadHardening (SubjectMatchRule_function, SubjectMatchRule_objc_method)
// CHECK-NEXT: SwiftAsyncName (SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: SwiftBridgedTypedef (SubjectMatchRule_type_alias)
// CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: SwiftError (SubjectMatchRule_function, SubjectMatchRule_objc_method)
Expand Down
22 changes: 11 additions & 11 deletions clang/test/SemaObjC/attr-swift.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,22 @@ + (SNFoo *)fooWithValue:(int)value value:(int)value2 __attribute__((swift_name("
+ (SNFoo *)fooWithConvertingValue:(int)value value:(int)value2 __attribute__((swift_name("init(_:extra:)")));

+ (SNFoo *)fooWithOtherValue:(int)value __attribute__((swift_name("init"))); // expected-warning {{parameter of 'swift_name' attribute must be a Swift function name string}}
+ (SNFoo *)fooWithAnotherValue:(int)value __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}}
+ (SNFoo *)fooWithYetAnotherValue:(int)value __attribute__((swift_name("foo(value:extra:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 1; got 2)}}
+ (SNFoo *)fooWithAnotherValue:(int)value __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in the signature specified by the 'swift_name' attribute (expected 1; got 0)}}
+ (SNFoo *)fooWithYetAnotherValue:(int)value __attribute__((swift_name("foo(value:extra:)"))); // expected-warning {{too many parameters in the signature specified by the 'swift_name' attribute (expected 1; got 2)}}

+ (SNFoo *)fooAndReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo()"))); // no-warning
+ (SNFoo *)fooWithValue:(int)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo(value:)"))); // no-warning
+ (SNFoo *)fooFromErrorCode:(const int *)errorCode __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}}
+ (SNFoo *)fooWithValue:(int)value fromErrorCode:(const int *)errorCode __attribute__((swift_name("foo(value:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}}
+ (SNFoo *)fooFromErrorCode:(const int *)errorCode __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in the signature specified by the 'swift_name' attribute (expected 1; got 0)}}
+ (SNFoo *)fooWithValue:(int)value fromErrorCode:(const int *)errorCode __attribute__((swift_name("foo(value:)"))); // expected-warning {{too few parameters in the signature specified by the 'swift_name' attribute (expected 2; got 1)}}
+ (SNFoo *)fooWithPointerA:(int *)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo()"))); // no-warning
+ (SNFoo *)fooWithPointerB:(int *)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo(pointer:)"))); // no-warning
+ (SNFoo *)fooWithPointerC:(int *)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo(pointer:errorCode:)"))); // no-warning
+ (SNFoo *)fooWithOtherFoo:(SNFoo *)other __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}}
+ (SNFoo *)fooWithOtherFoo:(SNFoo *)other __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in the signature specified by the 'swift_name' attribute (expected 1; got 0)}}

+ (instancetype)specialFoo __attribute__((swift_name("init(options:)")));
+ (instancetype)specialBar __attribute__((swift_name("init(options:extra:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 2)}}
+ (instancetype)specialBaz __attribute__((swift_name("init(_:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 1)}}
+ (instancetype)specialGarply __attribute__((swift_name("foo(options:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 1)}}
+ (instancetype)specialBar __attribute__((swift_name("init(options:extra:)"))); // expected-warning {{too many parameters in the signature specified by the 'swift_name' attribute (expected 0; got 2)}}
+ (instancetype)specialBaz __attribute__((swift_name("init(_:)"))); // expected-warning {{too many parameters in the signature specified by the 'swift_name' attribute (expected 0; got 1)}}
+ (instancetype)specialGarply __attribute__((swift_name("foo(options:)"))); // expected-warning {{too many parameters in the signature specified by the 'swift_name' attribute (expected 0; got 1)}}

+ (instancetype)trailingParen __attribute__((swift_name("foo("))); // expected-warning {{parameter of 'swift_name' attribute must be a Swift function name string}}
+ (instancetype)trailingColon:(int)value __attribute__((swift_name("foo(value)"))); // expected-warning {{parameter of 'swift_name' attribute must be a Swift function name string}}
Expand All @@ -90,16 +90,16 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct {
int global_int __attribute__((swift_name("GlobalInt")));

void foo1(int i) __attribute__((swift_name("foo"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}}
void foo2(int i) __attribute__((swift_name("foo()"))); // expected-warning{{too few parameters in 'swift_name' attribute (expected 1; got 0)}}
void foo2(int i) __attribute__((swift_name("foo(a:b:)"))); // expected-warning{{too many parameters in 'swift_name' attribute (expected 1; got 2)}}
void foo2(int i) __attribute__((swift_name("foo()"))); // expected-warning{{too few parameters in the signature specified by the 'swift_name' attribute (expected 1; got 0)}}
void foo2(int i) __attribute__((swift_name("foo(a:b:)"))); // expected-warning{{too many parameters in the signature specified by the 'swift_name' attribute (expected 1; got 2)}}
void foo3(int i, int j) __attribute__((swift_name("fooWithX(_:y:)"))); // okay
void foo4(int i, int *error) __attribute__((swift_name("fooWithA(_:)"))); // okay

typedef int some_int_type __attribute__((swift_name("SomeInt")));

struct Point3D createPoint3D(float x, float y, float z) __attribute__((swift_name("Point3D.init(x:y:z:)")));
struct Point3D rotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(self:radians:)")));
struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radians:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}}
struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radians:)"))); // expected-warning {{too few parameters in the signature specified by the 'swift_name' attribute (expected 2; got 1)}}

extern struct Point3D identityPoint __attribute__((swift_name("Point3D.identity")));

Expand Down
37 changes: 37 additions & 0 deletions clang/test/SemaObjC/attr-swift_name.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc -fblocks %s

#define SWIFT_NAME(name) __attribute__((__swift_name__(name)))
#define SWIFT_ASYNC_NAME(name) __attribute__((__swift_async_name__(name)))

@protocol P
@end

typedef int (^CallbackTy)(void);

@interface AsyncI<P>

- (void)doSomethingWithCallback:(CallbackTy)callback SWIFT_ASYNC_NAME("doSomething()");
- (void)doSomethingX:(int)x withCallback:(CallbackTy)callback SWIFT_ASYNC_NAME("doSomething(x:)");

// expected-warning@+1 {{too many parameters in the signature specified by the '__swift_async_name__' attribute (expected 1; got 2)}}
- (void)doSomethingY:(int)x withCallback:(CallbackTy)callback SWIFT_ASYNC_NAME("doSomething(x:y:)");

// expected-warning@+1 {{too few parameters in the signature specified by the '__swift_async_name__' attribute (expected 1; got 0)}}
- (void)doSomethingZ:(int)x withCallback:(CallbackTy)callback SWIFT_ASYNC_NAME("doSomething()");

// expected-warning@+1 {{'__swift_async_name__' attribute cannot be applied to a method with no parameters}}
- (void)doSomethingNone SWIFT_ASYNC_NAME("doSomething()");

// expected-error@+1 {{'__swift_async_name__' attribute takes one argument}}
- (void)brokenAttr __attribute__((__swift_async_name__("brokenAttr", 2)));

@end

void asyncFunc(CallbackTy callback) SWIFT_ASYNC_NAME("asyncFunc()");

// expected-warning@+1 {{'__swift_async_name__' attribute cannot be applied to a function with no parameters}}
void asyncNoParams(void) SWIFT_ASYNC_NAME("asyncNoParams()");

// expected-error@+1 {{'__swift_async_name__' attribute only applies to Objective-C methods and functions}}
SWIFT_ASYNC_NAME("NoAsync")
@protocol NoAsync @end
Loading