diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -696,7 +696,11 @@ if (const auto *NTTP = dyn_cast(*P)) { ID.AddInteger(1); ID.AddBoolean(NTTP->isParameterPack()); + const Expr *TC = NTTP->getPlaceholderTypeConstraint(); + ID.AddBoolean(TC != nullptr); ID.AddPointer(NTTP->getType().getCanonicalType().getAsOpaquePtr()); + if (TC) + TC->Profile(ID, C, /*Canonical=*/true); if (NTTP->isExpandedParameterPack()) { ID.AddBoolean(true); ID.AddInteger(NTTP->getNumExpansionTypes()); @@ -5751,9 +5755,6 @@ !TypeConstraintConcept && !IsDependent) return getAutoDeductType(); - if (TypeConstraintConcept) - TypeConstraintConcept = TypeConstraintConcept->getCanonicalDecl(); - // Look in the folding set for an existing type. void *InsertPos = nullptr; llvm::FoldingSetNodeID ID; @@ -5764,20 +5765,15 @@ QualType Canon; if (!IsCanon) { - if (DeducedType.isNull()) { - SmallVector CanonArgs; - bool AnyNonCanonArgs = - ::getCanonicalTemplateArguments(*this, TypeConstraintArgs, CanonArgs); - if (AnyNonCanonArgs) { - Canon = getAutoTypeInternal(QualType(), Keyword, IsDependent, IsPack, - TypeConstraintConcept, CanonArgs, true); - // Find the insert position again. - [[maybe_unused]] auto *Nothing = - AutoTypes.FindNodeOrInsertPos(ID, InsertPos); - assert(!Nothing && "canonical type broken"); - } - } else { + if (!DeducedType.isNull()) { Canon = DeducedType.getCanonicalType(); + } else if (TypeConstraintConcept) { + Canon = getAutoTypeInternal(QualType(), Keyword, IsDependent, IsPack, + nullptr, {}, true); + // Find the insert position again. + [[maybe_unused]] auto *Nothing = + AutoTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!Nothing && "canonical type broken"); } } @@ -6333,7 +6329,9 @@ if (auto *TX = dyn_cast(X)) { auto *TY = cast(Y); return TX->isParameterPack() == TY->isParameterPack() && - TX->getASTContext().hasSameType(TX->getType(), TY->getType()); + TX->getASTContext().hasSameType(TX->getType(), TY->getType()) && + isSameConstraintExpr(TX->getPlaceholderTypeConstraint(), + TY->getPlaceholderTypeConstraint()); } auto *TX = cast(X); diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -529,6 +529,9 @@ ID.AddInteger(0); ID.AddBoolean(NTTP->isParameterPack()); NTTP->getType().getCanonicalType().Profile(ID); + ID.AddBoolean(NTTP->hasPlaceholderTypeConstraint()); + if (const Expr *E = NTTP->getPlaceholderTypeConstraint()) + E->Profile(ID, C, /*Canonical=*/true); continue; } if (const auto *TTP = dyn_cast(D)) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -8666,10 +8666,11 @@ // C++2a [class.spaceship]p2 [P2002R0]: // Let R be the declared return type [...]. If R is auto, [...]. Otherwise, // R shall not contain a placeholder type. - if (DCK == DefaultedComparisonKind::ThreeWay && - FD->getDeclaredReturnType()->getContainedDeducedType() && - !Context.hasSameType(FD->getDeclaredReturnType(), - Context.getAutoDeductType())) { + if (QualType RT = FD->getDeclaredReturnType(); + DCK == DefaultedComparisonKind::ThreeWay && + RT->getContainedDeducedType() && + (!Context.hasSameType(RT, Context.getAutoDeductType()) || + RT->getContainedAutoType()->isConstrained())) { Diag(FD->getLocation(), diag::err_defaulted_comparison_deduced_return_type_not_auto) << (int)DCK << FD->getDeclaredReturnType() << Context.AutoDeductTy diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -7943,12 +7943,26 @@ : Kind), TemplateArgLoc)) return false; - } else if (Kind != Sema::TPL_TemplateTemplateArgumentMatch) { + } + + if (Kind != Sema::TPL_TemplateTemplateArgumentMatch && + !isa(Old)) { const Expr *NewC = nullptr, *OldC = nullptr; - if (const auto *TC = cast(New)->getTypeConstraint()) - NewC = TC->getImmediatelyDeclaredConstraint(); - if (const auto *TC = cast(Old)->getTypeConstraint()) - OldC = TC->getImmediatelyDeclaredConstraint(); + + if (isa(New)) { + if (const auto *TC = cast(New)->getTypeConstraint()) + NewC = TC->getImmediatelyDeclaredConstraint(); + if (const auto *TC = cast(Old)->getTypeConstraint()) + OldC = TC->getImmediatelyDeclaredConstraint(); + } else if (isa(New)) { + if (const Expr *E = cast(New) + ->getPlaceholderTypeConstraint()) + NewC = E; + if (const Expr *E = cast(Old) + ->getPlaceholderTypeConstraint()) + OldC = E; + } else + llvm_unreachable("unexpected template parameter type"); auto Diagnose = [&] { S.Diag(NewC ? NewC->getBeginLoc() : New->getBeginLoc(), diff --git a/clang/test/CXX/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp b/clang/test/CXX/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp --- a/clang/test/CXX/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp +++ b/clang/test/CXX/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp @@ -82,18 +82,12 @@ template class A {}; template C auto e(A) { return 0; } - - // FIXME: The error here does not make sense. template auto e<>(A<>); - // expected-error@-1 {{explicit instantiation of 'e' does not refer to a function template}} - // expected-note@-5 {{candidate template ignored: failed template argument deduction}} - - // FIXME: Should be able to instantiate this with no errors. - template C auto e(A); - // expected-error@-1 {{explicit instantiation of 'e' does not refer to a function template}} - // expected-note@-10 {{candidate template ignored: could not match 'C auto' against 'C auto'}} - - template C<> auto e<>(A<>); + template auto e(A); + + template C auto d(A) { return 0; } + template C<> auto d<>(A<>); + template C auto d(A); template A c(Ts...); int f = e(c(1, 2));