Index: docs/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -2067,6 +2067,61 @@ 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 appears as part of a default function argument the invocation +point is the location of the caller. When the builtins appear as part of a +NSDMI 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 ============================= Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -3800,6 +3800,79 @@ } }; +/// BuiltinSourceLocExpr - Represents a function call to one of +/// __builtin_LINE(), __builtin_COLUMN(), __builtin_FUNCTION(), or +/// __BUILTIN_FILE() +class SourceLocExpr final : public Expr { +public: + enum IdentType { Function, File, Line, Column }; + +private: + SourceLocation BuiltinLoc, RParenLoc; + Stmt *Value; + unsigned Type : 2; + unsigned IsInDefaultArgOrInit : 1; + static const char *GetBuiltinStr(IdentType T); + +public: + SourceLocExpr(IdentType Type, SourceLocation BLoc, SourceLocation RParenLoc, + QualType Ty); + SourceLocExpr(IdentType Type, SourceLocation BLoc, SourceLocation RParenLoc, + Expr *E, bool IsInDefaultArgOrInit); + + /// \brief Build an empty call expression. + explicit SourceLocExpr(EmptyShell Empty) + : Expr(SourceLocExprClass, Empty), Value(nullptr) {} + + /// \brief Check if an expression contains an unresolved SourceLocExpr. + static bool containsSourceLocExpr(const Expr *E); + + const char *getBuiltinStr() const LLVM_READONLY { + return GetBuiltinStr(static_cast(Type)); + } + IdentType getIdentType() const LLVM_READONLY { + return static_cast(Type); + } + SourceLocation getLocation() const LLVM_READONLY { return BuiltinLoc; } + + SourceLocation getLocStart() const LLVM_READONLY { return BuiltinLoc; } + SourceLocation getLocEnd() const LLVM_READONLY { return RParenLoc; } + + const Expr *getSubExpr() const { return cast_or_null(Value); } + Expr *getSubExpr() { return cast_or_null(Value); } + + bool isUnresolved() const { return Value == nullptr; } + + bool isInDefaultArgOrInit() const { return IsInDefaultArgOrInit; } + + child_range children() { + return Value ? child_range(&Value, &Value + 1) + : child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return Value ? const_child_range(&Value, &Value + 1) + : const_child_range(child_iterator(), child_iterator()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == SourceLocExprClass; + } + +private: + friend class ASTStmtReader; + + void setIdentType(IdentType T) { Type = T; } + + void setIsInDefaultArgOrInit(bool V = true) { IsInDefaultArgOrInit = V; } + void setSubExpr(Expr *E) { + Value = E; + } + + void setLocStart(SourceLocation L) { BuiltinLoc = L; } + void setLocEnd(SourceLocation L) { RParenLoc = L; } +}; + /// @brief Describes an C or C++ initializer list. /// /// InitListExpr describes an initializer list, which can be used to Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2472,6 +2472,8 @@ 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()) { Index: include/clang/Basic/StmtNodes.td =================================================================== --- include/clang/Basic/StmtNodes.td +++ include/clang/Basic/StmtNodes.td @@ -91,6 +91,7 @@ def VAArgExpr : DStmt; def GenericSelectionExpr : DStmt; def PseudoObjectExpr : DStmt; +def SourceLocExpr : DStmt; // Atomic expressions def AtomicExpr : DStmt; Index: include/clang/Basic/TokenKinds.def =================================================================== --- include/clang/Basic/TokenKinds.def +++ include/clang/Basic/TokenKinds.def @@ -384,6 +384,11 @@ 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) Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2336,6 +2336,10 @@ /// in a 'block', this returns the containing context. NamedDecl *getCurFunctionOrMethodDecl(); + /// getDeclForCurContext - Return the Decl for the current block, lambda, + /// captured statement, function, or translation unit. + Decl *getDeclForCurContext(); + /// Add this decl to the scope shadowed decl chains. void PushOnScopeChains(NamedDecl *D, Scope *S, bool AddToContext = true); @@ -4392,6 +4396,34 @@ ExprResult BuildVAArgExpr(SourceLocation BuiltinLoc, Expr *E, TypeSourceInfo *TInfo, SourceLocation RPLoc); + /// __builtin_LINE(), __builtin_FUNCTION(), __builtin_FILE() + ExprResult ActOnSourceLocExpr(Scope *S, SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc); + ExprResult BuildUnresolvedSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc); + ExprResult BuildSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, SourceLocation RPLoc, + SourceLocation InvocationLoc, Decl *CurContext, + bool IsInDefaultArgOrInit); + + /// \brief Build an expression representing the value of the builtin call + /// + /// \param Type The type of the source location expression to build + /// (line, file, function) + /// + /// \param AtLoc The location at which the builtin should be evaluated. + /// + /// \param CurContext The context in which the builtin should be evaluated. + ExprResult BuildSourceLocValue(SourceLocExpr::IdentType Type, + SourceLocation AtLoc, Decl *CurContext); + + /// \brief Transform an expression containing unresolved SourceLocExpr's while + /// resolving them to the specified `Loc` + ExprResult TransformInitWithUnresolvedSourceLocExpr(Expr *Init, + SourceLocation Loc); + // __null ExprResult ActOnGNUNullExpr(SourceLocation TokenLoc); Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -1341,6 +1341,8 @@ EXPR_CHOOSE, /// \brief A GNUNullExpr record. EXPR_GNU_NULL, + /// \brief A SourceLocExpr record + EXPR_SOURCE_LOC, /// \brief A ShuffleVectorExpr record. EXPR_SHUFFLE_VECTOR, /// \brief A ConvertVectorExpr record. Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1809,6 +1809,62 @@ return OverOps[Opc]; } +SourceLocExpr::SourceLocExpr(IdentType Type, SourceLocation BLoc, + SourceLocation RParenLoc, QualType Ty) + : Expr(SourceLocExprClass, Ty, VK_RValue, OK_Ordinary, + Ty->isDependentType(), Ty->isDependentType(), Ty->isDependentType(), + false), + BuiltinLoc(BLoc), RParenLoc(RParenLoc), Value(nullptr), Type(Type), + IsInDefaultArgOrInit(true) {} + +SourceLocExpr::SourceLocExpr(IdentType Type, SourceLocation BLoc, + SourceLocation RParenLoc, Expr *E, + bool IsInDefaultArgOrInit) + : Expr(SourceLocExprClass, E->getType(), E->getValueKind(), + E->getObjectKind(), false, false, false, false), + BuiltinLoc(BLoc), RParenLoc(RParenLoc), Value(E), Type(Type), + IsInDefaultArgOrInit(IsInDefaultArgOrInit) { + assert(!E->getType()->isDependentType() && !E->isTypeDependent() && + !E->isValueDependent() && !E->isInstantiationDependent()); +} + + +const char *SourceLocExpr::GetBuiltinStr(IdentType Type) { + switch (Type) { + case File: + return "__builtin_FILE"; + case Function: + return "__builtin_FUNCTION"; + case Line: + return "__builtin_LINE"; + case Column: + return "__builtin_COLUMN"; + } +} + +namespace { +class CheckForSourceLocVisitor + : public ConstStmtVisitor { + +public: + CheckForSourceLocVisitor() {} + bool VisitExpr(const Expr *Node) { + bool HasSourceLocExpr = false; + for (const Stmt *SubStmt : Node->children()) + HasSourceLocExpr |= Visit(SubStmt); + return HasSourceLocExpr; + } + bool VisitSourceLocExpr(const SourceLocExpr *SLE) { + return SLE->isUnresolved() || SLE->isInDefaultArgOrInit(); + } +}; +} // namespace + +bool SourceLocExpr::containsSourceLocExpr(const Expr *E) { + assert(E); + return CheckForSourceLocVisitor{}.Visit(E); +} + InitListExpr::InitListExpr(const ASTContext &C, SourceLocation lbraceloc, ArrayRef initExprs, SourceLocation rbraceloc) : Expr(InitListExprClass, QualType(), VK_RValue, OK_Ordinary, false, false, @@ -2934,6 +2990,7 @@ case ObjCAvailabilityCheckExprClass: case CXXUuidofExprClass: case OpaqueValueExprClass: + case SourceLocExprClass: // These never have a side-effect. return false; Index: lib/AST/ExprClassification.cpp =================================================================== --- lib/AST/ExprClassification.cpp +++ lib/AST/ExprClassification.cpp @@ -300,6 +300,12 @@ case Expr::UserDefinedLiteralClass: case Expr::CUDAKernelCallExprClass: return ClassifyUnnamed(Ctx, cast(E)->getCallReturnType(Ctx)); + case Expr::SourceLocExprClass: { + auto *SLE = cast(E); + if (!SLE->isUnresolved()) + return ClassifyInternal(Ctx, SLE->getSubExpr()); + return Cl::CL_PRValue; + } // __builtin_choose_expr is equivalent to the chosen expression. case Expr::ChooseExprClass: Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -4511,6 +4511,11 @@ return Error(E); return StmtVisitorTy::Visit(E->getExpr()); } + bool VisitSourceLocExpr(const SourceLocExpr *E) { + if (!E->getSubExpr()) + return Error(E); + return StmtVisitorTy::Visit(E->getSubExpr()); + } // 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) @@ -5919,7 +5924,6 @@ // Not found: return nullptr. return ZeroInitialization(E); } - default: return visitNonBuiltinCallExpr(E); } @@ -6960,6 +6964,11 @@ bool VisitBinaryOperator(const BinaryOperator *E); bool VisitOffsetOfExpr(const OffsetOfExpr *E); bool VisitUnaryOperator(const UnaryOperator *E); + bool VisitSourceLocExpr(const SourceLocExpr *E) { + if (!E->getSubExpr()) + return Error(E); + return Visit(E->getSubExpr()); + } bool VisitCastExpr(const CastExpr* E); bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E); @@ -10280,6 +10289,11 @@ // GCC considers the GNU __null value to be an integral constant expression. return NoDiag(); + case Expr::SourceLocExprClass:{ // FIXME make this constexpr + auto *SLE = cast(E); + assert(SLE && SLE->getSubExpr()); + return CheckEvalInICE(SLE->getSubExpr(), Ctx); + } case Expr::SubstNonTypeTemplateParmExprClass: return CheckICE(cast(E)->getReplacement(), Ctx); @@ -10552,10 +10566,9 @@ return CheckICE(cast(E)->getExpr(), Ctx); case Expr::CXXDefaultInitExprClass: return CheckICE(cast(E)->getExpr(), Ctx); - case Expr::ChooseExprClass: { + case Expr::ChooseExprClass: return CheckICE(cast(E)->getChosenSubExpr(), Ctx); } - } llvm_unreachable("Invalid StmtClass!"); } Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -3373,7 +3373,7 @@ case Expr::AsTypeExprClass: case Expr::PseudoObjectExprClass: case Expr::AtomicExprClass: - { + case Expr::SourceLocExprClass: { if (!NullOut) { // As bad as this diagnostic is, it's better than crashing. DiagnosticsEngine &Diags = Context.getDiags(); Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -1305,6 +1305,10 @@ // Expr printing methods. //===----------------------------------------------------------------------===// +void StmtPrinter::VisitSourceLocExpr(SourceLocExpr *Node) { + OS << Node->getBuiltinStr() << "()"; +} + void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) { if (auto *OCED = dyn_cast(Node->getDecl())) { OCED->getInit()->IgnoreImpCasts()->printPretty(OS, nullptr, Policy); Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1810,6 +1810,11 @@ VisitExpr(E); } +void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) { + VisitExpr(E); + VisitExpr(E->getSubExpr()); +} + void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) { VisitExpr(S); } Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -3440,7 +3440,18 @@ CodeGenFunction &CGF; bool disabledDebugInfo; DisableDebugLocationUpdates(CodeGenFunction &CGF, const Expr *E) : CGF(CGF) { - if ((disabledDebugInfo = isa(E) && CGF.getDebugInfo())) + // FIXME(EricWF) + bool ShouldDisable = [&] { + if (!CGF.getDebugInfo()) + return false; + if (isa(E)) + return true; + if (const SourceLocExpr *SLE = dyn_cast(E)) { + return true; + } + return false; + }(); + if ((disabledDebugInfo = ShouldDisable)) CGF.disableDebugInfo(); } ~DisableDebugLocationUpdates() { Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -1167,6 +1167,8 @@ // bitfield lvalue or some other non-simple lvalue? return LV; } + case Expr::SourceLocExprClass: + return EmitLValue(cast(E)->getSubExpr()); case Expr::CXXDefaultArgExprClass: return EmitLValue(cast(E)->getExpr()); Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -182,6 +182,7 @@ CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); Visit(DIE->getExpr()); } + void VisitSourceLocExpr(SourceLocExpr *SLE) { Visit(SLE->getSubExpr()); } void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E); void VisitCXXConstructExpr(const CXXConstructExpr *E); void VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E); Index: lib/CodeGen/CGExprConstant.cpp =================================================================== --- lib/CodeGen/CGExprConstant.cpp +++ lib/CodeGen/CGExprConstant.cpp @@ -759,6 +759,10 @@ llvm_unreachable("Invalid CastKind"); } + llvm::Constant *VisitSourceLocExpr(SourceLocExpr *SLE) { + return Visit(SLE->getSubExpr()); + } + llvm::Constant *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { return Visit(DAE->getExpr()); } Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -572,7 +572,9 @@ Value *VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E) { return EmitLoadOfLValue(E); } - + Value *VisitSourceLocExpr(SourceLocExpr *SLE) { + return Visit(SLE->getSubExpr()); + } Value *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { return Visit(DAE->getExpr()); } @@ -1778,7 +1780,7 @@ } case CK_IntToOCLSampler: - return CGF.CGM.createOpenCLIntToSamplerConversion(E, CGF); + return CGF.CGM.createOpenCLIntToSamplerConversion(E, CGF); } // end of switch Index: lib/Parse/ParseExpr.cpp =================================================================== --- lib/Parse/ParseExpr.cpp +++ lib/Parse/ParseExpr.cpp @@ -619,6 +619,10 @@ /// [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 ']' @@ -1072,6 +1076,10 @@ 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()); @@ -1991,6 +1999,10 @@ /// [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: @@ -2210,6 +2222,34 @@ 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::IdentType Type = [&] { + 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(getCurScope(), Type, StartLoc, + ConsumeParen()); + break; + } } if (Res.isInvalid()) Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -1107,6 +1107,22 @@ return nullptr; } +Decl *Sema::getDeclForCurContext() { + Decl *currentDecl = nullptr; + if (const BlockScopeInfo *BSI = getCurBlock()) + currentDecl = BSI->TheDecl; + else if (const LambdaScopeInfo *LSI = getCurLambda()) + currentDecl = LSI->CallOperator; + else if (const CapturedRegionScopeInfo *CSI = getCurCapturedRegion()) + currentDecl = CSI->TheCapturedDecl; + else + currentDecl = getCurFunctionOrMethodDecl(); + + if (!currentDecl) + currentDecl = Context.getTranslationUnitDecl(); + return currentDecl; +} + void Sema::EmitCurrentDiagnostic(unsigned DiagID) { // FIXME: It doesn't make sense to me that DiagID is an incoming argument here // and yet we also use the current diag ID on the DiagnosticsEngine. This has Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -62,7 +62,7 @@ public: CheckDefaultArgumentVisitor(Expr *defarg, Sema *s) - : DefaultArg(defarg), S(s) {} + : DefaultArg(defarg), S(s) {} bool VisitExpr(Expr *Node); bool VisitDeclRefExpr(DeclRefExpr *DRE); @@ -4542,6 +4542,19 @@ return false; } + +static MemInitResult rebuildCtorInit(Sema &SemaRef, BaseAndFieldInfo &Info, + FieldDecl *Field, Expr *Init) { + // assert(!Field->getType()->isDependentType()); + // assert(!Field->getInClassInitializer()->isTypeDependent()); + SourceLocation Loc = Info.Ctor->getLocation(); + ExprResult InitRes = + SemaRef.TransformInitWithUnresolvedSourceLocExpr(Init, Loc); + if (InitRes.isInvalid()) + return true; + return SemaRef.BuildMemberInitializer(Field, InitRes.get(), Loc); +} + static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info, FieldDecl *Field, IndirectFieldDecl *Indirect = nullptr) { @@ -4550,9 +4563,16 @@ // Overwhelmingly common case: we have a direct initializer for this field. if (CXXCtorInitializer *Init = - Info.AllBaseFields.lookup(Field->getCanonicalDecl())) + Info.AllBaseFields.lookup(Field->getCanonicalDecl())) { + if (SourceLocExpr::containsSourceLocExpr(Init->getInit())) { + MemInitResult InitExpr = + rebuildCtorInit(SemaRef, Info, Field, Init->getInit()); + if (InitExpr.isInvalid()) + return true; + Init = InitExpr.get(); + } return Info.addFieldInitializer(Init); - + } // C++11 [class.base.init]p8: // if the entity is a non-static data member that has a // brace-or-equal-initializer and either @@ -4573,6 +4593,13 @@ SemaRef.BuildCXXDefaultInitExpr(Info.Ctor->getLocation(), Field); if (DIE.isInvalid()) return true; + if (SourceLocExpr::containsSourceLocExpr(Field->getInClassInitializer())) { + MemInitResult ME = + rebuildCtorInit(SemaRef, Info, Field, Field->getInClassInitializer()); + if (ME.isInvalid()) + return true; + return Info.addFieldInitializer(ME.get()); + } CXXCtorInitializer *Init; if (Indirect) Init = new (SemaRef.Context) Index: lib/Sema/SemaExceptionSpec.cpp =================================================================== --- lib/Sema/SemaExceptionSpec.cpp +++ lib/Sema/SemaExceptionSpec.cpp @@ -1271,6 +1271,7 @@ case Expr::PredefinedExprClass: case Expr::SizeOfPackExprClass: case Expr::StringLiteralClass: + case Expr::SourceLocExprClass: // These expressions can never throw. return CT_Cannot; Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -3007,19 +3007,9 @@ ExprResult Sema::BuildPredefinedExpr(SourceLocation Loc, PredefinedExpr::IdentType IT) { // Pick the current block, lambda, captured statement or function. - Decl *currentDecl = nullptr; - if (const BlockScopeInfo *BSI = getCurBlock()) - currentDecl = BSI->TheDecl; - else if (const LambdaScopeInfo *LSI = getCurLambda()) - currentDecl = LSI->CallOperator; - else if (const CapturedRegionScopeInfo *CSI = getCurCapturedRegion()) - currentDecl = CSI->TheCapturedDecl; - else - currentDecl = getCurFunctionOrMethodDecl(); - - if (!currentDecl) { + Decl *currentDecl = getDeclForCurContext(); + if (currentDecl == Context.getTranslationUnitDecl()) { Diag(Loc, diag::ext_predef_outside_function); - currentDecl = Context.getTranslationUnitDecl(); } QualType ResTy; @@ -4840,12 +4830,22 @@ Arg = ArgE.getAs(); } else { assert(Param && "can't use default arguments without a known callee"); - ExprResult ArgExpr = BuildCXXDefaultArgExpr(CallLoc, FDecl, Param); + if (!ArgExpr.isInvalid() && + SourceLocExpr::containsSourceLocExpr(Param->getDefaultArg())) { + ArgExpr = TransformInitWithUnresolvedSourceLocExpr( + Param->getDefaultArg(), CallLoc); + if (ArgExpr.isInvalid()) + return true; + InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, + Param, + ProtoArgType); + ArgExpr = PerformCopyInitialization( + Entity, SourceLocation(), ArgExpr.get(), IsListInitialization, AllowExplicit); + } if (ArgExpr.isInvalid()) return true; - Arg = ArgExpr.getAs(); } @@ -12849,6 +12849,138 @@ return new (Context) GNUNullExpr(Ty, TokenLoc); } +namespace { +/// A visitor for rebuilding SourceLocExpr's located in default initializers. +struct RebuildSourceLocExprInInit : public TreeTransform { + typedef TreeTransform BaseTransform; + + SourceLocation CallerLoc; + Decl *CallerDecl; + +public: + RebuildSourceLocExprInInit(Sema &S, SourceLocation CallerLoc, Decl *CallerDecl) + : BaseTransform(S), CallerLoc(CallerLoc), CallerDecl(CallerDecl) {} + + bool AlwaysRebuild() { return true; } + + StmtResult TransformStmt(Stmt *S) { + llvm_unreachable("unexpected statement!"); + } + + ExprResult TransformSourceLocExpr(SourceLocExpr *E) { + if (!E->isUnresolved() && !E->isInDefaultArgOrInit()) + return E; + return SemaRef.BuildSourceLocExpr(E->getIdentType(), E->getLocStart(), + E->getLocEnd(), CallerLoc, CallerDecl, + E->isInDefaultArgOrInit()); + } +}; +} // namespace + + +/// Given a function expression of unknown-any type, try to rebuild it +/// to have a function type. +ExprResult Sema::TransformInitWithUnresolvedSourceLocExpr(Expr *Init, + SourceLocation Loc) { + Decl *currentDecl = getDeclForCurContext(); + ExprResult Result = + RebuildSourceLocExprInInit(*this, Loc, currentDecl).TransformExpr(Init); + return Result; +} + +static QualType GetTypeForSourceLocExpr(const ASTContext &C, + SourceLocExpr::IdentType Type) { + if (Type == SourceLocExpr::Line || Type == SourceLocExpr::Column) + return C.UnsignedIntTy; + return C.getPointerType(C.CharTy.withConst()); +} + +ExprResult Sema::ActOnSourceLocExpr(Scope *S, SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc) { + bool RequiresTransform = + // The expression appears within a default argument + S->isFunctionPrototypeScope() || + // The expression appears within a NSDMI expression + S->isClassScope(); + + if (RequiresTransform) + return BuildUnresolvedSourceLocExpr(Type, BuiltinLoc, RPLoc); + + return BuildSourceLocExpr(Type, BuiltinLoc, RPLoc, BuiltinLoc, + getDeclForCurContext(), + /*IsInDefaultArgOrInit*/ false); +} + +ExprResult Sema::BuildUnresolvedSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc) { + return new (Context) SourceLocExpr(Type, BuiltinLoc, RPLoc, + GetTypeForSourceLocExpr(Context, Type)); +} + +ExprResult Sema::BuildSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc, + SourceLocation InvocationLoc, + Decl *currentDecl, + bool IsInDefaultArgOrInit) { + assert(currentDecl); + ExprResult Val = BuildSourceLocValue(Type, InvocationLoc, currentDecl); + if (Val.isInvalid()) + return ExprError(); + return new (Context) + SourceLocExpr(Type, BuiltinLoc, RPLoc, Val.get(), IsInDefaultArgOrInit); +} + +ExprResult Sema::BuildSourceLocValue(SourceLocExpr::IdentType Type, + SourceLocation CallerLoc, + Decl *CallerDecl) { + assert(CallerDecl && "cannot be null"); + PresumedLoc PLoc = + SourceMgr.getPresumedLoc(SourceMgr.getExpansionRange(CallerLoc).second); + + auto CreateString = [&](StringRef SVal) { + QualType CharTy = Context.CharTy.withConst(); + QualType StrTy = Context.getConstantArrayType( + CharTy, llvm::APInt(32, SVal.size() + 1), ArrayType::Normal, 0); + StringLiteral *Lit = + StringLiteral::Create(Context, SVal, StringLiteral::Ascii, + /*Pascal*/ false, StrTy, CallerLoc); + assert(Lit && "should not be null"); + return UsualUnaryConversions(Lit); + }; + + auto CreateInt = [&](unsigned Value) { + unsigned MaxWidth = Context.getTargetInfo().getIntWidth(); + llvm::APInt IntVal(MaxWidth, Value); + return IntegerLiteral::Create(Context, IntVal, Context.UnsignedIntTy, + CallerLoc); + }; + + ExprResult Res; + switch (Type) { + case SourceLocExpr::Column: + case SourceLocExpr::Line: + Res = CreateInt(Type == SourceLocExpr::Line ? PLoc.getLine() + : PLoc.getColumn()); + break; + case SourceLocExpr::File: + Res = CreateString(PLoc.getFilename()); + break; + case SourceLocExpr::Function: { + if (CallerDecl == Context.getTranslationUnitDecl()) + Res = CreateString(""); + else + Res = CreateString( + PredefinedExpr::ComputeName(PredefinedExpr::Function, CallerDecl)); + break; + } + } + assert(!Res.isInvalid() && Res.isUsable()); + return Res.get(); +} + bool Sema::ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&Exp, bool Diagnose) { if (!getLangOpts().ObjC1) Index: lib/Sema/SemaInit.cpp =================================================================== --- lib/Sema/SemaInit.cpp +++ lib/Sema/SemaInit.cpp @@ -555,6 +555,17 @@ // shall be initialized from its brace-or-equal-initializer [...] if (Field->hasInClassInitializer()) { ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); + if (SourceLocExpr::containsSourceLocExpr( + Field->getInClassInitializer())) { + ExprResult Res = SemaRef.TransformInitWithUnresolvedSourceLocExpr( + Field->getInClassInitializer(), ILE->getLocStart()); + if (Res.isInvalid()) { + hadError = true; + return; + } + DIE = SemaRef.PerformCopyInitialization(MemberEntity, SourceLocation(), + Res.get(), true, true); + } if (DIE.isInvalid()) { hadError = true; return; Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -2928,6 +2928,22 @@ RParenLoc, Length, PartialArgs); } + /// \brief FIXME + ExprResult RebuildSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc, Expr *E, + bool IsInDefaultArgOrInit) { + return new (SemaRef.Context) + SourceLocExpr(Type, BuiltinLoc, RPLoc, E, IsInDefaultArgOrInit); + } + + /// \brief FIXME + ExprResult RebuildUnresolvedSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc) { + return SemaRef.BuildUnresolvedSourceLocExpr(Type, BuiltinLoc, RPLoc); + } + /// \brief Build a new Objective-C boxed expression. /// /// By default, performs semantic analysis to build the new expression. @@ -9183,8 +9199,7 @@ return ExprError(); if (!getDerived().AlwaysRebuild() && - Callee.get() == E->getCallee() && - !ArgChanged) + Callee.get() == E->getCallee() && !ArgChanged) return SemaRef.MaybeBindToTemporary(E); // FIXME: Wrong source location information for the '('. @@ -9792,6 +9807,22 @@ return getDerived().TransformCallExpr(E); } +template +ExprResult TreeTransform::TransformSourceLocExpr(SourceLocExpr *E) { + if (!E->isUnresolved()) { + ExprResult Res = getDerived().TransformExpr(E->getSubExpr()); + if (Res.isInvalid()) + return ExprError(); + return getDerived().RebuildSourceLocExpr( + E->getIdentType(), E->getLocStart(), E->getLocEnd(), Res.get(), + E->isInDefaultArgOrInit()); + } else { + assert(E->isUnresolved()); + return getDerived().RebuildUnresolvedSourceLocExpr( + E->getIdentType(), E->getLocStart(), E->getLocEnd()); + } +} + template ExprResult TreeTransform::TransformCUDAKernelCallExpr(CUDAKernelCallExpr *E) { @@ -10026,8 +10057,7 @@ if (!Param) return ExprError(); - if (!getDerived().AlwaysRebuild() && - Param == E->getParam()) + if (!getDerived().AlwaysRebuild() && Param == E->getParam()) return E; return getDerived().RebuildCXXDefaultArgExpr(E->getUsedLocation(), Param); Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -862,6 +862,15 @@ E->setIsMicrosoftABI(Record.readInt()); } +void ASTStmtReader::VisitSourceLocExpr(SourceLocExpr *E) { + VisitExpr(E); + E->setSubExpr(Record.readSubExpr()); + E->setLocStart(ReadSourceLocation()); + E->setLocEnd(ReadSourceLocation()); + E->setIdentType(static_cast(Record.readInt())); + E->setIsInDefaultArgOrInit(Record.readInt()); +} + void ASTStmtReader::VisitAddrLabelExpr(AddrLabelExpr *E) { VisitExpr(E); E->setAmpAmpLoc(ReadSourceLocation()); @@ -3342,6 +3351,10 @@ 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; Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -843,6 +843,16 @@ Code = serialization::EXPR_VA_ARG; } +void ASTStmtWriter::VisitSourceLocExpr(SourceLocExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getSubExpr()); + Record.AddSourceLocation(E->getLocStart()); + Record.AddSourceLocation(E->getLocEnd()); + Record.push_back(E->getIdentType()); + Record.push_back(E->isInDefaultArgOrInit()); + Code = serialization::EXPR_SOURCE_LOC; +} + void ASTStmtWriter::VisitAddrLabelExpr(AddrLabelExpr *E) { VisitExpr(E); Record.AddSourceLocation(E->getAmpAmpLoc()); Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1000,6 +1000,7 @@ case Stmt::NoInitExprClass: case Stmt::SizeOfPackExprClass: case Stmt::StringLiteralClass: + case Stmt::SourceLocExprClass: case Stmt::ObjCStringLiteralClass: case Stmt::CXXPseudoDestructorExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: Index: test/CodeGenCXX/builtin_FUNCTION.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/builtin_FUNCTION.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -std=c++1z -fblocks %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %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(); + +// 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(); +} + +template +void test_template(const char *f = __builtin_FUNCTION()) { + (void)__builtin_FUNCTION(); +} +void do_template_test() { + test_template(); +} +} // namespace test_func Index: test/CodeGenCXX/builtin_LINE.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/builtin_LINE.cpp @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -std=c++1z -fblocks %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s + +extern "C" int sink; +struct Tag1 {}; +struct Tag2 {}; +struct Tag3 {}; +struct Tag4 {}; + +constexpr int get_line_constexpr(int l = __builtin_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(); + +struct InClassInit { + int Init = __builtin_LINE(); + InClassInit() = default; + constexpr InClassInit(Tag1, int l = __builtin_LINE()) : Init(l) {} + constexpr InClassInit(Tag2) : Init(__builtin_LINE()) {} + InClassInit(Tag3, int l = __builtin_LINE()) : Init(l) {} + InClassInit(Tag4) : Init(__builtin_LINE()) {} + + 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 void @_ZN11InClassInitC1E4Tag4(%struct.InClassInit* %test_five) + InClassInit test_five{Tag4{}}; +} + +int get_line(int l = __builtin_LINE()) { + return l; +} + +// 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(); +} Index: test/Parser/builtin_source_location.c =================================================================== --- /dev/null +++ test/Parser/builtin_source_location.c @@ -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 ')'}} +} Index: test/SemaCXX/Inputs/source-location-file.h =================================================================== --- /dev/null +++ test/SemaCXX/Inputs/source-location-file.h @@ -0,0 +1,43 @@ + +// 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 +constexpr U test_function_template(T, U u = U::current()) { + return u; +} + +template +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 + constexpr TestClass(int, T, U u = U::current()) : ctor_info(u) {} +}; + +template +struct AggrClass { + int x; + T info; + T init_info = T::current(); +}; + +} // namespace source_location_file Index: test/SemaCXX/source_location.cpp =================================================================== --- /dev/null +++ test/SemaCXX/source_location.cpp @@ -0,0 +1,291 @@ +// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify %s +// expected-no-diagnostics + +#define assert(...) ((__VA_ARGS__) ? ((void)0) : throw 42) + +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 *func() 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 +constexpr T identity(T t) { + return t; +} + +template +struct Pair { + T first; + U second; +}; + +template +constexpr bool is_same = false; +template +constexpr bool is_same = true; + +// test types +static_assert(is_same); +static_assert(is_same); +static_assert(is_same); +static_assert(is_same); + +// 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 constexpr SL GlobalS = SL::current(); + +static_assert(GlobalS.line() == __LINE__ - 2); + +constexpr bool test_line_fn() { + constexpr SL S = SL::current(); + static_assert(S.line() == (__LINE__ - 1), ""); + constexpr SL S2 = SL::current( + + ); + + static_assert(S2.line() == (__LINE__ - 4), ""); + return true; +} +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__, ""); +} + +template +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 +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 constexpr InMemInitTemplate(T, T, T, V info = U::current()) + : info(info) {} +}; +void test_mem_init_template() { + constexpr int line_offset = 8; + static_assert(InMemInitTemplate{}.check(__LINE__ - line_offset), ""); + static_assert(InMemInitTemplate{42}.check(__LINE__ - line_offset), ""); + static_assert(InMemInitTemplate{42, 42}.check(__LINE__ - line_offset), ""); + static_assert(InMemInitTemplate{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 +struct AggInitTemplate { + constexpr bool check(int expect) const { + return expect == info.line(); + } + T x; + U info = U::current(); +}; + +template +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 + 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__, ""); +} + +} // namespace test_line + +//===----------------------------------------------------------------------===// +// __builtin_FILE() +//===----------------------------------------------------------------------===// + +namespace test_file { +constexpr const char *test_file_simple(const char *__f = __builtin_FILE()) { + return __f; +} +void test_function() { + 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() { + using SLF::TestClass; + constexpr TestClass Default; + constexpr TestClass InParam{42}; + constexpr TestClass Template{42, 42}; + 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_func() { + return __func__; +} +constexpr bool test_func() { + return is_equal(__func__, test_func_simple()) && + !is_equal(get_func(), test_func_simple()); +} +static_assert(test_func()); + +template +constexpr Pair test_func_template(T, U u = U::current()) { + static_assert(is_equal(__func__, U::current().func())); + return {u, U::current()}; +} +template +void func_template_tests() { + constexpr auto P = test_func_template(42); + static_assert(is_equal(P.first.func(), __func__), ""); + static_assert(!is_equal(P.second.func(), __func__), ""); +} +template void func_template_tests(); + +template +struct TestCtor { + T info = T::current(); + T ctor_info; + TestCtor() = default; + template + 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.func(), __func__)); + static_assert(is_equal(Default.info.func(), "TestCtor")); + static_assert(is_equal(Template.info.func(), "TestCtor")); + static_assert(is_equal(Template.ctor_info.func(), __func__)); +} + +constexpr SL global_sl = SL::current(); +static_assert(is_equal(global_sl.func(), "")); + +} // namespace test_func + +//===----------------------------------------------------------------------===// +// __builtin_COLUMN() +//===----------------------------------------------------------------------===// + +namespace test_column { + +} // namespace test_column