Skip to content

Commit

Permalink
Implement __builtin_LINE() et. al. to support source location capture.
Browse files Browse the repository at this point in the history
Summary:
This patch implements the source location builtins `__builtin_LINE(), `__builtin_FUNCTION()`, `__builtin_FILE()` and `__builtin_COLUMN()`. These builtins are needed to implement [`std::experimental::source_location`](https://rawgit.com/cplusplus/fundamentals-ts/v2/main.html#reflection.src_loc.creation).

With the exception of `__builtin_COLUMN`, GCC also implements these builtins, and Clangs behavior is intended to match as closely as possible. 

Reviewers: rsmith, joerg, aaron.ballman, bogner, majnemer, shafik, martong

Reviewed By: rsmith

Subscribers: rnkovacs, loskutov, riccibruno, mgorny, kunitoki, alexr, majnemer, hfinkel, cfe-commits

Differential Revision: https://reviews.llvm.org/D37035

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@360937 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
EricWF committed May 16, 2019
1 parent 197e8c7 commit c786ab0
Show file tree
Hide file tree
Showing 44 changed files with 1,702 additions and 64 deletions.
55 changes: 55 additions & 0 deletions docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2300,6 +2300,61 @@ automatically will insert one if the first argument to `llvm.coro.suspend` is
token `none`. If a user calls `__builin_suspend`, clang will insert `token none`
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.
**Syntax**:
.. code-block:: c
const char *__builtin_FILE();
const char *__builtin_FUNCTION();
unsigned __builtin_LINE();
unsigned __builtin_COLUMN(); // Clang only
**Example of use**:
.. code-block:: c++
void my_assert(bool pred, int line = __builtin_LINE(), // Captures line of caller
const char* file = __builtin_FILE(),
const char* function = __builtin_FUNCTION()) {
if (pred) return;
printf("%s:%d assertion failed in function %s\n", file, line, function);
std::abort();
}
struct MyAggregateType {
int x;
int line = __builtin_LINE(); // captures line where aggregate initialization occurs
};
static_assert(MyAggregateType{42}.line == __LINE__);
struct MyClassType {
int line = __builtin_LINE(); // captures line of the constructor used during initialization
constexpr MyClassType(int) { assert(line == __LINE__); }
};
**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.
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
default member initializer, the invocation point is the location of the
constructor or aggregate initialization used to create the object. Otherwise
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.
Non-standard C++11 Attributes
=============================
Expand Down
15 changes: 15 additions & 0 deletions include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
llvm::DenseMap<const MaterializeTemporaryExpr *, APValue *>
MaterializedTemporaryValues;

/// A cache mapping a string value to a StringLiteral object with the same
/// value.
///
/// This is lazily created. This is intentionally not serialized.
mutable llvm::StringMap<StringLiteral *> StringLiteralCache;

/// Representation of a "canonical" template template parameter that
/// is used in canonical template names.
class CanonicalTemplateTemplateParm : public llvm::FoldingSetNode {
Expand Down Expand Up @@ -1323,6 +1329,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
ArrayType::ArraySizeModifier ASM,
unsigned IndexTypeQuals) const;

/// Return a type for a constant array for a string literal of the
/// specified element type and length.
QualType getStringLiteralArrayType(QualType EltTy, unsigned Length) const;

/// Returns a vla type where known sizes are replaced with [*].
QualType getVariableArrayDecayedType(QualType Ty) const;

Expand Down Expand Up @@ -2805,6 +2815,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
APValue *getMaterializedTemporaryValue(const MaterializeTemporaryExpr *E,
bool MayCreate);

/// Return a string representing the human readable name for the specified
/// function declaration or file name. Used by SourceLocExpr and
/// PredefinedExpr to cache evaluated results.
StringLiteral *getPredefinedStringLiteralFromCache(StringRef Key) const;

//===--------------------------------------------------------------------===//
// Statistics
//===--------------------------------------------------------------------===//
Expand Down
75 changes: 75 additions & 0 deletions include/clang/AST/CurrentSourceLocExprScope.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===--- CurrentSourceLocExprScope.h ----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines types used to track the current context needed to evaluate
// a SourceLocExpr.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_CURRENT_SOURCE_LOC_EXPR_SCOPE_H
#define LLVM_CLANG_AST_CURRENT_SOURCE_LOC_EXPR_SCOPE_H

#include <cassert>

namespace clang {
class Expr;

/// Represents the current source location and context used to determine the
/// value of the source location builtins (ex. __builtin_LINE), including the
/// context of default argument and default initializer expressions.
class CurrentSourceLocExprScope {
/// The CXXDefaultArgExpr or CXXDefaultInitExpr we're currently evaluating.
const Expr *DefaultExpr = nullptr;

public:
/// A RAII style scope guard used for tracking the current source
/// location and context as used by the source location builtins
/// (ex. __builtin_LINE).
class SourceLocExprScopeGuard;

const Expr *getDefaultExpr() const { return DefaultExpr; }

explicit CurrentSourceLocExprScope() = default;

private:
explicit CurrentSourceLocExprScope(const Expr *DefaultExpr)
: DefaultExpr(DefaultExpr) {}

CurrentSourceLocExprScope(CurrentSourceLocExprScope const &) = default;
CurrentSourceLocExprScope &
operator=(CurrentSourceLocExprScope const &) = default;
};

class CurrentSourceLocExprScope::SourceLocExprScopeGuard {
public:
SourceLocExprScopeGuard(const Expr *DefaultExpr,
CurrentSourceLocExprScope &Current)
: Current(Current), OldVal(Current), Enable(false) {
assert(DefaultExpr && "the new scope should not be empty");
if ((Enable = (Current.getDefaultExpr() == nullptr)))
Current = CurrentSourceLocExprScope(DefaultExpr);
}

~SourceLocExprScopeGuard() {
if (Enable)
Current = OldVal;
}

private:
SourceLocExprScopeGuard(SourceLocExprScopeGuard const &) = delete;
SourceLocExprScopeGuard &operator=(SourceLocExprScopeGuard const &) = delete;

CurrentSourceLocExprScope &Current;
CurrentSourceLocExprScope OldVal;
bool Enable;
};

} // end namespace clang

#endif // LLVM_CLANG_AST_CURRENT_SOURCE_LOC_EXPR_SCOPE_H
64 changes: 64 additions & 0 deletions include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -4171,6 +4171,70 @@ class VAArgExpr : public Expr {
}
};

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

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

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

/// Build an empty call expression.
explicit SourceLocExpr(EmptyShell Empty) : Expr(SourceLocExprClass, Empty) {}

/// Return the result of evaluating this SourceLocExpr in the specified
/// (and possibly null) default argument or initialization context.
APValue EvaluateInContext(const ASTContext &Ctx,
const Expr *DefaultExpr) const;

/// Return a string representing the name of the specific builtin function.
StringRef getBuiltinStr() const;

IdentKind getIdentKind() const {
return static_cast<IdentKind>(SourceLocExprBits.Kind);
}

bool isStringType() const {
switch (getIdentKind()) {
case File:
case Function:
return true;
case Line:
case Column:
return false;
}
}
bool isIntType() const LLVM_READONLY { return !isStringType(); }

/// If the SourceLocExpr has been resolved return the subexpression
/// representing the resolved value. Otherwise return null.
const DeclContext *getParentContext() const { return ParentContext; }
DeclContext *getParentContext() { return ParentContext; }

SourceLocation getLocation() const { return BuiltinLoc; }
SourceLocation getBeginLoc() const { return BuiltinLoc; }
SourceLocation getEndLoc() const { return RParenLoc; }

child_range children() {
return child_range(child_iterator(), child_iterator());
}

const_child_range children() const {
return const_child_range(child_iterator(), child_iterator());
}

static bool classof(const Stmt *T) {
return T->getStmtClass() == SourceLocExprClass;
}

private:
friend class ASTStmtReader;
};

/// Describes an C or C++ initializer list.
///
/// InitListExpr describes an initializer list, which can be used to
Expand Down
33 changes: 26 additions & 7 deletions include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -1121,15 +1121,19 @@ class CXXDefaultArgExpr final : public Expr {
/// The parameter whose default is being used.
ParmVarDecl *Param;

CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *Param)
/// The context where the default argument expression was used.
DeclContext *UsedContext;

CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *Param,
DeclContext *UsedContext)
: Expr(SC,
Param->hasUnparsedDefaultArg()
? Param->getType().getNonReferenceType()
: Param->getDefaultArg()->getType(),
Param->getDefaultArg()->getValueKind(),
Param->getDefaultArg()->getObjectKind(), false, false, false,
false),
Param(Param) {
Param(Param), UsedContext(UsedContext) {
CXXDefaultArgExprBits.Loc = Loc;
}

Expand All @@ -1139,8 +1143,10 @@ class CXXDefaultArgExpr final : public Expr {
// \p Param is the parameter whose default argument is used by this
// expression.
static CXXDefaultArgExpr *Create(const ASTContext &C, SourceLocation Loc,
ParmVarDecl *Param) {
return new (C) CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param);
ParmVarDecl *Param,
DeclContext *UsedContext) {
return new (C)
CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param, UsedContext);
}

// Retrieve the parameter that the argument was created from.
Expand All @@ -1151,6 +1157,9 @@ class CXXDefaultArgExpr final : public Expr {
const Expr *getExpr() const { return getParam()->getDefaultArg(); }
Expr *getExpr() { return getParam()->getDefaultArg(); }

const DeclContext *getUsedContext() const { return UsedContext; }
DeclContext *getUsedContext() { return UsedContext; }

/// Retrieve the location where this default argument was actually used.
SourceLocation getUsedLocation() const { return CXXDefaultArgExprBits.Loc; }

Expand Down Expand Up @@ -1190,17 +1199,20 @@ class CXXDefaultInitExpr : public Expr {
/// The field whose default is being used.
FieldDecl *Field;

/// The context where the default initializer expression was used.
DeclContext *UsedContext;

CXXDefaultInitExpr(const ASTContext &Ctx, SourceLocation Loc,
FieldDecl *Field, QualType Ty);
FieldDecl *Field, QualType Ty, DeclContext *UsedContext);

CXXDefaultInitExpr(EmptyShell Empty) : Expr(CXXDefaultInitExprClass, Empty) {}

public:
/// \p Field is the non-static data member whose default initializer is used
/// by this expression.
static CXXDefaultInitExpr *Create(const ASTContext &Ctx, SourceLocation Loc,
FieldDecl *Field) {
return new (Ctx) CXXDefaultInitExpr(Ctx, Loc, Field, Field->getType());
FieldDecl *Field, DeclContext *UsedContext) {
return new (Ctx) CXXDefaultInitExpr(Ctx, Loc, Field, Field->getType(), UsedContext);
}

/// Get the field whose initializer will be used.
Expand All @@ -1217,6 +1229,13 @@ class CXXDefaultInitExpr : public Expr {
return Field->getInClassInitializer();
}

const DeclContext *getUsedContext() const { return UsedContext; }
DeclContext *getUsedContext() { return UsedContext; }

/// Retrieve the location where this default initializer expression was
/// actually used.
SourceLocation getUsedLocation() const { return getBeginLoc(); }

SourceLocation getBeginLoc() const { return CXXDefaultInitExprBits.Loc; }
SourceLocation getEndLoc() const { return CXXDefaultInitExprBits.Loc; }

Expand Down
2 changes: 2 additions & 0 deletions include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2550,6 +2550,8 @@ DEF_TRAVERSE_STMT(PredefinedExpr, {})
DEF_TRAVERSE_STMT(ShuffleVectorExpr, {})
DEF_TRAVERSE_STMT(ConvertVectorExpr, {})
DEF_TRAVERSE_STMT(StmtExpr, {})
DEF_TRAVERSE_STMT(SourceLocExpr, {})

DEF_TRAVERSE_STMT(UnresolvedLookupExpr, {
TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
if (S->hasExplicitTemplateArgs()) {
Expand Down
12 changes: 12 additions & 0 deletions include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,17 @@ class alignas(void *) Stmt {
unsigned ResultIndex : 32 - 8 - NumExprBits;
};

class SourceLocExprBitfields {
friend class ASTStmtReader;
friend class SourceLocExpr;

unsigned : NumExprBits;

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

//===--- C++ Expression bitfields classes ---===//

class CXXOperatorCallExprBitfields {
Expand Down Expand Up @@ -935,6 +946,7 @@ class alignas(void *) Stmt {
ParenListExprBitfields ParenListExprBits;
GenericSelectionExprBitfields GenericSelectionExprBits;
PseudoObjectExprBitfields PseudoObjectExprBits;
SourceLocExprBitfields SourceLocExprBits;

// C++ Expressions
CXXOperatorCallExprBitfields CXXOperatorCallExprBits;
Expand Down
1 change: 1 addition & 0 deletions include/clang/Basic/StmtNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def ParenListExpr : DStmt<Expr>;
def VAArgExpr : DStmt<Expr>;
def GenericSelectionExpr : DStmt<Expr>;
def PseudoObjectExpr : DStmt<Expr>;
def SourceLocExpr : DStmt<Expr>;

// Wrapper expressions
def FullExpr : DStmt<Expr, 1>;
Expand Down
5 changes: 5 additions & 0 deletions include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,11 @@ KEYWORD(__alignof , KEYALL)
KEYWORD(__attribute , KEYALL)
KEYWORD(__builtin_choose_expr , KEYALL)
KEYWORD(__builtin_offsetof , KEYALL)
KEYWORD(__builtin_FILE , KEYALL)
KEYWORD(__builtin_FUNCTION , KEYALL)
KEYWORD(__builtin_LINE , KEYALL)
KEYWORD(__builtin_COLUMN , KEYALL)

// __builtin_types_compatible_p is a GNU C extension that we handle like a C++
// type trait.
TYPE_TRAIT_2(__builtin_types_compatible_p, TypeCompatible, KEYNOCXX)
Expand Down
Loading

0 comments on commit c786ab0

Please sign in to comment.