Skip to content

Commit

Permalink
[Clang] Implement __builtin_source_location.
Browse files Browse the repository at this point in the history
This builtin returns the address of a global instance of the
`std::source_location::__impl` type, which must be defined (with an
appropriate shape) before calling the builtin.

It will be used to implement std::source_location in libc++ in a
future change. The builtin is compatible with GCC's implementation,
and libstdc++'s usage. An intentional divergence is that GCC declares
the builtin's return type to be `const void*` (for
ease-of-implementation reasons), while Clang uses the actual type,
`const std::source_location::__impl*`.

In order to support this new functionality, I've also added a new
'UnnamedGlobalConstantDecl'. This artificial Decl is modeled after
MSGuidDecl, and is used to represent a generic concept of an lvalue
constant with global scope, deduplicated by its value. It's possible
that MSGuidDecl itself, or some of the other similar sorts of things
in Clang might be able to be refactored onto this more-generic
concept, but there's enough special-case weirdness in MSGuidDecl that
I gave up attempting to share code there, at least for now.

Finally, for compatibility with libstdc++'s <source_location> header,
I've added a second exception to the "cannot cast from void* to T* in
constant evaluation" rule. This seems a bit distasteful, but feels
like the best available option.

Reviewers: aaron.ballman, erichkeane

Differential Revision: https://reviews.llvm.org/D120159
  • Loading branch information
jyknight committed Mar 28, 2022
1 parent e2485f3 commit d614874
Show file tree
Hide file tree
Showing 36 changed files with 666 additions and 198 deletions.
25 changes: 18 additions & 7 deletions clang/docs/LanguageExtensions.rst
Expand Up @@ -3377,10 +3377,9 @@ as the first argument to the intrinsic.
Source location builtins
------------------------
Clang provides experimental builtins to support C++ standard library implementation
of ``std::experimental::source_location`` as specified in http://wg21.link/N4600.
With the exception of ``__builtin_COLUMN``, these builtins are also implemented by
GCC.
Clang provides builtins to support C++ standard library implementation
of ``std::source_location`` as specified in C++20. With the exception
of ``__builtin_COLUMN``, these builtins are also implemented by GCC.
**Syntax**:
Expand All @@ -3390,6 +3389,7 @@ GCC.
const char *__builtin_FUNCTION();
unsigned __builtin_LINE();
unsigned __builtin_COLUMN(); // Clang only
const std::source_location::__impl *__builtin_source_location();
**Example of use**:
Expand All @@ -3416,9 +3416,11 @@ GCC.
**Description**:
The builtins ``__builtin_LINE``, ``__builtin_FUNCTION``, and ``__builtin_FILE`` return
the values, at the "invocation point", for ``__LINE__``, ``__FUNCTION__``, and
``__FILE__`` respectively. These builtins are constant expressions.
The builtins ``__builtin_LINE``, ``__builtin_FUNCTION``, and ``__builtin_FILE``
return the values, at the "invocation point", for ``__LINE__``,
``__FUNCTION__``, and ``__FILE__`` respectively. ``__builtin_COLUMN`` similarly
returns the column, though there is no corresponding macro. These builtins are
constant expressions.
When the builtins appear as part of a default function argument the invocation
point is the location of the caller. When the builtins appear as part of a
Expand All @@ -3429,6 +3431,15 @@ the invocation point is the same as the location of the builtin.
When the invocation point of ``__builtin_FUNCTION`` is not a function scope the
empty string is returned.
The builtin ``__builtin_source_location`` returns a pointer to constant static
data of type ``std::source_location::__impl``. This type must have already been
defined, and must contain exactly four fields: ``const char *_M_file_name``,
``const char *_M_function_name``, ``<any-integral-type> _M_line``, and
``<any-integral-type> _M_column``. The fields will be populated in the same
manner as the above four builtins, except that ``_M_function_name`` is populated
with ``__PRETTY_FUNCTION__`` rather than ``__FUNCTION__``.
Alignment builtins
------------------
Clang provides builtins to support checking and adjusting alignment of
Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Expand Up @@ -193,6 +193,8 @@ C++20 Feature Support
it is called through a template instantiation. This fixes
`Issue 54578 <https://github.com/llvm/llvm-project/issues/54578>`_.

- Implemented `__builtin_source_location()` which enables library support for std::source_location.

C++2b Feature Support
^^^^^^^^^^^^^^^^^^^^^

Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Expand Up @@ -314,6 +314,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// Mapping from GUIDs to the corresponding MSGuidDecl.
mutable llvm::FoldingSet<MSGuidDecl> MSGuidDecls;

/// Mapping from APValues to the corresponding UnnamedGlobalConstantDecl.
mutable llvm::FoldingSet<UnnamedGlobalConstantDecl>
UnnamedGlobalConstantDecls;

/// Mapping from APValues to the corresponding TemplateParamObjects.
mutable llvm::FoldingSet<TemplateParamObjectDecl> TemplateParamObjectDecls;

Expand Down Expand Up @@ -3064,6 +3068,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// GUID value.
MSGuidDecl *getMSGuidDecl(MSGuidDeclParts Parts) const;

/// Return a declaration for a uniquified anonymous global constant
/// corresponding to a given APValue.
UnnamedGlobalConstantDecl *
getUnnamedGlobalConstantDecl(QualType Ty, const APValue &Value) const;

/// Return the template parameter object of the given type with the given
/// value.
TemplateParamObjectDecl *getTemplateParamObjectDecl(QualType T,
Expand Down
47 changes: 47 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Expand Up @@ -4206,6 +4206,53 @@ class MSGuidDecl : public ValueDecl,
static bool classofKind(Kind K) { return K == Decl::MSGuid; }
};

/// An artificial decl, representing a global anonymous constant value which is
/// uniquified by value within a translation unit.
///
/// These is currently only used to back the LValue returned by
/// __builtin_source_location, but could potentially be used for other similar
/// situations in the future.
class UnnamedGlobalConstantDecl : public ValueDecl,
public Mergeable<UnnamedGlobalConstantDecl>,
public llvm::FoldingSetNode {

// The constant value of this global.
APValue Value;

void anchor() override;

UnnamedGlobalConstantDecl(DeclContext *DC, QualType T, const APValue &Val);

static UnnamedGlobalConstantDecl *Create(const ASTContext &C, QualType T,
const APValue &APVal);
static UnnamedGlobalConstantDecl *CreateDeserialized(ASTContext &C,
unsigned ID);

// Only ASTContext::getUnnamedGlobalConstantDecl and deserialization create
// these.
friend class ASTContext;
friend class ASTReader;
friend class ASTDeclReader;

public:
/// Print this in a human-readable format.
void printName(llvm::raw_ostream &OS) const override;

const APValue &getValue() const { return Value; }

static void Profile(llvm::FoldingSetNodeID &ID, QualType Ty,
const APValue &APVal) {
Ty.Profile(ID);
APVal.Profile(ID);
}
void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getType(), getValue());
}

static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == Decl::UnnamedGlobalConstant; }
};

/// Insertion operator for diagnostics. This allows sending an AccessSpecifier
/// into a diagnostic with <<.
const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
Expand Down
17 changes: 9 additions & 8 deletions clang/include/clang/AST/Expr.h
Expand Up @@ -4680,16 +4680,17 @@ class VAArgExpr : public Expr {
};

/// Represents a function call to one of __builtin_LINE(), __builtin_COLUMN(),
/// __builtin_FUNCTION(), or __builtin_FILE().
/// __builtin_FUNCTION(), __builtin_FILE(), or __builtin_source_location().
class SourceLocExpr final : public Expr {
SourceLocation BuiltinLoc, RParenLoc;
DeclContext *ParentContext;

public:
enum IdentKind { Function, File, Line, Column };
enum IdentKind { Function, File, Line, Column, SourceLocStruct };

SourceLocExpr(const ASTContext &Ctx, IdentKind Type, SourceLocation BLoc,
SourceLocation RParenLoc, DeclContext *Context);
SourceLocExpr(const ASTContext &Ctx, IdentKind Type, QualType ResultTy,
SourceLocation BLoc, SourceLocation RParenLoc,
DeclContext *Context);

/// Build an empty call expression.
explicit SourceLocExpr(EmptyShell Empty) : Expr(SourceLocExprClass, Empty) {}
Expand All @@ -4706,18 +4707,18 @@ class SourceLocExpr final : public Expr {
return static_cast<IdentKind>(SourceLocExprBits.Kind);
}

bool isStringType() const {
bool isIntType() const {
switch (getIdentKind()) {
case File:
case Function:
return true;
case SourceLocStruct:
return false;
case Line:
case Column:
return false;
return true;
}
llvm_unreachable("unknown source location expression kind");
}
bool isIntType() const LLVM_READONLY { return !isStringType(); }

/// If the SourceLocExpr has been resolved return the subexpression
/// representing the resolved value. Otherwise return null.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Expand Up @@ -2039,6 +2039,7 @@ DEF_TRAVERSE_DECL(BindingDecl, {
DEF_TRAVERSE_DECL(MSPropertyDecl, { TRY_TO(TraverseDeclaratorHelper(D)); })

DEF_TRAVERSE_DECL(MSGuidDecl, {})
DEF_TRAVERSE_DECL(UnnamedGlobalConstantDecl, {})

DEF_TRAVERSE_DECL(TemplateParamObjectDecl, {})

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/Stmt.h
Expand Up @@ -595,7 +595,7 @@ class alignas(void *) Stmt {

/// The kind of source location builtin represented by the SourceLocExpr.
/// Ex. __builtin_LINE, __builtin_FUNCTION, ect.
unsigned Kind : 2;
unsigned Kind : 3;
};

class StmtExprBitfields {
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DeclNodes.td
Expand Up @@ -41,6 +41,7 @@ def Named : DeclNode<Decl, "named declarations", 1>;
def OMPDeclareReduction : DeclNode<Value>, DeclContext;
def OMPDeclareMapper : DeclNode<Value>, DeclContext;
def MSGuid : DeclNode<Value>;
def UnnamedGlobalConstant : DeclNode<Value>;
def TemplateParamObject : DeclNode<Value>;
def Declarator : DeclNode<Value, "declarators", 1>;
def Field : DeclNode<Declarator, "non-static data members">;
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -11556,4 +11556,9 @@ def err_riscv_builtin_requires_extension : Error<
"builtin requires at least one of the following extensions support to be enabled : %0">;
def err_riscv_builtin_invalid_lmul : Error<
"LMUL argument must be in the range [0,3] or [5,7]">;

def err_std_source_location_impl_not_found : Error<
"'std::source_location::__impl' was not found; it must be defined before '__builtin_source_location' is called">;
def err_std_source_location_impl_malformed : Error<
"'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'">;
} // end of sema component.
1 change: 1 addition & 0 deletions clang/include/clang/Basic/TokenKinds.def
Expand Up @@ -432,6 +432,7 @@ KEYWORD(__builtin_FILE , KEYALL)
KEYWORD(__builtin_FUNCTION , KEYALL)
KEYWORD(__builtin_LINE , KEYALL)
KEYWORD(__builtin_COLUMN , KEYALL)
KEYWORD(__builtin_source_location , KEYCXX)

// __builtin_types_compatible_p is a GNU C extension that we handle like a C++
// type trait.
Expand Down
9 changes: 7 additions & 2 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -1144,6 +1144,10 @@ class Sema final {
/// The MSVC "_GUID" struct, which is defined in MSVC header files.
RecordDecl *MSVCGuidDecl;

/// The C++ "std::source_location::__impl" struct, defined in
/// \<source_location>.
RecordDecl *StdSourceLocationImplDecl;

/// Caches identifiers/selectors for NSFoundation APIs.
std::unique_ptr<NSAPI> NSAPIObj;

Expand Down Expand Up @@ -5684,14 +5688,15 @@ class Sema final {
TypeSourceInfo *TInfo, SourceLocation RPLoc);

// __builtin_LINE(), __builtin_FUNCTION(), __builtin_FILE(),
// __builtin_COLUMN()
// __builtin_COLUMN(), __builtin_source_location()
ExprResult ActOnSourceLocExpr(SourceLocExpr::IdentKind Kind,
SourceLocation BuiltinLoc,
SourceLocation RPLoc);

// Build a potentially resolved SourceLocExpr.
ExprResult BuildSourceLocExpr(SourceLocExpr::IdentKind Kind,
SourceLocation BuiltinLoc, SourceLocation RPLoc,
QualType ResultTy, SourceLocation BuiltinLoc,
SourceLocation RPLoc,
DeclContext *ParentContext);

// __null
Expand Down
7 changes: 5 additions & 2 deletions clang/include/clang/Serialization/ASTBitCodes.h
Expand Up @@ -41,7 +41,7 @@ namespace serialization {
/// Version 4 of AST files also requires that the version control branch and
/// revision match exactly, since there is no backward compatibility of
/// AST files at this time.
const unsigned VERSION_MAJOR = 16;
const unsigned VERSION_MAJOR = 17;

/// AST file minor version number supported by this version of
/// Clang.
Expand Down Expand Up @@ -1504,7 +1504,10 @@ enum DeclCode {
/// An OMPDeclareReductionDecl record.
DECL_OMP_DECLARE_REDUCTION,

DECL_LAST = DECL_OMP_DECLARE_REDUCTION
/// A UnnamedGlobalConstantDecl record.
DECL_UNNAMED_GLOBAL_CONSTANT,

DECL_LAST = DECL_UNNAMED_GLOBAL_CONSTANT
};

/// Record codes for each kind of statement or expression.
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Expand Up @@ -11875,6 +11875,23 @@ ASTContext::getMSGuidDecl(MSGuidDecl::Parts Parts) const {
return New;
}

UnnamedGlobalConstantDecl *
ASTContext::getUnnamedGlobalConstantDecl(QualType Ty,
const APValue &APVal) const {
llvm::FoldingSetNodeID ID;
UnnamedGlobalConstantDecl::Profile(ID, Ty, APVal);

void *InsertPos;
if (UnnamedGlobalConstantDecl *Existing =
UnnamedGlobalConstantDecls.FindNodeOrInsertPos(ID, InsertPos))
return Existing;

UnnamedGlobalConstantDecl *New =
UnnamedGlobalConstantDecl::Create(*this, Ty, APVal);
UnnamedGlobalConstantDecls.InsertNode(New, InsertPos);
return New;
}

TemplateParamObjectDecl *
ASTContext::getTemplateParamObjectDecl(QualType T, const APValue &V) const {
assert(T->isRecordType() && "template param object of unexpected type");
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/AST/ASTImporter.cpp
Expand Up @@ -6669,6 +6669,7 @@ ExpectedStmt ASTNodeImporter::VisitExpr(Expr *E) {

ExpectedStmt ASTNodeImporter::VisitSourceLocExpr(SourceLocExpr *E) {
Error Err = Error::success();
auto ToType = importChecked(Err, E->getType());
auto BLoc = importChecked(Err, E->getBeginLoc());
auto RParenLoc = importChecked(Err, E->getEndLoc());
if (Err)
Expand All @@ -6678,8 +6679,8 @@ ExpectedStmt ASTNodeImporter::VisitSourceLocExpr(SourceLocExpr *E) {
return ParentContextOrErr.takeError();

return new (Importer.getToContext())
SourceLocExpr(Importer.getToContext(), E->getIdentKind(), BLoc, RParenLoc,
*ParentContextOrErr);
SourceLocExpr(Importer.getToContext(), E->getIdentKind(), ToType, BLoc,
RParenLoc, *ParentContextOrErr);
}

ExpectedStmt ASTNodeImporter::VisitVAArgExpr(VAArgExpr *E) {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/DeclBase.cpp
Expand Up @@ -838,6 +838,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case ExternCContext:
case Decomposition:
case MSGuid:
case UnnamedGlobalConstant:
case TemplateParamObject:

case UsingDirective:
Expand Down
25 changes: 25 additions & 0 deletions clang/lib/AST/DeclCXX.cpp
Expand Up @@ -3363,6 +3363,31 @@ APValue &MSGuidDecl::getAsAPValue() const {
return APVal;
}

void UnnamedGlobalConstantDecl::anchor() {}

UnnamedGlobalConstantDecl::UnnamedGlobalConstantDecl(DeclContext *DC,
QualType Ty,
const APValue &Value)
: ValueDecl(Decl::UnnamedGlobalConstant, DC, SourceLocation(),
DeclarationName(), Ty),
Value(Value) {}

UnnamedGlobalConstantDecl *
UnnamedGlobalConstantDecl::Create(const ASTContext &C, QualType T,
const APValue &Value) {
DeclContext *DC = C.getTranslationUnitDecl();
return new (C, DC) UnnamedGlobalConstantDecl(DC, T, Value);
}

UnnamedGlobalConstantDecl *
UnnamedGlobalConstantDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
return new (C, ID) UnnamedGlobalConstantDecl(nullptr, QualType(), APValue());
}

void UnnamedGlobalConstantDecl::printName(llvm::raw_ostream &OS) const {
OS << "unnamed-global-constant";
}

static const char *getAccessName(AccessSpecifier AS) {
switch (AS) {
case AS_none:
Expand Down

0 comments on commit d614874

Please sign in to comment.