Here’s another shot at this.
pretty_function.patch (2.37 KB)
Here’s another shot at this.
pretty_function.patch (2.37 KB)
Missing test case. Please don't use application/octet-stream for
patches...
Joerg
Here’s another shot at this. This version will print template parameter names instead of actual types used in the instantiation. It covers return type, function parameters and parameters from the enclosing class/es.
Note that we don’t show template parameter names when it comes to classes:
ClassTemplate::memberFunction(T) [T = int] // GCC would show ClassTemplate
This part of the string is obtained by calling FunctionDecl::getQualifiedNameAsString. Showing template parameter names like GCC would require duplicating most of the code in getQualifiedNameAsString without any significant gain.
I added a few tests to test\CodeGenCXX\predefined-expr.cpp that cover:
There is also a FIXME that says: “Maybe this should use DeclPrinter with a special “print predefined expr” policy instead”. I’m not really sure what this means as I am not familiar with printers and policies, but if you think it makes sense just point me in the right direction and I’ll be more than happy to rework the code.
P.S. Gmail sets the mime type, let’s see if changing the extension to .txt helps
patch.txt (8.91 KB)
Here's another shot at this. This version will print template parameter names instead of actual types used in the instantiation. It covers return type, function parameters and parameters from the enclosing class/es.
Thanks for working on this. I have a few comments:
- FD->getParamDecl(i)->getType().getAsStringInternal(Param, Policy);
+ QualType Type = FD->getParamDecl(i)->getType();
+ if (const SubstTemplateTypeParmType *TTP
+ = dyn_cast<SubstTemplateTypeParmType>(Type))
+ TTP->getReplacedParameter()->desugar()
+ .getAsStringInternal(Param, Policy);
+ else
+ Type.getAsStringInternal(Param, Policy);
POut << Param;
This is only going to work when the function parameter type is a template parameter type (T), but not a compound type involving T (e.g, T*). You'll want to find a solution that works recursively.
Note that we don't show template parameter names when it comes to classes:
ClassTemplate<int>::memberFunction(T) [T = int] // GCC would show ClassTemplate<T>
This part of the string is obtained by calling FunctionDecl::getQualifiedNameAsString. Showing template parameter names like GCC would require duplicating most of the code in getQualifiedNameAsString without any significant gain.
Or adding flags to the existing type/nested-name-specifier printers to print the template parameters rather than the substituted types.
I added a few tests to test\CodeGenCXX\predefined-expr.cpp that cover:
- template parameter as return type
- function template with two template parameters
- template parameter that doesn't show up in the function declaration (used only inside the body)
- nested classes with template parameters
- non type template parameter
- template template parameter
Cool, thanks for being thorough. I suggest adding some more tests with compound types.
There is also a FIXME that says: "Maybe this should use DeclPrinter with a special "print predefined expr" policy instead". I'm not really sure what this means as I am not familiar with printers and policies, but if you think it makes sense just point me in the right direction and I'll be more than happy to rework the code.
The policy it is referring to is the PrintingPolicy struct, defined in PrettyPrinter.h. You could add a new flag there that says "don't replace a substituted template parameter with its replacement", and teach the code in TypePrinter.cpp to obey that flag. Your code, above, is already passing a policy through (the Policy) parameter, so it would be fairly easy to tweak it at the source. That approach should be both simpler and more robust.
There's a completely different approach you could take: if the function is instantiated from a template, you could retrieve the template from which it was instantiated and print *that* type, along with the mapping from template parameters to template arguments. That way, you don't have to change type printing at all, because what you're printing still has the template parameters in place.
- Doug
I tried what you suggested, and almost got it to work
except in one case:
template
class Class
{
public:
template
void member(T t, U u){}
};
Class c;
c.member(0, 0.0f);
This is a function template specialization. I call FD->getPrimaryTemplate()->getTemplatedDecl(); But the first parameter is still a SubstTemplateTypeParmType, so it prints int instead of T. What am I doing wrong, I would expect this to be a member specialization kind?
There are multiple templates here, so you need to look through both of
them. "FD->getPrimaryTemplate()->getTemplatedDecl()" only gets you
the function template which is a member of Class<int>.
-Eli
Thanks Eli, I was thinking along those lines, but how do I actually get the other one? FunctionDecl can only be one kind, and this one is TK_FunctionTemplateSpecialization. I took a look at the one returned by the getPrimaryTemplate()->getTemplatedDecl(), but that one is TK_FunctionTemplate.
No idea. I'm not entirely sure we even track that.
-Eli
This should just be getInstantiatedFromMemberTemplate().
John.
That did the trick, but I don't understand why, what is the difference
between getTemplatedDecl (underlying template declaration) and
getInstantiatedFromMemberTemplate (previous declaration of this
template). And then there is also getPreviousDecl (previous
declaration of this function template). Doxygen comments aren't very
revealing here, and I'm sure that knowing the terminology from the
standard would help, but can somebody please explain.
Anyway this is the new patch. Two things to note here:
1. Since I don't really understand this special case I'm not sure if
my logic for handling it is OK (the tests that I have all pass). I'm
talking about the part inside (TK ==
TK_FunctionTemplateSpecialization) branch.
2. I added the test for compound types and I noticed that pointer
return type is printed like "T *func". I know that we prefer printing
the asterisk like this when it comes to variables, but is this OK for
function return type?
patch.txt (9.68 KB)
This comes up a lot, and I might as well give a definitive answer
on the mailing list.
Redeclaration is pretty much independent of all the others, so I'll
get it out of the way first. In C/C++, in certain contexts, you can just
declare something multiple times, and it's still considered to be
the same entity. For example:
class A;
class A;
That's all that the "previous declaration" links and redeclaration
iterators are doing: walking this list of redeclarations. Redeclarations
are "mostly" all semantically equivalent, but sometimes later
declarations can add extra information, like new attributes or
default arguments.
All the other links have to do with templates, one way or another.
Every template declaration is implemented as two separate nodes in
the AST: the template (ClassTemplateDecl or FunctionTemplateDecl)
and its pattern (CXXRecordDecl or FunctionDecl, respectively).
This is a 1-1 relationship, and you map back and forth with
getTemplatedDecl() and getDescribed{Class,Function}Template().
The former is only provided on template declarations; the latter is
only meaningful for declarations that are being used as the pattern
declaration for some template declaration.
For example, if you have:
template <class T> class A { ... };
you can think of the ClassTemplateDecl (the template) as being the
"template <class T>" part and the CXXRecordDecl (the pattern)
as being the "class A { ... }" part.
A specialization is just some specific application of template
arguments to a template, something like std::vector<int>. It
doesn't even necessarily have a definition: for example, if you
write std::vector<int> somewhere, but don't do anything that
requires it to be a complete type, it just stays an incomplete
specialization. The template<->specialization relationship
is many-to-1; templates have hashtables of their known
specializations, and specializations can get back to their
template through the specialization info. For functions, you
get that with getTemplateSpecializationInfo() (which returns
null to indicate that this function is not a specialization). For
classes, you get that by dyn_cast<>ing to
ClassTemplateSpecializationDecl (which produces null
to indicate that this class is not a specialization).
For example, if you have:
template <class T> class A { ... };
A<int> foo;
the type of 'foo' is ultimately a RecordType whose decl is
a ClassTemplateSpecializationDecl whose template is A.
The final kind of relationship is instantiation. Usually, when
you do need a definition for a template specialization, you
get it by instantiating a definition provided by some template
declaration. This basically just means copying that template's
pattern declaration while substituting in the template arguments
as necessary. When a specialization has a definition, the
specialization info will tell you where it came from.
Copying the pattern usually entails copying some number of
declarations; we say that the new declarations are instantiated
from the old ones. The AST doesn't track *every* such
relationship, but for certain class members (basically everything
but fields) we do. In these cases, you can get from the instantiated
declaration back to the pattern declaration it was instantiated
from using methods like getInstantiatedFromMemberDecl().
For example, if you have:
template <class T> class A {
T create() { return T(); }
};
A<int> foo;
the CXXMethodDecl "int A<int>::create()" will point back to the
CXXMethodDecl "T A<T>::create()".
This can all get a little confusing in the case of nested templates.
If you've got this:
template <class T> class A {
template <class U> U create(T);
};
Then:
char A<int>::create<char>(int)
is a specialization of the member function template
template <class U> U A<int>::create(int)
which was instantiated from the member function template
template <class T> template <class U> U A<T>::create(T).
Note that class templates can be arbitrarily nested, so if you're trying
to get all the back to the original source code, you might have to chase
an arbitrary number of these "instantiated from" links.
John.
That did the trick, but I don't understand why, what is the difference
between getTemplatedDecl (underlying template declaration) and
getInstantiatedFromMemberTemplate (previous declaration of this
template). And then there is also getPreviousDecl (previous
declaration of this function template). Doxygen comments aren't very
revealing here, and I'm sure that knowing the terminology from the
standard would help, but can somebody please explain.Anyway this is the new patch. Two things to note here:
Much improved!
1. Since I don't really understand this special case I'm not sure if
my logic for handling it is OK (the tests that I have all pass). I'm
talking about the part inside (TK ==
TK_FunctionTemplateSpecialization) branch.
+ if (TK == FunctionDecl::TK_FunctionTemplateSpecialization) {
+ FunctionTemplateDecl *Primary = FD->getPrimaryTemplate();
+ FunctionTemplateDecl *TemplatedDecl
+ = Primary->getInstantiatedFromMemberTemplate();
+ Decl = TemplatedDecl ? TemplatedDecl->getTemplatedDecl()
+ : Primary->getTemplatedDecl();
+ }
+ else if (TK == FunctionDecl::TK_MemberSpecialization)
+ Decl = FD->getInstantiatedFromMemberFunction();
I gave getInstantiatedFromMemberTemplate() is proper Doxygen comment in r152826.
- Doug
As John noted, if you want to get back to the original source, you'll need to make this a loop. Plus, you'll probably > want to check if it was an explicit specialization and stop iterating at that point, since explicit specializations were > written by the user (rather than instantiated).
After some poking around I realized that I've seen code like this
somewhere. This is exactly what getTemplateInstantiationPattern does,
it even handles the explicit specialization case that I wasn't aware
of. But I still can't think of the test case where repeated calls to
getInstantiatedFromMemberTemplate are needed, please help.
There are a few cases here, too. For example, you could have a CXXRecordDecl that's an instantiation of a member class, e.g.,
template<typename T>
struct X {
struct Inner {
void f();
};
};and you don't want to skip "Inner" in the printing.
I think that you're wrong on this one, this loop is used only to
collect classes that have template parameters. This is how I print the
template parameter names inside the square brackets.
getQualifiedNameAsString prints the class names, I haven't touched
that part so nothing gets skipped.
It's a bit contrived, but think deeply nested templates:
template <class T> class A {
template <class U> class B {
template <class V> class C {
};
};
};
A<char>::B<char>::C<V> will be instantiated from A<char>::B<U>::C<V>, which will be instantiated from A<T>::B<U>::C<V>.
John.
Sorry John, but I tried exactly this, with C having a template member
function and getInstantiatedFromMemberTemplate is called only once.
This should be it. Additional tests for explicit specializations (both
function and class template) in which case specialized template
parameters are not mentioned. I'm still interested in adding a test
case where getInstantiatedFromMemberTemplate needs to be called more
than once, I just couldn't figure this one out.
pretty_function.txt (10.2 KB)
This is looking really good. I have a few more minor comments:
+ const FunctionDecl *Decl = FD->getTemplateInstantiationPattern();
+ if (Decl == 0) Decl = FD;
This would be cleaner as:
if (const FunctionDecl *Pattern = FD->getTemplateInstantiationPattern())
Decl = Pattern;
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
Qualifiers ThisQuals = Qualifiers::fromCVRMask(MD->getTypeQualifiers());
if (ThisQuals.hasConst())
- Proto += " const";
+ POut << " const";
if (ThisQuals.hasVolatile())
- Proto += " volatile";
+ POut << " volatile";
}
Any chance I could get you to add support for C++11 ref-qualifiers here? For example,
struct X {
X &operator=(const X&) &;
};
+ std::string TemplateParams;
+ llvm::raw_string_ostream TOut(TemplateParams);
+ for (SpecsTy::reverse_iterator I = Specs.rbegin(), E = Specs.rend();
+ I != E; ++I) {
+ const TemplateParameterList *Params
+ = (*I)->getSpecializedTemplate()->getTemplateParameters();
+ const TemplateArgumentList &Args = (*I)->getTemplateArgs();
+ assert(Params->size() == Args.size());
+ for (unsigned i = 0, numParams = Params->size(); i != numParams; ++i) {
+ TOut << Params->getParam(i)->getNameAsString() << " = ";
+ Args.get(i).print(Policy, TOut);
+ TOut << ", ";
+ }
+ }
Some template parameters might be unnamed. Shouldn't we just skip those? (Same comment below)
Thanks for working on this, it's a big improvement!
- Doug
This would be cleaner as:
if \(const FunctionDecl \*Pattern = FD\->getTemplateInstantiationPattern\(\)\) Decl = Pattern;
I wanted to save a line of code but you're right, this is easier to
understand. Fixed.
Any chance I could get you to add support for C++11 ref-qualifiers here? For example,
struct X \{ X &operator=\(const X&\) &; \};
No problem, but since I don't even know what ref-qualifiers are I'd
like to get the current version committed first if you don't mind.
Some template parameters might be unnamed. Shouldn't we just skip those? (Same comment below)
Never seen one of those in my life, but you learn something new every
day. Test added, they are skipped now.
pretty_function.txt (10.7 KB)
This is a typical case of forward declaration, where the names do not matter, only the arity (for types) and actual types (for non-types) does.
namespace std { template <typename, typename, typename, typename> class map; }
It occurs even more frequently in friends declaration, in my experience.
In essence, the idea is similar to not naming the unused parameters of a function.
– Matthieu