Skip to content

Commit

Permalink
Recover from missing 'typename' in sizeof(T::InnerType)
Browse files Browse the repository at this point in the history
Summary:
'sizeof' is a UnaryExprOrTypeTrait, and it can contain either a type or
an expression.  This change threads a RecoveryTSI parameter through the
layers between TransformUnaryExprOrTypeTrait the point at which we look
up the type.  If lookup finds a single type result after instantiation,
we now build TypeSourceInfo for it just like a normal transformation
would.

This fixes the last error in the hello world ATL app that I've been
working with, and it now links and runs with clang.  Please try it and
file bugs!

Reviewers: rsmith

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D4108

llvm-svn: 210855
  • Loading branch information
rnk committed Jun 12, 2014
1 parent 02ae690 commit 32506ed
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 41 deletions.
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -3550,7 +3550,7 @@ def note_typename_refers_here : Note<
"referenced member %0 is declared here">;
def err_typename_missing : Error<
"missing 'typename' prior to dependent type name '%0%1'">;
def warn_typename_missing : ExtWarn<
def ext_typename_missing : ExtWarn<
"missing 'typename' prior to dependent type name '%0%1'">,
InGroup<DiagGroup<"typename-missing">>;
def ext_typename_outside_of_template : ExtWarn<
Expand Down
7 changes: 4 additions & 3 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -3403,9 +3403,10 @@ class Sema {
const LookupResult &R,
bool HasTrailingLParen);

ExprResult BuildQualifiedDeclarationNameExpr(CXXScopeSpec &SS,
const DeclarationNameInfo &NameInfo,
bool IsAddressOfOperand);
ExprResult BuildQualifiedDeclarationNameExpr(
CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
bool IsAddressOfOperand, TypeSourceInfo **RecoveryTSI = nullptr);

ExprResult BuildDependentDeclRefExpr(const CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
const DeclarationNameInfo &NameInfo,
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaDecl.cpp
Expand Up @@ -517,7 +517,7 @@ bool Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II,
else if (isDependentScopeSpecifier(*SS)) {
unsigned DiagID = diag::err_typename_missing;
if (getLangOpts().MSVCCompat && isMicrosoftMissingTypename(SS, S))
DiagID = diag::warn_typename_missing;
DiagID = diag::ext_typename_missing;

Diag(SS->getRange().getBegin(), DiagID)
<< SS->getScopeRep() << II->getName()
Expand Down
45 changes: 35 additions & 10 deletions clang/lib/Sema/SemaExpr.cpp
Expand Up @@ -2185,7 +2185,8 @@ ExprResult Sema::ActOnIdExpression(Scope *S,
ExprResult
Sema::BuildQualifiedDeclarationNameExpr(CXXScopeSpec &SS,
const DeclarationNameInfo &NameInfo,
bool IsAddressOfOperand) {
bool IsAddressOfOperand,
TypeSourceInfo **RecoveryTSI) {
DeclContext *DC = computeDeclContext(SS, false);
if (!DC)
return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(),
Expand All @@ -2210,15 +2211,39 @@ Sema::BuildQualifiedDeclarationNameExpr(CXXScopeSpec &SS,
return ExprError();
}

if (R.isSingleResult() && R.getAsSingle<TypeDecl>()) {
// Diagnose a missing typename if this resolved unambiguously to a type in a
// dependent context.
// FIXME: Issue a fixit and recover as though the user had written
// 'typename'.
Diag(SS.getBeginLoc(), diag::err_typename_missing)
<< SS.getScopeRep() << NameInfo.getName().getAsString()
<< SourceRange(SS.getBeginLoc(), NameInfo.getEndLoc());
return ExprError();
if (const TypeDecl *TD = R.getAsSingle<TypeDecl>()) {
// Diagnose a missing typename if this resolved unambiguously to a type in
// a dependent context. If we can recover with a type, downgrade this to
// a warning in Microsoft compatibility mode.
unsigned DiagID = diag::err_typename_missing;
if (RecoveryTSI && getLangOpts().MSVCCompat)
DiagID = diag::ext_typename_missing;
SourceLocation Loc = SS.getBeginLoc();
auto D = Diag(Loc, DiagID);
D << SS.getScopeRep() << NameInfo.getName().getAsString()
<< SourceRange(Loc, NameInfo.getEndLoc());

// Don't recover if the caller isn't expecting us to or if we're in a SFINAE
// context.
if (!RecoveryTSI)
return ExprError();

// Only issue the fixit if we're prepared to recover.
D << FixItHint::CreateInsertion(Loc, "typename ");

// Recover by pretending this was an elaborated type.
QualType Ty = Context.getTypeDeclType(TD);
TypeLocBuilder TLB;
TLB.pushTypeSpec(Ty).setNameLoc(NameInfo.getLoc());

QualType ET = getElaboratedType(ETK_None, SS, Ty);
ElaboratedTypeLoc QTL = TLB.push<ElaboratedTypeLoc>(ET);
QTL.setElaboratedKeywordLoc(SourceLocation());
QTL.setQualifierLoc(SS.getWithLocInContext(Context));

*RecoveryTSI = TLB.getTypeSourceInfo(Context, ET);

return ExprEmpty();
}

// Defend against this resolving to an implicit member access. We usually
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaTemplate.cpp
Expand Up @@ -2897,7 +2897,7 @@ Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS,
if (ClassTemplateDecl *Temp = R.getAsSingle<ClassTemplateDecl>()) {
Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_class_template)
<< SS.getScopeRep()
<< NameInfo.getName() << SS.getRange();
<< NameInfo.getName().getAsString() << SS.getRange();
Diag(Temp->getLocation(), diag::note_referenced_class_template);
return ExprError();
}
Expand Down
87 changes: 62 additions & 25 deletions clang/lib/Sema/TreeTransform.h
Expand Up @@ -604,8 +604,15 @@ class TreeTransform {
}

ExprResult TransformAddressOfOperand(Expr *E);

ExprResult TransformDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E,
bool IsAddressOfOperand);
bool IsAddressOfOperand,
TypeSourceInfo **RecoveryTSI);

ExprResult TransformParenDependentScopeDeclRefExpr(
ParenExpr *PE, DependentScopeDeclRefExpr *DRE, bool IsAddressOfOperand,
TypeSourceInfo **RecoveryTSI);

StmtResult TransformOMPExecutableDirective(OMPExecutableDirective *S);

// FIXME: We use LLVM_ATTRIBUTE_NOINLINE because inlining causes a ridiculous
Expand Down Expand Up @@ -2288,16 +2295,17 @@ class TreeTransform {
SourceLocation TemplateKWLoc,
const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs,
bool IsAddressOfOperand) {
bool IsAddressOfOperand,
TypeSourceInfo **RecoveryTSI) {
CXXScopeSpec SS;
SS.Adopt(QualifierLoc);

if (TemplateArgs || TemplateKWLoc.isValid())
return getSema().BuildQualifiedTemplateIdExpr(SS, TemplateKWLoc,
NameInfo, TemplateArgs);
return getSema().BuildQualifiedTemplateIdExpr(SS, TemplateKWLoc, NameInfo,
TemplateArgs);

return getSema().BuildQualifiedDeclarationNameExpr(SS, NameInfo,
IsAddressOfOperand);
return getSema().BuildQualifiedDeclarationNameExpr(
SS, NameInfo, IsAddressOfOperand, RecoveryTSI);
}

/// \brief Build a new template-id expression.
Expand Down Expand Up @@ -6708,7 +6716,7 @@ template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformAddressOfOperand(Expr *E) {
if (DependentScopeDeclRefExpr *DRE = dyn_cast<DependentScopeDeclRefExpr>(E))
return getDerived().TransformDependentScopeDeclRefExpr(DRE, true);
return getDerived().TransformDependentScopeDeclRefExpr(DRE, true, nullptr);
else
return getDerived().TransformExpr(E);
}
Expand Down Expand Up @@ -6853,8 +6861,22 @@ TreeTransform<Derived>::TransformUnaryExprOrTypeTraitExpr(
EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated,
Sema::ReuseLambdaContextDecl);

ExprResult SubExpr = getDerived().TransformExpr(E->getArgumentExpr());
if (SubExpr.isInvalid())
// Try to recover if we have something like sizeof(T::X) where X is a type.
// Notably, there must be *exactly* one set of parens if X is a type.
TypeSourceInfo *RecoveryTSI = nullptr;
ExprResult SubExpr;
auto *PE = dyn_cast<ParenExpr>(E->getArgumentExpr());
if (auto *DRE =
PE ? dyn_cast<DependentScopeDeclRefExpr>(PE->getSubExpr()) : nullptr)
SubExpr = getDerived().TransformParenDependentScopeDeclRefExpr(
PE, DRE, false, &RecoveryTSI);
else
SubExpr = getDerived().TransformExpr(E->getArgumentExpr());

if (RecoveryTSI) {
return getDerived().RebuildUnaryExprOrTypeTrait(
RecoveryTSI, E->getOperatorLoc(), E->getKind(), E->getSourceRange());
} else if (SubExpr.isInvalid())
return ExprError();

if (!getDerived().AlwaysRebuild() && SubExpr.get() == E->getArgumentExpr())
Expand Down Expand Up @@ -8234,18 +8256,37 @@ TreeTransform<Derived>::TransformExpressionTraitExpr(ExpressionTraitExpr *E) {
E->getTrait(), E->getLocStart(), SubExpr.get(), E->getLocEnd());
}

template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformDependentScopeDeclRefExpr(
DependentScopeDeclRefExpr *E) {
return TransformDependentScopeDeclRefExpr(E, /*IsAddressOfOperand*/false);
template <typename Derived>
ExprResult TreeTransform<Derived>::TransformParenDependentScopeDeclRefExpr(
ParenExpr *PE, DependentScopeDeclRefExpr *DRE, bool AddrTaken,
TypeSourceInfo **RecoveryTSI) {
ExprResult NewDRE = getDerived().TransformDependentScopeDeclRefExpr(
DRE, AddrTaken, RecoveryTSI);

// Propagate both errors and recovered types, which return ExprEmpty.
if (!NewDRE.isUsable())
return NewDRE;

// We got an expr, wrap it up in parens.
if (!getDerived().AlwaysRebuild() && NewDRE.get() == DRE)
return PE;
return getDerived().RebuildParenExpr(NewDRE.get(), PE->getLParen(),
PE->getRParen());
}

template <typename Derived>
ExprResult TreeTransform<Derived>::TransformDependentScopeDeclRefExpr(
DependentScopeDeclRefExpr *E) {
return TransformDependentScopeDeclRefExpr(E, /*IsAddressOfOperand=*/false,
nullptr);
}

template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformDependentScopeDeclRefExpr(
DependentScopeDeclRefExpr *E,
bool IsAddressOfOperand) {
bool IsAddressOfOperand,
TypeSourceInfo **RecoveryTSI) {
assert(E->getQualifierLoc());
NestedNameSpecifierLoc QualifierLoc
= getDerived().TransformNestedNameSpecifierLoc(E->getQualifierLoc());
Expand All @@ -8270,11 +8311,9 @@ TreeTransform<Derived>::TransformDependentScopeDeclRefExpr(
NameInfo.getName() == E->getDeclName())
return E;

return getDerived().RebuildDependentScopeDeclRefExpr(QualifierLoc,
TemplateKWLoc,
NameInfo,
/*TemplateArgs*/nullptr,
IsAddressOfOperand);
return getDerived().RebuildDependentScopeDeclRefExpr(
QualifierLoc, TemplateKWLoc, NameInfo, /*TemplateArgs=*/nullptr,
IsAddressOfOperand, RecoveryTSI);
}

TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc());
Expand All @@ -8283,11 +8322,9 @@ TreeTransform<Derived>::TransformDependentScopeDeclRefExpr(
TransArgs))
return ExprError();

return getDerived().RebuildDependentScopeDeclRefExpr(QualifierLoc,
TemplateKWLoc,
NameInfo,
&TransArgs,
IsAddressOfOperand);
return getDerived().RebuildDependentScopeDeclRefExpr(
QualifierLoc, TemplateKWLoc, NameInfo, &TransArgs, IsAddressOfOperand,
RecoveryTSI);
}

template<typename Derived>
Expand Down
61 changes: 61 additions & 0 deletions clang/test/SemaTemplate/ms-sizeof-missing-typename.cpp
@@ -0,0 +1,61 @@
// RUN: %clang_cc1 -std=c++11 -fms-compatibility -fsyntax-only -verify %s

// If we were even more clever, we'd tell the user to use one set of parens to
// get the size of this type, so they don't get errors after inserting typename.

namespace basic {
template <typename T> int type_f() { return sizeof T::type; } // expected-error {{missing 'typename' prior to dependent type name 'X::type'}}
template <typename T> int type_g() { return sizeof(T::type); } // expected-warning {{missing 'typename' prior to dependent type name 'X::type'}}
template <typename T> int type_h() { return sizeof((T::type)); } // expected-error {{missing 'typename' prior to dependent type name 'X::type'}}
template <typename T> int value_f() { return sizeof T::not_a_type; }
template <typename T> int value_g() { return sizeof(T::not_a_type); }
template <typename T> int value_h() { return sizeof((T::not_a_type)); }
struct X {
typedef int type;
static const int not_a_type;
};
int bar() {
return
type_f<X>() + // expected-note-re {{in instantiation {{.*}} requested here}}
type_g<X>() + // expected-note-re {{in instantiation {{.*}} requested here}}
type_h<X>() + // expected-note-re {{in instantiation {{.*}} requested here}}
value_f<X>() +
value_f<X>() +
value_f<X>();
}
}

namespace nested_sizeof {
template <typename T>
struct Foo {
enum {
// expected-warning@+2 {{use 'template' keyword to treat 'InnerTemplate' as a dependent template name}}
// expected-warning@+1 {{missing 'typename' prior to dependent type name 'Bar::InnerType'}}
x1 = sizeof(typename T::/*template*/ InnerTemplate<sizeof(/*typename*/ T::InnerType)>),
// expected-warning@+1 {{missing 'typename' prior to dependent type name 'Bar::InnerType'}}
x2 = sizeof(typename T::template InnerTemplate<sizeof(/*typename*/ T::InnerType)>),
// expected-warning@+1 {{use 'template' keyword to treat 'InnerTemplate' as a dependent template name}}
y1 = sizeof(typename T::/*template*/ InnerTemplate<sizeof(T::InnerVar)>),
y2 = sizeof(typename T::template InnerTemplate<sizeof(T::InnerVar)>),
z = sizeof(T::template InnerTemplate<sizeof(T::InnerVar)>::x),
};
};
struct Bar {
template <int N>
struct InnerTemplate { int x[N]; };
typedef double InnerType;
static const int InnerVar = 42;
};
template struct Foo<Bar>; // expected-note-re {{in instantiation {{.*}} requested here}}
}

namespace ambiguous_missing_parens {
// expected-error@+1 {{'Q::U' instantiated to a class template, not a function template}}
template <typename T> void f() { int a = sizeof T::template U<0> + 4; }
struct Q {
// expected-error@+1 {{class template declared here}}
template <int> struct U {};
};
// expected-note-re@+1 {{in instantiation {{.*}} requested here}}
template void f<Q>();
}

0 comments on commit 32506ed

Please sign in to comment.