"'%0' cannot be the name of a variable or data member")
DIAG(err_parameter_name_omitted, ERROR,
"parameter name omitted")
+DIAG(warn_decl_in_param_list, WARNING,
+ "declaration of %0 will not be visible outside of this function")
DIAG(err_declarator_need_ident, ERROR,
"declarator requires an identifier")
/// ControlScope - The controlling scope in a if/switch/while/for statement.
ControlScope = 0x10,
- /// CXXClassScope - The scope of a C++ struct/union/class definition.
- CXXClassScope = 0x20,
+ /// ClassScope - The scope of a struct/union/class definition.
+ ClassScope = 0x20,
/// BlockScope - This is a scope that corresponds to a block object.
/// Blocks serve as top-level scopes for some objects like labels, they
/// template parameters of a C++ template. Template parameter
/// scope starts at the 'template' keyword and ends when the
/// template declaration ends.
- TemplateParamScope = 0x80
+ TemplateParamScope = 0x80,
+
+ /// FunctionPrototypeScope - This is a scope that corresponds to the
+ /// parameters within a function prototype.
+ FunctionPrototypeScope = 0x100
};
private:
/// The parent scope for this scope. This is null for the translation-unit
/// Flags - This contains a set of ScopeFlags, which indicates how the scope
/// interrelates with other control flow statements.
- unsigned Flags : 8;
+ unsigned Flags : 9;
/// WithinElse - Whether this scope is part of the "else" branch in
/// its parent ControlScope.
void* getEntity() const { return Entity; }
void setEntity(void *E) { Entity = E; }
- /// isCXXClassScope - Return true if this scope is a C++ class scope.
- bool isCXXClassScope() const {
- return (getFlags() & Scope::CXXClassScope);
+ /// isClassScope - Return true if this scope is a class/struct/union scope.
+ bool isClassScope() const {
+ return (getFlags() & Scope::ClassScope);
}
/// isInCXXInlineMethodScope - Return true if this scope is a C++ inline
bool isInCXXInlineMethodScope() const {
if (const Scope *FnS = getFnParent()) {
assert(FnS->getParent() && "TUScope not created?");
- return FnS->getParent()->isCXXClassScope();
+ return FnS->getParent()->isClassScope();
}
return false;
}
return getFlags() & Scope::TemplateParamScope;
}
+ /// isFunctionPrototypeScope - Return true if this scope is a
+ /// function prototype scope.
+ bool isFunctionPrototypeScope() const {
+ return getFlags() & Scope::FunctionPrototypeScope;
+ }
+
/// isWithinElse - Whether we are within the "else" of the
/// ControlParent (if any).
bool isWithinElse() const { return WithinElse; }
// Introduce the parameters into scope and parse their default
// arguments.
- ParseScope PrototypeScope(this, Scope::FnScope|Scope::DeclScope);
+ ParseScope PrototypeScope(this,
+ Scope::FunctionPrototypeScope|Scope::DeclScope);
for (unsigned I = 0, N = LM.DefaultArgs.size(); I != N; ++I) {
// Introduce the parameter into scope.
Actions.ActOnDelayedCXXMethodParameter(CurScope, LM.DefaultArgs[I].Param);
// constructor declaration. We're done with the decl-specifiers
// and will treat this token as an identifier.
if (getLang().CPlusPlus &&
- CurScope->isCXXClassScope() &&
+ CurScope->isClassScope() &&
Actions.isCurrentClassName(*Tok.getIdentifierInfo(), CurScope) &&
NextToken().getKind() == tok::l_paren)
goto DoneWithDeclSpec;
unsigned TagType, DeclTy *TagDecl) {
SourceLocation LBraceLoc = ConsumeBrace();
- ParseScope StructScope(this, Scope::DeclScope);
+ ParseScope StructScope(this, Scope::ClassScope|Scope::DeclScope);
Actions.ActOnTagStartDefinition(CurScope, TagDecl);
// Empty structs are an extension in C (C99 6.7.2.1p7), but are allowed in
// Enter function-declaration scope, limiting any declarators to the
// function prototype scope, including parameter declarators.
- ParseScope PrototypeScope(this, Scope::FnScope|Scope::DeclScope);
+ ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope|Scope::DeclScope);
bool IsVariadic = false;
while (1) {
SourceLocation LBraceLoc = ConsumeBrace();
- if (!CurScope->isCXXClassScope() && // Not about to define a nested class.
+ if (!CurScope->isClassScope() && // Not about to define a nested class.
CurScope->isInCXXInlineMethodScope()) {
// We will define a local class of an inline method.
// Push a new LexedMethodsForTopClass for its inline methods.
}
// Enter a scope for the class.
- ParseScope ClassScope(this, Scope::CXXClassScope|Scope::DeclScope);
+ ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope);
Actions.ActOnTagStartDefinition(CurScope, TagDecl);
//
// FIXME: Only function bodies and constructor ctor-initializers are
// parsed correctly, fix the rest.
- if (!CurScope->getParent()->isCXXClassScope()) {
+ if (!CurScope->getParent()->isClassScope()) {
// We are not inside a nested class. This class and its nested classes
// are complete and we can parse the delayed portions of method
// declarations and the lexed inline method definitions.
// Enter function-declaration scope, limiting any declarators to the
// function prototype scope, including parameter declarators.
- ParseScope PrototypeScope(this, Scope::FnScope|Scope::DeclScope);
+ ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope|Scope::DeclScope);
// Read all the argument declarations.
while (isDeclarationSpecifier()) {
}
DeclContext *DC = CurContext;
+ DeclContext *LexicalContext = CurContext;
ScopedDecl *PrevDecl = 0;
if (Name && SS.isNotEmpty()) {
PrevDecl = 0;
}
}
+ } else if (TK == TK_Reference && SS.isEmpty() && Name &&
+ (Kind != TagDecl::TK_enum)) {
+ // C++ [basic.scope.pdecl]p5:
+ // -- for an elaborated-type-specifier of the form
+ //
+ // class-key identifier
+ //
+ // if the elaborated-type-specifier is used in the
+ // decl-specifier-seq or parameter-declaration-clause of a
+ // function defined in namespace scope, the identifier is
+ // declared as a class-name in the namespace that contains
+ // the declaration; otherwise, except as a friend
+ // declaration, the identifier is declared in the smallest
+ // non-class, non-function-prototype scope that contains the
+ // declaration.
+ //
+ // C99 6.7.2.3p8 has a similar (but not identical!) provision for
+ // C structs and unions.
+
+ // Find the context where we'll be declaring the tag.
+ while (DC->isRecord())
+ DC = DC->getParent();
+ LexicalContext = DC;
+
+ // Find the scope where we'll be declaring the tag.
+ while (S->isClassScope() ||
+ (getLangOptions().CPlusPlus && S->isFunctionPrototypeScope()) ||
+ (S->getFlags() & Scope::DeclScope == 0))
+ S = S->getParent();
}
CreateNewDecl:
if (Attr)
ProcessDeclAttributeList(New, Attr);
+ // If we're declaring or defining
+ if (Name && S->isFunctionPrototypeScope() && !getLangOptions().CPlusPlus)
+ Diag(Loc, diag::warn_decl_in_param_list) << Context.getTagDeclType(New);
+
// Set the lexical context. If the tag has a C++ scope specifier, the
// lexical context will be different from the semantic context.
- New->setLexicalDeclContext(CurContext);
+ New->setLexicalDeclContext(LexicalContext);
// If this has an identifier, add it to the scope stack.
if (Name) {
S = S->getParent();
// Add it to the decl chain.
- PushOnScopeChains(New, S);
+ if (LexicalContext != CurContext) {
+ // FIXME: PushOnScopeChains should not rely on CurContext!
+ DeclContext *OldContext = CurContext;
+ CurContext = LexicalContext;
+ PushOnScopeChains(New, S);
+ CurContext = OldContext;
+ } else
+ PushOnScopeChains(New, S);
} else if (getLangOptions().CPlusPlus) {
// FIXME: We also want to do this for C, but if this tag is
// defined within a structure CurContext will point to the context
// enclosing the structure, and we would end up inserting the tag
// type into the wrong place.
- CurContext->addDecl(Context, New);
+ LexicalContext->addDecl(Context, New);
}
return New;
--- /dev/null
+// RUN: clang -fsyntax-only -pedantic -verify %s
+
+/* This test checks the introduction of struct and union types based
+ on a type specifier of the form "struct-or-union identifier" when they
+ type has not yet been declared. See C99 6.7.2.3p8. */
+
+typedef struct S1 {
+ union {
+ struct S2 *x;
+ struct S3 *y;
+ } u1;
+} S1;
+
+int test_struct_scope(S1 *s1, struct S2 *s2, struct S3 *s3) {
+ if (s1->u1.x == s2) return 1;
+ if (s1->u1.y == s3) return 1;
+ return 0;
+}
+
+int test_struct_scope_2(S1 *s1) {
+ struct S2 { int x; } *s2 = 0;
+ if (s1->u1.x == s2) return 1; /* expected-warning {{comparison of distinct pointer types ('struct S2 *' and 'struct S2 *')}} */
+ return 0;
+}
+
+// FIXME: We do not properly implement C99 6.2.1p4, which says that
+// the type "struct S4" declared in the function parameter list has
+// block scope within the function definition. The problem, in this
+// case, is that the code is ill-formed but we warn about the two S4's
+// being incompatible (we think they are two different types).
+int test_struct_scope_3(struct S4 * s4) { // expected-warning{{declaration of 'struct S4' will not be visible outside of this function}}
+ struct S4 { int y; } *s4_2 = 0;
+ /* if (s4 == s4_2) return 1; */
+ return 0;
+}
+
+void f(struct S5 { int y; } s5); // expected-warning{{declaration of 'struct S5' will not be visible outside of this function}}
--- /dev/null
+// RUN: clang -fsyntax-only -verify %s
+
+// Test the use of elaborated-type-specifiers to inject the names of
+// structs (or classes or unions) into an outer scope as described in
+// C++ [basic.scope.pdecl]p5.
+typedef struct S1 {
+ union {
+ struct S2 *x;
+ struct S3 *y;
+ };
+} S1;
+
+bool test_elab(S1 *s1, struct S2 *s2, struct S3 *s3) {
+ if (s1->x == s2) return true;
+ if (s1->y == s3) return true;
+ return false;
+}
+
+namespace NS {
+ class X {
+ public:
+ void test_elab2(struct S4 *s4);
+ };
+
+ void X::test_elab2(S4 *s4) { }
+}
+
+void test_X_elab(NS::X x) {
+ struct S4 *s4 = 0;
+ x.test_elab2(s4); // expected-error{{incompatible type passing 'struct S4 *', expected 'struct S4 *'}}
+}
+
+namespace NS {
+ S4 *get_S4();
+}
+
+void test_S5_scope() {
+ S4 *s4; // expected-error{{use of undeclared identifier 'S4'}}
+}
+
+// FIXME: the warning below should be an error!
+int test_funcparam_scope(struct S5 * s5) {
+ struct S5 { int y; } *s5_2 = 0;
+ if (s5 == s5_2) return 1; // expected-warning {{comparison of distinct pointer types ('struct S5 *' and 'struct S5 *')}}
+ return 0;
+}
+
+