I was very puzzled to learn that AST for the fragment
@interface I
@end
template <typename>
void decode(I *p) {
for (I *k in p) {}
}
void decode(I *p) {
decode<int>(p);
}differs heavily when templatized and non-templatized:
|-FunctionTemplateDecl 0x7fbfe0863940 <line:4:1, line:7:1> line:5:6 decode | |-TemplateTypeParmDecl 0x7fbfe0863690 <line:4:11> col:11 typename depth 0 index 0 | |-FunctionDecl 0x7fbfe08638a0 <line:5:1, line:7:1> line:5:6 decode 'void (I *__strong)' | | |-ParmVarDecl 0x7fbfe08637a0 <col:13, col:16> col:16 referenced p 'I *__strong' | | `-CompoundStmt 0x7fbfe0863b88 <col:19, line:7:1> | | `-ObjCForCollectionStmt 0x7fbfe0863b50 <line:6:3, col:20> | | |-DeclStmt 0x7fbfe0863a50 <col:8, col:13> | | | `-VarDecl 0x7fbfe08639f0 <col:8, col:11> col:11 k 'I *const __strong' | | |-ImplicitCastExpr 0x7fbfe0863a90 <col:16> 'I *' <LValueToRValue> | | | `-DeclRefExpr 0x7fbfe0863a68 <col:16> 'I *__strong' lvalue ParmVar 0x7fbfe08637a0 'p' 'I *__strong' | | `-CompoundStmt 0x7fbfe0863b78 <col:19, col:20> | `-FunctionDecl 0x7fbfe0863f80 <line:5:1, line:7:1> line:5:6 used decode 'void (I *__strong)' | |-TemplateArgument type 'int' | |-ParmVarDecl 0x7fbfe0863ef8 <col:13, col:16> col:16 used p 'I *__strong' | `-CompoundStmt 0x7fbfe0890cf0 <col:19, line:7:1> | `-ObjCForCollectionStmt 0x7fbfe0890cc8 <line:6:3, col:20> | |-DeclStmt 0x7fbfe0890c70 <col:8, col:13> | | `-VarDecl 0x7fbfe0890c00 <col:8, col:11> col:11 k 'I *__strong' callinit | | `-ImplicitValueInitExpr 0x7fbfe0890c60 <<invalid sloc>> 'I *__strong' | |-ImplicitCastExpr 0x7fbfe0890cb0 <col:16> 'I *' <LValueToRValue> | | `-DeclRefExpr 0x7fbfe0890c88 <col:16> 'I *__strong' lvalue ParmVar 0x7fbfe0863ef8 'p' 'I *__strong' | `-CompoundStmt 0x7fbfe0863b78 <col:19, col:20>
Note how in the instantiated version ImplicitValueInitExpr unexpectedly appears.
While objects are auto-initialized under ARC, it does not make sense to have an initializer for a for-loop variable, and it makes even less sense to have such a different AST for instantiated and non-instantiated version.
Digging deeper, I have found that there are two separate Sema* files for dealing with templates and for dealing with non-templatized code. In a non-templatized version, an initialization was performed only for variables which are not loop variables for an Objective-C loop and not variables for a C++ for-in loop:
if (FRI && (Tok.is(tok::colon) || isTokIdentifier_in())) {
bool IsForRangeLoop = false;
if (TryConsumeToken(tok::colon, FRI->ColonLoc)) {
IsForRangeLoop = true;
if (Tok.is(tok::l_brace))
FRI->RangeExpr = ParseBraceInitializer();
else
FRI->RangeExpr = ParseExpression();
}
Decl *ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
if (IsForRangeLoop)
Actions.ActOnCXXForRangeDecl(ThisDecl);
Actions.FinalizeDeclaration(ThisDecl);
D.complete(ThisDecl);
return Actions.FinalizeDeclaratorGroup(getCurScope(), DS, ThisDecl);
}
SmallVector<Decl *, 8> DeclsInGroup;
Decl *FirstDecl = ParseDeclarationAfterDeclaratorAndAttributes(
D, ParsedTemplateInfo(), FRI);However the code in SemaTemplateInstantiateDecl was inconsistent, guarding only against C++ for-in loops.
rdar://38391075