[Clang] Implement __builtin_source_location.

This builtin returns the address of a global instance of the
`std::source_location::__impl` type, which must be defined (with an
appropriate shape) before calling the builtin.

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

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

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

Reviewers: aaron.ballman, erichkeane

Differential Revision: https://reviews.llvm.org/D120159
This commit is contained in:
James Y Knight 2022-03-28 18:27:18 -04:00
parent e2485f3c59
commit d614874900
36 changed files with 667 additions and 199 deletions

View File

@ -3377,10 +3377,9 @@ as the first argument to the intrinsic.
Source location builtins
------------------------
Clang provides experimental builtins to support C++ standard library implementation
of ``std::experimental::source_location`` as specified in http://wg21.link/N4600.
With the exception of ``__builtin_COLUMN``, these builtins are also implemented by
GCC.
Clang provides builtins to support C++ standard library implementation
of ``std::source_location`` as specified in C++20. With the exception
of ``__builtin_COLUMN``, these builtins are also implemented by GCC.
**Syntax**:
@ -3390,6 +3389,7 @@ GCC.
const char *__builtin_FUNCTION();
unsigned __builtin_LINE();
unsigned __builtin_COLUMN(); // Clang only
const std::source_location::__impl *__builtin_source_location();
**Example of use**:
@ -3416,9 +3416,11 @@ GCC.
**Description**:
The builtins ``__builtin_LINE``, ``__builtin_FUNCTION``, and ``__builtin_FILE`` return
the values, at the "invocation point", for ``__LINE__``, ``__FUNCTION__``, and
``__FILE__`` respectively. These builtins are constant expressions.
The builtins ``__builtin_LINE``, ``__builtin_FUNCTION``, and ``__builtin_FILE``
return the values, at the "invocation point", for ``__LINE__``,
``__FUNCTION__``, and ``__FILE__`` respectively. ``__builtin_COLUMN`` similarly
returns the column, though there is no corresponding macro. These builtins are
constant expressions.
When the builtins appear as part of a default function argument the invocation
point is the location of the caller. When the builtins appear as part of a
@ -3429,6 +3431,15 @@ the invocation point is the same as the location of the builtin.
When the invocation point of ``__builtin_FUNCTION`` is not a function scope the
empty string is returned.
The builtin ``__builtin_source_location`` returns a pointer to constant static
data of type ``std::source_location::__impl``. This type must have already been
defined, and must contain exactly four fields: ``const char *_M_file_name``,
``const char *_M_function_name``, ``<any-integral-type> _M_line``, and
``<any-integral-type> _M_column``. The fields will be populated in the same
manner as the above four builtins, except that ``_M_function_name`` is populated
with ``__PRETTY_FUNCTION__`` rather than ``__FUNCTION__``.
Alignment builtins
------------------
Clang provides builtins to support checking and adjusting alignment of

View File

@ -193,6 +193,8 @@ C++20 Feature Support
it is called through a template instantiation. This fixes
`Issue 54578 <https://github.com/llvm/llvm-project/issues/54578>`_.
- Implemented `__builtin_source_location()` which enables library support for std::source_location.
C++2b Feature Support
^^^^^^^^^^^^^^^^^^^^^

View File

@ -314,6 +314,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// Mapping from GUIDs to the corresponding MSGuidDecl.
mutable llvm::FoldingSet<MSGuidDecl> MSGuidDecls;
/// Mapping from APValues to the corresponding UnnamedGlobalConstantDecl.
mutable llvm::FoldingSet<UnnamedGlobalConstantDecl>
UnnamedGlobalConstantDecls;
/// Mapping from APValues to the corresponding TemplateParamObjects.
mutable llvm::FoldingSet<TemplateParamObjectDecl> TemplateParamObjectDecls;
@ -3064,6 +3068,11 @@ public:
/// GUID value.
MSGuidDecl *getMSGuidDecl(MSGuidDeclParts Parts) const;
/// Return a declaration for a uniquified anonymous global constant
/// corresponding to a given APValue.
UnnamedGlobalConstantDecl *
getUnnamedGlobalConstantDecl(QualType Ty, const APValue &Value) const;
/// Return the template parameter object of the given type with the given
/// value.
TemplateParamObjectDecl *getTemplateParamObjectDecl(QualType T,

View File

@ -4206,6 +4206,53 @@ public:
static bool classofKind(Kind K) { return K == Decl::MSGuid; }
};
/// An artificial decl, representing a global anonymous constant value which is
/// uniquified by value within a translation unit.
///
/// These is currently only used to back the LValue returned by
/// __builtin_source_location, but could potentially be used for other similar
/// situations in the future.
class UnnamedGlobalConstantDecl : public ValueDecl,
public Mergeable<UnnamedGlobalConstantDecl>,
public llvm::FoldingSetNode {
// The constant value of this global.
APValue Value;
void anchor() override;
UnnamedGlobalConstantDecl(DeclContext *DC, QualType T, const APValue &Val);
static UnnamedGlobalConstantDecl *Create(const ASTContext &C, QualType T,
const APValue &APVal);
static UnnamedGlobalConstantDecl *CreateDeserialized(ASTContext &C,
unsigned ID);
// Only ASTContext::getUnnamedGlobalConstantDecl and deserialization create
// these.
friend class ASTContext;
friend class ASTReader;
friend class ASTDeclReader;
public:
/// Print this in a human-readable format.
void printName(llvm::raw_ostream &OS) const override;
const APValue &getValue() const { return Value; }
static void Profile(llvm::FoldingSetNodeID &ID, QualType Ty,
const APValue &APVal) {
Ty.Profile(ID);
APVal.Profile(ID);
}
void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getType(), getValue());
}
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == Decl::UnnamedGlobalConstant; }
};
/// Insertion operator for diagnostics. This allows sending an AccessSpecifier
/// into a diagnostic with <<.
const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,

View File

@ -4680,16 +4680,17 @@ public:
};
/// Represents a function call to one of __builtin_LINE(), __builtin_COLUMN(),
/// __builtin_FUNCTION(), or __builtin_FILE().
/// __builtin_FUNCTION(), __builtin_FILE(), or __builtin_source_location().
class SourceLocExpr final : public Expr {
SourceLocation BuiltinLoc, RParenLoc;
DeclContext *ParentContext;
public:
enum IdentKind { Function, File, Line, Column };
enum IdentKind { Function, File, Line, Column, SourceLocStruct };
SourceLocExpr(const ASTContext &Ctx, IdentKind Type, SourceLocation BLoc,
SourceLocation RParenLoc, DeclContext *Context);
SourceLocExpr(const ASTContext &Ctx, IdentKind Type, QualType ResultTy,
SourceLocation BLoc, SourceLocation RParenLoc,
DeclContext *Context);
/// Build an empty call expression.
explicit SourceLocExpr(EmptyShell Empty) : Expr(SourceLocExprClass, Empty) {}
@ -4706,18 +4707,18 @@ public:
return static_cast<IdentKind>(SourceLocExprBits.Kind);
}
bool isStringType() const {
bool isIntType() const {
switch (getIdentKind()) {
case File:
case Function:
return true;
case SourceLocStruct:
return false;
case Line:
case Column:
return false;
return true;
}
llvm_unreachable("unknown source location expression kind");
}
bool isIntType() const LLVM_READONLY { return !isStringType(); }
/// If the SourceLocExpr has been resolved return the subexpression
/// representing the resolved value. Otherwise return null.

View File

@ -2039,6 +2039,7 @@ DEF_TRAVERSE_DECL(BindingDecl, {
DEF_TRAVERSE_DECL(MSPropertyDecl, { TRY_TO(TraverseDeclaratorHelper(D)); })
DEF_TRAVERSE_DECL(MSGuidDecl, {})
DEF_TRAVERSE_DECL(UnnamedGlobalConstantDecl, {})
DEF_TRAVERSE_DECL(TemplateParamObjectDecl, {})

View File

@ -595,7 +595,7 @@ protected:
/// The kind of source location builtin represented by the SourceLocExpr.
/// Ex. __builtin_LINE, __builtin_FUNCTION, ect.
unsigned Kind : 2;
unsigned Kind : 3;
};
class StmtExprBitfields {

View File

@ -41,6 +41,7 @@ def Named : DeclNode<Decl, "named declarations", 1>;
def OMPDeclareReduction : DeclNode<Value>, DeclContext;
def OMPDeclareMapper : DeclNode<Value>, DeclContext;
def MSGuid : DeclNode<Value>;
def UnnamedGlobalConstant : DeclNode<Value>;
def TemplateParamObject : DeclNode<Value>;
def Declarator : DeclNode<Value, "declarators", 1>;
def Field : DeclNode<Declarator, "non-static data members">;

View File

@ -11556,4 +11556,9 @@ def err_riscv_builtin_requires_extension : Error<
"builtin requires at least one of the following extensions support to be enabled : %0">;
def err_riscv_builtin_invalid_lmul : Error<
"LMUL argument must be in the range [0,3] or [5,7]">;
def err_std_source_location_impl_not_found : Error<
"'std::source_location::__impl' was not found; it must be defined before '__builtin_source_location' is called">;
def err_std_source_location_impl_malformed : Error<
"'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'">;
} // end of sema component.

View File

@ -432,6 +432,7 @@ KEYWORD(__builtin_FILE , KEYALL)
KEYWORD(__builtin_FUNCTION , KEYALL)
KEYWORD(__builtin_LINE , KEYALL)
KEYWORD(__builtin_COLUMN , KEYALL)
KEYWORD(__builtin_source_location , KEYCXX)
// __builtin_types_compatible_p is a GNU C extension that we handle like a C++
// type trait.

View File

@ -1144,6 +1144,10 @@ public:
/// The MSVC "_GUID" struct, which is defined in MSVC header files.
RecordDecl *MSVCGuidDecl;
/// The C++ "std::source_location::__impl" struct, defined in
/// \<source_location>.
RecordDecl *StdSourceLocationImplDecl;
/// Caches identifiers/selectors for NSFoundation APIs.
std::unique_ptr<NSAPI> NSAPIObj;
@ -5684,14 +5688,15 @@ public:
TypeSourceInfo *TInfo, SourceLocation RPLoc);
// __builtin_LINE(), __builtin_FUNCTION(), __builtin_FILE(),
// __builtin_COLUMN()
// __builtin_COLUMN(), __builtin_source_location()
ExprResult ActOnSourceLocExpr(SourceLocExpr::IdentKind Kind,
SourceLocation BuiltinLoc,
SourceLocation RPLoc);
// Build a potentially resolved SourceLocExpr.
ExprResult BuildSourceLocExpr(SourceLocExpr::IdentKind Kind,
SourceLocation BuiltinLoc, SourceLocation RPLoc,
QualType ResultTy, SourceLocation BuiltinLoc,
SourceLocation RPLoc,
DeclContext *ParentContext);
// __null

View File

@ -41,7 +41,7 @@ namespace serialization {
/// Version 4 of AST files also requires that the version control branch and
/// revision match exactly, since there is no backward compatibility of
/// AST files at this time.
const unsigned VERSION_MAJOR = 16;
const unsigned VERSION_MAJOR = 17;
/// AST file minor version number supported by this version of
/// Clang.
@ -1504,7 +1504,10 @@ enum DeclCode {
/// An OMPDeclareReductionDecl record.
DECL_OMP_DECLARE_REDUCTION,
DECL_LAST = DECL_OMP_DECLARE_REDUCTION
/// A UnnamedGlobalConstantDecl record.
DECL_UNNAMED_GLOBAL_CONSTANT,
DECL_LAST = DECL_UNNAMED_GLOBAL_CONSTANT
};
/// Record codes for each kind of statement or expression.

View File

@ -11875,6 +11875,23 @@ ASTContext::getMSGuidDecl(MSGuidDecl::Parts Parts) const {
return New;
}
UnnamedGlobalConstantDecl *
ASTContext::getUnnamedGlobalConstantDecl(QualType Ty,
const APValue &APVal) const {
llvm::FoldingSetNodeID ID;
UnnamedGlobalConstantDecl::Profile(ID, Ty, APVal);
void *InsertPos;
if (UnnamedGlobalConstantDecl *Existing =
UnnamedGlobalConstantDecls.FindNodeOrInsertPos(ID, InsertPos))
return Existing;
UnnamedGlobalConstantDecl *New =
UnnamedGlobalConstantDecl::Create(*this, Ty, APVal);
UnnamedGlobalConstantDecls.InsertNode(New, InsertPos);
return New;
}
TemplateParamObjectDecl *
ASTContext::getTemplateParamObjectDecl(QualType T, const APValue &V) const {
assert(T->isRecordType() && "template param object of unexpected type");

View File

@ -6669,6 +6669,7 @@ ExpectedStmt ASTNodeImporter::VisitExpr(Expr *E) {
ExpectedStmt ASTNodeImporter::VisitSourceLocExpr(SourceLocExpr *E) {
Error Err = Error::success();
auto ToType = importChecked(Err, E->getType());
auto BLoc = importChecked(Err, E->getBeginLoc());
auto RParenLoc = importChecked(Err, E->getEndLoc());
if (Err)
@ -6678,8 +6679,8 @@ ExpectedStmt ASTNodeImporter::VisitSourceLocExpr(SourceLocExpr *E) {
return ParentContextOrErr.takeError();
return new (Importer.getToContext())
SourceLocExpr(Importer.getToContext(), E->getIdentKind(), BLoc, RParenLoc,
*ParentContextOrErr);
SourceLocExpr(Importer.getToContext(), E->getIdentKind(), ToType, BLoc,
RParenLoc, *ParentContextOrErr);
}
ExpectedStmt ASTNodeImporter::VisitVAArgExpr(VAArgExpr *E) {

View File

@ -838,6 +838,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case ExternCContext:
case Decomposition:
case MSGuid:
case UnnamedGlobalConstant:
case TemplateParamObject:
case UsingDirective:

View File

@ -3363,6 +3363,31 @@ APValue &MSGuidDecl::getAsAPValue() const {
return APVal;
}
void UnnamedGlobalConstantDecl::anchor() {}
UnnamedGlobalConstantDecl::UnnamedGlobalConstantDecl(DeclContext *DC,
QualType Ty,
const APValue &Value)
: ValueDecl(Decl::UnnamedGlobalConstant, DC, SourceLocation(),
DeclarationName(), Ty),
Value(Value) {}
UnnamedGlobalConstantDecl *
UnnamedGlobalConstantDecl::Create(const ASTContext &C, QualType T,
const APValue &Value) {
DeclContext *DC = C.getTranslationUnitDecl();
return new (C, DC) UnnamedGlobalConstantDecl(DC, T, Value);
}
UnnamedGlobalConstantDecl *
UnnamedGlobalConstantDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
return new (C, ID) UnnamedGlobalConstantDecl(nullptr, QualType(), APValue());
}
void UnnamedGlobalConstantDecl::printName(llvm::raw_ostream &OS) const {
OS << "unnamed-global-constant";
}
static const char *getAccessName(AccessSpecifier AS) {
switch (AS) {
case AS_none:

View File

@ -2136,26 +2136,11 @@ 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,
QualType ResultTy, SourceLocation BLoc,
SourceLocation RParenLoc,
DeclContext *ParentContext)
: Expr(SourceLocExprClass, getDecayedSourceLocExprType(Ctx, Kind),
VK_PRValue, OK_Ordinary),
: Expr(SourceLocExprClass, ResultTy, VK_PRValue, OK_Ordinary),
BuiltinLoc(BLoc), RParenLoc(RParenLoc), ParentContext(ParentContext) {
SourceLocExprBits.Kind = Kind;
setDependence(ExprDependence::None);
@ -2171,6 +2156,8 @@ StringRef SourceLocExpr::getBuiltinStr() const {
return "__builtin_LINE";
case Column:
return "__builtin_COLUMN";
case SourceLocStruct:
return "__builtin_source_location";
}
llvm_unreachable("unexpected IdentKind!");
}
@ -2207,7 +2194,7 @@ APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx,
return MakeStringLiteral(Path);
}
case SourceLocExpr::Function: {
const Decl *CurDecl = dyn_cast_or_null<Decl>(Context);
const auto *CurDecl = dyn_cast<Decl>(Context);
return MakeStringLiteral(
CurDecl ? PredefinedExpr::ComputeName(PredefinedExpr::Function, CurDecl)
: std::string(""));
@ -2220,6 +2207,54 @@ APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx,
: PLoc.getColumn();
return APValue(IntVal);
}
case SourceLocExpr::SourceLocStruct: {
// Fill in a std::source_location::__impl structure, by creating an
// artificial file-scoped CompoundLiteralExpr, and returning a pointer to
// that.
const CXXRecordDecl *ImplDecl = getType()->getPointeeCXXRecordDecl();
assert(ImplDecl);
// Construct an APValue for the __impl struct, and get or create a Decl
// corresponding to that. Note that we've already verified that the shape of
// the ImplDecl type is as expected.
APValue Value(APValue::UninitStruct(), 0, 4);
for (FieldDecl *F : ImplDecl->fields()) {
StringRef Name = F->getName();
if (Name == "_M_file_name") {
SmallString<256> Path(PLoc.getFilename());
Ctx.getLangOpts().remapPathPrefix(Path);
Value.getStructField(F->getFieldIndex()) = MakeStringLiteral(Path);
} else if (Name == "_M_function_name") {
// Note: this emits the PrettyFunction name -- different than what
// __builtin_FUNCTION() above returns!
const auto *CurDecl = dyn_cast<Decl>(Context);
Value.getStructField(F->getFieldIndex()) = MakeStringLiteral(
CurDecl && !isa<TranslationUnitDecl>(CurDecl)
? StringRef(PredefinedExpr::ComputeName(
PredefinedExpr::PrettyFunction, CurDecl))
: "");
} else if (Name == "_M_line") {
QualType Ty = F->getType();
llvm::APSInt IntVal(Ctx.getIntWidth(Ty),
Ty->hasUnsignedIntegerRepresentation());
IntVal = PLoc.getLine();
Value.getStructField(F->getFieldIndex()) = APValue(IntVal);
} else if (Name == "_M_column") {
QualType Ty = F->getType();
llvm::APSInt IntVal(Ctx.getIntWidth(Ty),
Ty->hasUnsignedIntegerRepresentation());
IntVal = PLoc.getColumn();
Value.getStructField(F->getFieldIndex()) = APValue(IntVal);
}
}
UnnamedGlobalConstantDecl *GV =
Ctx.getUnnamedGlobalConstantDecl(getType()->getPointeeType(), Value);
return APValue(GV, CharUnits::Zero(), ArrayRef<APValue::LValuePathEntry>{},
false);
}
}
llvm_unreachable("unhandled case");
}

View File

@ -465,14 +465,11 @@ static Cl::Kinds ClassifyDecl(ASTContext &Ctx, const Decl *D) {
islvalue = NTTParm->getType()->isReferenceType() ||
NTTParm->getType()->isRecordType();
else
islvalue = isa<VarDecl>(D) || isa<FieldDecl>(D) ||
isa<IndirectFieldDecl>(D) ||
isa<BindingDecl>(D) ||
isa<MSGuidDecl>(D) ||
isa<TemplateParamObjectDecl>(D) ||
(Ctx.getLangOpts().CPlusPlus &&
(isa<FunctionDecl>(D) || isa<MSPropertyDecl>(D) ||
isa<FunctionTemplateDecl>(D)));
islvalue =
isa<VarDecl, FieldDecl, IndirectFieldDecl, BindingDecl, MSGuidDecl,
UnnamedGlobalConstantDecl, TemplateParamObjectDecl>(D) ||
(Ctx.getLangOpts().CPlusPlus &&
(isa<FunctionDecl, MSPropertyDecl, FunctionTemplateDecl>(D)));
return islvalue ? Cl::CL_LValue : Cl::CL_PRValue;
}

View File

@ -1978,7 +1978,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
return true;
// ... the address of a function,
// ... the address of a GUID [MS extension],
return isa<FunctionDecl>(D) || isa<MSGuidDecl>(D);
// ... the address of an unnamed global constant
return isa<FunctionDecl, MSGuidDecl, UnnamedGlobalConstantDecl>(D);
}
if (B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>())
@ -2013,6 +2014,10 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
// Block variables at global or local static scope.
case Expr::BlockExprClass:
return !cast<BlockExpr>(E)->getBlockDecl()->hasCaptures();
// The APValue generated from a __builtin_source_location will be emitted as a
// literal.
case Expr::SourceLocExprClass:
return true;
case Expr::ImplicitValueInitExprClass:
// FIXME:
// We can never form an lvalue with an implicit value initialization as its
@ -4024,6 +4029,16 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
return CompleteObject(LVal.Base, &V, GD->getType());
}
// Allow reading the APValue from an UnnamedGlobalConstantDecl.
if (auto *GCD = dyn_cast<UnnamedGlobalConstantDecl>(D)) {
if (isModification(AK)) {
Info.FFDiag(E, diag::note_constexpr_modify_global);
return CompleteObject();
}
return CompleteObject(LVal.Base, const_cast<APValue *>(&GCD->getValue()),
GCD->getType());
}
// Allow reading from template parameter objects.
if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(D)) {
if (isModification(AK)) {
@ -8175,7 +8190,8 @@ static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info,
bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
const NamedDecl *D = E->getDecl();
if (isa<FunctionDecl, MSGuidDecl, TemplateParamObjectDecl>(D))
if (isa<FunctionDecl, MSGuidDecl, TemplateParamObjectDecl,
UnnamedGlobalConstantDecl>(D))
return Success(cast<ValueDecl>(D));
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
return VisitVarDecl(E, VD);
@ -8715,7 +8731,7 @@ public:
bool VisitCXXNewExpr(const CXXNewExpr *E);
bool VisitSourceLocExpr(const SourceLocExpr *E) {
assert(E->isStringType() && "SourceLocExpr isn't a pointer type?");
assert(!E->isIntType() && "SourceLocExpr isn't a pointer type?");
APValue LValResult = E->EvaluateInContext(
Info.Ctx, Info.CurrentCall->CurSourceLocExprScope.getDefaultExpr());
Result.setFrom(Info.Ctx, LValResult);
@ -8780,6 +8796,22 @@ bool PointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) {
return evaluateLValue(E->getSubExpr(), Result);
}
// Is the provided decl 'std::source_location::current'?
static bool IsDeclSourceLocationCurrent(const FunctionDecl *FD) {
if (!FD)
return false;
const IdentifierInfo *FnII = FD->getIdentifier();
if (!FnII || !FnII->isStr("current"))
return false;
const auto *RD = dyn_cast<RecordDecl>(FD->getParent());
if (!RD)
return false;
const IdentifierInfo *ClassII = RD->getIdentifier();
return RD->isInStdNamespace() && ClassII && ClassII->isStr("source_location");
}
bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
const Expr *SubExpr = E->getSubExpr();
@ -8797,14 +8829,23 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
// permitted in constant expressions in C++11. Bitcasts from cv void* are
// also static_casts, but we disallow them as a resolution to DR1312.
if (!E->getType()->isVoidPointerType()) {
if (!Result.InvalidBase && !Result.Designator.Invalid &&
// In some circumstances, we permit casting from void* to cv1 T*, when the
// actual pointee object is actually a cv2 T.
bool VoidPtrCastMaybeOK =
!Result.InvalidBase && !Result.Designator.Invalid &&
!Result.IsNullPtr &&
Info.Ctx.hasSameUnqualifiedType(Result.Designator.getType(Info.Ctx),
E->getType()->getPointeeType()) &&
Info.getStdAllocatorCaller("allocate")) {
// Inside a call to std::allocator::allocate and friends, we permit
// casting from void* back to cv1 T* for a pointer that points to a
// cv2 T.
E->getType()->getPointeeType());
// 1. We'll allow it in std::allocator::allocate, and anything which that
// calls.
// 2. We'll allow it in the body of std::source_location:current. This is
// necessary for libstdc++'s <source_location>, which gave its
// parameter the type void*, and cast from that back to `const __impl*`
// in the body. (Fixed for new versions in gcc.gnu.org/PR104602).
if (VoidPtrCastMaybeOK &&
(Info.getStdAllocatorCaller("allocate") ||
IsDeclSourceLocationCurrent(Info.CurrentCall->Callee))) {
// Permitted.
} else {
Result.Designator.setInvalid();
if (SubExpr->getType()->isVoidPointerType())

View File

@ -118,6 +118,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
case Decl::Label: // __label__ x;
case Decl::Import:
case Decl::MSGuid: // __declspec(uuid("..."))
case Decl::UnnamedGlobalConstant:
case Decl::TemplateParamObject:
case Decl::OMPThreadPrivate:
case Decl::OMPAllocate:

View File

@ -1909,6 +1909,9 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
if (auto *GD = dyn_cast<MSGuidDecl>(D))
return CGM.GetAddrOfMSGuidDecl(GD);
if (auto *GCD = dyn_cast<UnnamedGlobalConstantDecl>(D))
return CGM.GetAddrOfUnnamedGlobalConstantDecl(GCD);
if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(D))
return CGM.GetAddrOfTemplateParamObject(TPO);

View File

@ -2905,6 +2905,37 @@ ConstantAddress CodeGenModule::GetAddrOfMSGuidDecl(const MSGuidDecl *GD) {
return ConstantAddress(Addr, Ty, Alignment);
}
ConstantAddress CodeGenModule::GetAddrOfUnnamedGlobalConstantDecl(
const UnnamedGlobalConstantDecl *GCD) {
CharUnits Alignment = getContext().getTypeAlignInChars(GCD->getType());
llvm::GlobalVariable **Entry = nullptr;
Entry = &UnnamedGlobalConstantDeclMap[GCD];
if (*Entry)
return ConstantAddress(*Entry, (*Entry)->getValueType(), Alignment);
ConstantEmitter Emitter(*this);
llvm::Constant *Init;
const APValue &V = GCD->getValue();
assert(!V.isAbsent());
Init = Emitter.emitForInitializer(V, GCD->getType().getAddressSpace(),
GCD->getType());
auto *GV = new llvm::GlobalVariable(getModule(), Init->getType(),
/*isConstant=*/true,
llvm::GlobalValue::PrivateLinkage, Init,
".constant");
GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
GV->setAlignment(Alignment.getAsAlign());
Emitter.finalize(GV);
*Entry = GV;
return ConstantAddress(GV, GV->getValueType(), Alignment);
}
ConstantAddress CodeGenModule::GetAddrOfTemplateParamObject(
const TemplateParamObjectDecl *TPO) {
StringRef Name = getMangledName(TPO);

View File

@ -406,6 +406,8 @@ private:
llvm::StringMap<llvm::GlobalVariable *> CFConstantStringMap;
llvm::DenseMap<llvm::Constant *, llvm::GlobalVariable *> ConstantStringMap;
llvm::DenseMap<const UnnamedGlobalConstantDecl *, llvm::GlobalVariable *>
UnnamedGlobalConstantDeclMap;
llvm::DenseMap<const Decl*, llvm::Constant *> StaticLocalDeclMap;
llvm::DenseMap<const Decl*, llvm::GlobalVariable*> StaticLocalDeclGuardMap;
llvm::DenseMap<const Expr*, llvm::Constant *> MaterializedGlobalTemporaryMap;
@ -875,6 +877,10 @@ public:
/// Get the address of a GUID.
ConstantAddress GetAddrOfMSGuidDecl(const MSGuidDecl *GD);
/// Get the address of a UnnamedGlobalConstant
ConstantAddress
GetAddrOfUnnamedGlobalConstantDecl(const UnnamedGlobalConstantDecl *GCD);
/// Get the address of a template parameter object.
ConstantAddress
GetAddrOfTemplateParamObject(const TemplateParamObjectDecl *TPO);

View File

@ -791,6 +791,7 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback {
/// [GNU] '__builtin_FUNCTION' '(' ')'
/// [GNU] '__builtin_LINE' '(' ')'
/// [CLANG] '__builtin_COLUMN' '(' ')'
/// [GNU] '__builtin_source_location' '(' ')'
/// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')'
/// [GNU] '__null'
/// [OBJC] '[' objc-message-expr ']'
@ -1303,6 +1304,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
case tok::kw___builtin_FILE:
case tok::kw___builtin_FUNCTION:
case tok::kw___builtin_LINE:
case tok::kw___builtin_source_location:
if (NotPrimaryExpression)
*NotPrimaryExpression = true;
// This parses the complete suffix; we can return early.
@ -2508,6 +2510,7 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
/// [GNU] '__builtin_FUNCTION' '(' ')'
/// [GNU] '__builtin_LINE' '(' ')'
/// [CLANG] '__builtin_COLUMN' '(' ')'
/// [GNU] '__builtin_source_location' '(' ')'
/// [OCL] '__builtin_astype' '(' assignment-expression ',' type-name ')'
///
/// [GNU] offsetof-member-designator:
@ -2730,7 +2733,8 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
case tok::kw___builtin_COLUMN:
case tok::kw___builtin_FILE:
case tok::kw___builtin_FUNCTION:
case tok::kw___builtin_LINE: {
case tok::kw___builtin_LINE:
case tok::kw___builtin_source_location: {
// Attempt to consume the r-paren.
if (Tok.isNot(tok::r_paren)) {
Diag(Tok, diag::err_expected) << tok::r_paren;
@ -2747,6 +2751,8 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
return SourceLocExpr::Line;
case tok::kw___builtin_COLUMN:
return SourceLocExpr::Column;
case tok::kw___builtin_source_location:
return SourceLocExpr::SourceLocStruct;
default:
llvm_unreachable("invalid keyword");
}

View File

@ -202,8 +202,9 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp),
StdExperimentalNamespaceCache(nullptr), StdInitializerList(nullptr),
StdCoroutineTraitsCache(nullptr), CXXTypeInfoDecl(nullptr),
MSVCGuidDecl(nullptr), NSNumberDecl(nullptr), NSValueDecl(nullptr),
NSStringDecl(nullptr), StringWithUTF8StringMethod(nullptr),
MSVCGuidDecl(nullptr), StdSourceLocationImplDecl(nullptr),
NSNumberDecl(nullptr), NSValueDecl(nullptr), NSStringDecl(nullptr),
StringWithUTF8StringMethod(nullptr),
ValueWithBytesObjCTypeMethod(nullptr), NSArrayDecl(nullptr),
ArrayWithObjectsMethod(nullptr), NSDictionaryDecl(nullptr),
DictionaryWithObjectsMethod(nullptr), GlobalNewDeleteDeclared(false),

View File

@ -3439,6 +3439,10 @@ ExprResult Sema::BuildDeclarationNameExpr(
valueKind = VK_LValue;
break;
case Decl::UnnamedGlobalConstant:
valueKind = VK_LValue;
break;
case Decl::CXXMethod:
// If we're referring to a method with an __unknown_anytype
// result type, make the entire expression __unknown_anytype.
@ -14125,8 +14129,8 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
return MPTy;
}
}
} else if (!isa<FunctionDecl>(dcl) && !isa<NonTypeTemplateParmDecl>(dcl) &&
!isa<BindingDecl>(dcl) && !isa<MSGuidDecl>(dcl))
} else if (!isa<FunctionDecl, NonTypeTemplateParmDecl, BindingDecl,
MSGuidDecl, UnnamedGlobalConstantDecl>(dcl))
llvm_unreachable("Unknown/unexpected decl type");
}
@ -16310,18 +16314,111 @@ ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) {
return new (Context) GNUNullExpr(Ty, TokenLoc);
}
static CXXRecordDecl *LookupStdSourceLocationImpl(Sema &S, SourceLocation Loc) {
CXXRecordDecl *ImplDecl = nullptr;
// Fetch the std::source_location::__impl decl.
if (NamespaceDecl *Std = S.getStdNamespace()) {
LookupResult ResultSL(S, &S.PP.getIdentifierTable().get("source_location"),
Loc, Sema::LookupOrdinaryName);
if (S.LookupQualifiedName(ResultSL, Std)) {
if (auto *SLDecl = ResultSL.getAsSingle<RecordDecl>()) {
LookupResult ResultImpl(S, &S.PP.getIdentifierTable().get("__impl"),
Loc, Sema::LookupOrdinaryName);
if ((SLDecl->isCompleteDefinition() || SLDecl->isBeingDefined()) &&
S.LookupQualifiedName(ResultImpl, SLDecl)) {
ImplDecl = ResultImpl.getAsSingle<CXXRecordDecl>();
}
}
}
}
if (!ImplDecl || !ImplDecl->isCompleteDefinition()) {
S.Diag(Loc, diag::err_std_source_location_impl_not_found);
return nullptr;
}
// Verify that __impl is a trivial struct type, with no base classes, and with
// only the four expected fields.
if (ImplDecl->isUnion() || !ImplDecl->isStandardLayout() ||
ImplDecl->getNumBases() != 0) {
S.Diag(Loc, diag::err_std_source_location_impl_malformed);
return nullptr;
}
unsigned Count = 0;
for (FieldDecl *F : ImplDecl->fields()) {
StringRef Name = F->getName();
if (Name == "_M_file_name") {
if (F->getType() !=
S.Context.getPointerType(S.Context.CharTy.withConst()))
break;
Count++;
} else if (Name == "_M_function_name") {
if (F->getType() !=
S.Context.getPointerType(S.Context.CharTy.withConst()))
break;
Count++;
} else if (Name == "_M_line") {
if (!F->getType()->isIntegerType())
break;
Count++;
} else if (Name == "_M_column") {
if (!F->getType()->isIntegerType())
break;
Count++;
} else {
Count = 100; // invalid
break;
}
}
if (Count != 4) {
S.Diag(Loc, diag::err_std_source_location_impl_malformed);
return nullptr;
}
return ImplDecl;
}
ExprResult Sema::ActOnSourceLocExpr(SourceLocExpr::IdentKind Kind,
SourceLocation BuiltinLoc,
SourceLocation RPLoc) {
return BuildSourceLocExpr(Kind, BuiltinLoc, RPLoc, CurContext);
QualType ResultTy;
switch (Kind) {
case SourceLocExpr::File:
case SourceLocExpr::Function: {
QualType ArrTy = Context.getStringLiteralArrayType(Context.CharTy, 0);
ResultTy =
Context.getPointerType(ArrTy->getAsArrayTypeUnsafe()->getElementType());
break;
}
case SourceLocExpr::Line:
case SourceLocExpr::Column:
ResultTy = Context.UnsignedIntTy;
break;
case SourceLocExpr::SourceLocStruct:
if (!StdSourceLocationImplDecl) {
StdSourceLocationImplDecl =
LookupStdSourceLocationImpl(*this, BuiltinLoc);
if (!StdSourceLocationImplDecl)
return ExprError();
}
ResultTy = Context.getPointerType(
Context.getRecordType(StdSourceLocationImplDecl).withConst());
break;
}
return BuildSourceLocExpr(Kind, ResultTy, BuiltinLoc, RPLoc, CurContext);
}
ExprResult Sema::BuildSourceLocExpr(SourceLocExpr::IdentKind Kind,
QualType ResultTy,
SourceLocation BuiltinLoc,
SourceLocation RPLoc,
DeclContext *ParentContext) {
return new (Context)
SourceLocExpr(Context, Kind, BuiltinLoc, RPLoc, ParentContext);
SourceLocExpr(Context, Kind, ResultTy, BuiltinLoc, RPLoc, ParentContext);
}
bool Sema::CheckConversionToObjCLiteral(QualType DstType, Expr *&Exp,

View File

@ -7009,7 +7009,9 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
// -- a predefined __func__ variable
APValue::LValueBase Base = Value.getLValueBase();
auto *VD = const_cast<ValueDecl *>(Base.dyn_cast<const ValueDecl *>());
if (Base && (!VD || isa<LifetimeExtendedTemporaryDecl>(VD))) {
if (Base &&
(!VD ||
isa<LifetimeExtendedTemporaryDecl, UnnamedGlobalConstantDecl>(VD))) {
Diag(Arg->getBeginLoc(), diag::err_template_arg_not_decl_ref)
<< Arg->getSourceRange();
return ExprError();

View File

@ -867,6 +867,11 @@ Decl *TemplateDeclInstantiator::VisitMSGuidDecl(MSGuidDecl *D) {
llvm_unreachable("GUID declaration cannot be instantiated");
}
Decl *TemplateDeclInstantiator::VisitUnnamedGlobalConstantDecl(
UnnamedGlobalConstantDecl *D) {
llvm_unreachable("UnnamedGlobalConstantDecl cannot be instantiated");
}
Decl *TemplateDeclInstantiator::VisitTemplateParamObjectDecl(
TemplateParamObjectDecl *D) {
llvm_unreachable("template parameter objects cannot be instantiated");

View File

@ -3386,10 +3386,11 @@ public:
/// 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,
QualType ResultTy, SourceLocation BuiltinLoc,
SourceLocation RPLoc,
DeclContext *ParentContext) {
return getSema().BuildSourceLocExpr(Kind, BuiltinLoc, RPLoc, ParentContext);
return getSema().BuildSourceLocExpr(Kind, ResultTy, BuiltinLoc, RPLoc,
ParentContext);
}
/// Build a new Objective-C boxed expression.
@ -11653,8 +11654,8 @@ ExprResult TreeTransform<Derived>::TransformSourceLocExpr(SourceLocExpr *E) {
if (!getDerived().AlwaysRebuild() && !NeedRebuildFunc)
return E;
return getDerived().RebuildSourceLocExpr(E->getIdentKind(), E->getBeginLoc(),
E->getEndLoc(),
return getDerived().RebuildSourceLocExpr(E->getIdentKind(), E->getType(),
E->getBeginLoc(), E->getEndLoc(),
getSema().CurContext);
}

View File

@ -391,6 +391,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
case Decl::Field:
case Decl::MSProperty:
case Decl::MSGuid:
case Decl::UnnamedGlobalConstant:
case Decl::TemplateParamObject:
case Decl::ObjCIvar:
case Decl::ObjCAtDefsField:

View File

@ -370,6 +370,7 @@ namespace clang {
void VisitFieldDecl(FieldDecl *FD);
void VisitMSPropertyDecl(MSPropertyDecl *FD);
void VisitMSGuidDecl(MSGuidDecl *D);
void VisitUnnamedGlobalConstantDecl(UnnamedGlobalConstantDecl *D);
void VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D);
void VisitIndirectFieldDecl(IndirectFieldDecl *FD);
RedeclarableResult VisitVarDeclImpl(VarDecl *D);
@ -1428,6 +1429,17 @@ void ASTDeclReader::VisitMSGuidDecl(MSGuidDecl *D) {
Reader.getContext().setPrimaryMergedDecl(D, Existing->getCanonicalDecl());
}
void ASTDeclReader::VisitUnnamedGlobalConstantDecl(
UnnamedGlobalConstantDecl *D) {
VisitValueDecl(D);
D->Value = Record.readAPValue();
// Add this to the AST context's lookup structure, and merge if needed.
if (UnnamedGlobalConstantDecl *Existing =
Reader.getContext().UnnamedGlobalConstantDecls.GetOrInsertNode(D))
Reader.getContext().setPrimaryMergedDecl(D, Existing->getCanonicalDecl());
}
void ASTDeclReader::VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D) {
VisitValueDecl(D);
D->Value = Record.readAPValue();
@ -3709,6 +3721,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) {
case DECL_MS_GUID:
D = MSGuidDecl::CreateDeserialized(Context, ID);
break;
case DECL_UNNAMED_GLOBAL_CONSTANT:
D = UnnamedGlobalConstantDecl::CreateDeserialized(Context, ID);
break;
case DECL_TEMPLATE_PARAM_OBJECT:
D = TemplateParamObjectDecl::CreateDeserialized(Context, ID);
break;

View File

@ -96,6 +96,7 @@ namespace clang {
void VisitFieldDecl(FieldDecl *D);
void VisitMSPropertyDecl(MSPropertyDecl *D);
void VisitMSGuidDecl(MSGuidDecl *D);
void VisitUnnamedGlobalConstantDecl(UnnamedGlobalConstantDecl *D);
void VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D);
void VisitIndirectFieldDecl(IndirectFieldDecl *D);
void VisitVarDecl(VarDecl *D);
@ -966,6 +967,13 @@ void ASTDeclWriter::VisitMSGuidDecl(MSGuidDecl *D) {
Code = serialization::DECL_MS_GUID;
}
void ASTDeclWriter::VisitUnnamedGlobalConstantDecl(
UnnamedGlobalConstantDecl *D) {
VisitValueDecl(D);
Record.AddAPValue(D->getValue());
Code = serialization::DECL_UNNAMED_GLOBAL_CONSTANT;
}
void ASTDeclWriter::VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D) {
VisitValueDecl(D);
Record.AddAPValue(D->getValue());

View File

@ -10,71 +10,63 @@ void testRemap() {
#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;
namespace std {
class source_location {
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 {
static constexpr source_location current(const void *__p = __builtin_source_location()) noexcept {
source_location __loc;
__loc.set(__line, __col, __file, __func);
__loc.__m_impl = static_cast<const __impl *>(__p);
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;
static source_location bad_current(const void *__p = __builtin_source_location()) noexcept {
return current(__p);
}
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; }
};
constexpr unsigned int line() const noexcept { return __m_impl->_M_line; }
constexpr unsigned int column() const noexcept { return __m_impl->_M_column; }
constexpr const char *file() const noexcept { return __m_impl->_M_file_name; }
constexpr const char *function() const noexcept { return __m_impl->_M_function_name; }
using SL = source_location;
private:
// Note: The type name "std::source_location::__impl", and its constituent
// field-names are required by __builtin_source_location().
struct __impl {
const char *_M_file_name;
const char *_M_function_name;
unsigned _M_line;
unsigned _M_column;
};
const __impl *__m_impl = nullptr;
};
} // namespace std
using SL = std::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]]
// CHECK-GLOBAL-ONE-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 1000, i32 {{[0-9]+}} }, align 8
// CHECK-GLOBAL-ONE: @const_init_global ={{.*}} global %"class.std::source_location" { %"struct.std::source_location::__impl"* @[[IMPL]] }, align 8
#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: @runtime_init_global ={{.*}} global %"class.std::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-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 1100, i32 {{[0-9]+}} }, align 8
//
// 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(%struct.source_location) align 8 @runtime_init_global,
// CHECK-GLOBAL-TWO-SAME: i32 noundef 1100, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]],
// CHECK-GLOBAL-TWO: %call = call %"struct.std::source_location::__impl"* @_ZNSt15source_location11bad_currentEPKv({{.*}} @[[IMPL]]
// CHECK-GLOBAL-TWO: store %"struct.std::source_location::__impl"* %call, {{.*}} @runtime_init_global
#line 1100 "test_runtime_init.cpp"
SL runtime_init_global = SL::bad_current();
@ -83,11 +75,11 @@ 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-DAG: @[[FUNC:.*]] = {{.*}}c"void test_function()\00"
// CHECK-LOCAL-ONE-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 2100, i32 {{[0-9]+}} }, align 8
//
// CHECK-LOCAL-ONE: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 %local,
// CHECK-LOCAL-ONE-SAME: i32 noundef 2100, i32 noundef {{[0-9]+}},
// CHECK-LOCAL-ONE-SAME: {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]],
// CHECK-LOCAL-ONE: define {{.*}} @test_function
// CHECK-LOCAL-ONE: call %"struct.std::source_location::__impl"* @_ZNSt15source_location7currentEPKv({{.*}} @[[IMPL]]
#line 2100 "test_current.cpp"
SL local = SL::current();
}
@ -106,13 +98,14 @@ struct TestInit {
// 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-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 3400, i32 {{[0-9]+}} }, align 8
//
// 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(%struct.source_location) align 8 %[[TMP_ONE:[^,]*]],
// CHECK-CTOR-GLOBAL-SAME: i32 noundef 3400, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]],
// CHECK-CTOR-GLOBAL-NEXT: call void @_ZN8TestInitC1E15source_location(%struct.TestInit* {{[^,]*}} @GlobalInitVal, %struct.source_location* {{.*}}%[[TMP_ONE]])
// CHECK-CTOR-GLOBAL: call %"struct.std::source_location::__impl"* @_ZNSt15source_location7currentEPKv({{.*}} @[[IMPL]]
// CHECK-CTOR-GLOBAL-NOT: ret
// CHECK-CTOR-GLOBAL: call void @_ZN8TestInitC1ESt15source_location(%struct.TestInit* {{[^,]*}} @GlobalInitVal, %"struct.std::source_location::__impl"*
#line 3400 "GlobalInitVal.cpp"
TestInit GlobalInitVal;
@ -120,14 +113,16 @@ 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-DAG: @[[FUNC:.*]] = {{.*}}c"void test_init_function()\00"
// CHECK-CTOR-LOCAL-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 3500, i32 {{[0-9]+}} }, align 8
//
// CHECK-CTOR-LOCAL: define{{.*}} void @test_init_function()
// CHECK-CTOR-LOCAL-NOT: ret
//
// CHECK-CTOR-LOCAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 %[[TMP:[^,]*]],
// CHECK-CTOR-LOCAL-SAME: i32 noundef 3500, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]],
// CHECK-CTOR-LOCAL-NEXT: call void @_ZN8TestInitC1E15source_location(%struct.TestInit* {{[^,]*}} %init_local, %struct.source_location* {{.*}}%[[TMP]])
// CHECK-CTOR-LOCAL: call %"struct.std::source_location::__impl"* @_ZNSt15source_location7currentEPKv({{.*}} @[[IMPL]]
// CHECK-CTOR-LOCAL-NOT: ret
// CHECK-CTOR-LOCAL: call void @_ZN8TestInitC1ESt15source_location(%struct.TestInit* {{[^,]*}} %init_local, %"struct.std::source_location::__impl"*
#line 3500 "LocalInitVal.cpp"
TestInit init_local;
sink(init_local);
@ -144,26 +139,30 @@ struct TestInitConstexpr {
// 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: @[[FUNC_INIT:.*]] = {{.*}}c"TestInitConstexpr::TestInitConstexpr(SL)\00"
// CHECK-CONSTEXPR-T2-DAG: @[[IMPL_INIT:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE_INIT]], {{[^@]*}}@[[FUNC_INIT]], {{.*}} i32 4200, i32 {{[0-9]+}} }, align 8
// 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-DAG: @[[IMPL_ARG:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE_ARG]], {{[^@]*}}@[[EMPTY]], {{.*}} i32 4400, i32 {{[0-9]+}} }, align 8
//
// 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]]
// CHECK-CONSTEXPR-T2-SAME: %"class.std::source_location" { %"struct.std::source_location::__impl"* @[[IMPL_INIT]] },
// CHECK-CONSTEXPR-T2-SAME: %"class.std::source_location" { %"struct.std::source_location::__impl"* @[[IMPL_ARG]] }
#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: @[[FUNC:.*]] = {{.*}}c"void test_init_function_constexpr()\00"
// CHECK-CONSTEXPR-LOCAL-DAG: @[[FILE:.*]] = {{.*}}c"ConstexprLocal.cpp\00"
// CHECK-CONSTEXPR-LOCAL-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 4600, i32 {{[0-9]+}} }, align 8
//
// CHECK-CONSTEXPR-LOCAL: define{{.*}} void @test_init_function_constexpr()
// CHECK-CONSTEXPR-LOCAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 %[[TMP:[^,]*]],
// CHECK-CONSTEXPR-LOCAL-SAME: i32 noundef 4600, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]]
// CHECK-CONSTEXPR-LOCAL: call void @_ZN17TestInitConstexprC1E15source_location(%struct.TestInitConstexpr* {{[^,]*}} %local_val, {{.*}}%[[TMP]])
// CHECK-CONSTEXPR-LOCAL-NOT: ret
// CHECK-CONSTEXPR-LOCAL: call %"struct.std::source_location::__impl"* @_ZNSt15source_location7currentEPKv({{.*}} @[[IMPL]]
// CHECK-CONSTEXPR-LOCAL-NOT: ret
// CHECK-CONSTEXPR-LOCAL: call void @_ZN17TestInitConstexprC1ESt15source_location(%struct.TestInitConstexpr* {{[^,]*}} %local_val, %"struct.std::source_location::__impl"*
#line 4600 "ConstexprLocal.cpp"
TestInitConstexpr local_val;
}
@ -180,60 +179,44 @@ struct TestInitAgg {
// 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-DAG: @[[FUNC:.*]] = {{.*}}c"TestInitAgg::TestInitAgg()\00"
// CHECK-AGG-DEFAULT-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 5000, i32 {{[0-9]+}} }, align 8
//
// 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]]
// CHECK-AGG-DEFAULT-SAME: %"class.std::source_location" zeroinitializer,
// CHECK-AGG-DEFAULT-SAME: %"class.std::source_location" { %"struct.std::source_location::__impl"* @[[IMPL]] }
#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(%struct.source_location) align 8 %[[I2]],
// CHECK-AGG-BRACE-SAME: i32 noundef 5700, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]]
// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-AGG-INIT
// CHECK-AGG-INIT-DAG: @[[FUNC:.*]] = {{.*}}c"void test_agg_init()\00"
// CHECK-AGG-INIT-DAG: @[[FILE:.*]] = {{.*}}c"BraceInitEnd.cpp\00"
// CHECK-AGG-INIT-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 5700, i32 {{[0-9]+}} }, align 8
#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(%struct.source_location) align 8 %[[I2]],
// CHECK-AGG-EQUAL-SAME: i32 noundef 5900, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]]
// CHECK-AGG-INIT-DAG: @[[FILE:.*]] = {{.*}}c"EqualInitEnd.cpp\00"
// CHECK-AGG-INIT-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 5900, i32 {{[0-9]+}} }, align 8
#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(%struct.source_location) align 8 %[[I1]],
// CHECK-AGG-LIST-SAME: i32 noundef 6100, i32 noundef {{[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(%struct.source_location) align 8 %[[I2]],
// CHECK-AGG-LIST-SAME: i32 noundef 6200, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE_DEFAULT]], {{[^@]*}}@[[FUNC]]
// CHECK-AGG-INIT-DAG: @[[FILE_DEFAULT:.*]] = {{.*}}c"InitListEnd.cpp\00"
// CHECK-AGG-INIT-DAG: @[[FILE_ELEM:.*]] = {{.*}}c"ListElem.cpp\00"
// CHECK-AGG-INIT-DAG: @[[IMPL_DEFAULT:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE_ELEM]], {{[^@]*}}@[[FUNC]], {{.*}} i32 6100, i32 {{[0-9]+}} }, align 8
// CHECK-AGG-INIT-DAG: @[[IMPL_ELEM:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE_DEFAULT]], {{[^@]*}}@[[FUNC]], {{.*}} i32 6200, i32 {{[0-9]+}} }, align 8
#line 6000 "InitListStart.cpp"
TestInitAgg local_list_init =
{
@ -260,14 +243,8 @@ void test_template() {
// 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(%struct.source_location) align 8 %[[TMP:[^,]*]],
// CHECK-TEMPL-SAME: i32 noundef 7300, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]]
// CHECK-TEMPL-DAG: @[[FUNC:.*]] = {{.*}}c"void test_template() [T = std::source_location, V = [[INT_ID]]]\00"
// CHECK-TEMPL-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 7300, i32 {{[0-9]+}} }, align 8
#line 7300 "local_templ.cpp"
TestTemplate<T, V> local_templ;
}

View File

@ -9,37 +9,39 @@ 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;
class source_location {
struct __impl;
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 {
static constexpr source_location current(const __impl *__p = __builtin_source_location()) noexcept {
source_location __loc;
__loc.__m_line = __line;
__loc.__m_col = __col;
__loc.__m_file = __file;
__loc.__m_func = __func;
__loc.__m_impl = __p;
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; }
constexpr unsigned int line() const noexcept { return __m_impl ? __m_impl->_M_line : 0; }
constexpr unsigned int column() const noexcept { return __m_impl ? __m_impl->_M_column : 0; }
constexpr const char *file() const noexcept { return __m_impl ? __m_impl->_M_file_name : ""; }
constexpr const char *function() const noexcept { return __m_impl ? __m_impl->_M_function_name : ""; }
private:
// Note: The type name "std::source_location::__impl", and its constituent
// field-names are required by __builtin_source_location().
struct __impl {
const char *_M_file_name;
const char *_M_function_name;
unsigned _M_line;
unsigned _M_column;
};
const __impl *__m_impl = nullptr;
public:
using public_impl_alias = __impl;
};
} // namespace experimental
} // namespace std
using SL = std::experimental::source_location;
using SL = std::source_location;
#include "Inputs/source-location-file.h"
namespace SLF = source_location_file;
@ -75,12 +77,14 @@ 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 *>);
static_assert(is_same<decltype(__builtin_source_location()), const std::source_location::public_impl_alias *>);
// test noexcept
static_assert(noexcept(__builtin_LINE()));
static_assert(noexcept(__builtin_COLUMN()));
static_assert(noexcept(__builtin_FILE()));
static_assert(noexcept(__builtin_FUNCTION()));
static_assert(noexcept(__builtin_source_location()));
//===----------------------------------------------------------------------===//
// __builtin_LINE()
@ -354,7 +358,7 @@ 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()));
static_assert(is_equal(__PRETTY_FUNCTION__, U::current().function()));
return {u, U::current()};
}
template <class T>
@ -376,10 +380,11 @@ struct TestCtor {
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__));
static const char *XYZZY = Template.info.function();
static_assert(is_equal(Default.info.function(), "test_func::TestCtor<>::TestCtor() [T = std::source_location]"));
static_assert(is_equal(Default.ctor_info.function(), ""));
static_assert(is_equal(Template.info.function(), "test_func::TestCtor<>::TestCtor(int, U) [T = std::source_location, U = std::source_location]"));
static_assert(is_equal(Template.ctor_info.function(), __PRETTY_FUNCTION__));
}
constexpr SL global_sl = SL::current();
@ -521,7 +526,7 @@ constexpr bool test_in_func() {
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"));
static_assert(is_equal(b.a.info.function(), "bool test_out_of_line_init::test_in_func()"));
return true;
}
static_assert(test_in_func());

View File

@ -0,0 +1,105 @@
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify -DTEST=1 %s
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify -DTEST=2 %s
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify -DTEST=3 %s
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify -DTEST=4 %s
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify -DTEST=5 %s
#if TEST == 1
auto test1a = __builtin_source_location(); // expected-error {{'std::source_location::__impl' was not found}}
namespace std {
inline namespace NS {
struct source_location;
}
}
auto test1b = __builtin_source_location(); // expected-error {{'std::source_location::__impl' was not found}}
namespace std {
inline namespace NS {
struct source_location {
struct __impl;
};
}
}
auto test1c = __builtin_source_location(); // expected-error {{'std::source_location::__impl' was not found}}
#elif TEST == 2
auto test2a = __builtin_source_location(); // expected-error {{'std::source_location::__impl' was not found}}
namespace std {
inline namespace NS {
struct source_location {
struct __impl { int x; };
};
}
}
auto test2b = __builtin_source_location(); // expected-error {{'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'}}
#elif TEST == 3
namespace std {
struct source_location {
struct __impl {
int other_member;
char _M_line;
const char *_M_file_name;
char _M_column;
const char *_M_function_name;
};
};
}
auto test3 = __builtin_source_location(); // expected-error {{'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'}}
#elif TEST == 4
namespace std {
struct source_location {
struct parent {};
struct __impl : public parent {
char _M_line;
const char *_M_file_name;
char _M_column;
const char *_M_function_name;
};
};
}
auto test4 = __builtin_source_location(); // expected-error {{'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'}}
#elif TEST == 5
namespace std {
struct source_location {
struct __impl {
signed char _M_line; // odd integral type to choose, but ok!
const char *_M_file_name;
signed char _M_column;
const char *_M_function_name;
static int other_member; // static members are OK
};
using BuiltinT = decltype(__builtin_source_location()); // OK.
};
}
// Verify that the address cannot be used as a non-type template argument.
template <auto X = __builtin_source_location()>
auto fn1() {return X;} // expected-note {{candidate template ignored: substitution failure: non-type template argument does not refer to any declaration}}
auto test5a = fn1<>(); // expected-error {{no matching function for call to 'fn1'}}
// (But using integer subobjects by value is okay.)
template <auto X = __builtin_source_location()->_M_column>
auto fn2() {return X;}
auto test5b = fn2<>();
// While it's not semantically required, for efficiency, we ensure that two
// source-locations with the same content will point to the same object. Given
// the odd definition of the struct used here (using 'signed char'), any
// line-number modulo 256 will thus have the same content, and be deduplicated.
#line 128
constexpr auto sl1 = __builtin_source_location();
#line 384
constexpr auto sl2 = __builtin_source_location();
constexpr auto sl3 = __builtin_source_location();
static_assert(sl1 == sl2);
static_assert(sl1 != sl3);
static_assert(sl1->_M_line == -128);
#endif

View File

@ -6478,6 +6478,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
case Decl::Binding:
case Decl::MSProperty:
case Decl::MSGuid:
case Decl::UnnamedGlobalConstant:
case Decl::TemplateParamObject:
case Decl::IndirectField:
case Decl::ObjCIvar: