Index: clang/lib/AST/Interp/ByteCodeExprGen.h =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.h +++ clang/lib/AST/Interp/ByteCodeExprGen.h @@ -35,6 +35,7 @@ template class DeclScope; template class OptionScope; template class ArrayIndexScope; +template class SourceLocScope; /// Compilation context for expressions. template @@ -100,6 +101,7 @@ bool VisitPredefinedExpr(const PredefinedExpr *E); bool VisitCXXThrowExpr(const CXXThrowExpr *E); bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E); + bool VisitSourceLocExpr(const SourceLocExpr *E); protected: bool visitExpr(const Expr *E) override; @@ -148,6 +150,8 @@ bool visitRecordInitializer(const Expr *Initializer); /// Creates and initializes a variable from the given decl. bool visitVarDecl(const VarDecl *VD); + /// Visit an APValue. + bool visitAPValue(const APValue &Val, PrimType ValType, const Expr *E); /// Visits an expression and converts it to a boolean. bool visitBool(const Expr *E); @@ -207,6 +211,7 @@ friend class DeclScope; friend class OptionScope; friend class ArrayIndexScope; + friend class SourceLocScope; /// Emits a zero initializer. bool visitZeroInitializer(QualType QT, const Expr *E); @@ -235,12 +240,14 @@ llvm::function_ref Indirect); /// Emits an APSInt constant. + bool emitConst(const llvm::APSInt &Value, PrimType Ty, const Expr *E); bool emitConst(const llvm::APSInt &Value, const Expr *E); bool emitConst(const llvm::APInt &Value, const Expr *E) { return emitConst(static_cast(Value), E); } /// Emits an integer constant. + template bool emitConst(T Value, PrimType Ty, const Expr *E); template bool emitConst(T Value, const Expr *E); /// Returns the CXXRecordDecl for the type of the given expression, @@ -281,6 +288,9 @@ /// Current argument index. Needed to emit ArrayInitIndexExpr. std::optional ArrayIndex; + /// DefaultInit- or DefaultArgExpr, needed for SourceLocExpr. + const Expr *SourceLocDefaultExpr = nullptr; + /// Flag indicating if return value is to be discarded. bool DiscardResult = false; }; @@ -436,6 +446,28 @@ std::optional OldArrayIndex; }; +template class SourceLocScope final { +public: + SourceLocScope(ByteCodeExprGen *Ctx, const Expr *DefaultExpr) + : Ctx(Ctx) { + assert(DefaultExpr); + // We only switch if the current SourceLocDefaultExpr is null. + if (!Ctx->SourceLocDefaultExpr) { + Enabled = true; + Ctx->SourceLocDefaultExpr = DefaultExpr; + } + } + + ~SourceLocScope() { + if (Enabled) + Ctx->SourceLocDefaultExpr = nullptr; + } + +private: + ByteCodeExprGen *Ctx; + bool Enabled = false; +}; + } // namespace interp } // namespace clang Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1138,6 +1138,62 @@ return this->emitInvalidCast(CastKind::Reinterpret, E); } +template +bool ByteCodeExprGen::VisitSourceLocExpr(const SourceLocExpr *E) { + if (DiscardResult) + return true; + + const APValue Val = + E->EvaluateInContext(Ctx.getASTContext(), SourceLocDefaultExpr); + + // Things like __builtin_LINE(). + if (E->getType()->isIntegerType()) { + assert(Val.isInt()); + const APSInt &I = Val.getInt(); + return this->emitConst(I, E); + } + // Otherwise, the APValue is an LValue, with only one element. + // Theoretically, we don't need the APValue at all of course. + assert(E->getType()->isPointerType()); + assert(Val.isLValue()); + const APValue::LValueBase &Base = Val.getLValueBase(); + if (const Expr *LValueExpr = Base.dyn_cast()) + return this->visit(LValueExpr); + + // Otherwise, we have a decl (which is the case for + // __builtin_source_location). + assert(Base.is()); + assert(Val.getLValuePath().size() == 0); + const auto *BaseDecl = Base.dyn_cast(); + assert(BaseDecl); + + auto *UGCD = cast(BaseDecl); + + std::optional GlobalIndex = P.getOrCreateGlobal(UGCD); + if (!GlobalIndex) + return false; + + if (!this->emitGetPtrGlobal(*GlobalIndex, E)) + return false; + + const Record *R = getRecord(E->getType()); + const APValue &V = UGCD->getValue(); + for (unsigned I = 0, N = R->getNumFields(); I != N; ++I) { + const Record::Field *F = R->getField(I); + const APValue &FieldValue = V.getStructField(I); + + PrimType FieldT = classifyPrim(F->Decl->getType()); + + if (!this->visitAPValue(FieldValue, FieldT, E)) + return false; + if (!this->emitInitField(FieldT, F->Offset, E)) + return false; + } + + // Leave the pointer to the global on the stack. + return true; +} + template bool ByteCodeExprGen::discard(const Expr *E) { if (E->containsErrors()) return false; @@ -1384,8 +1440,8 @@ template template -bool ByteCodeExprGen::emitConst(T Value, const Expr *E) { - switch (classifyPrim(E->getType())) { +bool ByteCodeExprGen::emitConst(T Value, PrimType Ty, const Expr *E) { + switch (Ty) { case PT_Sint8: return this->emitConstSint8(Value, E); case PT_Uint8: @@ -1414,10 +1470,22 @@ } template -bool ByteCodeExprGen::emitConst(const APSInt &Value, const Expr *E) { +template +bool ByteCodeExprGen::emitConst(T Value, const Expr *E) { + return this->emitConst(Value, classifyPrim(E->getType()), E); +} + +template +bool ByteCodeExprGen::emitConst(const APSInt &Value, PrimType Ty, + const Expr *E) { if (Value.isSigned()) - return this->emitConst(Value.getSExtValue(), E); - return this->emitConst(Value.getZExtValue(), E); + return this->emitConst(Value.getSExtValue(), Ty, E); + return this->emitConst(Value.getZExtValue(), Ty, E); +} + +template +bool ByteCodeExprGen::emitConst(const APSInt &Value, const Expr *E) { + return this->emitConst(Value, classifyPrim(E->getType()), E); } template @@ -1722,6 +1790,7 @@ return this->visit(CE); } else if (const auto *DIE = dyn_cast(Initializer)) { + SourceLocScope SLS(this, DIE); return this->visitInitializer(DIE->getExpr()); } else if (const auto *BBCE = dyn_cast(Initializer)) { return this->visit(BBCE); @@ -1901,6 +1970,22 @@ return false; } +template +bool ByteCodeExprGen::visitAPValue(const APValue &Val, + PrimType ValType, const Expr *E) { + assert(!DiscardResult); + if (Val.isInt()) + return this->emitConst(Val.getInt(), ValType, E); + + if (Val.isLValue()) { + APValue::LValueBase Base = Val.getLValueBase(); + if (const Expr *BaseExpr = Base.dyn_cast()) + return this->visit(BaseExpr); + } + + return false; +} + template bool ByteCodeExprGen::VisitBuiltinCallExpr(const CallExpr *E) { const Function *Func = getFunction(E->getDirectCallee()); @@ -2014,14 +2099,16 @@ bool ByteCodeExprGen::VisitCXXDefaultInitExpr( const CXXDefaultInitExpr *E) { assert(classify(E->getType())); + SourceLocScope SLS(this, E); return this->visit(E->getExpr()); } template bool ByteCodeExprGen::VisitCXXDefaultArgExpr( const CXXDefaultArgExpr *E) { - const Expr *SubExpr = E->getExpr(); + SourceLocScope SLS(this, E); + const Expr *SubExpr = E->getExpr(); if (std::optional T = classify(E->getExpr())) return this->visit(SubExpr); Index: clang/lib/AST/Interp/Program.cpp =================================================================== --- clang/lib/AST/Interp/Program.cpp +++ clang/lib/AST/Interp/Program.cpp @@ -161,9 +161,12 @@ const Expr *Init) { assert(!getGlobal(VD)); bool IsStatic, IsExtern; - if (auto *Var = dyn_cast(VD)) { + if (const auto *Var = dyn_cast(VD)) { IsStatic = Context::shouldBeGloballyIndexed(VD); IsExtern = !Var->getAnyInitializer(); + } else if (isa(VD)) { + IsStatic = true; + IsExtern = false; } else { IsStatic = false; IsExtern = true; Index: clang/test/AST/Interp/builtin-functions.cpp =================================================================== --- clang/test/AST/Interp/builtin-functions.cpp +++ clang/test/AST/Interp/builtin-functions.cpp @@ -133,3 +133,31 @@ _Complex double CD = __arithmetic_fence(CD); #endif } + +namespace std { +struct source_location { + struct __impl { + unsigned int _M_line; + const char *_M_file_name; + signed char _M_column; + const char *_M_function_name; + }; + using BuiltinT = decltype(__builtin_source_location()); // OK. +}; +} + +namespace SourceLocation { + constexpr auto A = __builtin_source_location(); + static_assert(A->_M_line == 150, ""); + static_assert(A->_M_column == 22, ""); + static_assert(__builtin_strcmp(A->_M_function_name, "") == 0, ""); + static_assert(__builtin_strcmp(A->_M_file_name, __FILE__) == 0, ""); + + static_assert(__builtin_LINE() == 156, ""); + + struct Foo { + int a = __builtin_LINE(); + }; + + static_assert(Foo{}.a == 162, ""); +}