Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7909,7 +7909,7 @@ "lvalue reference type">; def err_incorrect_defaulted_constexpr : Error< "defaulted definition of %sub{select_special_member_kind}0 " - "is not constexpr">; + "is not constexpr%select{| because:}1">; def err_incorrect_defaulted_consteval : Error< "defaulted declaration of %sub{select_special_member_kind}0 " "cannot be consteval because implicit definition is not constexpr">; @@ -7932,7 +7932,19 @@ def note_vbase_moved_here : Note< "%select{%1 is a virtual base class of base class %2 declared here|" "virtual base class %1 declared here}0">; - +def note_special_member_declared_here : Note< + "%sub{select_special_member_kind}0 declared here">; +def note_constexpr_defaulted_special_member : Note< + "%select{|anonymous }5%select{non-static data member|base class}1" + "%select{ %2|}5 of %3 has a non-constexpr %select{|implicit }4" + "%sub{select_special_member_kind}0">; +def note_constexpr_defaulted_virtual_base : Note< + "%0 inherits virtually from %1">; +def note_constexpr_defaulted_union : Note< + "unions require exactly one non-static data member initializer" + " to have a constexpr %sub{select_special_member_kind}0">; +def note_constexpr_defaulted_non_literal : Note< + "%0 is not a literal type">; def ext_implicit_exception_spec_mismatch : ExtWarn< "function previously declared with an %select{explicit|implicit}0 exception " "specification redeclared with an %select{implicit|explicit}0 exception " Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -6483,24 +6483,29 @@ /// Is the special member function which would be selected to perform the /// specified operation on the specified class type a constexpr constructor? -static bool -specialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl, - Sema::CXXSpecialMember CSM, unsigned Quals, - bool ConstRHS, - CXXConstructorDecl *InheritedCtor = nullptr, - Sema::InheritedConstructorInfo *Inherited = nullptr) { +static bool specialMemberIsConstexpr( + Sema &S, CXXRecordDecl *ClassDecl, Sema::CXXSpecialMember CSM, + unsigned Quals, bool ConstRHS, CXXConstructorDecl *InheritedCtor = nullptr, + Sema::InheritedConstructorInfo *Inherited = nullptr, + SourceLocation *SMLoc = nullptr) { // If we're inheriting a constructor, see if we need to call it for this base // class. if (InheritedCtor) { assert(CSM == Sema::CXXDefaultConstructor); auto BaseCtor = Inherited->findConstructorForBase(ClassDecl, InheritedCtor).first; - if (BaseCtor) + if (BaseCtor) { + if (SMLoc && !BaseCtor->isImplicit()) + *SMLoc = BaseCtor->getLocation(); return BaseCtor->isConstexpr(); + } } - if (CSM == Sema::CXXDefaultConstructor) - return ClassDecl->hasConstexprDefaultConstructor(); + if (CSM == Sema::CXXDefaultConstructor) { + bool IsConstexprCtor = ClassDecl->hasConstexprDefaultConstructor(); + if (IsConstexprCtor || !SMLoc) + return IsConstexprCtor; + } Sema::SpecialMemberOverloadResult SMOR = lookupCallFromSpecialMember(S, ClassDecl, CSM, Quals, ConstRHS); @@ -6508,21 +6513,96 @@ // A constructor we wouldn't select can't be "involved in initializing" // anything. return true; + if (SMLoc && !SMOR.getMethod()->isImplicit()) + *SMLoc = SMOR.getMethod()->getLocation(); return SMOR.getMethod()->isConstexpr(); } /// Determine whether the specified special member function would be constexpr /// if it were implicitly defined. static bool defaultedSpecialMemberIsConstexpr( - Sema &S, CXXRecordDecl *ClassDecl, Sema::CXXSpecialMember CSM, + Sema &S, const CXXRecordDecl *ClassDecl, Sema::CXXSpecialMember CSM, bool ConstArg, CXXConstructorDecl *InheritedCtor = nullptr, - Sema::InheritedConstructorInfo *Inherited = nullptr) { + Sema::InheritedConstructorInfo *Inherited = nullptr, + SmallVectorImpl *DiagNotes = nullptr) { if (!S.getLangOpts().CPlusPlus11) return false; + SourceLocation SMLoc; + SourceLocation DiagLoc; + enum class FailKind { + Subobject, + Union, + VirtualBase, + NonLiteral, + }; + /// This lambda will gathering all information and add notes for diagnostics. + auto Fail = [&](FailKind FailureKind, const Decl *FaillingDecl = nullptr) { + if (DiagNotes) { + switch (FailureKind) { + case FailKind::Subobject: { + PartialDiagnostic PDiag(diag::note_constexpr_defaulted_special_member, + S.Context.getDiagAllocator()); + auto *Field = dyn_cast(FaillingDecl); + auto *Record = dyn_cast(FaillingDecl); + SourceLocation Loc = FaillingDecl->getLocation(); + if (!Record) + Record = Field->getType()->getAsCXXRecordDecl(); + if (DiagLoc.isValid()) + Loc = DiagLoc; + PDiag << CSM << isa(FaillingDecl); + if (Field) + PDiag << Field; + else + PDiag << Record; + PDiag << ClassDecl << SMLoc.isInvalid() + << (Field ? Field->getDeclName().isEmpty() : false); + DiagNotes->emplace_back(Loc, PDiag); + if (SMLoc.isValid()) { + DiagNotes->emplace_back( + SMLoc, PartialDiagnostic(diag::note_special_member_declared_here, + S.Context.getDiagAllocator()) + << CSM); + return false; + } + if (!Record) + return false; + defaultedSpecialMemberIsConstexpr(S, Record, CSM, ConstArg, nullptr, + nullptr, DiagNotes); + return false; + } + case FailKind::VirtualBase: { + PartialDiagnostic PDiag(diag::note_constexpr_defaulted_virtual_base, + S.Context.getDiagAllocator()); + const CXXBaseSpecifier &Base = *ClassDecl->bases_begin(); + PDiag << ClassDecl << Base.getType(); + DiagNotes->emplace_back(Base.getBeginLoc(), PDiag); + return false; + } + case FailKind::Union: + DiagNotes->emplace_back( + SourceLocation(), + PartialDiagnostic(diag::note_constexpr_defaulted_union, + S.Context.getDiagAllocator()) + << CSM); + return false; + case FailKind::NonLiteral: + // FIXME: Explain why it is not literal. + DiagNotes->emplace_back( + SourceLocation(), + PartialDiagnostic(diag::note_constexpr_defaulted_non_literal, + S.Context.getDiagAllocator()) + << ClassDecl); + return false; + } + } + return false; + }; + // C++11 [dcl.constexpr]p4: // In the definition of a constexpr constructor [...] bool Ctor = true; + bool IsCtorConstexpr = false; switch (CSM) { case Sema::CXXDefaultConstructor: if (Inherited) @@ -6533,7 +6613,10 @@ // // This is important for performance; we need to know whether the default // constructor is constexpr to determine whether the type is a literal type. - return ClassDecl->defaultedDefaultConstructorIsConstexpr(); + IsCtorConstexpr = ClassDecl->defaultedDefaultConstructorIsConstexpr(); + if (DiagNotes && !IsCtorConstexpr) + break; + return IsCtorConstexpr; case Sema::CXXCopyConstructor: case Sema::CXXMoveConstructor: @@ -6560,20 +6643,23 @@ // If we squint, this is guaranteed, since exactly one non-static data member // will be initialized (if the constructor isn't deleted), we just don't know // which one. - if (Ctor && ClassDecl->isUnion()) - return CSM == Sema::CXXDefaultConstructor - ? ClassDecl->hasInClassInitializer() || - !ClassDecl->hasVariantMembers() - : true; + if (Ctor && ClassDecl->isUnion()) { + if (CSM == Sema::CXXDefaultConstructor && + !(ClassDecl->hasInClassInitializer() || + !ClassDecl->hasVariantMembers())) + return Fail(FailKind::Union); + else + return true; + } // -- the class shall not have any virtual base classes; if (Ctor && ClassDecl->getNumVBases()) - return false; + return Fail(FailKind::VirtualBase); // C++1y [class.copy]p26: // -- [the class] is a literal type, and if (!Ctor && !ClassDecl->isLiteral()) - return false; + return Fail(FailKind::NonLiteral); // -- every constructor involved in initializing [...] base class // sub-objects shall be a constexpr constructor; @@ -6583,10 +6669,12 @@ const RecordType *BaseType = B.getType()->getAs(); if (!BaseType) continue; + DiagLoc = B.getBeginLoc(); CXXRecordDecl *BaseClassDecl = cast(BaseType->getDecl()); if (!specialMemberIsConstexpr(S, BaseClassDecl, CSM, 0, ConstArg, - InheritedCtor, Inherited)) - return false; + InheritedCtor, Inherited, + DiagNotes ? &SMLoc : nullptr)) + return Fail(FailKind::Subobject, BaseClassDecl); } // -- every constructor involved in initializing non-static data members @@ -6606,10 +6694,11 @@ CXXRecordDecl *FieldRecDecl = cast(RecordTy->getDecl()); if (!specialMemberIsConstexpr(S, FieldRecDecl, CSM, BaseType.getCVRQualifiers(), - ConstArg && !F->isMutable())) - return false; + ConstArg && !F->isMutable(), nullptr, + nullptr, DiagNotes ? &SMLoc : nullptr)) + return Fail(FailKind::Subobject, F); } else if (CSM == Sema::CXXDefaultConstructor) { - return false; + return Fail(FailKind::Subobject, F); } } @@ -6804,17 +6893,22 @@ // destructors in C++1y), this is checked elsewhere. // // FIXME: This should not apply if the member is deleted. - bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM, - HasConstParam); - if ((getLangOpts().CPlusPlus14 ? !isa(MD) - : isa(MD)) && - MD->isConstexpr() && !Constexpr && - MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) { + bool InvalidIfNotConstexpr = + ((getLangOpts().CPlusPlus14 ? !isa(MD) + : isa(MD)) && + MD->isConstexpr() && + MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate); + llvm::SmallVector DiagNotes; + bool Constexpr = defaultedSpecialMemberIsConstexpr( + *this, RD, CSM, HasConstParam, /*InheritedCtor=*/nullptr, + /*Inherited=*/nullptr, InvalidIfNotConstexpr ? &DiagNotes : nullptr); + if (InvalidIfNotConstexpr && !Constexpr) { Diag(MD->getBeginLoc(), MD->isConsteval() ? diag::err_incorrect_defaulted_consteval : diag::err_incorrect_defaulted_constexpr) - << CSM; - // FIXME: Explain why the special member can't be constexpr. + << CSM << (DiagNotes.size() != 0); + for (auto Note : DiagNotes) + Diag(Note.first, Note.second); HadError = true; }