Skip to content
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
2 changes: 2 additions & 0 deletions clang/include/clang/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <algorithm>
#include <cassert>


namespace clang {
class ASTContext;
class AttributeCommonInfo;
Expand Down Expand Up @@ -78,6 +79,7 @@ class Attr : public AttributeCommonInfo {
= std::function<bool( // Should return false if the operation failed, true if successful
IdentifierInfo *, // attribute name
SmallVector<llvm::PointerUnion<Expr *, IdentifierLoc *>, 2>, // arguments
SmallVector<void *, 2>, // type args as opaque QualType pointers
AttributeCommonInfo::Form // attribute form
)>;

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -3450,7 +3450,7 @@ def ZeroCallUsedRegs : InheritableAttr {
def Pascal : DeclOrTypeAttr {
let Spellings = [Clang<"pascal">, CustomKeyword<"__pascal">,
CustomKeyword<"_pascal">];
// let Subjects = [Function, ObjCMethod];
// let Subjects = [Function, ObjCMethod];
let Documentation = [Undocumented];
}

Expand Down
19 changes: 11 additions & 8 deletions clang/lib/AST/ExprConstantMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1899,19 +1899,22 @@ static const ParsedAttr* toSyntacticForm(const Attr* val, ASTContext * C) {
auto onArgs = [&](
IdentifierInfo * attrName,
SmallVector<llvm::PointerUnion<Expr *, IdentifierLoc *>, 2> argExprs,
SmallVector<void *, 2> typeArgs,
AttributeCommonInfo::Form /* Do we need this fed back to us at all ?...*/
) {
AttributeScopeInfo scope;
if (val->hasScope())
scope = AttributeScopeInfo(val->getScopeName(), val->getLoc());
recoveredAttr = scratchpad.pool.create(
attrName,
val->getRange(),
scope,
argExprs.data(),
argExprs.size(),
val->getForm()
);
if (!typeArgs.empty() && argExprs.size() == 1 && argExprs[0].isNull()) {
ParsedType pt = ParsedType::getFromOpaquePtr(typeArgs[0]);
recoveredAttr = scratchpad.pool.createTypeAttribute(
attrName, val->getRange(), scope, pt, val->getForm(),
SourceLocation());
} else {
recoveredAttr = scratchpad.pool.create(
attrName, val->getRange(), scope,
argExprs.data(), argExprs.size(), val->getForm());
}
return recoveredAttr != nullptr;
};
// FIXME why is this not just returning the vector of args...
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/ParsedAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,12 @@ void ParsedAttr::profile(llvm::FoldingSetNodeID& ID,
ProfileExpr(ID, expr);
}
}

// Profile type argument if present (e.g., [[gsl::Owner(int)]])
if (hasParsedType()) {
QualType QT = getTypeArg().get();
QT.getCanonicalType().Profile(ID);
}
}

bool ParsedAttr::checkExactlyNumArgs(Sema &S, unsigned Num) const {
Expand Down
8 changes: 7 additions & 1 deletion clang/utils/TableGen/ClangAttrEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2625,6 +2625,7 @@ static bool isReflectableAttr(const Record* R) {
|| isVariadicStringLiteralArgument(arg)
|| isBoolArgument(arg)
|| isIntArgument(arg)
|| isTypeArgument(arg)
|| isExprArgument(arg);
};
std::vector<const Record *> ArgRecords = R->getValueAsListOfDefs("Args");
Expand All @@ -2636,6 +2637,7 @@ static void writeExtractSyntacticArgumentFunction(const Record &R,
raw_ostream &OS) {
OS << "bool " << R.getName() << "Attr::extractSyntacticArguments(ASTContext& C, OnSyntacticArgument onSyntax, SourceLocation srcLocation) const {\n";
OS << " SmallVector<llvm::PointerUnion<Expr *, IdentifierLoc *>, 2> args;\n";
OS << " SmallVector<void *, 2> typeArgs;\n";
OS << " const AttributeCommonInfo* info = this;\n";
OS << " IdentifierInfo &attrName = C.Idents.get(info->getAttrName()->getName());\n\n";

Expand All @@ -2657,6 +2659,9 @@ static void writeExtractSyntacticArgumentFunction(const Record &R,

if (isIntArgument(Arg)) {
OS << " args.push_back(IntegerLiteral::Create(C, llvm::APInt(32, " << Accessor << "), C.IntTy, srcLocation));\n";
} else if (isTypeArgument(Arg)) {
OS << " args.push_back(nullptr);\n";
OS << " typeArgs.push_back(" << Accessor << ".getAsOpaquePtr());\n";
} else if (isBoolArgument(Arg)) {
OS << " args.push_back(CXXBoolLiteralExpr::Create(C, " << Accessor << ", C.BoolTy, srcLocation));\n";
} else if (isIdentifierArgument(Arg, true)) {
Expand Down Expand Up @@ -2718,7 +2723,7 @@ static void writeExtractSyntacticArgumentFunction(const Record &R,
emitExprFromArg(OS, arg);
}
OS << "\n";
OS << " return onSyntax(&attrName, args, info->getForm());\n";
OS << " return onSyntax(&attrName, args, typeArgs, info->getForm());\n";
OS << "}\n";
}

Expand Down Expand Up @@ -3293,6 +3298,7 @@ static void emitAttributes(const RecordKeeper &Records, raw_ostream &OS,
OS << " = std::function<bool(\n";
OS << " IdentifierInfo *,\n"; // Attr name
OS << " SmallVector<llvm::PointerUnion<Expr *, IdentifierLoc *>, 2>,\n"; // Args
OS << " SmallVector<void *, 2>,\n"; // Type args as opaque QualType pointers
OS << " AttributeCommonInfo::Form\n"; // Form
OS << " )>;\n";
OS << "\n";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//===----------------------------------------------------------------------===//
//
// Copyright 2024 Bloomberg Finance L.P.
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03 || c++11 || c++14 || c++17 || c++20
// ADDITIONAL_COMPILE_FLAGS: -freflection-latest -fattribute-reflection

// <experimental/reflection>
//
// [reflection]
//
// Attribute reflection for attributes with type arguments (TypeArgument in
// Attr.td). Verifies that type-arg attributes are reflectable, that
// different type arguments compare unequal, and that has_attribute properly
// distinguishes them.

#include <meta>

struct [[gsl::Owner(int)]] OwnerInt {};
struct [[gsl::Owner(float)]] OwnerFloat {};
struct [[gsl::Pointer(int)]] PointerInt {};

// Basic reflectability
constexpr auto ownerIntAttr = ^^[[gsl::Owner(int)]];
constexpr auto ownerFloatAttr = ^^[[gsl::Owner(float)]];
constexpr auto pointerIntAttr = ^^[[gsl::Pointer(int)]];

static_assert(std::meta::is_attribute(ownerIntAttr));
static_assert(std::meta::is_attribute(ownerFloatAttr));
static_assert(std::meta::is_attribute(pointerIntAttr));

// Identity: same attr + same type arg are equal
static_assert(^^[[gsl::Owner(int)]] == ^^[[gsl::Owner(int)]]);
static_assert(^^[[gsl::Pointer(int)]] == ^^[[gsl::Pointer(int)]]);

// Different type arg => not equal
static_assert(^^[[gsl::Owner(int)]] != ^^[[gsl::Owner(float)]]);

// Different attr name => not equal (even with same type arg)
static_assert(^^[[gsl::Owner(int)]] != ^^[[gsl::Pointer(int)]]);

// has_attribute: matches when type arg agrees
static_assert(std::meta::has_attribute(^^OwnerInt, ownerIntAttr));
static_assert(std::meta::has_attribute(^^OwnerFloat, ownerFloatAttr));
static_assert(std::meta::has_attribute(^^PointerInt, pointerIntAttr));

// has_attribute: does NOT match when type arg differs
static_assert(!std::meta::has_attribute(^^OwnerInt, ownerFloatAttr));
static_assert(!std::meta::has_attribute(^^OwnerFloat, ownerIntAttr));

// ignore_argument: matches regardless of type arg
static_assert(std::meta::has_attribute(
^^OwnerInt,
ownerFloatAttr,
std::meta::attribute_comparison::ignore_argument
));

// ignore_namespace: gsl::Owner vs Owner
static_assert(std::meta::has_attribute(
^^OwnerInt,
^^[[gsl::Owner(int)]],
std::meta::attribute_comparison::ignore_namespace
));

int main() { return 0; }
Loading