From 9cf98d26e7b1204478cc13ae3df44a6843965c11 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 28 Jul 2020 15:51:13 -0700 Subject: [PATCH] PR46637: Fix handling of placeholder types in trailing-return-types. Only permit a placeholder type in a trailing-return-type if it would also have been permitted in the decl-specifier sequence of a corresponding declaration with no trailing-return-type. The standard doesn't actually say this, but this is the only thing that makes sense. Also fix handling of an 'auto' in a trailing-return-type in a parameter of a generic lambda. We used to crash if we saw such a thing. --- clang/include/clang/Sema/DeclSpec.h | 9 ++ clang/include/clang/Sema/Sema.h | 2 + clang/lib/Sema/SemaTemplateDeduction.cpp | 7 ++ clang/lib/Sema/SemaType.cpp | 117 +++++++++++---------- clang/test/SemaCXX/auto-type-from-cxx.cpp | 3 +- clang/test/SemaCXX/cxx1y-deduced-return-type.cpp | 8 ++ clang/test/SemaCXX/cxx1y-generic-lambdas.cpp | 8 ++ clang/test/SemaCXX/trailing-return-0x.cpp | 7 ++ clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp | 11 ++ 9 files changed, 115 insertions(+), 57 deletions(-) diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 8db03ba..0a22b5a 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -2435,6 +2435,15 @@ public: return true; return false; } + /// Get the trailing return type appearing (at any level) within this + /// declarator. + ParsedType getTrailingReturnType() const { + for (const auto &Chunk : type_objects()) + if (Chunk.Kind == DeclaratorChunk::Function && + Chunk.Fun.hasTrailingReturnType()) + return Chunk.Fun.getTrailingReturnType(); + return ParsedType(); + } /// \brief Sets a trailing requires clause for this declarator. void setTrailingRequiresClause(Expr *TRC) { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 63e2d0d..fd4300f 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8179,6 +8179,8 @@ public: /// Completely replace the \c auto in \p TypeWithAuto by /// \p Replacement. This does not retain any \c auto type sugar. QualType ReplaceAutoType(QualType TypeWithAuto, QualType Replacement); + TypeSourceInfo *ReplaceAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto, + QualType Replacement); /// Result type of DeduceAutoType. enum DeduceAutoResult { diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 8e7b4e1..1f7d0f0 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4896,6 +4896,13 @@ QualType Sema::ReplaceAutoType(QualType TypeWithAuto, .TransformType(TypeWithAuto); } +TypeSourceInfo *Sema::ReplaceAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto, + QualType TypeToReplaceAuto) { + return SubstituteDeducedTypeTransform(*this, TypeToReplaceAuto, + /*UseTypeSugar*/ false) + .TransformType(TypeWithAuto); +} + void Sema::DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init) { if (isa(Init)) Diag(VDecl->getLocation(), diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 4c7eece..78c65ad 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -3099,24 +3099,10 @@ static void diagnoseRedundantReturnTypeQualifiers(Sema &S, QualType RetTy, D.getDeclSpec().getUnalignedSpecLoc()); } -static void CopyTypeConstraintFromAutoType(Sema &SemaRef, const AutoType *Auto, - AutoTypeLoc AutoLoc, - TemplateTypeParmDecl *TP, - SourceLocation EllipsisLoc) { - - TemplateArgumentListInfo TAL(AutoLoc.getLAngleLoc(), AutoLoc.getRAngleLoc()); - for (unsigned Idx = 0; Idx < AutoLoc.getNumArgs(); ++Idx) - TAL.addArgument(AutoLoc.getArgLoc(Idx)); - - SemaRef.AttachTypeConstraint( - AutoLoc.getNestedNameSpecifierLoc(), AutoLoc.getConceptNameInfo(), - AutoLoc.getNamedConcept(), - AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr, TP, EllipsisLoc); -} - -static QualType InventTemplateParameter( - TypeProcessingState &state, QualType T, TypeSourceInfo *TSI, AutoType *Auto, - InventedTemplateParameterInfo &Info) { +static std::pair +InventTemplateParameter(TypeProcessingState &state, QualType T, + TypeSourceInfo *TrailingTSI, AutoType *Auto, + InventedTemplateParameterInfo &Info) { Sema &S = state.getSema(); Declarator &D = state.getDeclarator(); @@ -3141,13 +3127,25 @@ static QualType InventTemplateParameter( IsParameterPack, /*HasTypeConstraint=*/Auto->isConstrained()); InventedTemplateParam->setImplicit(); Info.TemplateParams.push_back(InventedTemplateParam); - // Attach type constraints + + // Attach type constraints to the new parameter. if (Auto->isConstrained()) { - if (TSI) { - CopyTypeConstraintFromAutoType( - S, Auto, TSI->getTypeLoc().getContainedAutoTypeLoc(), - InventedTemplateParam, D.getEllipsisLoc()); + if (TrailingTSI) { + // The 'auto' appears in a trailing return type we've already built; + // extract its type constraints to attach to the template parameter. + AutoTypeLoc AutoLoc = TrailingTSI->getTypeLoc().getContainedAutoTypeLoc(); + TemplateArgumentListInfo TAL(AutoLoc.getLAngleLoc(), AutoLoc.getRAngleLoc()); + for (unsigned Idx = 0; Idx < AutoLoc.getNumArgs(); ++Idx) + TAL.addArgument(AutoLoc.getArgLoc(Idx)); + + S.AttachTypeConstraint(AutoLoc.getNestedNameSpecifierLoc(), + AutoLoc.getConceptNameInfo(), + AutoLoc.getNamedConcept(), + AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr, + InventedTemplateParam, D.getEllipsisLoc()); } else { + // The 'auto' appears in the decl-specifiers; we've not finished forming + // TypeSourceInfo for it yet. TemplateIdAnnotation *TemplateId = D.getDeclSpec().getRepAsTemplateId(); TemplateArgumentListInfo TemplateArgsInfo; if (TemplateId->LAngleLoc.isValid()) { @@ -3165,15 +3163,16 @@ static QualType InventTemplateParameter( } } - // If TSI is nullptr, this is a constrained declspec auto and the type - // constraint will be attached later in TypeSpecLocFiller - // Replace the 'auto' in the function parameter with this invented // template type parameter. // FIXME: Retain some type sugar to indicate that this was written // as 'auto'? - return state.ReplaceAutoType( - T, QualType(InventedTemplateParam->getTypeForDecl(), 0)); + QualType Replacement(InventedTemplateParam->getTypeForDecl(), 0); + QualType NewT = state.ReplaceAutoType(T, Replacement); + TypeSourceInfo *NewTSI = + TrailingTSI ? S.ReplaceAutoTypeSourceInfo(TrailingTSI, Replacement) + : nullptr; + return {NewT, NewTSI}; } static TypeSourceInfo * @@ -3232,8 +3231,19 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, if (!D.getAttributes().empty()) distributeTypeAttrsFromDeclarator(state, T); + // Find the deduced type in this type. Look in the trailing return type if we + // have one, otherwise in the DeclSpec type. + // FIXME: The standard wording doesn't currently describe this. + DeducedType *Deduced = T->getContainedDeducedType(); + bool DeducedIsTrailingReturnType = false; + if (Deduced && isa(Deduced) && D.hasTrailingReturnType()) { + QualType T = SemaRef.GetTypeFromParser(D.getTrailingReturnType()); + Deduced = T.isNull() ? nullptr : T->getContainedDeducedType(); + DeducedIsTrailingReturnType = true; + } + // C++11 [dcl.spec.auto]p5: reject 'auto' if it is not in an allowed context. - if (DeducedType *Deduced = T->getContainedDeducedType()) { + if (Deduced) { AutoType *Auto = dyn_cast(Deduced); int Error = -1; @@ -3267,10 +3277,6 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, } else if (!SemaRef.getCurScope()->isFunctionDeclarationScope()) { Error = 21; break; - } else if (D.hasTrailingReturnType()) { - // This might be OK, but we'll need to convert the trailing return - // type later. - break; } Info = &SemaRef.InventedParameterInfos.back(); @@ -3284,7 +3290,12 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, Info = SemaRef.getCurLambda(); assert(Info && "No LambdaScopeInfo on the stack!"); } - T = InventTemplateParameter(state, T, nullptr, Auto, *Info); + + // We'll deal with inventing template parameters for 'auto' in trailing + // return types when we pick up the trailing return type when processing + // the function chunk. + if (!DeducedIsTrailingReturnType) + T = InventTemplateParameter(state, T, nullptr, Auto, *Info).first; break; } case DeclaratorContext::MemberContext: { @@ -3382,20 +3393,6 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, (!SemaRef.getLangOpts().CPlusPlus11 || !IsCXXAutoType)) Error = 13; - bool HaveTrailing = false; - - // C++11 [dcl.spec.auto]p2: 'auto' is always fine if the declarator - // contains a trailing return type. That is only legal at the outermost - // level. Check all declarator chunks (outermost first) anyway, to give - // better diagnostics. - // We don't support '__auto_type' with trailing return types. - // FIXME: Should we only do this for 'auto' and not 'decltype(auto)'? - if (SemaRef.getLangOpts().CPlusPlus11 && IsCXXAutoType && - D.hasTrailingReturnType()) { - HaveTrailing = true; - Error = -1; - } - SourceRange AutoRange = D.getDeclSpec().getTypeSpecTypeLoc(); if (D.getName().getKind() == UnqualifiedIdKind::IK_ConversionFunctionId) AutoRange = D.getName().getSourceRange(); @@ -3425,8 +3422,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, T = SemaRef.Context.IntTy; D.setInvalidType(true); - } else if (Auto && !HaveTrailing && - D.getContext() != DeclaratorContext::LambdaExprContext) { + } else if (Auto && D.getContext() != DeclaratorContext::LambdaExprContext) { // If there was a trailing return type, we already got // warn_cxx98_compat_trailing_return_type in the parser. SemaRef.Diag(AutoRange.getBegin(), @@ -4879,12 +4875,21 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // An error occurred parsing the trailing return type. T = Context.IntTy; D.setInvalidType(true); - } else if (S.getLangOpts().CPlusPlus20) - // Handle cases like: `auto f() -> auto` or `auto f() -> C auto`. - if (AutoType *Auto = T->getContainedAutoType()) - if (S.getCurScope()->isFunctionDeclarationScope()) - T = InventTemplateParameter(state, T, TInfo, Auto, - S.InventedParameterInfos.back()); + } else if (AutoType *Auto = T->getContainedAutoType()) { + // If the trailing return type contains an `auto`, we may need to + // invent a template parameter for it, for cases like + // `auto f() -> C auto` or `[](auto (*p) -> auto) {}`. + InventedTemplateParameterInfo *InventedParamInfo = nullptr; + if (D.getContext() == DeclaratorContext::PrototypeContext) + InventedParamInfo = &S.InventedParameterInfos.back(); + else if (D.getContext() == + DeclaratorContext::LambdaExprParameterContext) + InventedParamInfo = S.getCurLambda(); + if (InventedParamInfo) { + std::tie(T, TInfo) = InventTemplateParameter( + state, T, TInfo, Auto, *InventedParamInfo); + } + } } else { // This function type is not the type of the entity being declared, // so checking the 'auto' is not the responsibility of this chunk. diff --git a/clang/test/SemaCXX/auto-type-from-cxx.cpp b/clang/test/SemaCXX/auto-type-from-cxx.cpp index 961402f..21620f7 100644 --- a/clang/test/SemaCXX/auto-type-from-cxx.cpp +++ b/clang/test/SemaCXX/auto-type-from-cxx.cpp @@ -4,7 +4,8 @@ struct A { operator __auto_type() {} // expected-error {{'__auto_type' not allowed in conversion function type}} }; -__auto_type a() -> int; // expected-error {{'__auto_type' not allowed in function return type}} +__auto_type a() -> int; // expected-error {{function with trailing return type must specify return type 'auto'}} +__auto_type a2(); // expected-error {{'__auto_type' not allowed in function return type}} template __auto_type b() { return T::x; } // expected-error {{'__auto_type' not allowed in function return type}} auto c() -> __auto_type { __builtin_unreachable(); } // expected-error {{'__auto_type' not allowed in function return type}} diff --git a/clang/test/SemaCXX/cxx1y-deduced-return-type.cpp b/clang/test/SemaCXX/cxx1y-deduced-return-type.cpp index 687ecf2..958728b 100644 --- a/clang/test/SemaCXX/cxx1y-deduced-return-type.cpp +++ b/clang/test/SemaCXX/cxx1y-deduced-return-type.cpp @@ -620,3 +620,11 @@ namespace PR33222 { // FIXME: suppress this follow-on error: expected-error@-1 {{cannot initialize}} template<> int B::q() { return 0; } // expected-error {{return type}} } + +namespace PR46637 { + using A = auto () -> auto; // expected-error {{'auto' not allowed in type alias}} + using B = auto (*)() -> auto; // expected-error {{'auto' not allowed in type alias}} + template auto> struct X {}; // expected-error {{'auto' not allowed in template parameter until C++17}} + template struct Y { T x; }; + Y auto> y; // expected-error {{'auto' not allowed in template argument}} +} diff --git a/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp b/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp index 13ab7aa..52caaa5 100644 --- a/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp +++ b/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp @@ -1012,3 +1012,11 @@ namespace PR32638 { [](auto x) noexcept(noexcept(x)) { } (0); } } + +namespace PR46637 { + auto x = [](auto (*p)()) { return p(); }; + auto y = [](auto (*p)() -> auto) { return p(); }; + int f(); + void *v = x(f); // expected-error {{cannot initialize a variable of type 'void *' with an rvalue of type 'int'}} + void *w = y(f); // expected-error {{cannot initialize a variable of type 'void *' with an rvalue of type 'int'}} +} diff --git a/clang/test/SemaCXX/trailing-return-0x.cpp b/clang/test/SemaCXX/trailing-return-0x.cpp index c6b22c5..4834aaf 100644 --- a/clang/test/SemaCXX/trailing-return-0x.cpp +++ b/clang/test/SemaCXX/trailing-return-0x.cpp @@ -104,3 +104,10 @@ namespace PR16273 { }; } +namespace PR46637 { + using A = auto () -> auto; // expected-error {{'auto' not allowed in function return type}} + using B = auto (*)() -> auto; // expected-error {{'auto' not allowed in function return type}} + template auto> struct X {}; // expected-error {{'auto' not allowed in function return type}} + template struct Y { T x; }; + Y auto> y; // expected-error {{'auto' not allowed in function return type}} +} diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp index fdcc500..7538de3 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp @@ -448,3 +448,14 @@ namespace PR42108 { A(); // expected-error {{non-type template argument is not a constant expression}} expected-note 2{{temporary}} } } + +namespace PR46637 { + template auto> struct X { // expected-note {{here}} + auto call() { return f(); } + }; + X x; // expected-error {{incompatible initializer}} + + void *f(); + X y; + int n = y.call(); // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void *'}} +} -- 2.7.4