Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -3460,3 +3460,18 @@ let Subjects = SubjectList<[Function]>; let Documentation = [NoBuiltinDocs]; } + +def AcquireHandle : TypeAttr { + let Spellings = [Clang<"acquire_handle">]; + let Documentation = [AcquireHandleDocs]; +} + +def UseHandle : TypeAttr { + let Spellings = [Clang<"use_handle">]; + let Documentation = [UseHandleDocs]; +} + +def ReleaseHandle : TypeAttr { + let Spellings = [Clang<"release_handle">]; + let Documentation = [ReleaseHandleDocs]; +} Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -4565,3 +4565,62 @@ } }]; } + +def HandleDocs : DocumentationCategory<"Handle Attributes"> { + let Content = [{ +Handles are a way to identify resources like files, sockets, and processes. +They are more opaque than pointers and widely used in system programming. They +have similar risks such as never releasing a resource associated with a handle, +attempting to use a handle that was already released, or trying to release a +handle twice. Using the annotations below it is possible to make the ownership +of the handles clear: whose responsibility is to release them. They can also +aid static analysis tools to find bugs. + }]; +} + +def AcquireHandleDocs : Documentation { + let Category = HandleDocs; + let Content = [{ +If this annotation is on a function it is assumed to return a new handle. +In case this annotation is on an output parameter, the function is assumed +to fill the corresponding argument with a new handle. + +.. code-block:: c++ + + // Output arguments from Zircon. + zx_status_t zx_socket_create(uint32_t options, + zx_handle_t __attribute__((acquire_handle))* out0, + zx_handle_t __attribute__((acquire_handle))* out1); + + + // Returned handle. + int __attribute__((acquire_handle)) open(const char *path, int oflag, ... ); + }]; +} + +def UseHandleDocs : Documentation { + let Category = HandleDocs; + let Content = [{ +A function taking a handle by value might close the handle. If a function is +annotated with `use_handle` it is assumed to not to change the state of the +handle. It is also assumed to require an open handle to work with. + +.. code-block:: c++ + + zx_status_t zx_port_wait(zx_handle_t __attribute__((use_handle)) handle, + zx_time_t deadline, + zx_port_packet_t* packet); + }]; +} + +def ReleaseHandleDocs : Documentation { + let Category = HandleDocs; + let Content = [{ +If a function is annotated with `release_handle` it is assumed to close the handle. +It is also assumed to require an open handle to work with. + +.. code-block:: c++ + + zx_status_t zx_handle_close(zx_handle_t __attribute__((release_handle)) handle); + }]; +} Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3042,6 +3042,9 @@ "'%0' only applies to %select{function|pointer|" "Objective-C object or block pointer}1 types; type here is %2">, InGroup; +def warn_type_attribute_wrong_type_str : Warning< + "'%0' only applies to %1 types; type here is %2">, + InGroup; def warn_incomplete_encoded_type : Warning< "encoding of %0 type is incomplete because %1 component has unknown encoding">, InGroup>; Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -7403,6 +7403,20 @@ } } +template +static void handleHandleAttr(TypeProcessingState &State, QualType &CurType, + ParsedAttr &Attr) { + if (CurType->isFunctionType()) { + State.getSema().Diag(Attr.getLoc(), + diag::warn_type_attribute_wrong_type_str) + << Attr.getAttrName()->getName() << "non-function" << CurType; + return; + } + ASTContext &Ctx = State.getSema().Context; + CurType = State.getAttributedType(createSimpleAttr(Ctx, Attr), + CurType, CurType); + Attr.setUsedAsTypeAttr(); +} static void processTypeAttrs(TypeProcessingState &state, QualType &type, TypeAttrLocation TAL, @@ -7600,6 +7614,15 @@ else if (!handleFunctionTypeAttr(state, attr, type)) distributeFunctionTypeAttr(state, attr, type); break; + case ParsedAttr::AT_UseHandle: + handleHandleAttr(state, type, attr); + break; + case ParsedAttr::AT_AcquireHandle: + handleHandleAttr(state, type, attr); + break; + case ParsedAttr::AT_ReleaseHandle: + handleHandleAttr(state, type, attr); + break; } // Handle attributes that are defined in a macro. We do not want this to be Index: clang/test/Sema/attr-handles.cpp =================================================================== --- /dev/null +++ clang/test/Sema/attr-handles.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +void f(int __attribute__((acquire_handle)) * a); +void (*fp)(int __attribute__((use_handle)) handle); +auto lambda = [](int __attribute__((use_handle)) handle) -> + int __attribute__((acquire_handle)) { return 0; }; +void g(int __attribute__((acquire_handle)) a); // TODO: diagnose this. The acquire attribute only makes sense for outputs. +void h(int __attribute__((acquire_handle(1))) *a); // expected-error {{'acquire_handle' attribute takes no arguments}} +int i() __attribute__((release_handle)); // expected-warning {{'release_handle' only applies to non-function types; type here is 'int ()'}} +int j() __attribute__((use_handle)); // expected-warning {{'use_handle' only applies to non-function types; type here is 'int ()'}} +int __attribute__((acquire_handle)) a; // TODO: diagnose this. The type attribute only makes sense for function parameters, return types, type aliases.