From 4fdf1faedbca40787fd277a6fbd5061fd69b2708 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 11 Mar 2009 16:48:53 +0000 Subject: [PATCH] Add basic, hackish support for instantiation of typedefs in a class template. More importantly, start to sort out the issues regarding complete types and nested-name-specifiers, especially the question of: when do we instantiate a class template specialization that occurs to the left of a '::' in a nested-name-specifier? git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@66662 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.def | 4 +++ lib/Sema/Sema.h | 2 ++ lib/Sema/SemaCXXScopeSpec.cpp | 30 ++++++++++++++++++ lib/Sema/SemaDecl.cpp | 2 ++ lib/Sema/SemaExpr.cpp | 2 ++ lib/Sema/SemaLookup.cpp | 2 +- lib/Sema/SemaTemplateInstantiate.cpp | 35 +++++++++++++++++++-- test/SemaCXX/nested-name-spec.cpp | 8 +++-- test/SemaTemplate/instantiate-typedef.cpp | 15 +++++++++ 9 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 test/SemaTemplate/instantiate-typedef.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index 4589a97350..2c024de380 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -231,6 +231,10 @@ DIAG(error_property_implemented, ERROR, DIAG(warn_objc_property_attr_mutually_exclusive, WARNING, "property attributes '%0' and '%1' are mutually exclusive") +// C++ name lookup +DIAG(err_incomplete_nested_name_spec, ERROR, + "incomplete type %0 named in nested name specifier") + // C++ class members DIAG(err_storageclass_invalid_for_member, ERROR, "storage class specified for a member declaration") diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index b3669bc74d..2e02004113 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1394,6 +1394,8 @@ public: TypeTy *Ty, SourceLocation RParen); + bool RequireCompleteDeclContext(const CXXScopeSpec &SS); + /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the /// global scope ('::'). virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S, diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index c8a86cfd7d..7773d288ae 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -17,6 +17,36 @@ #include "llvm/ADT/STLExtras.h" using namespace clang; +/// \brief Require that the context specified by SS be complete. +/// +/// If SS refers to a type, this routine checks whether the type is +/// complete enough (or can be made complete enough) for name lookup +/// into the DeclContext. A type that is not yet completed can be +/// considered "complete enough" if it is a class/struct/union/enum +/// that is currently being defined. Or, if we have a type that names +/// a class template specialization that is not a complete type, we +/// will attempt to instantiate that class template. +bool Sema::RequireCompleteDeclContext(const CXXScopeSpec &SS) { + if (!SS.isSet() || SS.isInvalid()) + return false; + + DeclContext *DC = static_cast(SS.getScopeRep()); + if (TagDecl *Tag = dyn_cast(DC)) { + // If we're currently defining this type, then lookup into the + // type is okay: don't complain that it isn't complete yet. + const TagType *TagT = Context.getTypeDeclType(Tag)->getAsTagType(); + if (TagT->isBeingDefined()) + return false; + + // The type must be complete. + return RequireCompleteType(SS.getRange().getBegin(), + Context.getTypeDeclType(Tag), + diag::err_incomplete_nested_name_spec, + SS.getRange()); + } + + return false; +} /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the /// global scope ('::'). diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 35573bb33d..03d00b83a3 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1255,6 +1255,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl, D.getIdentifierLoc()); } else { // Something like "int foo::x;" DC = static_cast(D.getCXXScopeSpec().getScopeRep()); + // FIXME: RequireCompleteDeclContext(D.getCXXScopeSpec()); ? PrevDecl = LookupQualifiedName(DC, Name, LookupOrdinaryName, true); // C++ 7.3.1.2p2: @@ -2895,6 +2896,7 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK, goto CreateNewDecl; } + // FIXME: RequireCompleteDeclContext(SS)? DC = static_cast(SS.getScopeRep()); SearchDC = DC; // Look-up name inside 'foo::'. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 9a86d65f9a..95c0baae56 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -4344,6 +4344,8 @@ Sema::ExprResult Sema::ActOnBuiltinOffsetOf(Scope *S, if (!Dependent && !ArgTy->isRecordType()) return Diag(TypeLoc, diag::err_offsetof_record_type) << ArgTy; + // FIXME: Does the type need to be complete? + // Otherwise, create a null pointer as the base, and iteratively process // the offsetof designators. QualType ArgTyPtr = Context.getPointerType(ArgTy); diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index dd9c850d08..a93689c9a0 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -1035,7 +1035,7 @@ Sema::LookupParsedName(Scope *S, const CXXScopeSpec *SS, bool RedeclarationOnly, bool AllowBuiltinCreation, SourceLocation Loc) { if (SS) { - if (SS->isInvalid()) + if (SS->isInvalid() || RequireCompleteDeclContext(*SS)) return LookupResult::CreateLookupResult(Context, 0); if (SS->isSet()) diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 21694b2e73..d630e801b0 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -655,13 +655,44 @@ Sema::InstantiateClassTemplateSpecialization( // Start the definition of this instantiation. ClassTemplateSpec->startDefinition(); - // FIXME: Create the injected-class-name for the - // instantiation. Should this be a typedef or something like it? // Instantiate the base class specifiers. if (InstantiateBaseSpecifiers(ClassTemplateSpec, Template)) Invalid = true; + // FIXME: Create the injected-class-name for the + // instantiation. Should this be a typedef or something like it? + + RecordDecl *Pattern = Template->getTemplatedDecl(); + + for (RecordDecl::decl_iterator Member = Pattern->decls_begin(), + MemberEnd = Pattern->decls_end(); + Member != MemberEnd; ++Member) { + if (TypedefDecl *Typedef = dyn_cast(*Member)) { + // FIXME: Simplified instantiation of typedefs needs to be made + // "real". + QualType T = Typedef->getUnderlyingType(); + if (T->isDependentType()) { + T = InstantiateType(T, ClassTemplateSpec->getTemplateArgs(), + ClassTemplateSpec->getNumTemplateArgs(), + Typedef->getLocation(), + Typedef->getDeclName()); + if (T.isNull()) { + Invalid = true; + T = Context.IntTy; + } + } + + // Create the new typedef + TypedefDecl *New + = TypedefDecl::Create(Context, ClassTemplateSpec, + Typedef->getLocation(), + Typedef->getIdentifier(), + T); + ClassTemplateSpec->addDecl(New); + } + } + // FIXME: Instantiate all of the members. // Add any implicitly-declared members that we might need. diff --git a/test/SemaCXX/nested-name-spec.cpp b/test/SemaCXX/nested-name-spec.cpp index 2b6de3363b..f75b279a44 100644 --- a/test/SemaCXX/nested-name-spec.cpp +++ b/test/SemaCXX/nested-name-spec.cpp @@ -1,5 +1,4 @@ -// RUN: clang -fsyntax-only -verify -std=c++98 %s -// fails due to exact diagnostic matching +// RUN: clang -fsyntax-only -std=c++98 -verify %s namespace A { struct C { static int cx; @@ -151,5 +150,10 @@ void ::global_func2(int) { } // expected-error{{definition or redeclaration of ' void N::f() { } // okay +struct Y; // expected-note{{forward declaration of 'struct Y'}} +Y::foo y; // expected-error{{incomplete type 'struct Y' named in nested name specifier}} \ + // FIXME: ugly: expected-error{{invalid token after top level declarator}} + X::X() : a(5) { } // expected-error{{use of undeclared identifier 'X'}} \ // expected-error{{expected function body after function declarator}} + diff --git a/test/SemaTemplate/instantiate-typedef.cpp b/test/SemaTemplate/instantiate-typedef.cpp new file mode 100644 index 0000000000..8ecee50502 --- /dev/null +++ b/test/SemaTemplate/instantiate-typedef.cpp @@ -0,0 +1,15 @@ +// RUN: clang -fsyntax-only -verify %s + +template +struct add_pointer { + typedef T* type; // expected-error{{'type' declared as a pointer to a reference}} +}; + +add_pointer::type test1(int * ptr) { return ptr; } + +add_pointer::type test2(int * ptr) { + return ptr; // expected-error{{incompatible type returning 'int *', expected 'type' (aka 'float *')}} +} + +add_pointer::type // expected-note{{in instantiation of template class 'struct add_pointer' requested here}} +test3(); // FIXME: expected-error{{invalid token after top level declarator}} -- 2.40.0