diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3471,3 +3471,27 @@ let Subjects = SubjectList<[Function]>; let Documentation = [NoBuiltinDocs]; } + +// FIXME: This attribute is not inheritable, it will not be propagated to +// redecls. [[clang::lifetimebound]] has the same problems. This should be +// fixed in TableGen (by probably adding a new inheritable flag). +def AcquireHandle : DeclOrTypeAttr { + let Spellings = [Clang<"acquire_handle">]; + let Args = [StringArgument<"HandleType">]; + let Subjects = SubjectList<[Function, TypedefName, ParmVar]>; + let Documentation = [AcquireHandleDocs]; +} + +def UseHandle : InheritableParamAttr { + let Spellings = [Clang<"use_handle">]; + let Args = [StringArgument<"HandleType">]; + let Subjects = SubjectList<[ParmVar]>; + let Documentation = [UseHandleDocs]; +} + +def ReleaseHandle : InheritableParamAttr { + let Spellings = [Clang<"release_handle">]; + let Args = [StringArgument<"HandleType">]; + let Subjects = SubjectList<[ParmVar]>; + let Documentation = [ReleaseHandleDocs]; +} diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4696,3 +4696,64 @@ } }]; } + +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 or a function type 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* out1 [[clang::acquire_handle]]); + + + // Returned handle. + [[clang::acquire_handle]] int open(const char *path, int oflag, ... ); + int open(const char *path, int oflag, ... ) __attribute__((acquire_handle)); + }]; +} + +def UseHandleDocs : Documentation { + let Category = HandleDocs; + let Content = [{ +A function taking a handle by value might close the handle. If a function +parameter 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 handle [[clang::use_handle]], + zx_time_t deadline, + zx_port_packet_t* packet); + }]; +} + +def ReleaseHandleDocs : Documentation { + let Category = HandleDocs; + let Content = [{ +If a function parameter 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 handle [[clang::release_handle]]); + }]; +} diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3131,6 +3131,8 @@ def err_cconv_incomplete_param_type : Error< "parameter %0 must have a complete type to use function %1 with the %2 " "calling convention">; +def err_attribute_output_parameter : Error< + "attribute only applies to output parameters">; def ext_cannot_use_trivial_abi : ExtWarn< "'trivial_abi' cannot be applied to %0">, InGroup; diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1555,6 +1555,9 @@ case attr::NoDeref: OS << "noderef"; break; + case attr::AcquireHandle: + OS << "acquire_handle"; + break; } OS << "))"; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6601,6 +6601,31 @@ handleSimpleAttribute(S, D, AL); } +static void handeAcquireHandleAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + if (AL.isUsedAsTypeAttr()) + return; + // Warn if the parameter is definitely not an output parameter. + if (const auto *PVD = dyn_cast(D)) { + if (PVD->getType()->isIntegerType()) { + S.Diag(AL.getLoc(), diag::err_attribute_output_parameter) + << AL.getRange(); + return; + } + } + StringRef Argument; + if (!S.checkStringLiteralArgumentAttr(AL, 0, Argument)) + return; + D->addAttr(AcquireHandleAttr::Create(S.Context, Argument, AL)); +} + +template +static void handleHandleAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + StringRef Argument; + if (!S.checkStringLiteralArgumentAttr(AL, 0, Argument)) + return; + D->addAttr(Attr::Create(S.Context, Argument, AL)); +} + //===----------------------------------------------------------------------===// // Top Level Sema Entry Points //===----------------------------------------------------------------------===// @@ -7378,6 +7403,18 @@ case ParsedAttr::AT_ArmMveAlias: handleArmMveAliasAttr(S, D, AL); break; + + case ParsedAttr::AT_AcquireHandle: + handeAcquireHandleAttr(S, D, AL); + break; + + case ParsedAttr::AT_ReleaseHandle: + handleHandleAttr(S, D, AL); + break; + + case ParsedAttr::AT_UseHandle: + handleHandleAttr(S, D, AL); + break; } } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -7634,6 +7634,18 @@ else if (!handleFunctionTypeAttr(state, attr, type)) distributeFunctionTypeAttr(state, attr, type); break; + case ParsedAttr::AT_AcquireHandle: { + if (!type->isFunctionType()) + return; + StringRef HandleType; + if (!state.getSema().checkStringLiteralArgumentAttr(attr, 0, HandleType)) + return; + type = state.getAttributedType( + AcquireHandleAttr::Create(state.getSema().Context, HandleType, attr), + type, type); + attr.setUsedAsTypeAttr(); + break; + } } // Handle attributes that are defined in a macro. We do not want this to be diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -9,6 +9,7 @@ // CHECK-NEXT: AMDGPUWavesPerEU (SubjectMatchRule_function) // CHECK-NEXT: AVRSignal (SubjectMatchRule_function) // CHECK-NEXT: AbiTag (SubjectMatchRule_record_not_is_union, SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_namespace) +// CHECK-NEXT: AcquireHandle (SubjectMatchRule_function, SubjectMatchRule_type_alias, SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: Alias (SubjectMatchRule_function, SubjectMatchRule_variable_is_global) // CHECK-NEXT: AlignValue (SubjectMatchRule_variable, SubjectMatchRule_type_alias) // CHECK-NEXT: AllocSize (SubjectMatchRule_function) @@ -128,6 +129,7 @@ // CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union) +// CHECK-NEXT: ReleaseHandle (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function) // CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: Restrict (SubjectMatchRule_function) @@ -145,6 +147,7 @@ // CHECK-NEXT: Target (SubjectMatchRule_function) // CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member) // CHECK-NEXT: TrivialABI (SubjectMatchRule_record) +// CHECK-NEXT: UseHandle (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: VecReturn (SubjectMatchRule_record) // CHECK-NEXT: VecTypeHint (SubjectMatchRule_function) // CHECK-NEXT: WarnUnused (SubjectMatchRule_record) diff --git a/clang/test/Sema/attr-handles.cpp b/clang/test/Sema/attr-handles.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/attr-handles.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// Decl annotations. +void f(int *a __attribute__((acquire_handle("Fuchsia")))); +void (*fp)(int handle [[clang::use_handle("Fuchsia")]]); +auto lambda = [](int handle [[clang::use_handle("Fuchsia")]]){}; +void g(int a __attribute__((acquire_handle("Fuchsia")))); // expected-error {{attribute only applies to output parameters}} +void h(int *a __attribute__((acquire_handle))); // expected-error {{'acquire_handle' attribute takes one argument}} +void h(int *a __attribute__((acquire_handle(1)))); // expected-error {{attribute requires a string}} +void h(int *a __attribute__((acquire_handle("RandomString", "AndAnother")))); // expected-error {{'acquire_handle' attribute takes one argument}} +__attribute__((release_handle("Fuchsia"))) int i(); // expected-warning {{'release_handle' attribute only applies to parameters}} +__attribute__((use_handle("Fuchsia"))) int j(); // expected-warning {{'use_handle' attribute only applies to parameters}} +int a __attribute__((acquire_handle("Fuchsia"))); // expected-warning {{'acquire_handle' attribute only applies to functions, typedefs, and parameters}} +int (* __attribute__((acquire_handle("Fuchsia"))) fpt)(char *); // expected-warning {{'acquire_handle' attribute only applies to functions, typedefs, and parameters}} + +// Type annotations. +auto lambdat = [](int handle __attribute__((use_handle("Fuchsia")))) + __attribute__((acquire_handle("Fuchsia"))) -> int { return 0; }; +int __attribute((acquire_handle("Fuchsia"))) ta; // expected-warning {{'acquire_handle' attribute only applies to functions, typedefs, and parameters}} + +// Typedefs. +typedef int callback(char *) __attribute__((acquire_handle("Fuchsia")));