diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f3961844db86..945ab5986f94 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -219,6 +219,12 @@ def err_static_assert_expression_is_not_constant : Error< "static_assert expression is not an integral constant expression">; def err_static_assert_failed : Error<"static_assert failed \"%0\"">; +def err_abstract_type_in_decl : Error< + "%select{return|parameter|variable|field}0 type %1 is an abstract class">; + +def note_pure_virtual_function : Note< + "pure virtual function %0">; + // C++ name lookup def err_incomplete_nested_name_spec : Error< "incomplete type %0 named in nested name specifier">; diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 4082440f42fb..64cc088e9679 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -151,6 +151,13 @@ public: /// FieldCollector - Collects CXXFieldDecls during parsing of C++ classes. llvm::OwningPtr FieldCollector; + typedef llvm::SmallPtrSet RecordDeclSetTy; + + /// PureVirtualClassDiagSet - a set of class declarations which we have + /// emitted a list of pure virtual functions. Used to prevent emitting the + /// same list more than once. + llvm::OwningPtr PureVirtualClassDiagSet; + /// \brief A mapping from external names to the most recent /// locally-scoped external declaration with that name. /// @@ -1627,6 +1634,8 @@ public: SourceLocation Loc, SourceRange Range); std::string getAmbiguousPathsDisplayString(BasePaths &Paths); + bool RequireNonAbstractType(SourceLocation Loc, QualType T, unsigned SelID); + //===--------------------------------------------------------------------===// // C++ Overloaded Operators [C++ 13.5] // diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 7b45d5232fb2..255fd6c5ba49 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1637,6 +1637,12 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, } else if (SC == VarDecl::None) SC = VarDecl::Static; } + + // The variable can not have an abstract class type. + if (RequireNonAbstractType(D.getIdentifierLoc(), R, 2 /* variable type */)) + InvalidDecl = true; + + // The variable can not NewVD = VarDecl::Create(Context, DC, D.getIdentifierLoc(), II, R, SC, // FIXME: Move to DeclGroup... @@ -1804,6 +1810,12 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, bool isVirtual = D.getDeclSpec().isVirtualSpecified(); bool isExplicit = D.getDeclSpec().isExplicitSpecified(); + // Check that the return type is not an abstract class type. + if (RequireNonAbstractType(D.getIdentifierLoc(), + R->getAsFunctionType()->getResultType(), + 0 /* return type */)) + InvalidDecl = true; + bool isVirtualOkay = false; FunctionDecl *NewFD; if (D.getKind() == Declarator::DK_Constructor) { @@ -1977,8 +1989,15 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, Diag(Param->getLocation(), diag::ext_param_typedef_of_void); } } else if (FTI.NumArgs > 0 && FTI.ArgInfo[0].Param != 0) { - for (unsigned i = 0, e = FTI.NumArgs; i != e; ++i) - Params.push_back((ParmVarDecl *)FTI.ArgInfo[i].Param); + for (unsigned i = 0, e = FTI.NumArgs; i != e; ++i) { + ParmVarDecl *PVD = (ParmVarDecl *)FTI.ArgInfo[i].Param; + + // Function parameters cannot have abstract class types. + if (RequireNonAbstractType(PVD->getLocation(), PVD->getType(), + 1 /* parameter type */)) + InvalidDecl = true; + Params.push_back(PVD); + } } NewFD->setParams(Context, &Params[0], Params.size()); @@ -3512,6 +3531,10 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, } } + // Fields can not have abstract class types + if (RequireNonAbstractType(Loc, T, 3 /* field type */)) + InvalidDecl = true; + // If this is declared as a bit-field, check the bit-field. if (BitWidth && VerifyBitField(Loc, II, T, BitWidth)) { InvalidDecl = true; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index b18b6c8f84c5..60d169215642 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -731,7 +731,10 @@ namespace { } } - bool empty() const { return Methods.empty(); } + bool empty() const { return Methods.empty(); } + + MethodList::const_iterator methods_begin() { return Methods.begin(); } + MethodList::const_iterator methods_end() { return Methods.end(); } }; void PureVirtualMethodCollector::Collect(const CXXRecordDecl* RD, @@ -777,6 +780,47 @@ namespace { } } +bool Sema::RequireNonAbstractType(SourceLocation Loc, QualType T, + unsigned SelID) { + + if (!getLangOptions().CPlusPlus) + return false; + + const RecordType *RT = T->getAsRecordType(); + if (!RT) + return false; + + const CXXRecordDecl *RD = dyn_cast(RT->getDecl()); + if (!RD) + return false; + + if (!RD->isAbstract()) + return false; + + Diag(Loc, diag::err_abstract_type_in_decl) << SelID << RD->getDeclName(); + + // Check if we've already emitted the list of pure virtual functions for this + // class. + if (PureVirtualClassDiagSet && PureVirtualClassDiagSet->count(RD)) + return true; + + PureVirtualMethodCollector Collector(Context, RD); + + for (PureVirtualMethodCollector::MethodList::const_iterator I = + Collector.methods_begin(), E = Collector.methods_end(); I != E; ++I) { + const CXXMethodDecl *MD = *I; + + Diag(MD->getLocation(), diag::note_pure_virtual_function) << + MD->getDeclName(); + } + + if (!PureVirtualClassDiagSet) + PureVirtualClassDiagSet.reset(new RecordDeclSetTy); + PureVirtualClassDiagSet->insert(RD); + + return true; +} + void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, DeclTy *TagDecl, SourceLocation LBrac, diff --git a/clang/test/SemaCXX/abstract.cpp b/clang/test/SemaCXX/abstract.cpp index a7a52116da88..e3c7651efb0d 100644 --- a/clang/test/SemaCXX/abstract.cpp +++ b/clang/test/SemaCXX/abstract.cpp @@ -9,7 +9,7 @@ #endif class C { - virtual void f() = 0; + virtual void f() = 0; // expected-note {{pure virtual function 'f'}} }; static_assert(__is_abstract(C), "C has a pure virtual function"); @@ -24,3 +24,11 @@ class E : D { }; static_assert(!__is_abstract(E), "E inherits from an abstract class but implements f"); + +C c; // expected-error {{variable type 'C' is an abstract class}} +void t1(C c); // expected-error {{parameter type 'C' is an abstract class}} +void t2(C); // expected-error {{parameter type 'C' is an abstract class}} + +struct S { + C c; // expected-error {{field type 'C' is an abstract class}} +};