Implement __builtin_LINE() et. al. to support source location capture.

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

llvm-svn: 360937
This commit is contained in:
Eric Fiselier 2019-05-16 21:04:15 +00:00
parent e7b680478c
commit 708afb56c1
44 changed files with 1702 additions and 64 deletions

View File

@ -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
=============================

View File

@ -271,6 +271,12 @@ private:
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 {
@ -1323,6 +1329,10 @@ public:
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;
@ -2805,6 +2815,11 @@ public:
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
//===--------------------------------------------------------------------===//

View File

@ -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

View File

@ -4171,6 +4171,70 @@ public:
}
};
/// 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

View File

@ -1121,7 +1121,11 @@ 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()
@ -1129,7 +1133,7 @@ class CXXDefaultArgExpr final : public Expr {
Param->getDefaultArg()->getValueKind(),
Param->getDefaultArg()->getObjectKind(), false, false, false,
false),
Param(Param) {
Param(Param), UsedContext(UsedContext) {
CXXDefaultArgExprBits.Loc = Loc;
}
@ -1139,8 +1143,10 @@ public:
// \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.
@ -1151,6 +1157,9 @@ public:
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; }
@ -1190,8 +1199,11 @@ 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) {}
@ -1199,8 +1211,8 @@ 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.
@ -1217,6 +1229,13 @@ public:
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; }

View File

@ -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()) {

View File

@ -550,6 +550,17 @@ protected:
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 {
@ -935,6 +946,7 @@ protected:
ParenListExprBitfields ParenListExprBits;
GenericSelectionExprBitfields GenericSelectionExprBits;
PseudoObjectExprBitfields PseudoObjectExprBits;
SourceLocExprBitfields SourceLocExprBits;
// C++ Expressions
CXXOperatorCallExprBitfields CXXOperatorCallExprBits;

View File

@ -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>;

View File

@ -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)

View File

@ -4612,6 +4612,21 @@ public:
ExprResult BuildVAArgExpr(SourceLocation BuiltinLoc, Expr *E,
TypeSourceInfo *TInfo, SourceLocation RPLoc);
// __builtin_LINE(), __builtin_FUNCTION(), __builtin_FILE(),
// __builtin_COLUMN()
ExprResult ActOnSourceLocExpr(SourceLocExpr::IdentKind Kind,
SourceLocation BuiltinLoc,
SourceLocation RPLoc);
/// Build a potentially resolved SourceLocExpr.
///
/// \param SubExpr - null when the SourceLocExpr is unresolved, otherwise
/// SubExpr will be a literal expression representing the value of the
/// builtin call.
ExprResult BuildSourceLocExpr(SourceLocExpr::IdentKind Kind,
SourceLocation BuiltinLoc, SourceLocation RPLoc,
DeclContext *ParentContext);
// __null
ExprResult ActOnGNUNullExpr(SourceLocation TokenLoc);

View File

@ -1732,6 +1732,9 @@ namespace serialization {
/// A GNUNullExpr record.
EXPR_GNU_NULL,
/// A SourceLocExpr record.
EXPR_SOURCE_LOC,
/// A ShuffleVectorExpr record.
EXPR_SHUFFLE_VECTOR,

View File

@ -10208,6 +10208,31 @@ ASTContext::getMaterializedTemporaryValue(const MaterializeTemporaryExpr *E,
return MaterializedTemporaryValues.lookup(E);
}
QualType ASTContext::getStringLiteralArrayType(QualType EltTy,
unsigned Length) const {
// A C++ string literal has a const-qualified element type (C++ 2.13.4p1).
if (getLangOpts().CPlusPlus || getLangOpts().ConstStrings)
EltTy = EltTy.withConst();
EltTy = adjustStringLiteralBaseType(EltTy);
// Get an array type for the string, according to C99 6.4.5. This includes
// the null terminator character.
return getConstantArrayType(EltTy, llvm::APInt(32, Length + 1),
ArrayType::Normal, /*IndexTypeQuals*/ 0);
}
StringLiteral *
ASTContext::getPredefinedStringLiteralFromCache(StringRef Key) const {
StringLiteral *&Result = StringLiteralCache[Key];
if (!Result)
Result = StringLiteral::Create(
*this, Key, StringLiteral::Ascii,
/*Pascal*/ false, getStringLiteralArrayType(CharTy, Key.size()),
SourceLocation());
return Result;
}
bool ASTContext::AtomicUsesUnsupportedLibcall(const AtomicExpr *E) const {
const llvm::Triple &T = getTargetInfo().getTriple();
if (!T.isOSDarwin())

View File

@ -6801,8 +6801,12 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
if (!ToParamOrErr)
return ToParamOrErr.takeError();
auto UsedContextOrErr = Importer.ImportContext(E->getUsedContext());
if (!UsedContextOrErr)
return UsedContextOrErr.takeError();
return CXXDefaultArgExpr::Create(
Importer.getToContext(), *ToUsedLocOrErr, *ToParamOrErr);
Importer.getToContext(), *ToUsedLocOrErr, *ToParamOrErr, *UsedContextOrErr);
}
ExpectedStmt
@ -7525,8 +7529,12 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
if (!ToFieldOrErr)
return ToFieldOrErr.takeError();
auto UsedContextOrErr = Importer.ImportContext(E->getUsedContext());
if (!UsedContextOrErr)
return UsedContextOrErr.takeError();
return CXXDefaultInitExpr::Create(
Importer.getToContext(), *ToBeginLocOrErr, *ToFieldOrErr);
Importer.getToContext(), *ToBeginLocOrErr, *ToFieldOrErr, *UsedContextOrErr);
}
ExpectedStmt ASTNodeImporter::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) {

View File

@ -10,13 +10,14 @@
//
//===----------------------------------------------------------------------===//
#include "clang/AST/Expr.h"
#include "clang/AST/APValue.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/RecordLayout.h"
@ -1992,6 +1993,91 @@ bool BinaryOperator::isNullPointerArithmeticExtension(ASTContext &Ctx,
return true;
}
static QualType getDecayedSourceLocExprType(const ASTContext &Ctx,
SourceLocExpr::IdentKind Kind) {
switch (Kind) {
case SourceLocExpr::File:
case SourceLocExpr::Function: {
QualType ArrTy = Ctx.getStringLiteralArrayType(Ctx.CharTy, 0);
return Ctx.getPointerType(ArrTy->getAsArrayTypeUnsafe()->getElementType());
}
case SourceLocExpr::Line:
case SourceLocExpr::Column:
return Ctx.UnsignedIntTy;
}
llvm_unreachable("unhandled case");
}
SourceLocExpr::SourceLocExpr(const ASTContext &Ctx, IdentKind Kind,
SourceLocation BLoc, SourceLocation RParenLoc,
DeclContext *ParentContext)
: Expr(SourceLocExprClass, getDecayedSourceLocExprType(Ctx, Kind),
VK_RValue, OK_Ordinary, false, false, false, false),
BuiltinLoc(BLoc), RParenLoc(RParenLoc), ParentContext(ParentContext) {
SourceLocExprBits.Kind = Kind;
}
StringRef SourceLocExpr::getBuiltinStr() const {
switch (getIdentKind()) {
case File:
return "__builtin_FILE";
case Function:
return "__builtin_FUNCTION";
case Line:
return "__builtin_LINE";
case Column:
return "__builtin_COLUMN";
}
llvm_unreachable("unexpected IdentKind!");
}
APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx,
const Expr *DefaultExpr) const {
SourceLocation Loc;
const DeclContext *Context;
std::tie(Loc,
Context) = [&]() -> std::pair<SourceLocation, const DeclContext *> {
if (auto *DIE = dyn_cast_or_null<CXXDefaultInitExpr>(DefaultExpr))
return {DIE->getUsedLocation(), DIE->getUsedContext()};
if (auto *DAE = dyn_cast_or_null<CXXDefaultArgExpr>(DefaultExpr))
return {DAE->getUsedLocation(), DAE->getUsedContext()};
return {this->getLocation(), this->getParentContext()};
}();
PresumedLoc PLoc = Ctx.getSourceManager().getPresumedLoc(
Ctx.getSourceManager().getExpansionRange(Loc).getEnd());
auto MakeStringLiteral = [&](StringRef Tmp) {
using LValuePathEntry = APValue::LValuePathEntry;
StringLiteral *Res = Ctx.getPredefinedStringLiteralFromCache(Tmp);
// Decay the string to a pointer to the first character.
LValuePathEntry Path[1] = {LValuePathEntry::ArrayIndex(0)};
return APValue(Res, CharUnits::Zero(), Path, /*OnePastTheEnd=*/false);
};
switch (getIdentKind()) {
case SourceLocExpr::File:
return MakeStringLiteral(PLoc.getFilename());
case SourceLocExpr::Function: {
const Decl *CurDecl = dyn_cast_or_null<Decl>(Context);
return MakeStringLiteral(
CurDecl ? PredefinedExpr::ComputeName(PredefinedExpr::Function, CurDecl)
: std::string(""));
}
case SourceLocExpr::Line:
case SourceLocExpr::Column: {
llvm::APSInt IntVal(Ctx.getIntWidth(Ctx.UnsignedIntTy),
/*IsUnsigned=*/true);
IntVal = getIdentKind() == SourceLocExpr::Line ? PLoc.getLine()
: PLoc.getColumn();
return APValue(IntVal);
}
}
llvm_unreachable("unhandled case");
}
InitListExpr::InitListExpr(const ASTContext &C, SourceLocation lbraceloc,
ArrayRef<Expr*> initExprs, SourceLocation rbraceloc)
: Expr(InitListExprClass, QualType(), VK_RValue, OK_Ordinary, false, false,
@ -3156,6 +3242,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case ObjCAvailabilityCheckExprClass:
case CXXUuidofExprClass:
case OpaqueValueExprClass:
case SourceLocExprClass:
// These never have a side-effect.
return false;

View File

@ -907,13 +907,14 @@ const IdentifierInfo *UserDefinedLiteral::getUDSuffix() const {
}
CXXDefaultInitExpr::CXXDefaultInitExpr(const ASTContext &Ctx, SourceLocation Loc,
FieldDecl *Field, QualType Ty)
FieldDecl *Field, QualType Ty,
DeclContext *UsedContext)
: Expr(CXXDefaultInitExprClass, Ty.getNonLValueExprType(Ctx),
Ty->isLValueReferenceType() ? VK_LValue : Ty->isRValueReferenceType()
? VK_XValue
: VK_RValue,
/*FIXME*/ OK_Ordinary, false, false, false, false),
Field(Field) {
Field(Field), UsedContext(UsedContext) {
CXXDefaultInitExprBits.Loc = Loc;
assert(Field->hasInClassInitializer());
}

View File

@ -191,6 +191,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::ArrayInitIndexExprClass:
case Expr::NoInitExprClass:
case Expr::DesignatedInitUpdateExprClass:
case Expr::SourceLocExprClass:
return Cl::CL_PRValue;
case Expr::ConstantExprClass:

View File

@ -37,6 +37,7 @@
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/CurrentSourceLocExprScope.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/Expr.h"
#include "clang/AST/OSLog.h"
@ -64,6 +65,9 @@ namespace {
struct CallStackFrame;
struct EvalInfo;
using SourceLocExprScopeGuard =
CurrentSourceLocExprScope::SourceLocExprScopeGuard;
static QualType getType(APValue::LValueBase B) {
if (!B) return QualType();
if (const ValueDecl *D = B.dyn_cast<const ValueDecl*>()) {
@ -106,12 +110,12 @@ namespace {
/// Get an LValue path entry, which is known to not be an array index, as a
/// field declaration.
static const FieldDecl *getAsField(APValue::LValuePathEntry E) {
return dyn_cast<FieldDecl>(E.getAsBaseOrMember().getPointer());
return dyn_cast_or_null<FieldDecl>(E.getAsBaseOrMember().getPointer());
}
/// Get an LValue path entry, which is known to not be an array index, as a
/// base class declaration.
static const CXXRecordDecl *getAsBaseClass(APValue::LValuePathEntry E) {
return dyn_cast<CXXRecordDecl>(E.getAsBaseOrMember().getPointer());
return dyn_cast_or_null<CXXRecordDecl>(E.getAsBaseOrMember().getPointer());
}
/// Determine whether this LValue path entry for a base class names a virtual
/// base class.
@ -464,6 +468,10 @@ namespace {
/// parameters' function scope indices.
APValue *Arguments;
/// Source location information about the default argument or default
/// initializer expression we're evaluating, if any.
CurrentSourceLocExprScope CurSourceLocExprScope;
// Note that we intentionally use std::map here so that references to
// values are stable.
typedef std::pair<const void *, unsigned> MapKeyTy;
@ -2732,6 +2740,9 @@ static unsigned getBaseIndex(const CXXRecordDecl *Derived,
/// Extract the value of a character from a string literal.
static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit,
uint64_t Index) {
assert(!isa<SourceLocExpr>(Lit) &&
"SourceLocExpr should have already been converted to a StringLiteral");
// FIXME: Support MakeStringConstant
if (const auto *ObjCEnc = dyn_cast<ObjCEncodeExpr>(Lit)) {
std::string Str;
@ -3469,6 +3480,7 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
// Check for special cases where there is no existing APValue to look at.
const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
if (Base && !LVal.getLValueCallIndex() && !Type.isVolatileQualified()) {
if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(Base)) {
// In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the
@ -5242,6 +5254,7 @@ public:
{ return StmtVisitorTy::Visit(E->getReplacement()); }
bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) {
TempVersionRAII RAII(*Info.CurrentCall);
SourceLocExprScopeGuard Guard(E, Info.CurrentCall->CurSourceLocExprScope);
return StmtVisitorTy::Visit(E->getExpr());
}
bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) {
@ -5249,8 +5262,10 @@ public:
// The initializer may not have been parsed yet, or might be erroneous.
if (!E->getExpr())
return Error(E);
SourceLocExprScopeGuard Guard(E, Info.CurrentCall->CurSourceLocExprScope);
return StmtVisitorTy::Visit(E->getExpr());
}
// We cannot create any objects for which cleanups are required, so there is
// nothing to do here; all cleanups must come from unevaluated subexpressions.
bool VisitExprWithCleanups(const ExprWithCleanups *E)
@ -6327,6 +6342,14 @@ public:
return true;
}
bool VisitSourceLocExpr(const SourceLocExpr *E) {
assert(E->isStringType() && "SourceLocExpr isn't a pointer type?");
APValue LValResult = E->EvaluateInContext(
Info.Ctx, Info.CurrentCall->CurSourceLocExprScope.getDefaultExpr());
Result.setFrom(Info.Ctx, LValResult);
return true;
}
// FIXME: Missing: @protocol, @selector
};
} // end anonymous namespace
@ -7982,7 +8005,7 @@ public:
bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E);
bool VisitSizeOfPackExpr(const SizeOfPackExpr *E);
bool VisitSourceLocExpr(const SourceLocExpr *E);
// FIXME: Missing: array subscript of vector, member of vector
};
@ -8058,6 +8081,12 @@ static bool EvaluateInteger(const Expr *E, APSInt &Result, EvalInfo &Info) {
return true;
}
bool IntExprEvaluator::VisitSourceLocExpr(const SourceLocExpr *E) {
APValue Evaluated = E->EvaluateInContext(
Info.Ctx, Info.CurrentCall->CurSourceLocExprScope.getDefaultExpr());
return Success(Evaluated, E);
}
static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result,
EvalInfo &Info) {
if (E->getType()->isFixedPointType()) {
@ -11899,7 +11928,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
case Expr::SizeOfPackExprClass:
case Expr::GNUNullExprClass:
// GCC considers the GNU __null value to be an integral constant expression.
case Expr::SourceLocExprClass:
return NoDiag();
case Expr::SubstNonTypeTemplateParmExprClass:

View File

@ -3589,6 +3589,7 @@ recurse:
case Expr::AsTypeExprClass:
case Expr::PseudoObjectExprClass:
case Expr::AtomicExprClass:
case Expr::SourceLocExprClass:
case Expr::FixedPointLiteralClass:
{
if (!NullOut) {

View File

@ -905,6 +905,10 @@ void StmtPrinter::VisitOMPTargetTeamsDistributeSimdDirective(
// Expr printing methods.
//===----------------------------------------------------------------------===//
void StmtPrinter::VisitSourceLocExpr(SourceLocExpr *Node) {
OS << Node->getBuiltinStr() << "()";
}
void StmtPrinter::VisitConstantExpr(ConstantExpr *Node) {
PrintExpr(Node->getSubExpr());
}

View File

@ -1882,6 +1882,10 @@ void StmtProfiler::VisitTypoExpr(const TypoExpr *E) {
VisitExpr(E);
}
void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) {
VisitExpr(E);
}
void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) {
VisitExpr(S);
}

View File

@ -1315,11 +1315,15 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) {
return LV;
}
case Expr::CXXDefaultArgExprClass:
return EmitLValue(cast<CXXDefaultArgExpr>(E)->getExpr());
case Expr::CXXDefaultArgExprClass: {
auto *DAE = cast<CXXDefaultArgExpr>(E);
CXXDefaultArgExprScope Scope(*this, DAE);
return EmitLValue(DAE->getExpr());
}
case Expr::CXXDefaultInitExprClass: {
CXXDefaultInitExprScope Scope(*this);
return EmitLValue(cast<CXXDefaultInitExpr>(E)->getExpr());
auto *DIE = cast<CXXDefaultInitExpr>(E);
CXXDefaultInitExprScope Scope(*this, DIE);
return EmitLValue(DIE->getExpr());
}
case Expr::CXXTypeidExprClass:
return EmitCXXTypeidLValue(cast<CXXTypeidExpr>(E));

View File

@ -165,10 +165,11 @@ public:
void VisitImplicitValueInitExpr(ImplicitValueInitExpr *E);
void VisitNoInitExpr(NoInitExpr *E) { } // Do nothing.
void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) {
CodeGenFunction::CXXDefaultArgExprScope Scope(CGF, DAE);
Visit(DAE->getExpr());
}
void VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) {
CodeGenFunction::CXXDefaultInitExprScope Scope(CGF);
CodeGenFunction::CXXDefaultInitExprScope Scope(CGF, DIE);
Visit(DIE->getExpr());
}
void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E);

View File

@ -213,10 +213,11 @@ public:
return Visit(E->getSubExpr());
}
ComplexPairTy VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) {
CodeGenFunction::CXXDefaultArgExprScope Scope(CGF, DAE);
return Visit(DAE->getExpr());
}
ComplexPairTy VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) {
CodeGenFunction::CXXDefaultInitExprScope Scope(CGF);
CodeGenFunction::CXXDefaultInitExprScope Scope(CGF, DIE);
return Visit(DIE->getExpr());
}
ComplexPairTy VisitExprWithCleanups(ExprWithCleanups *E) {

View File

@ -884,10 +884,6 @@ public:
llvm_unreachable("Invalid CastKind");
}
llvm::Constant *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE, QualType T) {
return Visit(DAE->getExpr(), T);
}
llvm::Constant *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE, QualType T) {
// No need for a DefaultInitExprScope: we don't handle 'this' in a
// constant expression.

View File

@ -16,6 +16,7 @@
#include "CGObjCRuntime.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "ConstantEmitter.h"
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
@ -640,12 +641,20 @@ public:
Value *VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E) {
return EmitLoadOfLValue(E);
}
Value *VisitSourceLocExpr(SourceLocExpr *SLE) {
auto &Ctx = CGF.getContext();
APValue Evaluated =
SLE->EvaluateInContext(Ctx, CGF.CurSourceLocExprScope.getDefaultExpr());
return ConstantEmitter(CGF.CGM, &CGF)
.emitAbstract(SLE->getLocation(), Evaluated, SLE->getType());
}
Value *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) {
CodeGenFunction::CXXDefaultArgExprScope Scope(CGF, DAE);
return Visit(DAE->getExpr());
}
Value *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) {
CodeGenFunction::CXXDefaultInitExprScope Scope(CGF);
CodeGenFunction::CXXDefaultInitExprScope Scope(CGF, DIE);
return Visit(DIE->getExpr());
}
Value *VisitCXXThisExpr(CXXThisExpr *TE) {

View File

@ -22,6 +22,7 @@
#include "EHScopeStack.h"
#include "VarBypassDetector.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/CurrentSourceLocExprScope.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprOpenMP.h"
@ -1393,6 +1394,12 @@ private:
SourceLocation LastStopPoint;
public:
/// Source location information about the default argument or member
/// initializer expression we're evaluating, if any.
CurrentSourceLocExprScope CurSourceLocExprScope;
using SourceLocExprScopeGuard =
CurrentSourceLocExprScope::SourceLocExprScopeGuard;
/// A scope within which we are constructing the fields of an object which
/// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use
/// if we need to evaluate a CXXDefaultInitExpr within the evaluation.
@ -1413,11 +1420,12 @@ public:
/// The scope of a CXXDefaultInitExpr. Within this scope, the value of 'this'
/// is overridden to be the object under construction.
class CXXDefaultInitExprScope {
class CXXDefaultInitExprScope {
public:
CXXDefaultInitExprScope(CodeGenFunction &CGF)
: CGF(CGF), OldCXXThisValue(CGF.CXXThisValue),
OldCXXThisAlignment(CGF.CXXThisAlignment) {
CXXDefaultInitExprScope(CodeGenFunction &CGF, const CXXDefaultInitExpr *E)
: CGF(CGF), OldCXXThisValue(CGF.CXXThisValue),
OldCXXThisAlignment(CGF.CXXThisAlignment),
SourceLocScope(E, CGF.CurSourceLocExprScope) {
CGF.CXXThisValue = CGF.CXXDefaultInitExprThis.getPointer();
CGF.CXXThisAlignment = CGF.CXXDefaultInitExprThis.getAlignment();
}
@ -1430,6 +1438,12 @@ public:
CodeGenFunction &CGF;
llvm::Value *OldCXXThisValue;
CharUnits OldCXXThisAlignment;
SourceLocExprScopeGuard SourceLocScope;
};
struct CXXDefaultArgExprScope : SourceLocExprScopeGuard {
CXXDefaultArgExprScope(CodeGenFunction &CGF, const CXXDefaultArgExpr *E)
: SourceLocExprScopeGuard(E, CGF.CurSourceLocExprScope) {}
};
/// The scope of an ArrayInitLoopExpr. Within this scope, the value of the

View File

@ -639,6 +639,10 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback {
/// [GNU] '__builtin_offsetof' '(' type-name ',' offsetof-member-designator')'
/// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ','
/// assign-expr ')'
/// [GNU] '__builtin_FILE' '(' ')'
/// [GNU] '__builtin_FUNCTION' '(' ')'
/// [GNU] '__builtin_LINE' '(' ')'
/// [CLANG] '__builtin_COLUMN' '(' ')'
/// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')'
/// [GNU] '__null'
/// [OBJC] '[' objc-message-expr ']'
@ -1106,6 +1110,10 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
case tok::kw___builtin_choose_expr:
case tok::kw___builtin_astype: // primary-expression: [OCL] as_type()
case tok::kw___builtin_convertvector:
case tok::kw___builtin_COLUMN:
case tok::kw___builtin_FILE:
case tok::kw___builtin_FUNCTION:
case tok::kw___builtin_LINE:
return ParseBuiltinPrimaryExpression();
case tok::kw___null:
return Actions.ActOnGNUNullExpr(ConsumeToken());
@ -2067,6 +2075,10 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
/// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ','
/// assign-expr ')'
/// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')'
/// [GNU] '__builtin_FILE' '(' ')'
/// [GNU] '__builtin_FUNCTION' '(' ')'
/// [GNU] '__builtin_LINE' '(' ')'
/// [CLANG] '__builtin_COLUMN' '(' ')'
/// [OCL] '__builtin_astype' '(' assignment-expression ',' type-name ')'
///
/// [GNU] offsetof-member-designator:
@ -2286,6 +2298,33 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
ConsumeParen());
break;
}
case tok::kw___builtin_COLUMN:
case tok::kw___builtin_FILE:
case tok::kw___builtin_FUNCTION:
case tok::kw___builtin_LINE: {
// Attempt to consume the r-paren.
if (Tok.isNot(tok::r_paren)) {
Diag(Tok, diag::err_expected) << tok::r_paren;
SkipUntil(tok::r_paren, StopAtSemi);
return ExprError();
}
SourceLocExpr::IdentKind Kind = [&] {
switch (T) {
case tok::kw___builtin_FILE:
return SourceLocExpr::File;
case tok::kw___builtin_FUNCTION:
return SourceLocExpr::Function;
case tok::kw___builtin_LINE:
return SourceLocExpr::Line;
case tok::kw___builtin_COLUMN:
return SourceLocExpr::Column;
default:
llvm_unreachable("invalid keyword");
}
}();
Res = Actions.ActOnSourceLocExpr(Kind, StartLoc, ConsumeParen());
break;
}
}
if (Res.isInvalid())

View File

@ -62,7 +62,7 @@ namespace {
public:
CheckDefaultArgumentVisitor(Expr *defarg, Sema *s)
: DefaultArg(defarg), S(s) {}
: DefaultArg(defarg), S(s) {}
bool VisitExpr(Expr *Node);
bool VisitDeclRefExpr(DeclRefExpr *DRE);
@ -13025,7 +13025,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
// If we already have the in-class initializer nothing needs to be done.
if (Field->getInClassInitializer())
return CXXDefaultInitExpr::Create(Context, Loc, Field);
return CXXDefaultInitExpr::Create(Context, Loc, Field, CurContext);
// If we might have already tried and failed to instantiate, don't try again.
if (Field->isInvalidDecl())
@ -13066,7 +13066,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
Field->setInvalidDecl();
return ExprError();
}
return CXXDefaultInitExpr::Create(Context, Loc, Field);
return CXXDefaultInitExpr::Create(Context, Loc, Field, CurContext);
}
// DR1351:

View File

@ -1289,6 +1289,7 @@ CanThrowResult Sema::canThrow(const Expr *E) {
case Expr::PredefinedExprClass:
case Expr::SizeOfPackExprClass:
case Expr::StringLiteralClass:
case Expr::SourceLocExprClass:
// These expressions can never throw.
return CT_Cannot;

View File

@ -1683,20 +1683,8 @@ Sema::ActOnStringLiteral(ArrayRef<Token> StringToks, Scope *UDLScope) {
Diag(RemovalDiagLoc, RemovalDiag);
}
QualType CharTyConst = CharTy;
// A C++ string literal has a const-qualified element type (C++ 2.13.4p1).
if (getLangOpts().CPlusPlus || getLangOpts().ConstStrings)
CharTyConst.addConst();
CharTyConst = Context.adjustStringLiteralBaseType(CharTyConst);
// Get an array type for the string, according to C99 6.4.5. This includes
// the nul terminator character as well as the string length for pascal
// strings.
QualType StrTy = Context.getConstantArrayType(
CharTyConst, llvm::APInt(32, Literal.GetNumStringChars() + 1),
ArrayType::Normal, 0);
QualType StrTy =
Context.getStringLiteralArrayType(CharTy, Literal.GetNumStringChars());
// Pass &StringTokLocs[0], StringTokLocs.size() to factory!
StringLiteral *Lit = StringLiteral::Create(Context, Literal.GetString(),
@ -4873,7 +4861,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
FunctionDecl *FD, ParmVarDecl *Param) {
if (CheckCXXDefaultArgExpr(CallLoc, FD, Param))
return ExprError();
return CXXDefaultArgExpr::Create(Context, CallLoc, Param);
return CXXDefaultArgExpr::Create(Context, CallLoc, Param, CurContext);
}
Sema::VariadicCallType
@ -5138,8 +5126,7 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl,
} else {
assert(Param && "can't use default arguments without a known callee");
ExprResult ArgExpr =
BuildCXXDefaultArgExpr(CallLoc, FDecl, Param);
ExprResult ArgExpr = BuildCXXDefaultArgExpr(CallLoc, FDecl, Param);
if (ArgExpr.isInvalid())
return true;
@ -14097,6 +14084,20 @@ ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) {
return new (Context) GNUNullExpr(Ty, TokenLoc);
}
ExprResult Sema::ActOnSourceLocExpr(SourceLocExpr::IdentKind Kind,
SourceLocation BuiltinLoc,
SourceLocation RPLoc) {
return BuildSourceLocExpr(Kind, BuiltinLoc, RPLoc, CurContext);
}
ExprResult Sema::BuildSourceLocExpr(SourceLocExpr::IdentKind Kind,
SourceLocation BuiltinLoc,
SourceLocation RPLoc,
DeclContext *ParentContext) {
return new (Context)
SourceLocExpr(Context, Kind, BuiltinLoc, RPLoc, ParentContext);
}
bool Sema::ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&Exp,
bool Diagnose) {
if (!getLangOpts().ObjC)

View File

@ -1097,12 +1097,7 @@ ExprResult Sema::BuildObjCEncodeExpression(SourceLocation AtLoc,
// The type of @encode is the same as the type of the corresponding string,
// which is an array type.
StrTy = Context.CharTy;
// A C++ string literal has a const-qualified element type (C++ 2.13.4p1).
if (getLangOpts().CPlusPlus || getLangOpts().ConstStrings)
StrTy.addConst();
StrTy = Context.getConstantArrayType(StrTy, llvm::APInt(32, Str.size()+1),
ArrayType::Normal, 0);
StrTy = Context.getStringLiteralArrayType(Context.CharTy, Str.size());
}
return new (Context) ObjCEncodeExpr(StrTy, EncodedTypeInfo, AtLoc, RParenLoc);

View File

@ -2713,9 +2713,9 @@ public:
/// By default, builds a new default-argument expression, which does not
/// require any semantic analysis. Subclasses may override this routine to
/// provide different behavior.
ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc,
ParmVarDecl *Param) {
return CXXDefaultArgExpr::Create(getSema().Context, Loc, Param);
ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc, ParmVarDecl *Param) {
return CXXDefaultArgExpr::Create(getSema().Context, Loc, Param,
getSema().CurContext);
}
/// Build a new C++11 default-initialization expression.
@ -2725,7 +2725,8 @@ public:
/// routine to provide different behavior.
ExprResult RebuildCXXDefaultInitExpr(SourceLocation Loc,
FieldDecl *Field) {
return CXXDefaultInitExpr::Create(getSema().Context, Loc, Field);
return CXXDefaultInitExpr::Create(getSema().Context, Loc, Field,
getSema().CurContext);
}
/// Build a new C++ zero-initialization expression.
@ -2979,6 +2980,18 @@ public:
RParenLoc, Length, PartialArgs);
}
/// Build a new expression representing a call to a source location
/// builtin.
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
ExprResult RebuildSourceLocExpr(SourceLocExpr::IdentKind Kind,
SourceLocation BuiltinLoc,
SourceLocation RPLoc,
DeclContext *ParentContext) {
return getSema().BuildSourceLocExpr(Kind, BuiltinLoc, RPLoc, ParentContext);
}
/// Build a new Objective-C boxed expression.
///
/// By default, performs semantic analysis to build the new expression.
@ -10132,6 +10145,19 @@ TreeTransform<Derived>::TransformCXXMemberCallExpr(CXXMemberCallExpr *E) {
return getDerived().TransformCallExpr(E);
}
template <typename Derived>
ExprResult TreeTransform<Derived>::TransformSourceLocExpr(SourceLocExpr *E) {
bool NeedRebuildFunc = E->getIdentKind() == SourceLocExpr::Function &&
getSema().CurContext != E->getParentContext();
if (!getDerived().AlwaysRebuild() && !NeedRebuildFunc)
return E;
return getDerived().RebuildSourceLocExpr(E->getIdentKind(), E->getBeginLoc(),
E->getEndLoc(),
getSema().CurContext);
}
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformCUDAKernelCallExpr(CUDAKernelCallExpr *E) {
@ -10358,8 +10384,8 @@ TreeTransform<Derived>::TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
if (!Param)
return ExprError();
if (!getDerived().AlwaysRebuild() &&
Param == E->getParam())
if (!getDerived().AlwaysRebuild() && Param == E->getParam() &&
E->getUsedContext() == SemaRef.CurContext)
return E;
return getDerived().RebuildCXXDefaultArgExpr(E->getUsedLocation(), Param);
@ -10373,7 +10399,8 @@ TreeTransform<Derived>::TransformCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
if (!Field)
return ExprError();
if (!getDerived().AlwaysRebuild() && Field == E->getField())
if (!getDerived().AlwaysRebuild() && Field == E->getField() &&
E->getUsedContext() == SemaRef.CurContext)
return E;
return getDerived().RebuildCXXDefaultInitExpr(E->getExprLoc(), Field);

View File

@ -968,6 +968,15 @@ void ASTStmtReader::VisitVAArgExpr(VAArgExpr *E) {
E->setIsMicrosoftABI(Record.readInt());
}
void ASTStmtReader::VisitSourceLocExpr(SourceLocExpr *E) {
VisitExpr(E);
E->ParentContext = ReadDeclAs<DeclContext>();
E->BuiltinLoc = ReadSourceLocation();
E->RParenLoc = ReadSourceLocation();
E->SourceLocExprBits.Kind =
static_cast<SourceLocExpr::IdentKind>(Record.readInt());
}
void ASTStmtReader::VisitAddrLabelExpr(AddrLabelExpr *E) {
VisitExpr(E);
E->setAmpAmpLoc(ReadSourceLocation());
@ -1487,12 +1496,14 @@ void ASTStmtReader::VisitCXXThrowExpr(CXXThrowExpr *E) {
void ASTStmtReader::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
VisitExpr(E);
E->Param = ReadDeclAs<ParmVarDecl>();
E->UsedContext = ReadDeclAs<DeclContext>();
E->CXXDefaultArgExprBits.Loc = ReadSourceLocation();
}
void ASTStmtReader::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
VisitExpr(E);
E->Field = ReadDeclAs<FieldDecl>();
E->UsedContext = ReadDeclAs<DeclContext>();
E->CXXDefaultInitExprBits.Loc = ReadSourceLocation();
}
@ -2651,6 +2662,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
S = new (Context) VAArgExpr(Empty);
break;
case EXPR_SOURCE_LOC:
S = new (Context) SourceLocExpr(Empty);
break;
case EXPR_ADDR_LABEL:
S = new (Context) AddrLabelExpr(Empty);
break;

View File

@ -909,6 +909,15 @@ void ASTStmtWriter::VisitVAArgExpr(VAArgExpr *E) {
Code = serialization::EXPR_VA_ARG;
}
void ASTStmtWriter::VisitSourceLocExpr(SourceLocExpr *E) {
VisitExpr(E);
Record.AddDeclRef(cast_or_null<Decl>(E->getParentContext()));
Record.AddSourceLocation(E->getBeginLoc());
Record.AddSourceLocation(E->getEndLoc());
Record.push_back(E->getIdentKind());
Code = serialization::EXPR_SOURCE_LOC;
}
void ASTStmtWriter::VisitAddrLabelExpr(AddrLabelExpr *E) {
VisitExpr(E);
Record.AddSourceLocation(E->getAmpAmpLoc());
@ -1468,6 +1477,7 @@ void ASTStmtWriter::VisitCXXThrowExpr(CXXThrowExpr *E) {
void ASTStmtWriter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
VisitExpr(E);
Record.AddDeclRef(E->getParam());
Record.AddDeclRef(cast_or_null<Decl>(E->getUsedContext()));
Record.AddSourceLocation(E->getUsedLocation());
Code = serialization::EXPR_CXX_DEFAULT_ARG;
}
@ -1475,6 +1485,7 @@ void ASTStmtWriter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
void ASTStmtWriter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
VisitExpr(E);
Record.AddDeclRef(E->getField());
Record.AddDeclRef(cast_or_null<Decl>(E->getUsedContext()));
Record.AddSourceLocation(E->getExprLoc());
Code = serialization::EXPR_CXX_DEFAULT_INIT;
}

View File

@ -1341,6 +1341,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::NoInitExprClass:
case Stmt::SizeOfPackExprClass:
case Stmt::StringLiteralClass:
case Stmt::SourceLocExprClass:
case Stmt::ObjCStringLiteralClass:
case Stmt::CXXPseudoDestructorExprClass:
case Stmt::SubstNonTypeTemplateParmExprClass:

View File

@ -0,0 +1,268 @@
// RUN: %clang_cc1 -std=c++2a -fblocks %s -triple %itanium_abi_triple -emit-llvm -o %t.ll
#line 8 "builtin-source-location.cpp"
struct source_location {
private:
unsigned int __m_line = 0;
unsigned int __m_col = 0;
const char *__m_file = nullptr;
const char *__m_func = nullptr;
public:
constexpr void set(unsigned l, unsigned c, const char *f, const char *func) {
__m_line = l;
__m_col = c;
__m_file = f;
__m_func = func;
}
static constexpr source_location current(
unsigned int __line = __builtin_LINE(),
unsigned int __col = __builtin_COLUMN(),
const char *__file = __builtin_FILE(),
const char *__func = __builtin_FUNCTION()) noexcept {
source_location __loc;
__loc.set(__line, __col, __file, __func);
return __loc;
}
static source_location bad_current(
unsigned int __line = __builtin_LINE(),
unsigned int __col = __builtin_COLUMN(),
const char *__file = __builtin_FILE(),
const char *__func = __builtin_FUNCTION()) noexcept {
source_location __loc;
__loc.set(__line, __col, __file, __func);
return __loc;
}
constexpr source_location() = default;
constexpr source_location(source_location const &) = default;
constexpr unsigned int line() const noexcept { return __m_line; }
constexpr unsigned int column() const noexcept { return __m_col; }
constexpr const char *file() const noexcept { return __m_file; }
constexpr const char *function() const noexcept { return __m_func; }
};
using SL = source_location;
extern "C" int sink(...);
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-GLOBAL-ONE
//
// CHECK-GLOBAL-ONE-DAG: @[[FILE:.*]] = {{.*}}c"test_const_init.cpp\00"
// CHECK-GLOBAL-ONE-DAG: @[[FUNC:.*]] = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
//
// CHECK-GLOBAL-ONE: @const_init_global = global %struct.source_location { i32 1000, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]]
#line 1000 "test_const_init.cpp"
SL const_init_global = SL::current();
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-GLOBAL-TWO
//
// CHECK-GLOBAL-TWO-DAG: @runtime_init_global = global %struct.source_location zeroinitializer, align 8
//
// CHECK-GLOBAL-TWO-DAG: @[[FILE:.*]] = {{.*}}c"test_runtime_init.cpp\00"
// CHECK-GLOBAL-TWO-DAG: @[[FUNC:.*]] = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
//
// CHECK-GLOBAL-TWO: define internal void @__cxx_global_var_init()
// CHECK-GLOBAL-TWO-NOT: ret
// CHECK-GLOBAL-TWO: call void @_ZN15source_location11bad_currentEjjPKcS1_(%struct.source_location* sret @runtime_init_global,
// CHECK-GLOBAL-TWO-SAME: i32 1100, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]],
#line 1100 "test_runtime_init.cpp"
SL runtime_init_global = SL::bad_current();
#line 2000 "test_function.cpp"
extern "C" void test_function() {
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-LOCAL-ONE
//
// CHECK-LOCAL-ONE-DAG: @[[FILE:.*]] = {{.*}}c"test_current.cpp\00"
// CHECK-LOCAL-ONE-DAG: @[[FUNC:.*]] = {{.*}}c"test_function\00"
//
// CHECK-LOCAL-ONE: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %local,
// CHECK-LOCAL-ONE-SAME: i32 2100, i32 {{[0-9]+}},
// CHECK-LOCAL-ONE-SAME: {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]],
#line 2100 "test_current.cpp"
SL local = SL::current();
}
#line 3000 "TestInitClass.cpp"
struct TestInit {
SL info = SL::current();
SL arg_info;
#line 3100 "TestInitCtor.cpp"
TestInit(SL arg_info = SL::current()) : arg_info(arg_info) {}
};
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-CTOR-GLOBAL
//
// CHECK-CTOR-GLOBAL-DAG: @GlobalInitVal = global %struct.TestInit zeroinitializer, align 8
// CHECK-CTOR-GLOBAL-DAG: @[[FILE:.*]] = {{.*}}c"GlobalInitVal.cpp\00"
// CHECK-CTOR-GLOBAL-DAG: @[[FUNC:.*]] = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
//
// CHECK-CTOR-GLOBAL: define internal void @__cxx_global_var_init.{{[0-9]+}}()
// CHECK-CTOR-GLOBAL-NOT: ret
//
// CHECK-CTOR-GLOBAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[TMP_ONE:[^,]*]],
// CHECK-CTOR-GLOBAL-SAME: i32 3400, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]],
// CHECK-CTOR-GLOBAL-NEXT: call void @_ZN8TestInitC1E15source_location(%struct.TestInit* @GlobalInitVal, %struct.source_location* {{[^%]*}}%[[TMP_ONE]])
#line 3400 "GlobalInitVal.cpp"
TestInit GlobalInitVal;
extern "C" void test_init_function() {
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-CTOR-LOCAL
//
// CHECK-CTOR-LOCAL-DAG: @[[FILE:.*]] = {{.*}}c"LocalInitVal.cpp\00"
// CHECK-CTOR-LOCAL-DAG: @[[FUNC:.*]] = {{.*}}c"test_init_function\00"
//
// CHECK-CTOR-LOCAL: define void @test_init_function()
// CHECK-CTOR-LOCAL-NOT: ret
//
// CHECK-CTOR-LOCAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[TMP:[^,]*]],
// CHECK-CTOR-LOCAL-SAME: i32 3500, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]],
// CHECK-CTOR-LOCAL-NEXT: call void @_ZN8TestInitC1E15source_location(%struct.TestInit* %init_local, %struct.source_location* {{[^%]*}}%[[TMP]])
#line 3500 "LocalInitVal.cpp"
TestInit init_local;
sink(init_local);
}
#line 4000 "ConstexprClass.cpp"
struct TestInitConstexpr {
SL info = SL::current();
SL arg_info;
#line 4200 "ConstexprCtor.cpp"
constexpr TestInitConstexpr(SL arg_info = SL::current()) : arg_info(arg_info) {}
};
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-CONSTEXPR-T2
//
// CHECK-CONSTEXPR-T2-DAG: @[[FILE_INIT:.*]] = {{.*}}c"ConstexprCtor.cpp\00"
// CHECK-CONSTEXPR-T2-DAG: @[[FUNC_INIT:.*]] = {{.*}}c"TestInitConstexpr\00"
// CHECK-CONSTEXPR-T2-DAG: @[[FILE_ARG:.*]] = {{.*}}c"ConstexprGlobal.cpp\00"
// CHECK-CONSTEXPR-T2-DAG: @[[EMPTY:.*]] = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
//
// CHECK-CONSTEXPR-T2: @ConstexprGlobal = global %struct.TestInitConstexpr {
// CHECK-CONSTEXPR-T2-SAME: %struct.source_location { i32 4200, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE_INIT]], {{[^@]*}}@[[FUNC_INIT]],
// CHECK-CONSTEXPR-T2-SAME: {{[^%]*}}%struct.source_location { i32 4400, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE_ARG]], {{[^@]*}}@[[EMPTY]]
#line 4400 "ConstexprGlobal.cpp"
TestInitConstexpr ConstexprGlobal;
extern "C" void test_init_function_constexpr() {
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-CONSTEXPR-LOCAL
//
// CHECK-CONSTEXPR-LOCAL-DAG: @[[FUNC:.*]] = {{.*}}c"test_init_function_constexpr\00"
// CHECK-CONSTEXPR-LOCAL-DAG: @[[FILE:.*]] = {{.*}}c"ConstexprLocal.cpp\00"
//
// CHECK-CONSTEXPR-LOCAL: define void @test_init_function_constexpr()
// CHECK-CONSTEXPR-LOCAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[TMP:[^,]*]],
// CHECK-CONSTEXPR-LOCAL-SAME: i32 4600, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]]
// CHECK-CONSTEXPR-LOCAL: call void @_ZN17TestInitConstexprC1E15source_location(%struct.TestInitConstexpr* %local_val, {{.*}}%[[TMP]])
#line 4600 "ConstexprLocal.cpp"
TestInitConstexpr local_val;
}
#line 5000 "TestInitAgg.cpp"
struct TestInitAgg {
#line 5100 "i1.cpp"
SL i1;
#line 5200 "i2.cpp"
SL i2 = SL::current();
#line 5300 "TestInitAggEnd.cpp"
};
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-AGG-DEFAULT
//
// CHECK-AGG-DEFAULT-DAG: @[[FILE:.*]] = {{.*}}c"TestInitAgg.cpp\00"
// CHECK-AGG-DEFAULT-DAG: @[[FUNC:.*]] = {{.*}}c"TestInitAgg\00"
//
// CHECK-AGG-DEFAULT: @GlobalAggDefault = global %struct.TestInitAgg {
// CHECK-AGG-DEFAULT-SAME: %struct.source_location zeroinitializer,
// CHECK-AGG-DEFAULT-SAME: %struct.source_location { i32 5000, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]]
#line 5400 "GlobalAggDefault.cpp"
TestInitAgg GlobalAggDefault;
#line 5500 "test_agg_init_test.cpp"
extern "C" void test_agg_init() {
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-AGG-BRACE
//
// CHECK-AGG-BRACE-DAG: @[[FILE:.*]] = {{.*}}c"BraceInitEnd.cpp\00"
// CHECK-AGG-BRACE-DAG: @[[FUNC:.*]] = {{.*}}c"test_agg_init\00"
//
// CHECK-AGG-BRACE: define void @test_agg_init()
// CHECK-AGG-BRACE: %[[I2:.*]] = getelementptr inbounds %struct.TestInitAgg, %struct.TestInitAgg* %local_brace_init, i32 0, i32 1
// CHECK-AGG-BRACE-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[I2]],
// CHECK-AGG-BRACE-SAME: i32 5700, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]]
#line 5600 "BraceInitStart.cpp"
TestInitAgg local_brace_init{
#line 5700 "BraceInitEnd.cpp"
};
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-AGG-EQUAL
//
// CHECK-AGG-EQUAL-DAG: @[[FILE:.*]] = {{.*}}c"EqualInitEnd.cpp\00"
// CHECK-AGG-EQUAL-DAG: @[[FUNC:.*]] = {{.*}}c"test_agg_init\00"
//
// CHECK-AGG-EQUAL: define void @test_agg_init()
// CHECK-AGG-EQUAL: %[[I2:.*]] = getelementptr inbounds %struct.TestInitAgg, %struct.TestInitAgg* %local_equal_init, i32 0, i32 1
// CHECK-AGG-EQUAL-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[I2]],
// CHECK-AGG-EQUAL-SAME: i32 5900, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]]
#line 5800 "EqualInitStart.cpp"
TestInitAgg local_equal_init =
{
#line 5900 "EqualInitEnd.cpp"
};
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-AGG-LIST
//
// CHECK-AGG-LIST-DAG: @[[FILE_DEFAULT:.*]] = {{.*}}c"InitListEnd.cpp\00"
// CHECK-AGG-LIST-DAG: @[[FILE_ELEM:.*]] = {{.*}}c"ListElem.cpp\00"
// CHECK-AGG-LIST-DAG: @[[FUNC:.*]] = {{.*}}c"test_agg_init\00"
//
// CHECK-AGG-LIST: define void @test_agg_init()
//
// CHECK-AGG-LIST: %[[I1:.*]] = getelementptr inbounds %struct.TestInitAgg, %struct.TestInitAgg* %local_list_init, i32 0, i32 0
// CHECK-AGG-LIST-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[I1]],
// CHECK-AGG-LIST-SAME: i32 6100, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE_ELEM]], {{[^@]*}}@[[FUNC]]
//
// CHECK-AGG-LIST: %[[I2:.*]] = getelementptr inbounds %struct.TestInitAgg, %struct.TestInitAgg* %local_list_init, i32 0, i32 1
// CHECK-AGG-LIST-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[I2]],
// CHECK-AGG-LIST-SAME: i32 6200, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE_DEFAULT]], {{[^@]*}}@[[FUNC]]
#line 6000 "InitListStart.cpp"
TestInitAgg local_list_init =
{
#line 6100 "ListElem.cpp"
{SL::current()}
#line 6200 "InitListEnd.cpp"
};
}
#line 7000 "TestTemplate.cpp"
template <class Tp, int>
struct TestTemplate {
Tp info = Tp::current();
Tp arg_info;
#line 7100 "TestTemplateCtor.cpp"
constexpr TestTemplate(Tp arg_info = Tp::current()) : arg_info(arg_info) {}
};
#line 7200 "test_template.cpp"
template <class T, int V>
void test_template() {
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-TEMPL -DINT_ID=0
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-TEMPL -DINT_ID=1
//
// CHECK-TEMPL-DAG: @[[FILE:.*]] = {{.*}}c"local_templ.cpp\00"
// CHECK-TEMPL-DAG: @[[FUNC:.*]] = {{.*}}c"test_template\00"
//
// CHECK-TEMPL: define weak_odr void @_Z13test_templateI15source_locationLi[[INT_ID]]EEvv()
// CHECK-TEMPL-NEXT: entry:
// CHECK-TEMPL-NOT: ret
//
// CHECK-TEMPL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[TMP:[^,]*]],
// CHECK-TEMPL-SAME: i32 7300, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]]
#line 7300 "local_templ.cpp"
TestTemplate<T, V> local_templ;
}
#line 7400 "EndTestTemplate.cpp"
template void test_template<SL, 0>();
template void test_template<SL, 1>();

View File

@ -0,0 +1,41 @@
// RUN: %clang_cc1 -std=c++2a -fblocks %s -triple %itanium_abi_triple -emit-llvm -o %t.ll
// RUN: FileCheck --input-file %t.ll %s
namespace test_func {
constexpr const char *test_default_arg(const char *f = __builtin_FUNCTION()) {
return f;
}
// CHECK: @[[EMPTY_STR:.+]] = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
// CHECK: @_ZN9test_func6globalE = global i8* getelementptr inbounds ([1 x i8], [1 x i8]* @[[EMPTY_STR]], i32 0, i32 0), align 8
const char *global = test_default_arg();
// CHECK: @_ZN9test_func10global_twoE = global i8* getelementptr inbounds ([1 x i8], [1 x i8]* @[[EMPTY_STR]], i32 0, i32 0), align 8
const char *global_two = __builtin_FUNCTION();
const char * const global_three = test_default_arg();
// CHECK: @[[STR_ONE:.+]] = private unnamed_addr constant [14 x i8] c"test_func_one\00", align 1
// CHECK: @[[STR_TWO:.+]] = private unnamed_addr constant [14 x i8] c"test_func_two\00", align 1
// CHECK: @[[STR_THREE:.+]] = private unnamed_addr constant [20 x i8] c"do_default_arg_test\00", align 1
// CHECK: define i8* @_ZN9test_func13test_func_oneEv()
// CHECK: ret i8* getelementptr inbounds ([14 x i8], [14 x i8]* @[[STR_ONE]], i32 0, i32 0)
const char *test_func_one() {
return __builtin_FUNCTION();
}
// CHECK: define i8* @_ZN9test_func13test_func_twoEv()
// CHECK: ret i8* getelementptr inbounds ([14 x i8], [14 x i8]* @[[STR_TWO]], i32 0, i32 0)
const char *test_func_two() {
return __builtin_FUNCTION();
}
// CHECK: define void @_ZN9test_func19do_default_arg_testEv()
// CHECK: %call = call i8* @_ZN9test_func16test_default_argEPKc(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @[[STR_THREE]], i32 0, i32 0))
void do_default_arg_test() {
test_default_arg();
}
} // namespace test_func

View File

@ -0,0 +1,95 @@
// RUN: %clang_cc1 -std=c++1z -fblocks %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s
extern "C" int sink;
extern "C" const volatile void* volatile ptr_sink = nullptr;
struct Tag1 {};
struct Tag2 {};
struct Tag3 {};
struct Tag4 {};
constexpr int get_line_constexpr(int l = __builtin_LINE()) {
return l;
}
int get_line_nonconstexpr(int l = __builtin_LINE()) {
return l;
}
int get_line(int l = __builtin_LINE()) {
return l;
}
int get_line2(int l = get_line()) { return l; }
// CHECK: @global_one = global i32 [[@LINE+1]], align 4
int global_one = __builtin_LINE();
// CHECK-NEXT: @global_two = global i32 [[@LINE+1]], align 4
int global_two = get_line_constexpr();
// CHECK: @_ZL12global_three = internal constant i32 [[@LINE+1]], align 4
const int global_three(get_line_constexpr());
// CHECK-LABEL: define internal void @__cxx_global_var_init
// CHECK: %call = call i32 @_Z21get_line_nonconstexpri(i32 [[@LINE+2]])
// CHECK-NEXT: store i32 %call, i32* @global_four, align 4
int global_four = get_line_nonconstexpr();
struct InClassInit {
int Init = __builtin_LINE();
int Init2 = get_line2();
InClassInit();
constexpr InClassInit(Tag1, int l = __builtin_LINE()) : Init(l), Init2(l) {}
constexpr InClassInit(Tag2) : Init(__builtin_LINE()), Init2(__builtin_LINE()) {}
InClassInit(Tag3, int l = __builtin_LINE());
InClassInit(Tag4, int l = get_line2());
static void test_class();
};
// CHECK-LABEL: define void @_ZN11InClassInit10test_classEv()
void InClassInit::test_class() {
// CHECK: call void @_ZN11InClassInitC1Ev(%struct.InClassInit* %test_one)
InClassInit test_one;
// CHECK-NEXT: call void @_ZN11InClassInitC1E4Tag1i(%struct.InClassInit* %test_two, i32 [[@LINE+1]])
InClassInit test_two{Tag1{}};
// CHECK-NEXT: call void @_ZN11InClassInitC1E4Tag2(%struct.InClassInit* %test_three)
InClassInit test_three{Tag2{}};
// CHECK-NEXT: call void @_ZN11InClassInitC1E4Tag3i(%struct.InClassInit* %test_four, i32 [[@LINE+1]])
InClassInit test_four(Tag3{});
// CHECK-NEXT: %[[CALL:.+]] = call i32 @_Z8get_linei(i32 [[@LINE+3]])
// CHECK-NEXT: %[[CALL2:.+]] = call i32 @_Z9get_line2i(i32 %[[CALL]])
// CHECK-NEXT: call void @_ZN11InClassInitC1E4Tag4i(%struct.InClassInit* %test_five, i32 %[[CALL2]])
InClassInit test_five(Tag4{});
}
// CHECK-LABEL: define void @_ZN11InClassInitC2Ev
// CHECK: store i32 [[@LINE+4]], i32* %Init, align 4
// CHECK: %call = call i32 @_Z8get_linei(i32 [[@LINE+3]])
// CHECK-NEXT: %call2 = call i32 @_Z9get_line2i(i32 %call)
// CHECK-NEXT: store i32 %call2, i32* %Init2, align 4
InClassInit::InClassInit() = default;
InClassInit::InClassInit(Tag3, int l) : Init(l) {}
// CHECK-LABEL: define void @_ZN11InClassInitC2E4Tag4i(%struct.InClassInit* %this, i32 %arg)
// CHECK: %[[TEMP:.+]] = load i32, i32* %arg.addr, align 4
// CHECK-NEXT: store i32 %[[TEMP]], i32* %Init, align 4
// CHECK: %[[CALL:.+]] = call i32 @_Z8get_linei(i32 [[@LINE+3]])
// CHECK-NEXT: %[[CALL2:.+]] = call i32 @_Z9get_line2i(i32 %[[CALL]])
// CHECK-NEXT: store i32 %[[CALL2]], i32* %Init2, align 4
InClassInit::InClassInit(Tag4, int arg) : Init(arg) {}
// CHECK-LABEL: define void @_Z13get_line_testv()
void get_line_test() {
// CHECK: %[[CALL:.+]] = call i32 @_Z8get_linei(i32 [[@LINE+2]])
// CHECK-NEXT: store i32 %[[CALL]], i32* @sink, align 4
sink = get_line();
// CHECK-NEXT: store i32 [[@LINE+1]], i32* @sink, align 4
sink = __builtin_LINE();
ptr_sink = &global_three;
}
void foo() {
const int N[] = {__builtin_LINE(), get_line_constexpr()};
}

View File

@ -293,6 +293,13 @@ void f24() {
f24_a();
}
// CHECK-LABEL: define
void f25_a(int x = __builtin_LINE()) {}
void f25() {
// CHECK: call void @_Z5f25_ai(i32 2700)
#line 2700
f25_a();
}
// CHECK: [[DBG_F1]] = !DILocation(line: 100,
// CHECK: [[DBG_FOO_VALUE]] = !DILocation(line: 200,
// CHECK: [[DBG_FOO_REF]] = !DILocation(line: 202,

View File

@ -0,0 +1,19 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
int main() {
int line = __builtin_LINE();
__builtin_LINE(42); // expected-error {{expected ')'}}
__builtin_LINE(double); // expected-error {{expected ')'}}
int column = __builtin_COLUMN();
__builtin_COLUMN(42); // expected-error {{expected ')'}}
__builtin_COLUMN(double); // expected-error {{expected ')'}}
const char *func = __builtin_FUNCTION();
__builtin_FUNCTION(42); // expected-error {{expected ')'}}
__builtin_FUNCTION(double); // expected-error {{expected ')'}}
const char *file = __builtin_FILE();
__builtin_FILE(42); // expected-error {{expected ')'}}
__builtin_FILE(double); // expected-error {{expected ')'}}
}

View File

@ -0,0 +1,32 @@
// RUN: %clang_cc1 -std=c90 -fconst-strings -DCONST_STRINGS -verify %s
// RUN: %clang_cc1 -std=c90 -verify %s
// expected-no-diagnostics
#define IsEqual(L, R) (__builtin_strcmp(L, R) == 0)
const char *const FILE = __builtin_FILE();
const char *const FUNC = __builtin_FUNCTION();
const unsigned LINE = __builtin_LINE();
const unsigned COL = __builtin_COLUMN();
#ifndef CONST_STRINGS
char *const NCFILE = __builtin_FILE();
char *const NCFUNC = __builtin_FUNCTION();
#endif
#ifdef CONST_STRINGS
_Static_assert(IsEqual(__builtin_FILE(), __FILE__), "");
_Static_assert(__builtin_LINE() == __LINE__, "");
_Static_assert(IsEqual("", __builtin_FUNCTION()), "");
#line 42 "my_file.c"
_Static_assert(__builtin_LINE() == 42, "");
_Static_assert(IsEqual(__builtin_FILE(), "my_file.c"), "");
_Static_assert(__builtin_COLUMN() == __builtin_strlen("_Static_assert(_"), "");
void foo() {
_Static_assert(IsEqual(__builtin_FUNCTION(), "foo"), "");
}
#endif // CONST_STRINGS

View File

@ -0,0 +1,44 @@
// NOTE: source_location.cpp must include this file after defining
// std::source_location.
namespace source_location_file {
constexpr const char *FILE = __FILE__;
constexpr SL global_info = SL::current();
constexpr SL test_function(SL v = SL::current()) {
return v;
}
constexpr SL test_function_indirect() {
return test_function();
}
template <class T, class U = SL>
constexpr U test_function_template(T, U u = U::current()) {
return u;
}
template <class T, class U = SL>
constexpr U test_function_template_indirect(T t) {
return test_function_template(t);
}
struct TestClass {
SL info = SL::current();
SL ctor_info;
TestClass() = default;
constexpr TestClass(int, SL cinfo = SL::current()) : ctor_info(cinfo) {}
template <class T, class U = SL>
constexpr TestClass(int, T, U u = U::current()) : ctor_info(u) {}
};
template <class T = SL>
struct AggrClass {
int x;
T info;
T init_info = T::current();
};
} // namespace source_location_file

View File

@ -0,0 +1,590 @@
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify %s
// expected-no-diagnostics
#define assert(...) ((__VA_ARGS__) ? ((void)0) : throw 42)
#define CURRENT_FROM_MACRO() SL::current()
#define FORWARD(...) __VA_ARGS__
template <unsigned>
struct Printer;
namespace std {
namespace experimental {
struct source_location {
private:
unsigned int __m_line = 0;
unsigned int __m_col = 0;
const char *__m_file = nullptr;
const char *__m_func = nullptr;
public:
static constexpr source_location current(
const char *__file = __builtin_FILE(),
const char *__func = __builtin_FUNCTION(),
unsigned int __line = __builtin_LINE(),
unsigned int __col = __builtin_COLUMN()) noexcept {
source_location __loc;
__loc.__m_line = __line;
__loc.__m_col = __col;
__loc.__m_file = __file;
__loc.__m_func = __func;
return __loc;
}
constexpr source_location() = default;
constexpr source_location(source_location const &) = default;
constexpr unsigned int line() const noexcept { return __m_line; }
constexpr unsigned int column() const noexcept { return __m_col; }
constexpr const char *file() const noexcept { return __m_file; }
constexpr const char *function() const noexcept { return __m_func; }
};
} // namespace experimental
} // namespace std
using SL = std::experimental::source_location;
#include "Inputs/source-location-file.h"
namespace SLF = source_location_file;
constexpr bool is_equal(const char *LHS, const char *RHS) {
while (*LHS != 0 && *RHS != 0) {
if (*LHS != *RHS)
return false;
++LHS;
++RHS;
}
return *LHS == 0 && *RHS == 0;
}
template <class T>
constexpr T identity(T t) {
return t;
}
template <class T, class U>
struct Pair {
T first;
U second;
};
template <class T, class U>
constexpr bool is_same = false;
template <class T>
constexpr bool is_same<T, T> = true;
// test types
static_assert(is_same<decltype(__builtin_LINE()), unsigned>);
static_assert(is_same<decltype(__builtin_COLUMN()), unsigned>);
static_assert(is_same<decltype(__builtin_FILE()), const char *>);
static_assert(is_same<decltype(__builtin_FUNCTION()), const char *>);
// test noexcept
static_assert(noexcept(__builtin_LINE()));
static_assert(noexcept(__builtin_COLUMN()));
static_assert(noexcept(__builtin_FILE()));
static_assert(noexcept(__builtin_FUNCTION()));
//===----------------------------------------------------------------------===//
// __builtin_LINE()
//===----------------------------------------------------------------------===//
namespace test_line {
static_assert(SL::current().line() == __LINE__);
static_assert(SL::current().line() == CURRENT_FROM_MACRO().line());
static constexpr SL GlobalS = SL::current();
static_assert(GlobalS.line() == __LINE__ - 2);
// clang-format off
constexpr bool test_line_fn() {
constexpr SL S = SL::current();
static_assert(S.line() == (__LINE__ - 1), "");
// The start of the call expression to `current()` begins at the token `SL`
constexpr int ExpectLine = __LINE__ + 3;
constexpr SL S2
=
SL // Call expression starts here
::
current
(
)
;
static_assert(S2.line() == ExpectLine, "");
static_assert(
FORWARD(
__builtin_LINE
(
)
)
== __LINE__ - 1, "");
static_assert(\
\
__builtin_LINE()\
\
== __LINE__ - 2, "");
static_assert(\
_\
_builtin_LINE()
== __LINE__ - 2, "");
return true;
}
// clang-format on
static_assert(test_line_fn());
static_assert(__builtin_LINE() == __LINE__, "");
constexpr int baz() { return 101; }
constexpr int test_line_fn_simple(int z = baz(), int x = __builtin_LINE()) {
return x;
}
void bar() {
static_assert(test_line_fn_simple() == __LINE__, "");
static_assert(test_line_fn_simple() == __LINE__, "");
}
struct CallExpr {
constexpr int operator()(int x = __builtin_LINE()) const { return x; }
};
constexpr CallExpr get_call() { return CallExpr{}; }
static_assert(get_call()() == __LINE__, "");
template <class T>
constexpr bool test_line_fn_template(T Expect, int L = __builtin_LINE()) {
return Expect == L;
}
static_assert(test_line_fn_template(__LINE__));
struct InMemInit {
constexpr bool check(int expect) const {
return info.line() == expect;
}
SL info = SL::current();
InMemInit() = default;
constexpr InMemInit(int) {}
};
static_assert(InMemInit{}.check(__LINE__ - 3), "");
static_assert(InMemInit{42}.check(__LINE__ - 3), "");
template <class T, class U = SL>
struct InMemInitTemplate {
constexpr bool check(int expect) const {
return info.line() == expect;
}
U info = U::current();
InMemInitTemplate() = default;
constexpr InMemInitTemplate(T) {}
constexpr InMemInitTemplate(T, T) : info(U::current()) {}
template <class V = U> constexpr InMemInitTemplate(T, T, T, V info = U::current())
: info(info) {}
};
void test_mem_init_template() {
constexpr int line_offset = 8;
static_assert(InMemInitTemplate<int>{}.check(__LINE__ - line_offset), "");
static_assert(InMemInitTemplate<unsigned>{42}.check(__LINE__ - line_offset), "");
static_assert(InMemInitTemplate<unsigned>{42, 42}.check(__LINE__ - line_offset), "");
static_assert(InMemInitTemplate<unsigned>{42, 42, 42}.check(__LINE__), "");
}
struct AggInit {
int x;
int y = __builtin_LINE();
constexpr bool check(int expect) const {
return y == expect;
}
};
constexpr AggInit AI{42};
static_assert(AI.check(__LINE__ - 1), "");
template <class T, class U = SL>
struct AggInitTemplate {
constexpr bool check(int expect) const {
return expect == info.line();
}
T x;
U info = U::current();
};
template <class T, class U = SL>
constexpr U test_fn_template(T, U u = U::current()) {
return u;
}
void fn_template_tests() {
static_assert(test_fn_template(42).line() == __LINE__, "");
}
struct TestMethodTemplate {
template <class T, class U = SL, class U2 = SL>
constexpr U get(T, U u = U::current(), U2 u2 = identity(U2::current())) const {
assert(u.line() == u2.line());
return u;
}
};
void method_template_tests() {
static_assert(TestMethodTemplate{}.get(42).line() == __LINE__, "");
}
struct InStaticInit {
static constexpr int LINE = __LINE__;
static constexpr const int x1 = __builtin_LINE();
static constexpr const int x2 = identity(__builtin_LINE());
static const int x3;
const int x4 = __builtin_LINE();
int x5 = __builtin_LINE();
};
const int InStaticInit::x3 = __builtin_LINE();
static_assert(InStaticInit::x1 == InStaticInit::LINE + 1, "");
static_assert(InStaticInit::x2 == InStaticInit::LINE + 2, "");
template <class T, int N = __builtin_LINE(), int Expect = -1>
constexpr void check_fn_template_param(T) {
constexpr int RealExpect = Expect == -1 ? __LINE__ - 2 : Expect;
static_assert(N == RealExpect);
}
template void check_fn_template_param(int);
template void check_fn_template_param<long, 42, 42>(long);
#line 100
struct AggBase {
#line 200
int x = __builtin_LINE();
int y = __builtin_LINE();
int z = __builtin_LINE();
};
#line 300
struct AggDer : AggBase {
};
#line 400
static_assert(AggDer{}.x == 400, "");
struct ClassBase {
#line 400
int x = __builtin_LINE();
int y = 0;
int z = 0;
#line 500
ClassBase() = default;
constexpr ClassBase(int yy, int zz = __builtin_LINE())
: y(yy), z(zz) {}
};
struct ClassDer : ClassBase {
#line 600
ClassDer() = default;
constexpr ClassDer(int yy) : ClassBase(yy) {}
constexpr ClassDer(int yy, int zz) : ClassBase(yy, zz) {}
};
#line 700
static_assert(ClassDer{}.x == 500, "");
static_assert(ClassDer{42}.x == 501, "");
static_assert(ClassDer{42}.z == 601, "");
static_assert(ClassDer{42, 42}.x == 501, "");
struct ClassAggDer : AggBase {
#line 800
ClassAggDer() = default;
constexpr ClassAggDer(int, int x = __builtin_LINE()) : AggBase{x} {}
};
static_assert(ClassAggDer{}.x == 100, "");
} // namespace test_line
//===----------------------------------------------------------------------===//
// __builtin_FILE()
//===----------------------------------------------------------------------===//
namespace test_file {
constexpr const char *test_file_simple(const char *__f = __builtin_FILE()) {
return __f;
}
void test_function() {
#line 900
static_assert(is_equal(test_file_simple(), __FILE__));
static_assert(is_equal(SLF::test_function().file(), __FILE__), "");
static_assert(is_equal(SLF::test_function_template(42).file(), __FILE__), "");
static_assert(is_equal(SLF::test_function_indirect().file(), SLF::global_info.file()), "");
static_assert(is_equal(SLF::test_function_template_indirect(42).file(), SLF::global_info.file()), "");
static_assert(test_file_simple() != nullptr);
static_assert(!is_equal(test_file_simple(), "source_location.cpp"));
}
void test_class() {
#line 315
using SLF::TestClass;
constexpr TestClass Default;
constexpr TestClass InParam{42};
constexpr TestClass Template{42, 42};
constexpr auto *F = Default.info.file();
constexpr auto Char = F[0];
static_assert(is_equal(Default.info.file(), SLF::FILE), "");
static_assert(is_equal(InParam.info.file(), SLF::FILE), "");
static_assert(is_equal(InParam.ctor_info.file(), __FILE__), "");
}
void test_aggr_class() {
using Agg = SLF::AggrClass<>;
constexpr Agg Default{};
constexpr Agg InitOne{42};
static_assert(is_equal(Default.init_info.file(), __FILE__), "");
static_assert(is_equal(InitOne.init_info.file(), __FILE__), "");
}
} // namespace test_file
//===----------------------------------------------------------------------===//
// __builtin_FUNCTION()
//===----------------------------------------------------------------------===//
namespace test_func {
constexpr const char *test_func_simple(const char *__f = __builtin_FUNCTION()) {
return __f;
}
constexpr const char *get_function() {
return __func__;
}
constexpr bool test_function() {
return is_equal(__func__, test_func_simple()) &&
!is_equal(get_function(), test_func_simple());
}
static_assert(test_function());
template <class T, class U = SL>
constexpr Pair<U, U> test_func_template(T, U u = U::current()) {
static_assert(is_equal(__func__, U::current().function()));
return {u, U::current()};
}
template <class T>
void func_template_tests() {
constexpr auto P = test_func_template(42);
//static_assert(is_equal(P.first.function(), __func__), "");
//static_assert(!is_equal(P.second.function(), __func__), "");
}
template void func_template_tests<int>();
template <class = int, class T = SL>
struct TestCtor {
T info = T::current();
T ctor_info;
TestCtor() = default;
template <class U = SL>
constexpr TestCtor(int, U u = U::current()) : ctor_info(u) {}
};
void ctor_tests() {
constexpr TestCtor<> Default;
constexpr TestCtor<> Template{42};
static_assert(!is_equal(Default.info.function(), __func__));
static_assert(is_equal(Default.info.function(), "TestCtor"));
static_assert(is_equal(Template.info.function(), "TestCtor"));
static_assert(is_equal(Template.ctor_info.function(), __func__));
}
constexpr SL global_sl = SL::current();
static_assert(is_equal(global_sl.function(), ""));
} // namespace test_func
//===----------------------------------------------------------------------===//
// __builtin_COLUMN()
//===----------------------------------------------------------------------===//
namespace test_column {
// clang-format off
constexpr bool test_column_fn() {
constexpr SL S = SL::current();
static_assert(S.line() == (__LINE__ - 1), "");
constexpr int Indent = 4;
{
// The start of the call expression to `current()` begins at the token `SL`
constexpr int ExpectCol = Indent + 3;
constexpr SL S2
=
SL // Call expression starts here
::
current
(
)
;
static_assert(S2.column() == ExpectCol, "");
}
{
constexpr int ExpectCol = 2;
constexpr int C =
__builtin_COLUMN // Expect call expression to start here
();
static_assert(C == ExpectCol);
}
return true;
}
#line 420
static_assert(test_column_fn());
// Test that the column matches the start of the call expression 'SL::current()'
static_assert(SL::current().column() == __builtin_strlen("static_assert(S"));
struct TestClass {
int x = __builtin_COLUMN();
TestClass() = default; /* indented to 3 spaces for testing */
constexpr TestClass(int, int o = __builtin_COLUMN()) : x(o) {}
};
struct TestAggClass {
int x = __builtin_COLUMN();
};
constexpr bool test_class() {
auto check = [](int V, const char* S, int indent = 4) {
assert(V == (__builtin_strlen(S) + indent));
};
{
TestClass t{};
check(t.x, " T", 0); // Start of default constructor decl.
}
{
TestClass t1
{42};
check(t1.x, "TestClass t"); // Start of variable being constructed.
}
{
TestAggClass t { };
check(t.x, "TestAggClass t { }");
}
{
TestAggClass t = { };
check(t.x, "TestAggClass t = { }");
}
return true;
}
static_assert(test_class());
// clang-format on
} // namespace test_column
// Test [reflection.src_loc.creation]p2
// > The value should be affected by #line (C++14 16.4) in the same manner as
// > for __LINE__ and __FILE__.
namespace test_pragma_line {
constexpr int StartLine = 42;
#line 42
static_assert(__builtin_LINE() == StartLine);
static_assert(__builtin_LINE() == StartLine + 1);
static_assert(SL::current().line() == StartLine + 2);
#line 44 "test_file.c"
static_assert(is_equal("test_file.c", __FILE__));
static_assert(is_equal("test_file.c", __builtin_FILE()));
static_assert(is_equal("test_file.c", SL::current().file()));
static_assert(is_equal("test_file.c", SLF::test_function().file()));
static_assert(is_equal(SLF::FILE, SLF::test_function_indirect().file()));
} // end namespace test_pragma_line
namespace test_out_of_line_init {
#line 4000 "test_out_of_line_init.cpp"
constexpr unsigned get_line(unsigned n = __builtin_LINE()) { return n; }
constexpr const char *get_file(const char *f = __builtin_FILE()) { return f; }
constexpr const char *get_func(const char *f = __builtin_FUNCTION()) { return f; }
#line 4100 "A.cpp"
struct A {
int n = __builtin_LINE();
int n2 = get_line();
const char *f = __builtin_FILE();
const char *f2 = get_file();
const char *func = __builtin_FUNCTION();
const char *func2 = get_func();
SL info = SL::current();
};
#line 4200 "B.cpp"
struct B {
A a = {};
};
#line 4300 "test_passed.cpp"
constexpr B b = {};
static_assert(b.a.n == 4300, "");
static_assert(b.a.n2 == 4300, "");
static_assert(b.a.info.line() == 4300, "");
static_assert(is_equal(b.a.f, "test_passed.cpp"));
static_assert(is_equal(b.a.f2, "test_passed.cpp"));
static_assert(is_equal(b.a.info.file(), "test_passed.cpp"));
static_assert(is_equal(b.a.func, ""));
static_assert(is_equal(b.a.func2, ""));
static_assert(is_equal(b.a.info.function(), ""));
constexpr bool test_in_func() {
#line 4400 "test_func_passed.cpp"
constexpr B b = {};
static_assert(b.a.n == 4400, "");
static_assert(b.a.n2 == 4400, "");
static_assert(b.a.info.line() == 4400, "");
static_assert(is_equal(b.a.f, "test_func_passed.cpp"));
static_assert(is_equal(b.a.f2, "test_func_passed.cpp"));
static_assert(is_equal(b.a.info.file(), "test_func_passed.cpp"));
static_assert(is_equal(b.a.func, "test_in_func"));
static_assert(is_equal(b.a.func2, "test_in_func"));
static_assert(is_equal(b.a.info.function(), "test_in_func"));
return true;
}
static_assert(test_in_func());
} // end namespace test_out_of_line_init
namespace test_global_scope {
#line 5000 "test_global_scope.cpp"
constexpr unsigned get_line(unsigned n = __builtin_LINE()) { return n; }
constexpr const char *get_file(const char *f = __builtin_FILE()) { return f; }
constexpr const char *get_func(const char *f = __builtin_FUNCTION()) { return f; }
#line 5100
struct InInit {
unsigned l = get_line();
const char *f = get_file();
const char *func = get_func();
#line 5200 "in_init.cpp"
constexpr InInit() {}
};
#line 5300
constexpr InInit II;
static_assert(II.l == 5200, "");
static_assert(is_equal(II.f, "in_init.cpp"));
static_assert(is_equal(II.func, "InInit"));
#line 5400
struct AggInit {
unsigned l = get_line();
const char *f = get_file();
const char *func = get_func();
};
#line 5500 "brace_init.cpp"
constexpr AggInit AI = {};
static_assert(AI.l == 5500);
static_assert(is_equal(AI.f, "brace_init.cpp"));
static_assert(is_equal(AI.func, ""));
} // namespace test_global_scope
namespace TestFuncInInit {
#line 6000 "InitClass.cpp"
struct Init {
SL info;
#line 6100 "InitCtor.cpp"
constexpr Init(SL info = SL::current()) : info(info) {}
};
#line 6200 "InitGlobal.cpp"
constexpr Init I;
static_assert(I.info.line() == 6200);
static_assert(is_equal(I.info.file(), "InitGlobal.cpp"));
} // namespace TestFuncInInit
namespace TestConstexprContext {
#line 7000 "TestConstexprContext.cpp"
constexpr const char* foo() { return __builtin_FILE(); }
#line 7100 "Bar.cpp"
constexpr const char* bar(const char* x = foo()) { return x; }
constexpr bool test() {
static_assert(is_equal(bar(), "TestConstexprContext.cpp"));
return true;
}
static_assert(test());
}

View File

@ -282,6 +282,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
case Stmt::ParenListExprClass:
case Stmt::PredefinedExprClass:
case Stmt::ShuffleVectorExprClass:
case Stmt::SourceLocExprClass:
case Stmt::ConvertVectorExprClass:
case Stmt::VAArgExprClass:
case Stmt::ObjCArrayLiteralClass: