llvm::SmallVector<std::pair<CXXRecordDecl *, SourceLocation>, 4>
ClassesWithUnmarkedVirtualMembers;
+ /// \brief Contains the set of classes with unmarked virtual members
+ /// that require a vtable.
+ llvm::SmallPtrSet<CXXRecordDecl *, 4> UnmarkedClassesRequiringVtable;
+
/// MaybeMarkVirtualMembersReferenced - If the passed in method is the
/// key function of the record decl, will mark virtual member functions as
/// referenced.
void MaybeMarkVirtualMembersReferenced(SourceLocation Loc, CXXMethodDecl *MD);
+ /// \brief If the given class does not have a key function, mark its
+ /// virtual members as referenced.
+ void MaybeMarkVirtualMembersReferenced(SourceLocation Loc, CXXRecordDecl *RD);
+
/// MarkVirtualMembersReferenced - Will mark all virtual members of the given
/// CXXRecordDecl referenced.
void MarkVirtualMembersReferenced(SourceLocation Loc,
}
};
+ /// \brief RAII class that determines when any errors have occurred
+ /// between the time the instance was created and the time it was
+ /// queried.
+ class ErrorTrap {
+ Sema &SemaRef;
+ unsigned PrevErrors;
+
+ public:
+ explicit ErrorTrap(Sema &SemaRef)
+ : SemaRef(SemaRef), PrevErrors(SemaRef.getDiagnostics().getNumErrors()) {}
+
+ /// \brief Determine whether any errors have occurred since this
+ /// object instance was created.
+ bool hasErrorOccurred() const {
+ return SemaRef.getDiagnostics().getNumErrors() > PrevErrors;
+ }
+ };
+
/// \brief A stack-allocated class that identifies which local
/// variable declaration instantiations are present in this scope.
///
assert(ClassDecl && "DefineImplicitDefaultConstructor - invalid constructor");
ImplicitlyDefinedFunctionScope Scope(*this, Constructor);
- if (SetBaseOrMemberInitializers(Constructor, 0, 0, /*AnyErrors=*/false)) {
+ ErrorTrap Trap(*this);
+ if (SetBaseOrMemberInitializers(Constructor, 0, 0, /*AnyErrors=*/false) ||
+ Trap.hasErrorOccurred()) {
Diag(CurrentLocation, diag::note_member_synthesized_at)
<< CXXConstructor << Context.getTagDeclType(ClassDecl);
Constructor->setInvalidDecl();
CXXRecordDecl *ClassDecl = Destructor->getParent();
assert(ClassDecl && "DefineImplicitDestructor - invalid destructor");
+ if (Destructor->isInvalidDecl())
+ return;
+
ImplicitlyDefinedFunctionScope Scope(*this, Destructor);
+ ErrorTrap Trap(*this);
MarkBaseAndMemberDestructorsReferenced(Destructor->getLocation(),
Destructor->getParent());
- // FIXME: If CheckDestructor fails, we should emit a note about where the
- // implicit destructor was needed.
- if (CheckDestructor(Destructor)) {
+ if (CheckDestructor(Destructor) || Trap.hasErrorOccurred()) {
Diag(CurrentLocation, diag::note_member_synthesized_at)
<< CXXDestructor << Context.getTagDeclType(ClassDecl);
CopyAssignOperator->setUsed();
ImplicitlyDefinedFunctionScope Scope(*this, CopyAssignOperator);
+ ErrorTrap Trap(*this);
// C++0x [class.copy]p30:
// The implicitly-defined or explicitly-defaulted copy assignment operator
}
}
+ if (Trap.hasErrorOccurred()) {
+ Diag(CurrentLocation, diag::note_member_synthesized_at)
+ << CXXCopyAssignment << Context.getTagDeclType(ClassDecl);
+ CopyAssignOperator->setInvalidDecl();
+ return;
+ }
+
if (Invalid) {
CopyAssignOperator->setInvalidDecl();
return;
assert(ClassDecl && "DefineImplicitCopyConstructor - invalid constructor");
ImplicitlyDefinedFunctionScope Scope(*this, CopyConstructor);
+ ErrorTrap Trap(*this);
- if (SetBaseOrMemberInitializers(CopyConstructor, 0, 0, /*AnyErrors=*/false)) {
+ if (SetBaseOrMemberInitializers(CopyConstructor, 0, 0, /*AnyErrors=*/false) ||
+ Trap.hasErrorOccurred()) {
Diag(CurrentLocation, diag::note_member_synthesized_at)
<< CXXCopyConstructor << Context.getTagDeclType(ClassDecl);
CopyConstructor->setInvalidDecl();
return;
TemplateSpecializationKind kind = RD->getTemplateSpecializationKind();
- if (kind == TSK_ImplicitInstantiation)
- ClassesWithUnmarkedVirtualMembers.push_back(std::make_pair(RD, Loc));
- else
+ if (kind == TSK_ImplicitInstantiation) {
+ CXXRecordDecl *CanonRD = cast<CXXRecordDecl>(RD->getCanonicalDecl());
+ if (UnmarkedClassesRequiringVtable.insert(CanonRD))
+ ClassesWithUnmarkedVirtualMembers.push_back(std::make_pair(RD, Loc));
+ } else
MarkVirtualMembersReferenced(Loc, RD);
}
+void Sema::MaybeMarkVirtualMembersReferenced(SourceLocation Loc,
+ CXXRecordDecl *RD) {
+ if (RD->isDependentContext() || !RD->isDynamicClass())
+ return;
+
+ if (RD->getTemplateSpecializationKind() != TSK_ImplicitInstantiation)
+ return;
+
+ // Classes with a key function will be dealt with when we see the
+ // definition of the key function.
+ if (Context.getKeyFunction(RD))
+ return;
+
+ CXXRecordDecl *CanonRD = cast<CXXRecordDecl>(RD->getCanonicalDecl());
+ if (UnmarkedClassesRequiringVtable.insert(CanonRD))
+ ClassesWithUnmarkedVirtualMembers.push_back(std::make_pair(RD, Loc));
+}
+
bool Sema::ProcessPendingClassesWithUnmarkedVirtualMembers() {
if (ClassesWithUnmarkedVirtualMembers.empty())
return false;
CXXRecordDecl *RD = ClassesWithUnmarkedVirtualMembers.back().first;
SourceLocation Loc = ClassesWithUnmarkedVirtualMembers.back().second;
ClassesWithUnmarkedVirtualMembers.pop_back();
+
+ // If an implicitly-instantiated class doesn't require a vtable,
+ // don't mark its virtual members as referenced.
+ if (RD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) {
+ CXXRecordDecl *CanonRD = cast<CXXRecordDecl>(RD->getCanonicalDecl());
+ if (!UnmarkedClassesRequiringVtable.count(CanonRD))
+ continue;
+ }
+
MarkVirtualMembersReferenced(Loc, RD);
}
if (T->getAs<RecordType>() &&
RequireCompleteType(TypeidLoc, T, diag::err_incomplete_typeid))
return ExprError();
-
+
return Owned(new (Context) CXXTypeidExpr(TypeInfoType.withConst(),
Operand,
SourceRange(TypeidLoc, RParenLoc)));
// When typeid is applied to an expression other than an lvalue of a
// polymorphic class type [...] [the] expression is an unevaluated
// operand. [...]
- if (RecordD->isPolymorphic() && E->isLvalue(Context) == Expr::LV_Valid)
+ if (RecordD->isPolymorphic() && E->isLvalue(Context) == Expr::LV_Valid) {
isUnevaluatedOperand = false;
+ MaybeMarkVirtualMembersReferenced(TypeidLoc, RecordD);
+ }
}
// C++ [expr.typeid]p4:
if (Res.isInvalid())
return true;
E = Res.takeAs<Expr>();
+
+ if (const RecordType *RecordTy = Ty->getAs<RecordType>()) {
+ // If we're throwing a polymorphic class, we need to make sure
+ // there is a vtable.
+ MaybeMarkVirtualMembersReferenced(ThrowLoc,
+ cast<CXXRecordDecl>(RecordTy->getDecl()));
+ }
+
return false;
}
A A::foo; // okay
class B : A { }; // expected-error {{base class 'test2::A' has private constructor}}
- B b;
+ B b; // expected-note{{implicit default constructor}}
class C : virtual A {
public:
};
class D : C { }; // expected-error {{inherited virtual base class 'test2::A' has private constructor}}
- D d;
+ D d; // expected-note{{implicit default constructor}}
}
// Implicit destructor calls.
};
class Derived3 : // expected-error 2 {{inherited virtual base class 'Base<2>' has private destructor}} \
- // expected-error 2 {{inherited virtual base class 'Base<3>' has private destructor}}
+ // expected-error 2 {{inherited virtual base class 'Base<3>' has private destructor}} \
+ // expected-note 2{{implicit default constructor}}
Base<0>, // expected-error 2 {{base class 'Base<0>' has private destructor}}
virtual Base<1>, // expected-error 2 {{base class 'Base<1>' has private destructor}}
Base2, // expected-error 2 {{base class 'test3::Base2' has private destructor}}
virtual Base3
- {};
- Derived3 d3;
+ {};
+ Derived3 d3; // expected-note {{implicit default constructor}}\
+ // expected-note{{implicit default destructor}}}
}
// Conversion functions.
class Test1 { A a; }; // expected-error {{private member}}
void test1() {
Test1 a;
- a = Test1();
+ a = Test1(); // expected-note{{implicit default copy}}
}
class Test2 : A {}; // expected-error {{private member}}
void test2() {
Test2 a;
- a = Test2();
+ a = Test2(); // expected-note{{implicit default copy}}
}
}
class Test1 { A a; }; // expected-error {{field of type 'test6::A' has private copy constructor}}
void test1(const Test1 &t) {
- Test1 a = t;
+ Test1 a = t; // expected-note{{implicit default copy}}
}
class Test2 : A {}; // expected-error {{base class 'test6::A' has private copy constructor}}
void test2(const Test2 &t) {
- Test2 a = t;
+ Test2 a = t; // expected-note{{implicit default copy}}
}
}
};
class X : Base { // // expected-error {{cannot define the implicit default assignment operator for 'X', because non-static const member 'cint' can't use default assignment operator}} \
-// expected-note{{assignment operator for 'Base' first required here}}
+// expected-note{{assignment operator for 'Base' first required here}} \
+ // expected-note{{implicit default copy assignment operator}}
public:
X();
const int cint; // expected-note {{declared here}}
// Test1
void f(X x, const X cx) {
- x = cx; // expected-note{{assignment operator for 'X' first required here}}
+ x = cx; // expected-note{{assignment operator for 'X' first required here}} \
+ // expected-note{{implicit default copy assignment operator}}
x = cx;
z1 = z2;
}
E1 e1, e2;
void j() {
- e1 = e2; // expected-note{{assignment operator for 'E1' first required here}}
+ // FIXME: duplicated!
+ e1 = e2; // expected-note{{assignment operator for 'E1' first required here}} \
+ // expected-note{{implicit default copy assignment operator}}
}
namespace ProtectedCheck {
X x;
};
- void f(Z z) { z = z; } //
+ void f(Z z) { z = z; } // expected-note{{implicit default copy assignment operator}}
+
}
namespace MultiplePaths {
}
HasOutOfLineKey<int> out_of_line;
+
+namespace std {
+ class type_info;
+}
+
+namespace PR7114 {
+ class A { virtual ~A(); }; // expected-note{{declared private here}}
+
+ template<typename T>
+ class B {
+ public:
+ class Inner : public A { }; // expected-error{{base class 'PR7114::A' has private destructor}}
+ static Inner i;
+ static const unsigned value = sizeof(i) == 4;
+ };
+
+ int f() { return B<int>::value; }
+
+ void test_typeid(B<float>::Inner bfi) {
+ (void)typeid(bfi); // expected-note{{implicit default destructor}}
+ }
+}
+