[cfe-commits] r58916 - in /cfe/trunk: include/clang/AST/DeclBase.h include/clang/AST/DeclCXX.h include/clang/Basic/DiagnosticKinds.def lib/Sema/Sema.cpp lib/Sema/Sema.h lib/Sema/SemaCXXScopeSpec.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaExpr.cpp test/SemaCXX/nested-name-spec.cpp

Douglas Gregor dgregor at apple.com
Fri Nov 14 12:57:06 PST 2008


On Nov 8, 2008, at 12:17 PM, Argiris Kirtzidis wrote:

> Author: akirtzidis
> Date: Sat Nov  8 11:17:31 2008
> New Revision: 58916
>
> URL: http://llvm.org/viewvc/llvm-project?rev=58916&view=rev
> Log:
> Implement Sema support for C++ nested-name-specifiers.

Very cool.

> +  bool isFileContext() const {
> +    return DeclKind == Decl::TranslationUnit || DeclKind ==  
> Decl::Namespace;
> +  }

isFileContext seems like a weird name for this function... I would  
have called it isNamespaceContext, since the translation unit context  
is the same as the global namespace context for name lookup's purposes.

> +DIAG(err_not_tag_in_scope, ERROR,
> +     "'%0' does not name a tag member in the specified scope")

The term "tag" is very much a compiler-writers term. Could we replace  
tag with '%1' and fill in the appropriate tag kind (struct, class,  
union, enum)?

> +  /// PreDeclaratorDC - Keeps the declaration context before  
> switching to the
> +  /// context of a declarator's nested-name-specifier.
> +  DeclContext *PreDeclaratorDC;

I assume that we'll eventually need this to be a stack of DeclContext  
*'s, to deal with something like:

	::foo::bar<wibble::wonka>::type

Perhaps we should just make it a stack now so we're ready for the  
future.

> +namespace {
> +  Decl *LookupNestedName(DeclContext *LookupCtx, bool  
> LookInParentCtx,
> +                         const IdentifierInfo &II, bool  
> &IdIsUndeclared) {
>
> +    IdentifierResolver::iterator
> +      I = IdentifierResolver::begin(&II, LookupCtx, LookInParentCtx),
> +      E = IdentifierResolver::end();
> +
> +    if (I == E) {
> +      IdIsUndeclared = true;
> +      return 0;
> +    }
> +    IdIsUndeclared = false;
> +
> +    // C++ 3.4.3p1 :
> +    // During the lookup for a name preceding the :: scope  
> resolution operator,
> +    // object, function, and enumerator names are ignored. If the  
> name found is
> +    // not a class-name or namespace-name, the program is ill-formed.
> +
> +    for (; I != E; ++I) {
> +      if (TypedefDecl *TD = dyn_cast<TypedefDecl>(*I)) {
> +        if (TD->getUnderlyingType()->isRecordType())
> +          break;
> +        continue;
> +      }
> +      if (((*I)->getIdentifierNamespace()&Decl::IDNS_Tag) && ! 
> isa<EnumDecl>(*I))
> +        break;
> +    }

I don't think this is quite right for C++03. The standard says that we  
ignore enumerator names (e.g., an EnumConstantDecl), but the check in  
the "if" is skipping enumeration names (EnumDecl). Here's an example  
where it matters:

namespace N {
   struct C {
     static float X;
   };

   typedef C Typedef;

   namespace M {
     enum E {
       X = 17
     };

     typedef E Typedef;

     void test() {
       float& f = Typedef::X;
     }
   }
}

GCC rejects this example:

/Users/dgregor/foo.C: In function ‘void N::M::test()’:
/Users/dgregor/foo.C:16: error: ‘Typedef’ is not a class or namespace

Interestingly, EDG accepts the example, and so does Clang, but I'm  
pretty certain that both are wrong. Qualified name lookup should find  
the typedef for the enumeration 'E', and then fail because we can't do  
qualified name lookup into an enumeration type in C++03. In C++0x, we  
*can* do qualified name lookup into an enumeration, so the example  
fails because we can't bind an 'int' to a reference to a 'float'.

For Clang, I think the fix is to remove that "isa" check and add a "||  
TD->getUnderlyingType()->isEnumeralType()" into the "if" inside the  
Typedef-checking block, then cope with the difference between C++03  
and C++0x semantics in the caller.

> +  // See if this is a redefinition of a variable in the same scope.
> +  if (!D.getCXXScopeSpec().isSet()) {
> +    DC = CurContext;
> +    PrevDecl = LookupDecl(II, Decl::IDNS_Ordinary, S);
> +  } else { // Something like "int foo::x;"
> +    DC =  
> static_cast<DeclContext*>(D.getCXXScopeSpec().getScopeRep());
> +    PrevDecl = LookupDecl(II, Decl::IDNS_Ordinary, S, DC);
> +
> +    // C++ 7.3.1.2p2:
> +    // Members (including explicit specializations of templates) of  
> a named
> +    // namespace can also be defined outside that namespace by  
> explicit
> +    // qualification of the name being defined, provided that the  
> entity being
> +    // defined was already declared in the namespace and the  
> definition appears
> +    // after the point of declaration in a namespace that encloses  
> the
> +    // declarations namespace.
> +    //
> +    if (PrevDecl == 0) {
> +      // No previous declaration in the qualifying scope.
> +      Diag(D.getIdentifierLoc(), diag::err_typecheck_no_member,
> +           II->getName(), D.getCXXScopeSpec().getRange());
> +    } else if (!CurContext->Encloses(DC)) {
> +      // The qualifying scope doesn't enclose the original  
> declaration.
> +      // Emit diagnostic based on current scope.
> +      SourceLocation L = D.getIdentifierLoc();
> +      SourceRange R = D.getCXXScopeSpec().getRange();
> +      if (isa<FunctionDecl>(CurContext)) {
> +        Diag(L, diag::err_invalid_declarator_in_function, II- 
> >getName(), R);
> +      } else {
> +      Diag(L, diag::err_invalid_declarator_scope, II->getName(),
> +           cast<NamedDecl>(DC)->getName(), R);
> +      }
> +    }
> +  }

Hey, nice! I didn't expect to see this in here :)


	- Doug




More information about the cfe-commits mailing list