Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -408,6 +408,7 @@ def InvalidOffsetof : DiagGroup<"invalid-offsetof">; def : DiagGroup<"strict-prototypes">; def StrictSelector : DiagGroup<"strict-selector-match">; +def UndefinedCallFromCtor : DiagGroup<"undefined-call-from-ctor">; def MethodDuplicate : DiagGroup<"duplicate-method-match">; def ObjCCStringFormat : DiagGroup<"cstring-format-directive">; def CoveredSwitchDefault : DiagGroup<"covered-switch-default">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6778,6 +6778,19 @@ def err_opencl_taking_function_address : Error< "taking address of function is not allowed">; +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; + def err_invalid_conversion_between_vector_and_scalar : Error< "invalid conversion between vector type %0 and scalar type %1">; 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/warn-undefined-in-ctor.cpp =================================================================== --- /dev/null +++ test/SemaCXX/warn-undefined-in-ctor.cpp @@ -0,0 +1,174 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s -Wundefined-call-from-ctor + +// Warn about member function calls +namespace MemberFunctionCallsTests { + + // 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 + }; +} + + + +// Warn about member function calls - example code from [C++11 12.6.2 p13] +namespace MemberFunctionCallsStdTests { + + 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 + }; + +} + + + + +// Warn about dynamic_cast on this +namespace DynamicCastTests { + + // 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 + }; +} + + + +// Warn about typeid on this +namespace std { + class type_info { + }; +} + +namespace TypeidTests { + + 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 + }; + +}