mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-12 02:47:10 +00:00
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:
parent
e7b680478c
commit
708afb56c1
@ -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
|
||||
=============================
|
||||
|
||||
|
@ -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
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
75
clang/include/clang/AST/CurrentSourceLocExprScope.h
Normal file
75
clang/include/clang/AST/CurrentSourceLocExprScope.h
Normal 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
|
@ -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
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -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;
|
||||
|
@ -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>;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -1732,6 +1732,9 @@ namespace serialization {
|
||||
/// A GNUNullExpr record.
|
||||
EXPR_GNU_NULL,
|
||||
|
||||
/// A SourceLocExpr record.
|
||||
EXPR_SOURCE_LOC,
|
||||
|
||||
/// A ShuffleVectorExpr record.
|
||||
EXPR_SHUFFLE_VECTOR,
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -3589,6 +3589,7 @@ recurse:
|
||||
case Expr::AsTypeExprClass:
|
||||
case Expr::PseudoObjectExprClass:
|
||||
case Expr::AtomicExprClass:
|
||||
case Expr::SourceLocExprClass:
|
||||
case Expr::FixedPointLiteralClass:
|
||||
{
|
||||
if (!NullOut) {
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
268
clang/test/CodeGenCXX/builtin-source-location.cpp
Normal file
268
clang/test/CodeGenCXX/builtin-source-location.cpp
Normal 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>();
|
41
clang/test/CodeGenCXX/builtin_FUNCTION.cpp
Normal file
41
clang/test/CodeGenCXX/builtin_FUNCTION.cpp
Normal 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
|
95
clang/test/CodeGenCXX/builtin_LINE.cpp
Normal file
95
clang/test/CodeGenCXX/builtin_LINE.cpp
Normal 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()};
|
||||
}
|
@ -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,
|
||||
|
19
clang/test/Parser/builtin_source_location.c
Normal file
19
clang/test/Parser/builtin_source_location.c
Normal 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 ')'}}
|
||||
}
|
32
clang/test/Sema/source_location.c
Normal file
32
clang/test/Sema/source_location.c
Normal 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
|
44
clang/test/SemaCXX/Inputs/source-location-file.h
Normal file
44
clang/test/SemaCXX/Inputs/source-location-file.h
Normal 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
|
590
clang/test/SemaCXX/source_location.cpp
Normal file
590
clang/test/SemaCXX/source_location.cpp
Normal 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());
|
||||
}
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user