diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 244290dc6393f..043a3eb6346e0 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1383,6 +1383,62 @@ bool ByteCodeExprGen::VisitCXXConstructExpr( return false; } +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; @@ -1694,8 +1750,8 @@ bool ByteCodeExprGen::dereferenceVar( 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: @@ -1724,10 +1780,22 @@ bool ByteCodeExprGen::emitConst(T Value, const Expr *E) { } 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 @@ -1923,6 +1991,22 @@ bool ByteCodeExprGen::visitVarDecl(const VarDecl *VD) { 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()); @@ -2054,14 +2138,16 @@ bool ByteCodeExprGen::VisitCXXDefaultInitExpr( return this->visitInitializer(E->getExpr()); 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); diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index 9b7593ce54f9e..8ff8bc568b687 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -35,6 +35,7 @@ template class VariableScope; template class DeclScope; template class OptionScope; template class ArrayIndexScope; +template class SourceLocScope; /// Compilation context for expressions. template @@ -102,6 +103,7 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E); bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E); + bool VisitSourceLocExpr(const SourceLocExpr *E); protected: bool visitExpr(const Expr *E) override; @@ -154,6 +156,8 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, /// 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); @@ -210,6 +214,7 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, friend class DeclScope; friend class OptionScope; friend class ArrayIndexScope; + friend class SourceLocScope; /// Emits a zero initializer. bool visitZeroInitializer(QualType QT, const Expr *E); @@ -239,12 +244,14 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, 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, @@ -285,6 +292,9 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, /// 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; @@ -444,6 +454,28 @@ template class ArrayIndexScope final { 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 diff --git a/clang/lib/AST/Interp/Program.cpp b/clang/lib/AST/Interp/Program.cpp index c1697bb7fa6d6..63901d90703dc 100644 --- a/clang/lib/AST/Interp/Program.cpp +++ b/clang/lib/AST/Interp/Program.cpp @@ -161,9 +161,12 @@ std::optional Program::createGlobal(const ValueDecl *VD, 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; diff --git a/clang/test/AST/Interp/builtin-functions.cpp b/clang/test/AST/Interp/builtin-functions.cpp index 55ebab1122d58..52ecee536938f 100644 --- a/clang/test/AST/Interp/builtin-functions.cpp +++ b/clang/test/AST/Interp/builtin-functions.cpp @@ -221,3 +221,31 @@ namespace fpclassify { namespace fabs { static_assert(__builtin_fabs(-14.0) == 14.0, ""); } + +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 == __LINE__ -1, ""); + 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() == __LINE__, ""); + + struct Foo { + int a = __builtin_LINE(); + }; + + static_assert(Foo{}.a == __LINE__, ""); +}