Trouble understand DeclContext

Hi,

I’m having trouble understanding the semantics of DeclContext. The documentation on this class (as of 3.9) refers to “declaration contexts” which it does not define and which does not seem to be a concept from the standard.

How was it determined which classes should inherit DeclContext? When should a nested DeclContext be added and when is it okay to use an existing DeclContext?

For example, DeclContext cannot correspond directly to declarative regions as defined in [basic.scope.declarative] (3.3.1) because only Decl subtypes inherit it. A block, for example, is something that opens a new region, and in the AST it is a CompoundStmt, which is not a Decl.

Also, how bad of an idea would it be to change the type hierarchy so more classes inherit DeclContext?

Thanks,

David Fontaine

Hi,

I'm having trouble understanding the semantics of DeclContext. The
documentation on this class (as of 3.9) refers to "declaration contexts"
which it does not define and which does not seem to be a concept from the
standard.

How was it determined which classes should inherit DeclContext? When
should a nested DeclContext be added and when is it okay to use an existing
DeclContext?

The DeclContext is a declaration that is a container for other

declarations. All non-leaf nodes of AST inherit it. If a contained
declaration itself can contain other declarations (nested class, method of
a class etc), it will be a nested DeclContext.

For example, DeclContext cannot correspond directly to declarative regions

as defined in [basic.scope.declarative] (3.3.1) because only Decl subtypes
inherit it. A block, for example, is something that opens a new region,
and in the AST it is a CompoundStmt, which is not a Decl.

Declarative regions contained in expressions, like CompoundStmt, are

organized using Scope objects. However Scope is a parser notion, and is not
represented in AST.

Thank you Serge.

I was the person asking earlier about named parameter scoping rules in function pointer declarations. Currently, these declarations are not in the AST. I would like to add them ​so that they can be referenced by attribute expressions.

“The DeclContext is a declaration that is a container for other declarations. All non-leaf nodes of AST inherit it. If a contained declaration itself can contain other declarations (nested class, method of a class etc), it will be a nested DeclContext.”

Based on this, it seems that VarDecl and FieldDecl should also inherit DeclContext, because a function pointer declaration, which may contain named parameters, would be one of those two. Any opinion on this?

-David

Nor VarDecl neither FieldDecl contain other declarations, so making them
DeclContext is not obvious. DeclContext is used in many places including
name lookup and such drastic semantic change can require redesign of other
compiler components. And what about such case:

int (* (*abc)(long x))(int x);

Two DeclContexts would be needed here.

I would propose you another way, which probably is less invasive. The
parameter names are not attributes of VarDecl, it looks more natural to
obtained from corresponding function type, which can be obtained:

VarDecl->getType()->getAs<PointerType>()->getPointeeType()->getAs<FunctionProtoType>()

If FunctionProtoType had method `getDecl` as `RecordDecl` has, you could
get the parameter declarations from corresponding FunctionDecl. So you need
to construct bogus function declaration when parsing function pointer
declaration and attach it to the FunctionProtoType. There is however a
problem with different declarations for the same type:

void (*pFn)(int x);
void (*pFn2)(int y);

The variables have the same type, but to keep named parameters you need
different FunctionDecls. Probably the bogus declarations should be linked
into redeclaration chain.

It is only an idea, I don't know if it is viable.

Thanks,
--Serge

I have some thoughts on this proposal, but rather than nitpick it inline, I think the issues would probably be better left to a code review. The primary concern about my proposal seems to be “it may be risky or infeasible to patch everything else to handle this change,” which could be made more concrete by me attempting to actually do it. Let me know if I’m misunderstanding. For now, I have a working prototype, but it has some ugly workarounds. I want to see if I can fix them properly without being too invasive, and how confident I am in the result.

Purely from a semantic perspective, hanging ParmVarDecls off of a VarDecl or FieldDecl seems the correct solution for function pointer parameters. Going through the type information and the redeclaration chain seems highly unfortunate even if it might be less code motion.

Thanks,
-David

And if such a ParmVarDecl is of function pointer type, you'd recursively hang any ParmVarDecls off that ParmVarDecl?

But what about a FunctionDecl whose return type is a function pointer type?

Purely from a semantic perspective, hanging ParmVarDecls off of a
VarDecl or FieldDecl seems the correct solution for function pointer
parameters. Going through the type information and the redeclaration
chain seems highly unfortunate even if it might be less code motion.

And if such a ParmVarDecl is of function pointer type, you'd recursively hang any ParmVarDecls off that ParmVarDecl?

Correct. That structure would directly mirror the input source.

But what about a FunctionDecl whose return type is a function pointer type?

I hadn't considered that case.

int (*ugh(int x))(int y);

For my specific use case, I'm fine with x being in the AST and y not (exactly as it is today), because the attributes I'm interested in (-Wthread-safety) apply to declarations, not types. (Are there attributes that apply to types? I don't actually know this.)

If we wanted to "fix" this case as well, then the way I see it, clang would need to understand that individual occurrences of _types_ (at least, function types) can themselves contain nested declarations. Which sounds like a far more drastic change than making VarDecl and FieldDecl inherit DeclContext.