Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -408,6 +408,7 @@ def ImplicitFallthroughPerFunction : DiagGroup<"implicit-fallthrough-per-function">; def ImplicitFallthrough : DiagGroup<"implicit-fallthrough", +def UndefinedCallFromCtor : DiagGroup<"undefined-call-from-ctor">; [ImplicitFallthroughPerFunction]>; def InvalidPPToken : DiagGroup<"invalid-pp-token">; def Trigraphs : DiagGroup<"trigraphs">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6778,6 +6778,19 @@ "taking address of function is not allowed">; def err_invalid_conversion_between_vector_and_scalar : Error< +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 warn_dynamic_cast_in_ctor_init : Warning< + "dynamic_cast with this as operand in ctor-initializer for base class %0 " + "results in undefined behavior">, + InGroup, DefaultIgnore; +def warn_typeid_in_ctor_init : Warning< + "typeid with this as operand in ctor-initializer for base class %0 " + "results in undefined behavior">, + InGroup, DefaultIgnore; + "invalid conversion between vector type %0 and scalar type %1">; // C++ member initializers. Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -2290,6 +2290,9 @@ bool InitList; FieldDecl *InitListFieldDecl; llvm::SmallVector InitFieldIndex; + // The base class that is initialized by the current CXXCtorInitializer + // or 0 if the current CXXCtorInitializer isn't initializing a base class. + const Type *CurrentBaseClass; public: typedef EvaluatedExprVisitor Inherited; @@ -2488,6 +2491,7 @@ Constructor = FieldConstructor; InitListExpr *ILE = dyn_cast(E); + CurrentBaseClass = BaseClass; if (ILE && Field) { InitList = true; @@ -2534,7 +2538,72 @@ Inherited::VisitCXXConstructExpr(E); } + void VisitCXXDynamicCastExpr(CXXDynamicCastExpr *E) { + // Calling dynamic_cast on the current object before + // all base classes are initialized is undefined behavior. + // [C++14/17 12.6.2 p16] [C++11 12.6.2 p13] + + // Check that we are currently initializing a base class (which means + // that not all base classes are initialized at the moment). + if (CurrentBaseClass) { + // Simple check if the operand of the dynamic_cast is a this expression + // and therefore results in undefined behavior. + if (isa(E->getSubExpr())) { + S.Diag(E->getExprLoc(), diag::warn_dynamic_cast_in_ctor_init) + << CurrentBaseClass->getAsCXXRecordDecl(); + } + } + Inherited::VisitCXXDynamicCastExpr(E); + } + + void VisitCXXTypeidExpr(CXXTypeidExpr *E) { + // Calling typeid on the current object before + // all base classes are initialized is undefined behavior. + // [C++14/17 12.6.2 p16] [C++11 12.6.2 p13] + + // Check that we are currently initializing a base class (which means + // that not all base classes are initialized at the moment). + if (CurrentBaseClass) { + // Simple check if the operand of the typeid is a this expression + // and therefore results in undefined behavior. + if (!E->isTypeOperand() && isa(E->getExprOperand())) { + S.Diag(E->getExprLoc(), diag::warn_typeid_in_ctor_init) + << CurrentBaseClass->getAsCXXRecordDecl(); + } + } + Inherited::VisitCXXTypeidExpr(E); + } + void VisitCXXMemberCallExpr(CXXMemberCallExpr *E) { + // Calling a member function of the current object before + // all base classes are initialized is undefined behavior. + // [C++14/17 12.6.2 p16] [C++11 12.6.2 p13] + + // Check that we are currently initializing a base class (which means + // that not all base classes are initialized at the moment). + if (CurrentBaseClass) { + + // Calling a member function of the current object in this context + // is undefined behavior. + // As getImplicitObjectArgument() returns 0 for member pointer + // calls we use dyn_cast_or_null instead of isa. + if (dyn_cast_or_null(E->getImplicitObjectArgument())) { + S.Diag(E->getExprLoc(), diag::warn_member_call_in_ctor_init) + << E << CurrentBaseClass->getAsCXXRecordDecl(); + } + + // Same as above but taking the implicit cast when calling + // a member function of a parent class into account. + auto ImplicitCast = + dyn_cast_or_null(E->getImplicitObjectArgument()); + if (ImplicitCast) { + if (isa(ImplicitCast->getSubExpr())) { + S.Diag(E->getExprLoc(), diag::warn_member_call_in_ctor_init) + << E << CurrentBaseClass->getAsCXXRecordDecl(); + } + } + } + Expr *Callee = E->getCallee(); if (isa(Callee)) { HandleValue(Callee, false /*AddressOf*/); Index: test/SemaCXX/ctor-init-with-dynamic-cast.cpp =================================================================== --- /dev/null +++ test/SemaCXX/ctor-init-with-dynamic-cast.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 -Wundefined-call-from-ctor + +// Helper class for the following test case. +class A { +public: + A(A *a) {} + A(unsigned long a) {} +}; + +// Calling dynamic cast on this before base class is initialized is undefined +// behavior. +class B : public A { + + A *j; + +public: + B() + : A(dynamic_cast(this) + 1), j(nullptr) { // expected-warning {{dynamic_cast with this as operand in ctor-initializer for base class 'A' results in undefined behavior}} + } + B(float var) + : A(sizeof(dynamic_cast(this) + 1)), j(nullptr) { // no-warning + } + B(int var) : A(nullptr), j(dynamic_cast(this)) {} // no-warning +}; Index: test/SemaCXX/ctor-init-with-member-call-std.cpp =================================================================== --- /dev/null +++ test/SemaCXX/ctor-init-with-member-call-std.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -Wundefined-call-from-ctor + +// Test UB in ctor-initializer with the example from [C++11 12.6.2 p13] +class A { +public: + A(int); +}; + +class B : public A { + int j; + +public: + int f(); + B() + : A(f()), // expected-warning {{member function call this->f() in ctor-initializer for base class 'A' results in undefined behavior}} + j(f()) {} // no-warning +}; + +class C { +public: + C(int); +}; +class D : public B, C { + int i; + +public: + D() + : C(f()), // expected-warning {{member function call this->f() in ctor-initializer for base class 'C' results in undefined behavior}} + i(f()) {} // no-warning +}; Index: test/SemaCXX/ctor-init-with-member-call.cpp =================================================================== --- /dev/null +++ test/SemaCXX/ctor-init-with-member-call.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -Wundefined-call-from-ctor + +// 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 { + int member_; + +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}} + } + B(float var) + : A(sizeof(get_i())) { // no-warning + } + B(int var) : A(0), member_(get_i()) {} // no-warning + + 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 +}; Index: test/SemaCXX/ctor-init-with-typeid.cpp =================================================================== --- /dev/null +++ test/SemaCXX/ctor-init-with-typeid.cpp @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s %s -std=c++11 -Wundefined-call-from-ctor + +namespace std { + class type_info { + }; +} + + +class A { +public: + A(const std::type_info& a) {} + A(unsigned a) {} +}; + +// Calling dynamic cast on this before base class is initialized is undefined +// behavior. +class B : public A { + + const std::type_info& j; + +public: + B() + : A(typeid(this)), j(typeid(nullptr)) { // expected-warning {{typeid with this as operand in ctor-initializer for base class 'A' results in undefined behavior}} + } + B(float var) + : A(sizeof(typeid(this))), j(typeid(nullptr)) { // no-warning + } + B(int var) : A(typeid(nullptr)), j(typeid(this)) {} // no-warning +};