From 37e849ad80731ac1b2ad1c64e73bced27802bd8b Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 14 Aug 2013 20:16:31 +0000 Subject: [PATCH] PR16875: The return type of a dependent function type is visible when it's referenced as a member of the current instantiation. In that case, deduce the type of the function to a dependent type rather than exposing an undeduced auto type to the rest of the current instantiation. The standard doesn't really say that the type is dependent in this case; I'll bring this up with CWG. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@188410 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaDecl.cpp | 25 ++++++++++++++++++--- lib/Sema/SemaStmt.cpp | 26 +++++++++++++++++----- lib/Sema/SemaTemplateDeduction.cpp | 9 ++++---- lib/Sema/SemaTemplateInstantiateDecl.cpp | 2 +- test/SemaCXX/cxx1y-deduced-return-type.cpp | 23 +++++++++++++++++++ 5 files changed, 70 insertions(+), 15 deletions(-) diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c583674622..9d4280ae9b 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2459,7 +2459,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S, // cannot be overloaded. // Go back to the type source info to compare the declared return types, - // per C++1y [dcl.type.auto]p??: + // per C++1y [dcl.type.auto]p13: // Redeclarations or specializations of a function or function template // with a declared return type that uses a placeholder type shall also // use that placeholder, not a deduced type. @@ -2494,9 +2494,14 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S, // defined, copy the deduced value from the old declaration. AutoType *OldAT = Old->getResultType()->getContainedAutoType(); if (OldAT && OldAT->isDeduced()) { - New->setType(SubstAutoType(New->getType(), OldAT->getDeducedType())); + New->setType( + SubstAutoType(New->getType(), + OldAT->isDependentType() ? Context.DependentTy + : OldAT->getDeducedType())); NewQType = Context.getCanonicalType( - SubstAutoType(NewQType, OldAT->getDeducedType())); + SubstAutoType(NewQType, + OldAT->isDependentType() ? Context.DependentTy + : OldAT->getDeducedType())); } } @@ -6688,6 +6693,20 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, Diag(D.getDeclSpec().getVirtualSpecLoc(), diag::err_auto_fn_virtual); } + if (getLangOpts().CPlusPlus1y && NewFD->isDependentContext() && + NewFD->getResultType()->isUndeducedType()) { + // If the function template is referenced directly (for instance, as a + // member of the current instantiation), pretend it has a dependent type. + // This is not really justified by the standard, but is the only sane + // thing to do. + const FunctionProtoType *FPT = + NewFD->getType()->castAs(); + QualType Result = SubstAutoType(FPT->getResultType(), + Context.DependentTy); + NewFD->setType(Context.getFunctionType(Result, FPT->getArgTypes(), + FPT->getExtProtoInfo())); + } + // C++ [dcl.fct.spec]p3: // The inline specifier shall not appear on a block scope function // declaration. diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 46e350bfa2..87e7e03b84 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -2611,7 +2611,21 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, IgnoreParens().castAs().getResultLoc(); QualType Deduced; - if (RetExpr) { + if (RetExpr && isa(RetExpr)) { + // If the deduction is for a return statement and the initializer is + // a braced-init-list, the program is ill-formed. + Diag(RetExpr->getExprLoc(), diag::err_auto_fn_return_init_list); + return true; + } + + if (FD->isDependentContext()) { + // C++1y [dcl.spec.auto]p12: + // Return type deduction [...] occurs when the definition is + // instantiated even if the function body contains a return + // statement with a non-type-dependent operand. + assert(AT->isDeduced() && "should have deduced to dependent type"); + return false; + } else if (RetExpr) { // If the deduction is for a return statement and the initializer is // a braced-init-list, the program is ill-formed. if (isa(RetExpr)) { @@ -2652,7 +2666,8 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, // the program is ill-formed. if (AT->isDeduced() && !FD->isInvalidDecl()) { AutoType *NewAT = Deduced->getContainedAutoType(); - if (!Context.hasSameType(AT->getDeducedType(), NewAT->getDeducedType())) { + if (!FD->isDependentContext() && + !Context.hasSameType(AT->getDeducedType(), NewAT->getDeducedType())) { Diag(ReturnLoc, diag::err_auto_fn_different_deductions) << (AT->isDecltypeAuto() ? 1 : 0) << NewAT->getDeducedType() << AT->getDeducedType(); @@ -2696,13 +2711,10 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // FIXME: Add a flag to the ScopeInfo to indicate whether we're performing // deduction. - bool HasDependentReturnType = FnRetType->isDependentType(); if (getLangOpts().CPlusPlus1y) { if (AutoType *AT = FnRetType->getContainedAutoType()) { FunctionDecl *FD = cast(CurContext); - if (CurContext->isDependentContext()) - HasDependentReturnType = true; - else if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) { + if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) { FD->setInvalidDecl(); return StmtError(); } else { @@ -2711,6 +2723,8 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { } } + bool HasDependentReturnType = FnRetType->isDependentType(); + ReturnStmt *Result = 0; if (FnRetType->isVoidType()) { if (RetValExp) { diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 898ced2dc1..c8669ae7b8 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -2581,7 +2581,6 @@ Sema::SubstituteExplicitTemplateArguments( } // Instantiate the return type. - // FIXME: exception-specifications? QualType ResultType; { // C++11 [expr.prim.general]p3: @@ -3555,11 +3554,11 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, // If the function has a deduced return type, substitute it for a dependent // type so that we treat it as a non-deduced context in what follows. - bool HasUndeducedReturnType = false; + bool HasDeducedReturnType = false; if (getLangOpts().CPlusPlus1y && InOverloadResolution && - Function->getResultType()->isUndeducedType()) { + Function->getResultType()->getContainedAutoType()) { FunctionType = SubstAutoType(FunctionType, Context.DependentTy); - HasUndeducedReturnType = true; + HasDeducedReturnType = true; } if (!ArgFunctionType.isNull()) { @@ -3581,7 +3580,7 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, // If the function has a deduced return type, deduce it now, so we can check // that the deduced function type matches the requested type. - if (HasUndeducedReturnType && + if (HasDeducedReturnType && Specialization->getResultType()->isUndeducedType() && DeduceReturnType(Specialization, Info.getLocation(), false)) return TDK_MiscellaneousDeductionFailure; diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 3379ebcd90..3cb20c79af 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3161,7 +3161,7 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, if (Function->getTemplateSpecializationKind() == TSK_ExplicitInstantiationDeclaration && !PatternDecl->isInlined() && - !PatternDecl->getResultType()->isUndeducedType()) + !PatternDecl->getResultType()->getContainedAutoType()) return; if (PatternDecl->isInlined()) diff --git a/test/SemaCXX/cxx1y-deduced-return-type.cpp b/test/SemaCXX/cxx1y-deduced-return-type.cpp index b8cb5f8632..e2565e6ea4 100644 --- a/test/SemaCXX/cxx1y-deduced-return-type.cpp +++ b/test/SemaCXX/cxx1y-deduced-return-type.cpp @@ -376,3 +376,26 @@ namespace MemberTemplatesWithDeduction { } } + +namespace CurrentInstantiation { + // PR16875 + template struct S { + auto f() { return T(); } + int g() { return f(); } + auto h(bool b) { + if (b) + return T(); + return h(true); + } + }; + int k1 = S().g(); + int k2 = S().h(false); + + template struct U { + auto f(); // expected-note {{here}} + int g() { return f(); } // expected-error {{cannot be used before it is defined}} + }; + template int U::g(); // expected-note {{in instantiation of}} + template auto U::f() { return T(); } + template int U::g(); // ok +} -- 2.40.0