From: Douglas Gregor Date: Tue, 13 Jul 2010 08:18:22 +0000 (+0000) Subject: When forming a function call or message send expression, be sure to X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5291c3cec0dbe8ad1d8e7e67e93af2b1586d5400;p=clang When forming a function call or message send expression, be sure to strip cv-qualifiers from the expression's type when the language calls for it: in C, that's all the time, while C++ only does it for non-class types. Centralized the computation of the call expression type in QualType::getCallResultType() and some helper functions in other nodes (FunctionDecl, ObjCMethodDecl, FunctionType), and updated all relevant callers of getResultType() to getCallResultType(). Fixes PR7598 and PR7463, along with a bunch of getResultType() call sites that weren't stripping references off the result type (nothing stripped cv-qualifiers properly before this change). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@108234 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index d5a7ec029d..39cd51f606 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -1382,6 +1382,12 @@ public: QualType getResultType() const { return getType()->getAs()->getResultType(); } + + /// \brief Determine the type of an expression that calls this function. + QualType getCallResultType() const { + return getType()->getAs()->getCallResultType(getASTContext()); + } + StorageClass getStorageClass() const { return StorageClass(SClass); } void setStorageClass(StorageClass SC) { SClass = SC; } diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index b0fd4cc96f..fb8596f50a 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -239,6 +239,12 @@ public: QualType getResultType() const { return MethodDeclType; } void setResultType(QualType T) { MethodDeclType = T; } + /// \brief Determine the type of an expression that sends a message to this + /// function. + QualType getSendResultType() const { + return getResultType().getCallResultType(getASTContext()); + } + TypeSourceInfo *getResultTypeSourceInfo() const { return ResultTInfo; } void setResultTypeSourceInfo(TypeSourceInfo *TInfo) { ResultTInfo = TInfo; } diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 8d03641e9d..a1a29e6fef 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -629,6 +629,14 @@ public: bool isAtLeastAsQualifiedAs(QualType Other) const; QualType getNonReferenceType() const; + /// \brief Determine the type of an expression that calls a function of + /// with the given result type. + /// + /// This routine removes a top-level reference (since there are no + /// expressions of reference type) and deletes top-level cvr-qualifiers + /// from non-class types (in C++) or all types (in C). + QualType getCallResultType(ASTContext &Context) const; + /// getDesugaredType - Return the specified type with any "sugar" removed from /// the type. This takes off typedefs, typeof's etc. If the outer level of /// the type is already concrete, it returns it unmodified. This is similar @@ -1888,6 +1896,7 @@ protected: public: QualType getResultType() const { return ResultType; } + unsigned getRegParmType() const { return RegParm; } bool getNoReturnAttr() const { return NoReturn; } CallingConv getCallConv() const { return (CallingConv)CallConv; } @@ -1895,6 +1904,12 @@ public: return ExtInfo(NoReturn, RegParm, (CallingConv)CallConv); } + /// \brief Determine the type of an expression that calls a function of + /// this type. + QualType getCallResultType(ASTContext &Context) const { + return getResultType().getCallResultType(Context); + } + static llvm::StringRef getNameForCallConv(CallingConv CC); static bool classof(const Type *T) { diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index c56631b4dc..edd0dcb915 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -120,6 +120,8 @@ def warn_use_out_of_scope_declaration : Warning< "use of out-of-scope declaration of %0">; def err_inline_non_function : Error< "'inline' can only appear on functions">; +def warn_qual_return_type : Warning< + "type qualifier on return type has no effect">; def warn_decl_shadow : Warning<"declaration shadows a %select{" diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index cc6ad5ab46..bd97b886fe 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -576,7 +576,10 @@ QualType CallExpr::getCallReturnType() const { CalleeType = FnTypePtr->getPointeeType(); else if (const BlockPointerType *BPT = CalleeType->getAs()) CalleeType = BPT->getPointeeType(); - + else if (const MemberPointerType *MPT + = CalleeType->getAs()) + CalleeType = MPT->getPointeeType(); + const FunctionType *FnType = CalleeType->getAs(); return FnType->getResultType(); } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 5a7aa89d6d..ab64eafbee 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -992,6 +992,22 @@ const char *BuiltinType::getName(const LangOptions &LO) const { void FunctionType::ANCHOR() {} // Key function for FunctionType. +QualType QualType::getCallResultType(ASTContext &Context) const { + if (const ReferenceType *RefType = getTypePtr()->getAs()) + return RefType->getPointeeType(); + + // C++0x [basic.lval]: + // Class prvalues can have cv-qualified types; non-class prvalues always + // have cv-unqualified types. + // + // See also C99 6.3.2.1p2. + if (!Context.getLangOptions().CPlusPlus || + !getTypePtr()->isDependentType() && !getTypePtr()->isRecordType()) + return getUnqualifiedType(); + + return *this; +} + llvm::StringRef FunctionType::getNameForCallConv(CallingConv CC) { switch (CC) { case CC_Default: llvm_unreachable("no name for default cc"); diff --git a/lib/Rewrite/RewriteObjC.cpp b/lib/Rewrite/RewriteObjC.cpp index 204ec26415..489fec9be0 100644 --- a/lib/Rewrite/RewriteObjC.cpp +++ b/lib/Rewrite/RewriteObjC.cpp @@ -2113,8 +2113,8 @@ CallExpr *RewriteObjC::SynthesizeCallToFunctionDecl( const FunctionType *FT = msgSendType->getAs(); CallExpr *Exp = - new (Context) CallExpr(*Context, ICE, args, nargs, FT->getResultType(), - EndLoc); + new (Context) CallExpr(*Context, ICE, args, nargs, + FT->getCallResultType(*Context), EndLoc); return Exp; } diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 217ada01c6..7a39f058c5 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -561,7 +561,7 @@ Sema::SemaBuiltinAtomicOverloaded(OwningExprResult TheCallResult) { TheCall->setCallee(PromotedCall); // Change the result type of the call to match the result type of the decl. - TheCall->setType(NewBuiltinDecl->getResultType()); + TheCall->setType(NewBuiltinDecl->getCallResultType()); // If the value type was converted to an integer when processing the // arguments (e.g. void* -> int), we need to convert the result back. diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 6a706dfefe..82861101e8 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -567,11 +567,11 @@ static QualType getDeclUsageType(ASTContext &C, NamedDecl *ND) { QualType T; if (FunctionDecl *Function = dyn_cast(ND)) - T = Function->getResultType(); + T = Function->getCallResultType(); else if (ObjCMethodDecl *Method = dyn_cast(ND)) - T = Method->getResultType(); + T = Method->getSendResultType(); else if (FunctionTemplateDecl *FunTmpl = dyn_cast(ND)) - T = FunTmpl->getTemplatedDecl()->getResultType(); + T = FunTmpl->getTemplatedDecl()->getCallResultType(); else if (EnumConstantDecl *Enumerator = dyn_cast(ND)) T = C.getTypeDeclType(cast(Enumerator->getDeclContext())); else if (ObjCPropertyDecl *Property = dyn_cast(ND)) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 9f9858001e..ce3bf11caa 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3004,7 +3004,7 @@ Sema::LookupMemberExpr(LookupResult &R, Expr *&BaseExpr, QualType PType; if (Getter) - PType = Getter->getResultType(); + PType = Getter->getSendResultType(); else // Get the expression type from Setter's incoming parameter. PType = (*(Setter->param_end() -1))->getType(); @@ -3180,7 +3180,7 @@ Sema::LookupMemberExpr(LookupResult &R, Expr *&BaseExpr, return ExprError(); return Owned(ObjCMessageExpr::Create(Context, - OMD->getResultType().getNonReferenceType(), + OMD->getSendResultType(), OpLoc, BaseExpr, Sel, OMD, NULL, 0, MemberLoc)); } @@ -3575,7 +3575,7 @@ Sema::ActOnCallExpr(Scope *S, ExprArg fn, SourceLocation LParenLoc, BO->getOpcode() == BinaryOperator::PtrMemI) { if (const FunctionProtoType *FPT = BO->getType()->getAs()) { - QualType ResultTy = FPT->getResultType().getNonReferenceType(); + QualType ResultTy = FPT->getCallResultType(Context); ExprOwningPtr TheCall(this, new (Context) CXXMemberCallExpr(Context, BO, Args, @@ -3665,7 +3665,7 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, return ExprError(); // We know the result type of the call, set it. - TheCall->setType(FuncT->getResultType().getNonReferenceType()); + TheCall->setType(FuncT->getCallResultType(Context)); if (const FunctionProtoType *Proto = dyn_cast(FuncT)) { if (ConvertArgumentsForCall(&*TheCall, Fn, FDecl, Proto, Args, NumArgs, diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 180b5911bf..5c693ab358 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -3032,7 +3032,7 @@ CXXMemberCallExpr *Sema::BuildCXXMemberCallExpr(Expr *Exp, MemberExpr *ME = new (Context) MemberExpr(Exp, /*IsArrow=*/false, Method, SourceLocation(), Method->getType()); - QualType ResultType = Method->getResultType().getNonReferenceType(); + QualType ResultType = Method->getCallResultType(); MarkDeclarationReferenced(Exp->getLocStart(), Method); CXXMemberCallExpr *CE = new (Context) CXXMemberCallExpr(Context, ME, 0, 0, ResultType, diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 520ea2af91..9f43471e0a 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -207,7 +207,7 @@ bool Sema::CheckMessageArgumentTypes(Expr **Args, unsigned NumArgs, return false; } - ReturnType = Method->getResultType().getNonReferenceType(); + ReturnType = Method->getSendResultType(); unsigned NumNamedArgs = Sel.getNumArgs(); // Method might have more arguments than selector indicates. This is due @@ -346,7 +346,7 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, Selector Sel = PP.getSelectorTable().getNullarySelector(Member); ObjCMethodDecl *Getter = IFace->lookupInstanceMethod(Sel); if (DiagnosePropertyAccessorMismatch(PD, Getter, MemberLoc)) - ResTy = Getter->getResultType(); + ResTy = Getter->getSendResultType(); return Owned(new (Context) ObjCPropertyRefExpr(PD, ResTy, MemberLoc, BaseExpr)); } @@ -402,7 +402,7 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, if (Getter) { QualType PType; - PType = Getter->getResultType(); + PType = Getter->getSendResultType(); return Owned(new (Context) ObjCImplicitSetterGetterRefExpr(Getter, PType, Setter, MemberLoc, BaseExpr)); } @@ -510,7 +510,7 @@ ActOnClassPropertyRefExpr(IdentifierInfo &receiverName, QualType PType; if (Getter) - PType = Getter->getResultType(); + PType = Getter->getSendResultType(); else { for (ObjCMethodDecl::param_iterator PI = Setter->param_begin(), E = Setter->param_end(); PI != E; ++PI) diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 5571c1b382..7536289afc 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -2942,7 +2942,7 @@ static void TryUserDefinedConversion(Sema &S, } // Add the user-defined conversion step that calls the conversion function. - QualType ConvType = Function->getResultType().getNonReferenceType(); + QualType ConvType = Function->getCallResultType(); if (ConvType->getAs()) { // If we're converting to a class type, there may be an copy if // the resulting temporary object (possible to create an object of diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 5b511d7d03..ee4c479f72 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -6668,7 +6668,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, DiagnoseUseOfDecl(Best->FoundDecl, OpLoc); // Determine the result type - QualType ResultTy = FnDecl->getResultType().getNonReferenceType(); + QualType ResultTy = FnDecl->getCallResultType(); // Build the actual expression node. Expr *FnExpr = new (Context) DeclRefExpr(FnDecl, FnDecl->getType(), @@ -6875,8 +6875,8 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, // Determine the result type QualType ResultTy - = FnDecl->getType()->getAs()->getResultType(); - ResultTy = ResultTy.getNonReferenceType(); + = FnDecl->getType()->getAs() + ->getCallResultType(Context); // Build the actual expression node. Expr *FnExpr = new (Context) DeclRefExpr(FnDecl, FnDecl->getType(), @@ -7032,8 +7032,8 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc, // Determine the result type QualType ResultTy - = FnDecl->getType()->getAs()->getResultType(); - ResultTy = ResultTy.getNonReferenceType(); + = FnDecl->getType()->getAs() + ->getCallResultType(Context); // Build the actual expression node. Expr *FnExpr = new (Context) DeclRefExpr(FnDecl, FnDecl->getType(), @@ -7221,7 +7221,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, ExprOwningPtr TheCall(this, new (Context) CXXMemberCallExpr(Context, MemExprE, Args, NumArgs, - Method->getResultType().getNonReferenceType(), + Method->getCallResultType(), RParenLoc)); // Check for a valid return type. @@ -7436,7 +7436,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Object, // Once we've built TheCall, all of the expressions are properly // owned. - QualType ResultTy = Method->getResultType().getNonReferenceType(); + QualType ResultTy = Method->getCallResultType(); ExprOwningPtr TheCall(this, new (Context) CXXOperatorCallExpr(Context, OO_Call, NewFn, MethodArgs, NumArgs + 1, @@ -7592,7 +7592,7 @@ Sema::BuildOverloadedArrowExpr(Scope *S, ExprArg BaseIn, SourceLocation OpLoc) { SourceLocation()); UsualUnaryConversions(FnExpr); - QualType ResultTy = Method->getResultType().getNonReferenceType(); + QualType ResultTy = Method->getCallResultType(); ExprOwningPtr TheCall(this, new (Context) CXXOperatorCallExpr(Context, OO_Arrow, FnExpr, &Base, 1, ResultTy, OpLoc)); diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 48bf7cbac4..dc3cea1a3c 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -818,7 +818,7 @@ QualType Sema::BuildFunctionType(QualType T, << T->isFunctionType() << T; return QualType(); } - + bool Invalid = false; for (unsigned Idx = 0; Idx < NumParamTypes; ++Idx) { QualType ParamType = adjustParameterType(ParamTypes[Idx]); @@ -1129,6 +1129,31 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, D.setInvalidType(true); } + // cv-qualifiers on return types are pointless except when the type is a + // class type in C++. + if (T.getCVRQualifiers() && D.getDeclSpec().getTypeQualifiers() && + (!getLangOptions().CPlusPlus || + (!T->isDependentType() && !T->isRecordType()))) { + unsigned Quals = D.getDeclSpec().getTypeQualifiers(); + SourceLocation Loc; + if (Quals & Qualifiers::Const) + Loc = D.getDeclSpec().getConstSpecLoc(); + else if (Quals & Qualifiers::Volatile) + Loc = D.getDeclSpec().getVolatileSpecLoc(); + else { + assert((Quals & Qualifiers::Restrict) && "Unknown type qualifier"); + Loc = D.getDeclSpec().getRestrictSpecLoc(); + } + + SemaDiagnosticBuilder DB = Diag(Loc, diag::warn_qual_return_type); + if (Quals & Qualifiers::Const) + DB << FixItHint::CreateRemoval(D.getDeclSpec().getConstSpecLoc()); + if (Quals & Qualifiers::Volatile) + DB << FixItHint::CreateRemoval(D.getDeclSpec().getVolatileSpecLoc()); + if (Quals & Qualifiers::Restrict) + DB << FixItHint::CreateRemoval(D.getDeclSpec().getRestrictSpecLoc()); + } + if (getLangOptions().CPlusPlus && D.getDeclSpec().isTypeSpecOwned()) { // C++ [dcl.fct]p6: // Types shall not be defined in return or parameter types. diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index db5e2d10f4..f3ad6c8b4b 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1952,7 +1952,7 @@ public: Expr **Subs = (Expr **)SubExprs.release(); CallExpr *TheCall = new (SemaRef.Context) CallExpr(SemaRef.Context, Callee, Subs, NumSubExprs, - Builtin->getResultType(), + Builtin->getCallResultType(), RParenLoc); OwningExprResult OwnedCall(SemaRef.Owned(TheCall)); diff --git a/test/CodeGen/volatile.c b/test/CodeGen/volatile.c index db87a37515..1a996defcf 100644 --- a/test/CodeGen/volatile.c +++ b/test/CodeGen/volatile.c @@ -1,8 +1,8 @@ // RUN: %clang_cc1 -emit-llvm < %s -o %t -// RUN: grep volatile %t | count 29 +// RUN: grep volatile %t | count 28 // RUN: grep memcpy %t | count 7 -// The number 29 comes from the current codegen for volatile loads; +// The number 28 comes from the current codegen for volatile loads; // if this number changes, it's not necessarily something wrong, but // something has changed to affect volatile load/store codegen @@ -64,7 +64,7 @@ int main() { i=vV[3]; i=VE.yx[1]; i=vVE.zy[1]; - i = aggFct().x; + i = aggFct().x; // Note: not volatile i=vtS; diff --git a/test/Sema/block-call.c b/test/Sema/block-call.c index 318bc6b2a3..28e6c68a80 100644 --- a/test/Sema/block-call.c +++ b/test/Sema/block-call.c @@ -13,9 +13,11 @@ int main() { int (^IFP) () = PFR; // OK - const int (^CIC) () = IFP; // expected-error {{incompatible block pointer types initializing 'int const (^)()' with an expression of type 'int (^)()'}} + const int (^CIC) () = IFP; // expected-error {{incompatible block pointer types initializing 'int const (^)()' with an expression of type 'int (^)()'}} \ + // expected-warning{{type qualifier on return type has no effect}} + + const int (^CICC) () = CIC; // expected-warning{{type qualifier on return type has no effect}} - const int (^CICC) () = CIC; int * const (^IPCC) () = 0; diff --git a/test/Sema/block-return.c b/test/Sema/block-return.c index 10b3b8480c..33fd183be0 100644 --- a/test/Sema/block-return.c +++ b/test/Sema/block-return.c @@ -109,8 +109,11 @@ void foo6() { void foo7() { - const int (^BB) (void) = ^{ const int i = 1; return i; }; // expected-error{{incompatible block pointer types initializing 'int const (^)(void)' with an expression of type 'int (^)(void)'}} - const int (^CC) (void) = ^const int{ const int i = 1; return i; }; // OK + const int (^BB) (void) = ^{ const int i = 1; return i; }; // expected-error{{incompatible block pointer types initializing 'int const (^)(void)' with an expression of type 'int (^)(void)'}} \ + // expected-warning{{type qualifier on return type has no effect}} + + const int (^CC) (void) = ^const int{ const int i = 1; return i; }; // expected-warning{{type qualifier on return type has no effect}} + int i; int (^FF) (void) = ^{ return i; }; // OK diff --git a/test/Sema/struct-cast.c b/test/Sema/struct-cast.c index dc7db130dc..3456665a8c 100644 --- a/test/Sema/struct-cast.c +++ b/test/Sema/struct-cast.c @@ -5,7 +5,8 @@ struct S { int two; }; -struct S const foo(void); +struct S const foo(void); // expected-warning{{type qualifier on return type has no effect}} + struct S tmp; diff --git a/test/SemaCXX/ambig-user-defined-conversions.cpp b/test/SemaCXX/ambig-user-defined-conversions.cpp index 7f67674874..9811859fcc 100644 --- a/test/SemaCXX/ambig-user-defined-conversions.cpp +++ b/test/SemaCXX/ambig-user-defined-conversions.cpp @@ -17,7 +17,8 @@ namespace test0 { void func(const char ci, const B b); // expected-note {{candidate function}} void func(const B b, const int ci); // expected-note {{candidate function}} - const int Test1() { + const int Test1() { // expected-warning{{type qualifier on return type has no effect}} + func(b1, f()); // expected-error {{call to 'func' is ambiguous}} return f(); // expected-error {{conversion from 'test0::B' to 'int const' is ambiguous}} } diff --git a/test/SemaCXX/conditional-expr.cpp b/test/SemaCXX/conditional-expr.cpp index d008b8d6ed..9cbc324467 100644 --- a/test/SemaCXX/conditional-expr.cpp +++ b/test/SemaCXX/conditional-expr.cpp @@ -282,3 +282,20 @@ namespace rdar7998817 { : X()); } } + +namespace PR7598 { + enum Enum { + v = 1, + }; + + const Enum g() { // expected-warning{{type qualifier on return type has no effect}} + return v; + } + + void f() { + const Enum v2 = v; + Enum e = false ? g() : v; + Enum e2 = false ? v2 : v; + } + +} diff --git a/test/SemaCXX/friend.cpp b/test/SemaCXX/friend.cpp index 4f86d6a94d..ba34efe36c 100644 --- a/test/SemaCXX/friend.cpp +++ b/test/SemaCXX/friend.cpp @@ -44,7 +44,8 @@ namespace test2 { // PR5134 namespace test3 { class Foo { - friend const int getInt(int inInt = 0); + friend const int getInt(int inInt = 0); // expected-warning{{type qualifier on return type has no effect}} + }; } diff --git a/test/SemaTemplate/deduction.cpp b/test/SemaTemplate/deduction.cpp index 8d00bb796e..25ffb67492 100644 --- a/test/SemaTemplate/deduction.cpp +++ b/test/SemaTemplate/deduction.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only %s +// RUN: %clang_cc1 -fsyntax-only -verify %s // Template argument deduction with template template parameters. template class A> @@ -98,3 +98,10 @@ namespace PR6257 { void f(const X& a); void test(A& a) { (void)f(a); } } + +// PR7463 +namespace PR7463 { + const int f (); // expected-warning{{type qualifier on return type has no effect}} + template void g (T_&); // expected-note{{T_ = int}} + void h (void) { g(f()); } // expected-error{{no matching function for call}} +}