From 32506ed8be375ed3226adea8b507ad260dc3e31d Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Thu, 12 Jun 2014 23:03:48 +0000 Subject: [PATCH] Recover from missing 'typename' in sizeof(T::InnerType) 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 --- .../clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/include/clang/Sema/Sema.h | 7 +- clang/lib/Sema/SemaDecl.cpp | 2 +- clang/lib/Sema/SemaExpr.cpp | 45 +++++++--- clang/lib/Sema/SemaTemplate.cpp | 2 +- clang/lib/Sema/TreeTransform.h | 87 +++++++++++++------ .../ms-sizeof-missing-typename.cpp | 61 +++++++++++++ 7 files changed, 165 insertions(+), 41 deletions(-) create mode 100644 clang/test/SemaTemplate/ms-sizeof-missing-typename.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 95b204f548f06..1751860f45263 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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>; def ext_typename_outside_of_template : ExtWarn< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index ac0897e0d9c7e..414a67e256305 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -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, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 09fcf1c8cbe49..68fecdb74acb6 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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() diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 7c076f8e8448a..2ae8b27be930b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -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(), @@ -2210,15 +2211,39 @@ Sema::BuildQualifiedDeclarationNameExpr(CXXScopeSpec &SS, return ExprError(); } - if (R.isSingleResult() && R.getAsSingle()) { - // 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()) { + // 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(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 diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 68c1c7430a49b..d6e2fb61660c3 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2897,7 +2897,7 @@ Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS, if (ClassTemplateDecl *Temp = R.getAsSingle()) { 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(); } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index eba23a5def8b3..8306885854839 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -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 @@ -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. @@ -6708,7 +6716,7 @@ template ExprResult TreeTransform::TransformAddressOfOperand(Expr *E) { if (DependentScopeDeclRefExpr *DRE = dyn_cast(E)) - return getDerived().TransformDependentScopeDeclRefExpr(DRE, true); + return getDerived().TransformDependentScopeDeclRefExpr(DRE, true, nullptr); else return getDerived().TransformExpr(E); } @@ -6853,8 +6861,22 @@ TreeTransform::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(E->getArgumentExpr()); + if (auto *DRE = + PE ? dyn_cast(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()) @@ -8234,18 +8256,37 @@ TreeTransform::TransformExpressionTraitExpr(ExpressionTraitExpr *E) { E->getTrait(), E->getLocStart(), SubExpr.get(), E->getLocEnd()); } -template -ExprResult -TreeTransform::TransformDependentScopeDeclRefExpr( - DependentScopeDeclRefExpr *E) { - return TransformDependentScopeDeclRefExpr(E, /*IsAddressOfOperand*/false); +template +ExprResult TreeTransform::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 +ExprResult TreeTransform::TransformDependentScopeDeclRefExpr( + DependentScopeDeclRefExpr *E) { + return TransformDependentScopeDeclRefExpr(E, /*IsAddressOfOperand=*/false, + nullptr); } template ExprResult TreeTransform::TransformDependentScopeDeclRefExpr( DependentScopeDeclRefExpr *E, - bool IsAddressOfOperand) { + bool IsAddressOfOperand, + TypeSourceInfo **RecoveryTSI) { assert(E->getQualifierLoc()); NestedNameSpecifierLoc QualifierLoc = getDerived().TransformNestedNameSpecifierLoc(E->getQualifierLoc()); @@ -8270,11 +8311,9 @@ TreeTransform::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()); @@ -8283,11 +8322,9 @@ TreeTransform::TransformDependentScopeDeclRefExpr( TransArgs)) return ExprError(); - return getDerived().RebuildDependentScopeDeclRefExpr(QualifierLoc, - TemplateKWLoc, - NameInfo, - &TransArgs, - IsAddressOfOperand); + return getDerived().RebuildDependentScopeDeclRefExpr( + QualifierLoc, TemplateKWLoc, NameInfo, &TransArgs, IsAddressOfOperand, + RecoveryTSI); } template diff --git a/clang/test/SemaTemplate/ms-sizeof-missing-typename.cpp b/clang/test/SemaTemplate/ms-sizeof-missing-typename.cpp new file mode 100644 index 0000000000000..ff8984feb795b --- /dev/null +++ b/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 int type_f() { return sizeof T::type; } // expected-error {{missing 'typename' prior to dependent type name 'X::type'}} +template int type_g() { return sizeof(T::type); } // expected-warning {{missing 'typename' prior to dependent type name 'X::type'}} +template int type_h() { return sizeof((T::type)); } // expected-error {{missing 'typename' prior to dependent type name 'X::type'}} +template int value_f() { return sizeof T::not_a_type; } +template int value_g() { return sizeof(T::not_a_type); } +template 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() + // expected-note-re {{in instantiation {{.*}} requested here}} + type_g() + // expected-note-re {{in instantiation {{.*}} requested here}} + type_h() + // expected-note-re {{in instantiation {{.*}} requested here}} + value_f() + + value_f() + + value_f(); +} +} + +namespace nested_sizeof { +template +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), + // expected-warning@+1 {{missing 'typename' prior to dependent type name 'Bar::InnerType'}} + x2 = sizeof(typename T::template InnerTemplate), + // expected-warning@+1 {{use 'template' keyword to treat 'InnerTemplate' as a dependent template name}} + y1 = sizeof(typename T::/*template*/ InnerTemplate), + y2 = sizeof(typename T::template InnerTemplate), + z = sizeof(T::template InnerTemplate::x), + }; +}; +struct Bar { + template + struct InnerTemplate { int x[N]; }; + typedef double InnerType; + static const int InnerVar = 42; +}; +template struct Foo; // 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 void f() { int a = sizeof T::template U<0> + 4; } +struct Q { + // expected-error@+1 {{class template declared here}} + template struct U {}; +}; +// expected-note-re@+1 {{in instantiation {{.*}} requested here}} +template void f(); +}