Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -3800,6 +3800,80 @@ } }; +/// BuiltinSourceLocExpr - Represents a function call to one of +/// __builtin_LINE(), +/// __builtin_FUNCTION(), or __BUILTIN_FILE() +class SourceLocExpr final : public Expr { +public: + enum IdentType { + Function, + File, + Line, + }; + + Stmt *Val; + IdentType Type; + SourceLocation BuiltinLoc, RParenLoc; + bool IsInDefaultArg : 1; + + static const char *GetBuiltinStr(IdentType T); + +public: + SourceLocExpr(IdentType Type, SourceLocation BLoc, SourceLocation RParenLoc, + bool IsInDefaultArg, Expr *E); + SourceLocExpr(IdentType Type, SourceLocation BLoc, SourceLocation RParenLoc, + bool IsInDefaultArg, QualType Ty); + + /// \brief Build an empty call expression. + SourceLocExpr(EmptyShell Empty) : Expr(SourceLocExprClass, Empty) {} + + static SourceLocExpr *Create(const ASTContext &Ctx, IdentType Type, + SourceLocation BLoc, SourceLocation RParenLoc, + bool IsInDefaultArg, QualType Ty, + Expr *E = nullptr); + + static bool containsSourceLocExpr(const Expr *E); + + const char *getBuiltinStr() const LLVM_READONLY { + return GetBuiltinStr(Type); + } + IdentType getIdentType() const LLVM_READONLY { return 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(Val); } + Expr *getSubExpr() { return cast_or_null(Val); } + void setSubExpr(Expr *E) { Val = E; } + + bool isInDefaultArg() const { return IsInDefaultArg; } + void setIsInDefaultArg(bool V = true) { IsInDefaultArg = V; } + + bool isUnresolved() const { return Val == nullptr; } + + child_range children() { + return Val ? child_range(&Val, &Val + 1) + : child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return Val ? const_child_range(&Val, &Val + 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 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,9 @@ KEYWORD(__attribute , KEYALL) KEYWORD(__builtin_choose_expr , KEYALL) KEYWORD(__builtin_offsetof , KEYALL) +KEYWORD(__builtin_FILE , KEYALL) +KEYWORD(__builtin_FUNCTION , KEYALL) +KEYWORD(__builtin_LINE , 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 @@ -4392,6 +4392,16 @@ ExprResult BuildVAArgExpr(SourceLocation BuiltinLoc, Expr *E, TypeSourceInfo *TInfo, SourceLocation RPLoc); + ExprResult ActOnSourceLocExpr(Scope *S, SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc); + ExprResult BuildSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, SourceLocation RPLoc, + bool IsInDefaultArg); + + ExprResult BuildSourceLocValue(SourceLocExpr::IdentType Type, + SourceLocation CallerLoc, Decl *CallerDecl); + // __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. @@ -1363,7 +1365,6 @@ EXPR_OBJC_ARRAY_LITERAL, EXPR_OBJC_DICTIONARY_LITERAL, - /// \brief An ObjCEncodeExpr record. EXPR_OBJC_ENCODE, /// \brief An ObjCSelectorExpr record. @@ -1405,7 +1406,7 @@ EXPR_OBJC_AVAILABILITY_CHECK, // C++ - + /// \brief A CXXCatchStmt record. STMT_CXX_CATCH, /// \brief A CXXTryStmt record. @@ -1439,59 +1440,59 @@ EXPR_CXX_STD_INITIALIZER_LIST, /// \brief A CXXBoolLiteralExpr record. EXPR_CXX_BOOL_LITERAL, - EXPR_CXX_NULL_PTR_LITERAL, // CXXNullPtrLiteralExpr - EXPR_CXX_TYPEID_EXPR, // CXXTypeidExpr (of expr). - EXPR_CXX_TYPEID_TYPE, // CXXTypeidExpr (of type). - EXPR_CXX_THIS, // CXXThisExpr - EXPR_CXX_THROW, // CXXThrowExpr - EXPR_CXX_DEFAULT_ARG, // CXXDefaultArgExpr - EXPR_CXX_DEFAULT_INIT, // CXXDefaultInitExpr - EXPR_CXX_BIND_TEMPORARY, // CXXBindTemporaryExpr + EXPR_CXX_NULL_PTR_LITERAL, // CXXNullPtrLiteralExpr + EXPR_CXX_TYPEID_EXPR, // CXXTypeidExpr (of expr). + EXPR_CXX_TYPEID_TYPE, // CXXTypeidExpr (of type). + EXPR_CXX_THIS, // CXXThisExpr + EXPR_CXX_THROW, // CXXThrowExpr + EXPR_CXX_DEFAULT_ARG, // CXXDefaultArgExpr + EXPR_CXX_DEFAULT_INIT, // CXXDefaultInitExpr + EXPR_CXX_BIND_TEMPORARY, // CXXBindTemporaryExpr EXPR_CXX_SCALAR_VALUE_INIT, // CXXScalarValueInitExpr EXPR_CXX_NEW, // CXXNewExpr EXPR_CXX_DELETE, // CXXDeleteExpr EXPR_CXX_PSEUDO_DESTRUCTOR, // CXXPseudoDestructorExpr - - EXPR_EXPR_WITH_CLEANUPS, // ExprWithCleanups - + + EXPR_EXPR_WITH_CLEANUPS, // ExprWithCleanups + EXPR_CXX_DEPENDENT_SCOPE_MEMBER, // CXXDependentScopeMemberExpr EXPR_CXX_DEPENDENT_SCOPE_DECL_REF, // DependentScopeDeclRefExpr EXPR_CXX_UNRESOLVED_CONSTRUCT, // CXXUnresolvedConstructExpr EXPR_CXX_UNRESOLVED_MEMBER, // UnresolvedMemberExpr EXPR_CXX_UNRESOLVED_LOOKUP, // UnresolvedLookupExpr - EXPR_CXX_EXPRESSION_TRAIT, // ExpressionTraitExpr - EXPR_CXX_NOEXCEPT, // CXXNoexceptExpr + EXPR_CXX_EXPRESSION_TRAIT, // ExpressionTraitExpr + EXPR_CXX_NOEXCEPT, // CXXNoexceptExpr - EXPR_OPAQUE_VALUE, // OpaqueValueExpr - EXPR_BINARY_CONDITIONAL_OPERATOR, // BinaryConditionalOperator - EXPR_TYPE_TRAIT, // TypeTraitExpr - EXPR_ARRAY_TYPE_TRAIT, // ArrayTypeTraitIntExpr - - EXPR_PACK_EXPANSION, // PackExpansionExpr - EXPR_SIZEOF_PACK, // SizeOfPackExpr - EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr - EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK,// SubstNonTypeTemplateParmPackExpr - EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr - EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr - EXPR_CXX_FOLD, // CXXFoldExpr + EXPR_OPAQUE_VALUE, // OpaqueValueExpr + EXPR_BINARY_CONDITIONAL_OPERATOR, // BinaryConditionalOperator + EXPR_TYPE_TRAIT, // TypeTraitExpr + EXPR_ARRAY_TYPE_TRAIT, // ArrayTypeTraitIntExpr + + EXPR_PACK_EXPANSION, // PackExpansionExpr + EXPR_SIZEOF_PACK, // SizeOfPackExpr + EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr + EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr + EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr + EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr + EXPR_CXX_FOLD, // CXXFoldExpr // CUDA - EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr + EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr // OpenCL - EXPR_ASTYPE, // AsTypeExpr + EXPR_ASTYPE, // AsTypeExpr // Microsoft - EXPR_CXX_PROPERTY_REF_EXPR, // MSPropertyRefExpr + EXPR_CXX_PROPERTY_REF_EXPR, // MSPropertyRefExpr EXPR_CXX_PROPERTY_SUBSCRIPT_EXPR, // MSPropertySubscriptExpr - EXPR_CXX_UUIDOF_EXPR, // CXXUuidofExpr (of expr). - EXPR_CXX_UUIDOF_TYPE, // CXXUuidofExpr (of type). - STMT_SEH_LEAVE, // SEHLeaveStmt - STMT_SEH_EXCEPT, // SEHExceptStmt - STMT_SEH_FINALLY, // SEHFinallyStmt - STMT_SEH_TRY, // SEHTryStmt + EXPR_CXX_UUIDOF_EXPR, // CXXUuidofExpr (of expr). + EXPR_CXX_UUIDOF_TYPE, // CXXUuidofExpr (of type). + STMT_SEH_LEAVE, // SEHLeaveStmt + STMT_SEH_EXCEPT, // SEHExceptStmt + STMT_SEH_FINALLY, // SEHFinallyStmt + STMT_SEH_TRY, // SEHTryStmt // OpenMP directives STMT_OMP_PARALLEL_DIRECTIVE, @@ -1544,10 +1545,10 @@ EXPR_OMP_ARRAY_SECTION, // ARC - EXPR_OBJC_BRIDGED_CAST, // ObjCBridgedCastExpr + EXPR_OBJC_BRIDGED_CAST, // ObjCBridgedCastExpr - STMT_MS_DEPENDENT_EXISTS, // MSDependentExistsStmt - EXPR_LAMBDA, // LambdaExpr + STMT_MS_DEPENDENT_EXISTS, // MSDependentExistsStmt + EXPR_LAMBDA, // LambdaExpr STMT_COROUTINE_BODY, STMT_CORETURN, EXPR_COAWAIT, Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1809,6 +1809,68 @@ return OverOps[Opc]; } +SourceLocExpr::SourceLocExpr(IdentType Type, SourceLocation BLoc, + SourceLocation RParenLoc, bool IsInDefaultArg, + Expr *E) + : Expr(SourceLocExprClass, E->getType(), E->getValueKind(), + E->getObjectKind(), E->isTypeDependent(), E->isValueDependent(), + E->isInstantiationDependent(), false), + Val(E), Type(Type), BuiltinLoc(BLoc), RParenLoc(RParenLoc), + IsInDefaultArg(IsInDefaultArg) {} + +SourceLocExpr::SourceLocExpr(IdentType Type, SourceLocation BLoc, + SourceLocation RParenLoc, bool IsInDefaultArg, + QualType Ty) + : Expr(SourceLocExprClass, Ty, VK_RValue, OK_Ordinary, + Ty->isDependentType(), Ty->isDependentType(), + Ty->isInstantiationDependentType(), false), + Val(nullptr), Type(Type), BuiltinLoc(BLoc), RParenLoc(RParenLoc), + IsInDefaultArg(IsInDefaultArg) {} + +SourceLocExpr *SourceLocExpr::Create(const ASTContext &Ctx, IdentType Type, + SourceLocation BLoc, + SourceLocation RParenLoc, + bool IsInDefaultArg, QualType Ty, + Expr *E) { + assert(!E || E->getType() == Ty); + if (E) + return new (Ctx) SourceLocExpr(Type, BLoc, RParenLoc, IsInDefaultArg, E); + return new (Ctx) SourceLocExpr(Type, BLoc, RParenLoc, IsInDefaultArg, Ty); +} + +const char *SourceLocExpr::GetBuiltinStr(IdentType Type) { + switch (Type) { + case File: + return "__builtin_FILE"; + case Function: + return "__builtin_FUNCTION"; + case Line: + return "__builtin_LINE"; + } +} + +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->isInDefaultArg() || SLE->isUnresolved(); + } +}; +} // namespace + +bool SourceLocExpr::containsSourceLocExpr(const Expr *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 +2996,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() || E->getIdentType() != SourceLocExpr::Line) + 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/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,9 @@ /// [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' '(' ')' /// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')' /// [GNU] '__null' /// [OBJC] '[' objc-message-expr ']' @@ -627,20 +630,18 @@ /// [OBJC] '\@encode' '(' type-name ')' /// [OBJC] objc-string-literal /// [C++] simple-type-specifier '(' expression-list[opt] ')' [C++ 5.2.3] -/// [C++11] simple-type-specifier braced-init-list [C++11 5.2.3] -/// [C++] typename-specifier '(' expression-list[opt] ')' [C++ 5.2.3] -/// [C++11] typename-specifier braced-init-list [C++11 5.2.3] -/// [C++] 'const_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] -/// [C++] 'dynamic_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] -/// [C++] 'reinterpret_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] -/// [C++] 'static_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] -/// [C++] 'typeid' '(' expression ')' [C++ 5.2p1] -/// [C++] 'typeid' '(' type-id ')' [C++ 5.2p1] -/// [C++] 'this' [C++ 9.3.2] -/// [G++] unary-type-trait '(' type-id ')' -/// [G++] binary-type-trait '(' type-id ',' type-id ')' [TODO] -/// [EMBT] array-type-trait '(' type-id ',' integer ')' -/// [clang] '^' block-literal +/// [C++11] simple-type-specifier braced-init-list [C++11 5.2.3] [C++] +/// typename-specifier '(' expression-list[opt] ')' [C++ 5.2.3] [C++11] +/// typename-specifier braced-init-list [C++11 5.2.3] [C++] +/// 'const_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] [C++] +/// 'dynamic_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] [C++] +/// 'reinterpret_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] [C++] +/// 'static_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] [C++] +/// 'typeid' '(' expression ')' [C++ 5.2p1] [C++] +/// 'typeid' '(' type-id ')' [C++ 5.2p1] [C++] +/// 'this' [C++ 9.3.2] [G++] unary-type-trait '(' type-id ')' [G++] +/// binary-type-trait '(' type-id ',' type-id ')' [TODO] [EMBT] +/// array-type-trait '(' type-id ',' integer ')' [clang] '^' block-literal /// /// constant: [C99 6.4.4] /// integer-constant @@ -650,14 +651,14 @@ /// /// id-expression: [C++ 5.1] /// unqualified-id -/// qualified-id +/// qualified-id /// /// unqualified-id: [C++ 5.1] /// identifier /// operator-function-id /// conversion-function-id -/// '~' class-name -/// template-id +/// '~' class-name +/// template-id /// /// new-expression: [C++ 5.3.4] /// '::'[opt] 'new' new-placement[opt] new-type-id @@ -722,7 +723,7 @@ /// '__trivially_copyable' /// /// binary-type-trait: -/// [GNU] '__is_base_of' +/// [GNU] '__is_base_of' /// [MS] '__is_convertible_to' /// '__is_convertible' /// '__is_same' @@ -1072,6 +1073,9 @@ 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_FILE: + case tok::kw___builtin_LINE: + case tok::kw___builtin_FUNCTION: return ParseBuiltinPrimaryExpression(); case tok::kw___null: return Actions.ActOnGNUNullExpr(ConsumeToken()); @@ -1991,6 +1995,9 @@ /// [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' '(' ')' /// [OCL] '__builtin_astype' '(' assignment-expression ',' type-name ')' /// /// [GNU] offsetof-member-designator: @@ -2210,6 +2217,28 @@ ConsumeParen()); break; } + case tok::kw___builtin_FILE: + case tok::kw___builtin_LINE: + case tok::kw___builtin_FUNCTION: { + // 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 = [&] { + if (T == tok::kw___builtin_FILE) + return SourceLocExpr::File; + if (T == tok::kw___builtin_FUNCTION) + return SourceLocExpr::Function; + if (T == tok::kw___builtin_LINE) + return SourceLocExpr::Line; + llvm_unreachable("invalid keyword"); + }(); + Res = Actions.ActOnSourceLocExpr(getCurScope(), Type, StartLoc, + ConsumeParen()); + break; + } } if (Res.isInvalid()) 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,21 @@ return false; } +ExprResult rebuildInitWithUnresolvedSourceLocExpr(Sema &S, Expr *Init, + SourceLocation Loc); + +static MemInitResult rebuildCtorInit(Sema &SemaRef, BaseAndFieldInfo &Info, + FieldDecl *Field) { + assert(!Field->getType()->isDependentType()); + assert(!Field->getInClassInitializer()->isTypeDependent()); + SourceLocation Loc = Info.Ctor->getLocation(); + ExprResult Init = rebuildInitWithUnresolvedSourceLocExpr( + SemaRef, Field->getInClassInitializer(), Loc); + if (Init.isInvalid()) + return true; + return SemaRef.BuildMemberInitializer(Field, Init.get(), Loc); +} + static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info, FieldDecl *Field, IndirectFieldDecl *Indirect = nullptr) { @@ -4550,9 +4565,15 @@ // 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(Field->getInClassInitializer())) { + MemInitResult InitExpr = rebuildCtorInit(SemaRef, Info, Field); + 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 +4594,12 @@ SemaRef.BuildCXXDefaultInitExpr(Info.Ctor->getLocation(), Field); if (DIE.isInvalid()) return true; + if (SourceLocExpr::containsSourceLocExpr(Field->getInClassInitializer())) { + MemInitResult ME = rebuildCtorInit(SemaRef, Info, Field); + 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 @@ -4788,6 +4788,9 @@ return false; } +ExprResult rebuildInitWithUnresolvedSourceLocExpr(Sema &S, Expr *Init, + SourceLocation Loc); + bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl, const FunctionProtoType *Proto, unsigned FirstParam, ArrayRef Args, @@ -4840,12 +4843,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 = rebuildInitWithUnresolvedSourceLocExpr( + *this, 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 +12862,141 @@ 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->isInDefaultArg()) + return E; + + ExprResult Value = + SemaRef.BuildSourceLocValue(E->getIdentType(), CallerLoc, CallerDecl); + if (Value.isInvalid()) + return ExprError(); + return BaseTransform::RebuildSourceLocExpr( + E->getIdentType(), E->getLocStart(), E->getLocEnd(), + E->isInDefaultArg(), E->getType(), Value.get()); + } +}; +} // namespace + +static Decl *GetCurrentDecl(Sema &S) { + Decl *currentDecl = nullptr; + if (const BlockScopeInfo *BSI = S.getCurBlock()) + currentDecl = BSI->TheDecl; + else if (const LambdaScopeInfo *LSI = S.getCurLambda()) + currentDecl = LSI->CallOperator; + else if (const CapturedRegionScopeInfo *CSI = S.getCurCapturedRegion()) + currentDecl = CSI->TheCapturedDecl; + else + currentDecl = S.getCurFunctionOrMethodDecl(); + + if (!currentDecl) { + currentDecl = S.Context.getTranslationUnitDecl(); + } + return currentDecl; +} + +/// Given a function expression of unknown-any type, try to rebuild it +/// to have a function type. +ExprResult rebuildInitWithUnresolvedSourceLocExpr(Sema &S, Expr *Init, + SourceLocation Loc) { + ExprResult Result = + RebuildSourceLocExprInInit(S, Loc, GetCurrentDecl(S)).TransformExpr(Init); + return Result; +} + +ExprResult Sema::ActOnSourceLocExpr(Scope *S, SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc) { + return BuildSourceLocExpr(Type, BuiltinLoc, RPLoc, + S->isFunctionPrototypeScope() || S->isClassScope()); +} + +static QualType GetTypeForSourceLocExpr(const ASTContext &C, + SourceLocExpr::IdentType Type) { + if (Type == SourceLocExpr::Line) + return C.UnsignedIntTy; + return C.getPointerType(C.CharTy.withConst()); +} + +ExprResult Sema::BuildSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc, bool IsInDefaultArg) { + Decl *currentDecl = GetCurrentDecl(*this); + bool IsTypeDependent = + cast(currentDecl)->isDependentContext() && + (Type == SourceLocExpr::Function || Type == SourceLocExpr::File); + if (IsInDefaultArg || IsTypeDependent) { + return new (Context) SourceLocExpr(Type, BuiltinLoc, RPLoc, IsInDefaultArg, + GetTypeForSourceLocExpr(Context, Type)); + } + ExprResult Val = BuildSourceLocValue(Type, RPLoc, currentDecl); + if (Val.isInvalid()) + return ExprError(); + return new (Context) + SourceLocExpr(Type, BuiltinLoc, RPLoc, IsInDefaultArg, Val.get()); +} + +ExprResult Sema::BuildSourceLocValue(SourceLocExpr::IdentType Type, + SourceLocation CallerLoc, + Decl *CallerDecl) { + assert(CallerDecl && "cannot be null"); + assert(!cast(CallerDecl)->isDependentContext()); + 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); + }; + + ExprResult Res; + switch (Type) { + case SourceLocExpr::Line: { + unsigned MaxWidth = Context.getTargetInfo().getIntWidth(); + llvm::APInt IntVal(MaxWidth, PLoc.getLine()); + Res = ExprResult(IntegerLiteral::Create(Context, IntVal, + Context.UnsignedIntTy, CallerLoc)); + 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 @@ -526,6 +526,9 @@ } } +ExprResult rebuildInitWithUnresolvedSourceLocExpr(Sema &S, Expr *Init, + SourceLocation Loc); + void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, const InitializedEntity &ParentEntity, InitListExpr *ILE, @@ -555,6 +558,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 = rebuildInitWithUnresolvedSourceLocExpr( + SemaRef, 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,19 @@ RParenLoc, Length, PartialArgs); } + /// \brief Build a new C++ default-argument expression. + /// + /// By default, builds a new default-argument expression, which does not + /// require any semantic analysis. Subclasses may override this routine to + /// provide different behavior. + ExprResult RebuildSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc, bool IsInDefaultArg, + QualType Ty, Expr *E) { + return SourceLocExpr::Create(getSema().Context, Type, BuiltinLoc, RPLoc, + IsInDefaultArg, Ty, E); + } + /// \brief Build a new Objective-C boxed expression. /// /// By default, performs semantic analysis to build the new expression. @@ -9183,8 +9196,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 +9804,16 @@ return getDerived().TransformCallExpr(E); } +template +ExprResult TreeTransform::TransformSourceLocExpr(SourceLocExpr *E) { + if (!E->isTypeDependent() && !E->isUnresolved()) + return E; + + return getDerived().RebuildSourceLocExpr(E->getIdentType(), E->getLocStart(), + E->getLocEnd(), E->isUnresolved(), + E->getType(), E->getSubExpr()); +} + template ExprResult TreeTransform::TransformCUDAKernelCallExpr(CUDAKernelCallExpr *E) { @@ -10026,8 +10048,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->setIsInDefaultArg(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->isInDefaultArg()); + 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,25 @@ +// RUN: %clang_cc1 -std=c++1z -fblocks %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s + +namespace test_func { +constexpr const char *get_func(const char *f = __builtin_FUNCTION()) { + return f; +} +constexpr const char *test_func_one() { + return __builtin_FUNCTION(); +} +constexpr const char *test_func_two() { + return get_func(); +} +const char *test_one = test_func_one(); +const char *test_two = test_func_two(); + +struct Pair { + const char *first; + const char *second; +}; +Pair test_same() { + return Pair{__builtin_FUNCTION(), __func__}; +} +const char *test_three = test_same().first; +const char *test_four = test_same().second; +} // 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/SemaCXX/source_location.cpp =================================================================== --- /dev/null +++ test/SemaCXX/source_location.cpp @@ -0,0 +1,134 @@ +// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify %s + +#define assert(...) ((__VA_ARGS__) ? ((void)0) : throw 42) + +namespace std { +namespace experimental { +struct source_location { +private: + unsigned int __m_line = 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 = 0) noexcept { + source_location __loc; + __loc.__m_line = __line; + __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 0; } + 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; + +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; +} + +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 { + SL info = SL::current(); + InMemInit() = default; + constexpr InMemInit(int) {} + + constexpr bool check(int expect) const { + return info.line() == expect; + } +}; + +static_assert(InMemInit{}.check(87), ""); +static_assert(InMemInit{42}.check(88), ""); + +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), ""); + +} // namespace test_line + +namespace test_file { +constexpr const char *test_file_simple(const char *__f = __builtin_FILE()) { + return __f; +} +static_assert(is_equal(test_file_simple(), __FILE__)); +static_assert(test_file_simple() != nullptr); +static_assert(!is_equal(test_file_simple(), "source_location.cpp")); +} // namespace test_file + +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()); +} // namespace test_func + +// expected-no-diagnostics +