Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6791,6 +6791,10 @@ "will never be used">, InGroup>, DefaultIgnore; +def warn_member_call_in_ctor_init : Warning< + "member function call %0 in ctor-initializer for base class %1 results in undefined behavior">, + InGroup>, DefaultIgnore; + def err_base_init_does_not_name_class : Error< "constructor initializer %0 does not name a class">; def err_base_init_direct_and_virtual : Error< Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -3873,6 +3873,35 @@ return false; } +namespace { + /// \brief Searches for the first member function call to a given class. + class HasMemberCall : public RecursiveASTVisitor { + CXXRecordDecl* OnlyForClass; + CXXMemberCallExpr *FoundMemberCall = nullptr; + + public: + explicit HasMemberCall(CXXRecordDecl* OnlyForClass) + : OnlyForClass(OnlyForClass) { + } + + /// \brief Returns the found member function call or 0 if + /// no fitting member function call was found. + CXXMemberCallExpr *getFoundMemberCall() { + return FoundMemberCall; + } + + bool VisitCXXMemberCallExpr(CXXMemberCallExpr *E) { + // Check if member call is actually to the given class. + if (E->getRecordDecl() == nullptr + || E->getRecordDecl()->getCanonicalDecl() == OnlyForClass) { + FoundMemberCall = E; + return false; + } + return true; + } + }; +} + bool Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, bool AnyErrors, ArrayRef Initializers) { if (Constructor->isDependentContext()) { @@ -3907,9 +3936,24 @@ for (unsigned i = 0; i < Initializers.size(); i++) { CXXCtorInitializer *Member = Initializers[i]; - if (Member->isBaseInitializer()) + if (Member->isBaseInitializer()) { + // Calling a member function from a ctor-initializer + // before the base class results in undefined behavior [12.6.2 16]. + // FIXME: We only check for member functions directly called from this + // CtorInitializer and not for indirectly called functions. + HasMemberCall Finder(ClassDecl); + Finder.TraverseStmt(Member->getInit()); + + if (Finder.getFoundMemberCall()) { + Diag(Member->getSourceLocation(), diag::warn_member_call_in_ctor_init) + << Finder.getFoundMemberCall() + << Member->getBaseClass()->getAsCXXRecordDecl(); + } + + + Info.AllBaseFields[Member->getBaseClass()->getAs()] = Member; - else { + } else { Info.AllBaseFields[Member->getAnyMember()->getCanonicalDecl()] = Member; if (IndirectFieldDecl *F = Member->getIndirectMember()) { Index: test/SemaCXX/ctor-init-with-member-call.cpp =================================================================== --- /dev/null +++ test/SemaCXX/ctor-init-with-member-call.cpp @@ -0,0 +1,77 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 -Wmember-call-in-ctor-init + +// Helper class for the following test cases. +class A { +public: + A(int i) { + } +}; + +// Calling member functions before bass class initialized is undefined behavior. +class B : public A { +public: + + B() : A(1 + get_i()) { // expected-warning {{member function call this->get_i() in ctor-initializer for base class 'A' results in undefined behavior}} + } + + int get_i() { + return 2; + } +}; + +// Same as previous test but with explicit this. +class C : public A { +public: + C() : A(this->get_i() + 1) { // expected-warning {{member function call this->get_i() in ctor-initializer for base class 'A' results in undefined behavior}} + } + + int get_i() { + return 2; + } +}; + +// Check that the whole ctor-initializer is checked for member calls. +class OtherA { +public: + OtherA(int i) { + } +}; + +class D : public OtherA, public A { +public: + D() : OtherA(this->get_i() + 1), A(this->get_i() + 1) { // expected-warning {{member function call this->get_i() in ctor-initializer for base class 'OtherA' results in undefined behavior}} \ + // expected-warning {{member function call this->get_i() in ctor-initializer for base class 'A' results in undefined behavior}} + } + + int get_i() { + return 2; + } +}; + +// Calling static functions of this class is not undefined behavior. +class E : public A { +public: + E() : A(this->get_i() + 1) { // no-warning + } + + static int get_i() { + return 2; + } +}; + + +// Calling other functions of this class is not undefined behavior. +int other_foo() { + return 2; +} +class F : public A { +public: + F() : A(other_foo()) {} // no-warning +}; + + +// Calling member functions of other classes is not undefined behavior. +class G : public A { +public: + G(B& b) : A(b.get_i()) {} // no-warning +}; \ No newline at end of file