mizvekov updated this revision to Diff 432339. mizvekov added a comment. - Just rebase, no review comments addressed yet. - One small churn in test/SemaCXX/deduced-return-void.cpp, which is new since last rebase. As this patch uses the template deduction machinery to also handle the mutiple return statements, we detect the inconsistent deduction before substitution, so we don't error out trying to form the reference to void.
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D111283/new/ https://reviews.llvm.org/D111283 Files: clang/include/clang/AST/ASTContext.h clang/include/clang/AST/Type.h clang/include/clang/Sema/Sema.h clang/lib/AST/ASTContext.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaExprCXX.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Sema/SemaStmt.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateDeduction.cpp clang/test/SemaCXX/deduced-return-void.cpp clang/test/SemaCXX/sugared-auto.cpp clang/test/SemaTemplate/deduction.cpp libcxx/DELETE.ME
Index: libcxx/DELETE.ME =================================================================== --- /dev/null +++ libcxx/DELETE.ME @@ -0,0 +1 @@ +D111283 Index: clang/test/SemaTemplate/deduction.cpp =================================================================== --- clang/test/SemaTemplate/deduction.cpp +++ clang/test/SemaTemplate/deduction.cpp @@ -162,6 +162,15 @@ } // namespace test4 +namespace test5 { + +template <bool, int = 0> class a {}; +template <class b> void c(b, b); +template <bool b> void c(a<b>, a<b>); +void d() { c(a<true>(), a<true>()); } + +} // namespace test5 + // Verify that we can deduce enum-typed arguments correctly. namespace test14 { enum E { E0, E1 }; Index: clang/test/SemaCXX/sugared-auto.cpp =================================================================== --- clang/test/SemaCXX/sugared-auto.cpp +++ clang/test/SemaCXX/sugared-auto.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++20 +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++20 -fblocks -fenable-matrix -Wno-dynamic-exception-spec enum class N {}; @@ -9,6 +9,26 @@ using Man = Animal; using Dog = Animal; +using ManPtr = Man *; +using DogPtr = Dog *; + +using SocratesPtr = ManPtr; + +using ConstMan = const Man; +using ConstDog = const Dog; + +using Virus = void; +using SARS = Virus; +using Ebola = Virus; + +using Bacteria = float; +using Bacilli = Bacteria; +using Vibrio = Bacteria; + +struct Plant; +using Gymnosperm = Plant; +using Angiosperm = Plant; + namespace variable { auto x1 = Animal(); @@ -41,3 +61,115 @@ N t3 = x3; // expected-error {{lvalue of type 'Animal' (aka 'int')}} } // namespace function_basic + +namespace function_multiple_basic { + +N t1 = [] { // expected-error {{rvalue of type 'Animal' (aka 'int')}} + if (true) + return Man(); + return Dog(); +}(); + +N t2 = []() -> decltype(auto) { // expected-error {{rvalue of type 'Animal' (aka 'int')}} + if (true) + return Man(); + return Dog(); +}(); + +N t3 = [] { // expected-error {{rvalue of type 'Animal' (aka 'int')}} + if (true) + return Dog(); + auto x = Man(); + return x; +}(); + +N t4 = [] { // expected-error {{rvalue of type 'int'}} + if (true) + return Dog(); + return 1; +}(); + +N t5 = [] { // expected-error {{rvalue of type 'Virus' (aka 'void')}} + if (true) + return Ebola(); + return SARS(); +}(); + +N t6 = [] { // expected-error {{rvalue of type 'void'}} + if (true) + return SARS(); + return; +}(); + +} // namespace function_multiple_basic + +#define TEST_AUTO(X, A, B) \ + auto X(A a, B b) { \ + if (0) \ + return a; \ + if (0) \ + return b; \ + return N(); \ + } +#define TEST_DAUTO(X, A, B) \ + decltype(auto) X(A a, B b) { \ + if (0) \ + return static_cast<A>(a); \ + if (0) \ + return static_cast<B>(b); \ + return N(); \ + } + +namespace misc { + +TEST_AUTO(t1, ManPtr, DogPtr) // expected-error {{but deduced as 'Animal *' (aka 'int *')}} +TEST_AUTO(t2, ManPtr, int *) // expected-error {{but deduced as 'int *'}} +TEST_AUTO(t3, SocratesPtr, ManPtr) // expected-error {{but deduced as 'ManPtr' (aka 'int *')}} + +TEST_AUTO(t4, _Atomic(Man), _Atomic(Dog)) // expected-error {{but deduced as '_Atomic(Animal)'}} + +using block_man = void (^)(Man); +using block_dog = void (^)(Dog); +TEST_AUTO(t5, block_man, block_dog) // expected-error {{but deduced as 'void (^)(Animal)'}} + +using fp1 = SARS (*)(Man, DogPtr) throw(Vibrio); +using fp2 = Ebola (*)(Dog, ManPtr) throw(Bacilli); +TEST_AUTO(t6, fp1, fp2); // expected-error {{but deduced as 'Virus (*)(Animal, Animal *) throw(Bacteria)' (aka 'void (*)(int, int *) throw(Bacteria)')}} + +using fp3 = SARS (*)() throw(Man); +using fp4 = Ebola (*)() throw(Vibrio); +auto t7(fp3 a, fp4 b) { + if (false) + return true ? a : b; + if (false) + return a; + return N(); // expected-error {{but deduced as 'SARS (*)() throw(Man, Vibrio)' (aka 'void (*)() throw(Man, Vibrio)')}} +} + +using fp5 = void (*)(const Man); +using fp6 = void (*)(Dog); +TEST_AUTO(t8, fp5, fp6); // expected-error {{but deduced as 'void (*)(const Animal)' (aka 'void (*)(const int)')}} + +using fp6 = void (*)(ConstMan); +using fp7 = void (*)(ConstDog); +TEST_AUTO(t10, fp6, fp7); // expected-error {{but deduced as 'void (*)(const Animal)' (aka 'void (*)(const int)')}} + +TEST_AUTO(t11, Man Angiosperm::*, Dog Gymnosperm::*) // expected-error {{but deduced as 'Animal Plant::*'}} + +TEST_DAUTO(t12, const Man &, const Dog &) // expected-error {{but deduced as 'const Animal &' (aka 'const int &')}} + +TEST_DAUTO(t13, Man &&, Dog &&) // expected-error {{but deduced as 'Animal &&' (aka 'int &&')}} + +using matrix_man = Man __attribute__((matrix_type(4, 4))); +using matrix_dog = Dog __attribute__((matrix_type(4, 4))); +TEST_AUTO(t14, matrix_man, matrix_dog) // expected-error {{but deduced as 'Animal __attribute__((matrix_type(4, 4)))'}} + +using vector_man = Man __attribute__((vector_size(4))); +using vector_dog = Dog __attribute__((vector_size(4))); +TEST_AUTO(t15, vector_man, vector_dog) // expected-error {{but deduced as '__attribute__((__vector_size__(1 * sizeof(Animal)))) Animal' (vector of 1 'Animal' value)}} + +using ext_vector_man = Man __attribute__((ext_vector_type(4))); +using ext_vector_dog = Dog __attribute__((ext_vector_type(4))); +TEST_AUTO(t16, ext_vector_man, ext_vector_dog) // expected-error {{but deduced as 'Animal __attribute__((ext_vector_type(4)))' (vector of 4 'Animal' values)}} + +} // namespace misc Index: clang/test/SemaCXX/deduced-return-void.cpp =================================================================== --- clang/test/SemaCXX/deduced-return-void.cpp +++ clang/test/SemaCXX/deduced-return-void.cpp @@ -115,7 +115,7 @@ } auto& f5() { return i; - return void(); // expected-error@-2 {{cannot form a reference to 'void'}} + return void(); // expected-error {{deduced as 'int' in earlier return statement}} } auto& f6() { return 42; } // expected-error {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} @@ -130,9 +130,9 @@ return i; return; // expected-error {{cannot deduce return type 'auto &' from omitted return expression}} }; -auto l5 = []() -> auto& { // expected-error {{cannot form a reference to 'void'}} +auto l5 = []() -> auto & { return i; - return void(); + return void(); // expected-error {{deduced as 'int' in earlier return statement}} }; auto l6 = []() -> auto& { return 42; // expected-error {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} Index: clang/lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- clang/lib/Sema/SemaTemplateDeduction.cpp +++ clang/lib/Sema/SemaTemplateDeduction.cpp @@ -235,11 +235,13 @@ case TemplateArgument::Null: llvm_unreachable("Non-deduced template arguments handled above"); - case TemplateArgument::Type: + case TemplateArgument::Type: { // If two template type arguments have the same type, they're compatible. - if (Y.getKind() == TemplateArgument::Type && - Context.hasSameType(X.getAsType(), Y.getAsType())) - return X; + QualType TX = X.getAsType(), TY = Y.getAsType(); + if (Y.getKind() == TemplateArgument::Type && Context.hasSameType(TX, TY)) + return DeducedTemplateArgument(Context.getCommonSugar(TX, TY), + X.wasDeducedFromArrayBound() || + Y.wasDeducedFromArrayBound()); // If one of the two arguments was deduced from an array bound, the other // supersedes it. @@ -248,6 +250,7 @@ // The arguments are not compatible. return DeducedTemplateArgument(); + } case TemplateArgument::Integral: // If we deduced a constant in one case and either a dependent expression or @@ -325,7 +328,9 @@ // If we deduced a null pointer and a dependent expression, keep the // null pointer. if (Y.getKind() == TemplateArgument::Expression) - return X; + return TemplateArgument( + Context.getCommonSugar(X.getNullPtrType(), Y.getAsExpr()->getType()), + true); // If we deduced a null pointer and an integral constant, keep the // integral constant. @@ -334,7 +339,8 @@ // If we deduced two null pointers, they are the same. if (Y.getKind() == TemplateArgument::NullPtr) - return X; + return TemplateArgument( + Context.getCommonSugar(X.getNullPtrType(), Y.getNullPtrType()), true); // All other combinations are incompatible. return DeducedTemplateArgument(); @@ -4521,42 +4527,9 @@ } // namespace -Sema::DeduceAutoResult -Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, QualType &Result, - Optional<unsigned> DependentDeductionDepth, - bool IgnoreConstraints) { - return DeduceAutoType(Type->getTypeLoc(), Init, Result, - DependentDeductionDepth, IgnoreConstraints); -} - -/// Attempt to produce an informative diagostic explaining why auto deduction -/// failed. -/// \return \c true if diagnosed, \c false if not. -static bool diagnoseAutoDeductionFailure(Sema &S, - Sema::TemplateDeductionResult TDK, - TemplateDeductionInfo &Info, - ArrayRef<SourceRange> Ranges) { - switch (TDK) { - case Sema::TDK_Inconsistent: { - // Inconsistent deduction means we were deducing from an initializer list. - auto D = S.Diag(Info.getLocation(), diag::err_auto_inconsistent_deduction); - D << Info.FirstArg << Info.SecondArg; - for (auto R : Ranges) - D << R; - return true; - } - - // FIXME: Are there other cases for which a custom diagnostic is more useful - // than the basic "types don't match" diagnostic? - - default: - return false; - } -} - -static Sema::DeduceAutoResult -CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, - AutoTypeLoc TypeLoc, QualType Deduced) { +static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, + AutoTypeLoc TypeLoc, + QualType Deduced) { ConstraintSatisfaction Satisfaction; ConceptDecl *Concept = Type.getTypeConstraintConcept(); TemplateArgumentListInfo TemplateArgs(TypeLoc.getLAngleLoc(), @@ -4571,11 +4544,11 @@ llvm::SmallVector<TemplateArgument, 4> Converted; if (S.CheckTemplateArgumentList(Concept, SourceLocation(), TemplateArgs, /*PartialTemplateArgs=*/false, Converted)) - return Sema::DAR_FailedAlreadyDiagnosed; + return true; if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()}, Converted, TypeLoc.getLocalSourceRange(), Satisfaction)) - return Sema::DAR_FailedAlreadyDiagnosed; + return true; if (!Satisfaction.IsSatisfied) { std::string Buf; llvm::raw_string_ostream OS(Buf); @@ -4589,11 +4562,11 @@ OS.flush(); S.Diag(TypeLoc.getConceptNameLoc(), diag::err_placeholder_constraints_not_satisfied) - << Deduced << Buf << TypeLoc.getLocalSourceRange(); + << Deduced << Buf << TypeLoc.getLocalSourceRange(); S.DiagnoseUnsatisfiedConstraint(Satisfaction); - return Sema::DAR_FailedAlreadyDiagnosed; + return true; } - return Sema::DAR_Succeeded; + return false; } /// Deduce the type for an auto type-specifier (C++11 [dcl.spec.auto]p6) @@ -4612,178 +4585,165 @@ /// parameter depth at which we should perform 'auto' deduction. /// \param IgnoreConstraints Set if we should not fail if the deduced type does /// not satisfy the type-constraint in the auto type. -Sema::DeduceAutoResult -Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, - Optional<unsigned> DependentDeductionDepth, - bool IgnoreConstraints) { +Sema::TemplateDeductionResult Sema::DeduceAutoType(TypeLoc Type, Expr *Init, + QualType &Result, + TemplateDeductionInfo &Info, + bool DependentDeduction, + bool IgnoreConstraints) { + assert(DependentDeduction || Info.getDeducedDepth() == 0); if (Init->containsErrors()) - return DAR_FailedAlreadyDiagnosed; - if (Init->getType()->isNonOverloadPlaceholderType()) { + return TDK_AlreadyDiagnosed; + + const AutoType *AT = Type.getType()->getContainedAutoType(); + assert(AT); + + if (Init->getType()->isNonOverloadPlaceholderType() || AT->isDecltypeAuto()) { ExprResult NonPlaceholder = CheckPlaceholderExpr(Init); if (NonPlaceholder.isInvalid()) - return DAR_FailedAlreadyDiagnosed; + return TDK_AlreadyDiagnosed; Init = NonPlaceholder.get(); } DependentAuto DependentResult = { /*.IsPack = */ (bool)Type.getAs<PackExpansionTypeLoc>()}; - if (!DependentDeductionDepth && + if (!DependentDeduction && (Type.getType()->isDependentType() || Init->isTypeDependent() || Init->containsUnexpandedParameterPack())) { Result = SubstituteDeducedTypeTransform(*this, DependentResult).Apply(Type); assert(!Result.isNull() && "substituting DependentTy can't fail"); - return DAR_Succeeded; + return TDK_Success; } - // Find the depth of template parameter to synthesize. - unsigned Depth = DependentDeductionDepth.getValueOr(0); - - // If this is a 'decltype(auto)' specifier, do the decltype dance. - // Since 'decltype(auto)' can only occur at the top of the type, we - // don't need to go digging for it. - if (const AutoType *AT = Type.getType()->getAs<AutoType>()) { - if (AT->isDecltypeAuto()) { - if (isa<InitListExpr>(Init)) { - Diag(Init->getBeginLoc(), diag::err_decltype_auto_initializer_list); - return DAR_FailedAlreadyDiagnosed; - } - - ExprResult ER = CheckPlaceholderExpr(Init); - if (ER.isInvalid()) - return DAR_FailedAlreadyDiagnosed; - QualType Deduced = getDecltypeForExpr(ER.get()); - assert(!Deduced.isNull()); - if (AT->isConstrained() && !IgnoreConstraints) { - auto ConstraintsResult = - CheckDeducedPlaceholderConstraints(*this, *AT, - Type.getContainedAutoTypeLoc(), - Deduced); - if (ConstraintsResult != DAR_Succeeded) - return ConstraintsResult; - } - Result = SubstituteDeducedTypeTransform(*this, Deduced).Apply(Type); - if (Result.isNull()) - return DAR_FailedAlreadyDiagnosed; - return DAR_Succeeded; - } else if (!getLangOpts().CPlusPlus) { - if (isa<InitListExpr>(Init)) { - Diag(Init->getBeginLoc(), diag::err_auto_init_list_from_c); - return DAR_FailedAlreadyDiagnosed; - } - } + auto *InitList = dyn_cast<InitListExpr>(Init); + if (!getLangOpts().CPlusPlus && InitList) { + Diag(Init->getBeginLoc(), diag::err_auto_init_list_from_c); + return TDK_AlreadyDiagnosed; } - SourceLocation Loc = Init->getExprLoc(); - - LocalInstantiationScope InstScope(*this); - - // Build template<class TemplParam> void Func(FuncParam); - TemplateTypeParmDecl *TemplParam = TemplateTypeParmDecl::Create( - Context, nullptr, SourceLocation(), Loc, Depth, 0, nullptr, false, false, - false); - QualType TemplArg = QualType(TemplParam->getTypeForDecl(), 0); - NamedDecl *TemplParamPtr = TemplParam; - FixedSizeTemplateParameterListStorage<1, false> TemplateParamsSt( - Context, Loc, Loc, TemplParamPtr, Loc, nullptr); - - QualType FuncParam = - SubstituteDeducedTypeTransform(*this, TemplArg, /*UseTypeSugar*/ true) - .Apply(Type); - assert(!FuncParam.isNull() && - "substituting template parameter for 'auto' failed"); - // Deduce type of TemplParam in Func(Init) SmallVector<DeducedTemplateArgument, 1> Deduced; Deduced.resize(1); - TemplateDeductionInfo Info(Loc, Depth); - // If deduction failed, don't diagnose if the initializer is dependent; it // might acquire a matching type in the instantiation. - auto DeductionFailed = [&](TemplateDeductionResult TDK, - ArrayRef<SourceRange> Ranges) -> DeduceAutoResult { + auto DeductionFailed = [&](TemplateDeductionResult TDK) { if (Init->isTypeDependent()) { Result = SubstituteDeducedTypeTransform(*this, DependentResult).Apply(Type); assert(!Result.isNull() && "substituting DependentTy can't fail"); - return DAR_Succeeded; + return TDK_Success; } - if (diagnoseAutoDeductionFailure(*this, TDK, Info, Ranges)) - return DAR_FailedAlreadyDiagnosed; - return DAR_Failed; + return TDK; }; SmallVector<OriginalCallArg, 4> OriginalCallArgs; - InitListExpr *InitList = dyn_cast<InitListExpr>(Init); - if (InitList) { - // Notionally, we substitute std::initializer_list<T> for 'auto' and deduce - // against that. Such deduction only succeeds if removing cv-qualifiers and - // references results in std::initializer_list<T>. - if (!Type.getType().getNonReferenceType()->getAs<AutoType>()) - return DAR_Failed; - - // Resolving a core issue: a braced-init-list containing any designators is - // a non-deduced context. - for (Expr *E : InitList->inits()) - if (isa<DesignatedInitExpr>(E)) - return DAR_Failed; + QualType DeducedType; + // If this is a 'decltype(auto)' specifier, do the decltype dance. + if (AT->isDecltypeAuto()) { + if (InitList) { + Diag(Init->getBeginLoc(), diag::err_decltype_auto_initializer_list); + return TDK_AlreadyDiagnosed; + } - SourceRange DeducedFromInitRange; - for (unsigned i = 0, e = InitList->getNumInits(); i < e; ++i) { - Expr *Init = InitList->getInit(i); + DeducedType = getDecltypeForExpr(Init); + assert(!DeducedType.isNull()); - if (auto TDK = DeduceTemplateArgumentsFromCallArgument( - *this, TemplateParamsSt.get(), 0, TemplArg, Init, - Info, Deduced, OriginalCallArgs, /*Decomposed*/ true, - /*ArgIdx*/ 0, /*TDF*/ 0)) - return DeductionFailed(TDK, {DeducedFromInitRange, - Init->getSourceRange()}); - - if (DeducedFromInitRange.isInvalid() && - Deduced[0].getKind() != TemplateArgument::Null) - DeducedFromInitRange = Init->getSourceRange(); + if (!Result.isNull()) { + if (!Context.hasSameType(DeducedType, Result)) { + Info.FirstArg = Result; + Info.SecondArg = DeducedType; + return DeductionFailed(TDK_Inconsistent); + } + DeducedType = Context.getCommonSugar(Result, DeducedType); } } else { - if (!getLangOpts().CPlusPlus && Init->refersToBitField()) { - Diag(Loc, diag::err_auto_bitfield); - return DAR_FailedAlreadyDiagnosed; - } - - if (auto TDK = DeduceTemplateArgumentsFromCallArgument( - *this, TemplateParamsSt.get(), 0, FuncParam, Init, Info, Deduced, - OriginalCallArgs, /*Decomposed*/ false, /*ArgIdx*/ 0, /*TDF*/ 0)) - return DeductionFailed(TDK, {}); - } - - // Could be null if somehow 'auto' appears in a non-deduced context. - if (Deduced[0].getKind() != TemplateArgument::Type) - return DeductionFailed(TDK_Incomplete, {}); - - QualType DeducedType = Deduced[0].getAsType(); + LocalInstantiationScope InstScope(*this); + + // Build template<class TemplParam> void Func(FuncParam); + SourceLocation Loc = Init->getExprLoc(); + TemplateTypeParmDecl *TemplParam = TemplateTypeParmDecl::Create( + Context, nullptr, SourceLocation(), Loc, Info.getDeducedDepth(), 0, + nullptr, false, false, false); + QualType TemplArg = QualType(TemplParam->getTypeForDecl(), 0); + NamedDecl *TemplParamPtr = TemplParam; + FixedSizeTemplateParameterListStorage<1, false> TemplateParamsSt( + Context, Loc, Loc, TemplParamPtr, Loc, nullptr); + + if (InitList) { + // Notionally, we substitute std::initializer_list<T> for 'auto' and + // deduce against that. Such deduction only succeeds if removing + // cv-qualifiers and references results in std::initializer_list<T>. + if (!Type.getType().getNonReferenceType()->getAs<AutoType>()) + return TDK_Invalid; + + SourceRange DeducedFromInitRange; + for (Expr *Init : InitList->inits()) { + // Resolving a core issue: a braced-init-list containing any designators + // is a non-deduced context. + if (isa<DesignatedInitExpr>(Init)) + return TDK_Invalid; + if (auto TDK = DeduceTemplateArgumentsFromCallArgument( + *this, TemplateParamsSt.get(), 0, TemplArg, Init, Info, Deduced, + OriginalCallArgs, /*Decomposed=*/true, + /*ArgIdx=*/0, /*TDF=*/0)) { + if (TDK == TDK_Inconsistent) { + Diag(Info.getLocation(), diag::err_auto_inconsistent_deduction) + << Info.FirstArg << Info.SecondArg << DeducedFromInitRange + << Init->getSourceRange(); + return DeductionFailed(TDK_AlreadyDiagnosed); + } + return DeductionFailed(TDK); + } - if (InitList) { - DeducedType = BuildStdInitializerList(DeducedType, Loc); - if (DeducedType.isNull()) - return DAR_FailedAlreadyDiagnosed; - } + if (DeducedFromInitRange.isInvalid() && + Deduced[0].getKind() != TemplateArgument::Null) + DeducedFromInitRange = Init->getSourceRange(); + } + } else { + if (!getLangOpts().CPlusPlus && Init->refersToBitField()) { + Diag(Loc, diag::err_auto_bitfield); + return TDK_AlreadyDiagnosed; + } + QualType FuncParam = + SubstituteDeducedTypeTransform(*this, TemplArg, /*UseTypeSugar=*/true) + .Apply(Type); + assert(!FuncParam.isNull() && + "substituting template parameter for 'auto' failed"); + if (!Result.isNull()) + Deduced[0] = DeducedTemplateArgument(Result); + if (auto TDK = DeduceTemplateArgumentsFromCallArgument( + *this, TemplateParamsSt.get(), 0, FuncParam, Init, Info, Deduced, + OriginalCallArgs, /*Decomposed=*/false, /*ArgIdx=*/0, /*TDF=*/0)) + return DeductionFailed(TDK); + } - QualType MaybeAuto = Type.getType().getNonReferenceType(); - while (MaybeAuto->isPointerType()) - MaybeAuto = MaybeAuto->getPointeeType(); - if (const auto *AT = MaybeAuto->getAs<AutoType>()) { - if (AT->isConstrained() && !IgnoreConstraints) { - auto ConstraintsResult = CheckDeducedPlaceholderConstraints( - *this, *AT, Type.getContainedAutoTypeLoc(), DeducedType); - if (ConstraintsResult != DAR_Succeeded) - return ConstraintsResult; + // Could be null if somehow 'auto' appears in a non-deduced context. + if (Deduced[0].getKind() != TemplateArgument::Type) + return DeductionFailed(TDK_Incomplete); + DeducedType = Deduced[0].getAsType(); + + if (InitList) { + DeducedType = BuildStdInitializerList(DeducedType, Loc); + if (DeducedType.isNull()) + return TDK_AlreadyDiagnosed; + if (!Result.isNull() && !Context.hasSameType(Result, DeducedType)) { + Info.FirstArg = Result; + Info.SecondArg = DeducedType; + return DeductionFailed(TDK_Inconsistent); + } } } + if (AT->isConstrained() && !IgnoreConstraints && + CheckDeducedPlaceholderConstraints( + *this, *AT, Type.getContainedAutoTypeLoc(), DeducedType)) + return TDK_AlreadyDiagnosed; + Result = SubstituteDeducedTypeTransform(*this, DeducedType).Apply(Type); if (Result.isNull()) - return DAR_FailedAlreadyDiagnosed; + return TDK_AlreadyDiagnosed; // Check that the deduced argument type is compatible with the original // argument type per C++ [temp.deduct.call]p4. @@ -4794,11 +4754,11 @@ if (auto TDK = CheckOriginalCallArgDeduction(*this, Info, OriginalArg, DeducedA)) { Result = QualType(); - return DeductionFailed(TDK, {}); + return DeductionFailed(TDK); } } - return DAR_Succeeded; + return TDK_Success; } QualType Sema::SubstAutoType(QualType TypeWithAuto, Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -6824,7 +6824,6 @@ // When checking a deduced template argument, deduce from its type even if // the type is dependent, in order to check the types of non-type template // arguments line up properly in partial ordering. - Optional<unsigned> Depth = Param->getDepth() + 1; Expr *DeductionArg = Arg; if (auto *PE = dyn_cast<PackExpansionExpr>(DeductionArg)) DeductionArg = PE->getPattern(); @@ -6840,20 +6839,27 @@ DeduceTemplateSpecializationFromInitializer(TSI, Entity, Kind, Inits); if (ParamType.isNull()) return ExprError(); - } else if (DeduceAutoType( - TSI, DeductionArg, ParamType, Depth, - // We do not check constraints right now because the - // immediately-declared constraint of the auto type is also - // an associated constraint, and will be checked along with - // the other associated constraints after checking the - // template argument list. - /*IgnoreConstraints=*/true) == DAR_Failed) { - Diag(Arg->getExprLoc(), - diag::err_non_type_template_parm_type_deduction_failure) - << Param->getDeclName() << Param->getType() << Arg->getType() - << Arg->getSourceRange(); - Diag(Param->getLocation(), diag::note_template_param_here); - return ExprError(); + } else { + TemplateDeductionInfo Info(DeductionArg->getExprLoc(), + Param->getDepth() + 1); + ParamType = QualType(); + TemplateDeductionResult Result = + DeduceAutoType(TSI->getTypeLoc(), DeductionArg, ParamType, Info, + /*DependentDeduction=*/true, + // We do not check constraints right now because the + // immediately-declared constraint of the auto type is + // also an associated constraint, and will be checked + // along with the other associated constraints after + // checking the template argument list. + /*IgnoreConstraints=*/true); + if (Result != TDK_Success && Result != TDK_AlreadyDiagnosed) { + Diag(Arg->getExprLoc(), + diag::err_non_type_template_parm_type_deduction_failure) + << Param->getDeclName() << Param->getType() << Arg->getType() + << Arg->getSourceRange(); + Diag(Param->getLocation(), diag::note_template_param_here); + return ExprError(); + } } // CheckNonTypeTemplateParameterType will produce a diagnostic if there's // an error. The error message normally references the parameter Index: clang/lib/Sema/SemaStmt.cpp =================================================================== --- clang/lib/Sema/SemaStmt.cpp +++ clang/lib/Sema/SemaStmt.cpp @@ -2300,11 +2300,14 @@ // If the type contained 'auto', deduce the 'auto' to 'id'. if (FirstType->getContainedAutoType()) { - OpaqueValueExpr OpaqueId(D->getLocation(), Context.getObjCIdType(), - VK_PRValue); + SourceLocation Loc = D->getLocation(); + OpaqueValueExpr OpaqueId(Loc, Context.getObjCIdType(), VK_PRValue); Expr *DeducedInit = &OpaqueId; - if (DeduceAutoType(D->getTypeSourceInfo(), DeducedInit, FirstType) == - DAR_Failed) + TemplateDeductionInfo Info(Loc); + FirstType = QualType(); + TemplateDeductionResult Result = DeduceAutoType( + D->getTypeSourceInfo()->getTypeLoc(), DeducedInit, FirstType, Info); + if (Result != TDK_Success && Result != TDK_AlreadyDiagnosed) DiagnoseAutoDeductionFailure(D, DeducedInit); if (FirstType.isNull()) { D->setInvalidDecl(); @@ -2368,10 +2371,16 @@ // Deduce the type for the iterator variable now rather than leaving it to // AddInitializerToDecl, so we can produce a more suitable diagnostic. QualType InitType; - if ((!isa<InitListExpr>(Init) && Init->getType()->isVoidType()) || - SemaRef.DeduceAutoType(Decl->getTypeSourceInfo(), Init, InitType) == - Sema::DAR_Failed) + if (!isa<InitListExpr>(Init) && Init->getType()->isVoidType()) { SemaRef.Diag(Loc, DiagID) << Init->getType(); + } else { + TemplateDeductionInfo Info(Init->getExprLoc()); + Sema::TemplateDeductionResult Result = SemaRef.DeduceAutoType( + Decl->getTypeSourceInfo()->getTypeLoc(), Init, InitType, Info); + if (Result != Sema::TDK_Success && Result != Sema::TDK_AlreadyDiagnosed) + SemaRef.Diag(Loc, DiagID) << Init->getType(); + } + if (InitType.isNull()) { Decl->setInvalidDecl(); return true; @@ -3761,17 +3770,13 @@ /// C++1y [dcl.spec.auto]p6. bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, SourceLocation ReturnLoc, - Expr *&RetExpr, - const AutoType *AT) { + Expr *RetExpr, const AutoType *AT) { // If this is the conversion function for a lambda, we choose to deduce its // type from the corresponding call operator, not from the synthesized return // statement within it. See Sema::DeduceReturnType. if (isLambdaConversionOperator(FD)) return false; - TypeLoc OrigResultType = getReturnTypeLoc(FD); - QualType Deduced; - if (RetExpr && isa<InitListExpr>(RetExpr)) { // If the deduction is for a return statement and the initializer is // a braced-init-list, the program is ill-formed. @@ -3791,87 +3796,74 @@ return false; } - if (RetExpr) { - // Otherwise, [...] deduce a value for U using the rules of template - // argument deduction. - DeduceAutoResult DAR = DeduceAutoType(OrigResultType, RetExpr, Deduced); - - if (DAR == DAR_Failed && !FD->isInvalidDecl()) - Diag(RetExpr->getExprLoc(), diag::err_auto_fn_deduction_failure) - << OrigResultType.getType() << RetExpr->getType(); - - if (DAR != DAR_Succeeded) - return true; - - // If a local type is part of the returned type, mark its fields as - // referenced. - LocalTypedefNameReferencer Referencer(*this); - Referencer.TraverseType(RetExpr->getType()); - } else { - // For a function with a deduced result type to return void, - // the result type as written must be 'auto' or 'decltype(auto)', - // possibly cv-qualified or constrained, but not ref-qualified. + TypeLoc OrigResultType = getReturnTypeLoc(FD); + // In the case of a return with no operand, the initializer is considered + // to be void(). + CXXScalarValueInitExpr VoidVal(Context.VoidTy, nullptr, SourceLocation()); + if (!RetExpr) { + // For a function with a deduced result type to return with omitted + // expression, the result type as written must be 'auto' or + // 'decltype(auto)', possibly cv-qualified or constrained, but not + // ref-qualified. if (!OrigResultType.getType()->getAs<AutoType>()) { Diag(ReturnLoc, diag::err_auto_fn_return_void_but_not_auto) - << OrigResultType.getType(); + << OrigResultType.getType(); return true; } - // In the case of a return with no operand, the initializer is considered - // to be 'void()'. - Expr *Dummy = new (Context) CXXScalarValueInitExpr( - Context.VoidTy, - Context.getTrivialTypeSourceInfo(Context.VoidTy, ReturnLoc), ReturnLoc); - DeduceAutoResult DAR = DeduceAutoType(OrigResultType, Dummy, Deduced); - - if (DAR == DAR_Failed && !FD->isInvalidDecl()) - Diag(ReturnLoc, diag::err_auto_fn_deduction_failure) - << OrigResultType.getType() << Dummy->getType(); - - if (DAR != DAR_Succeeded) - return true; + RetExpr = &VoidVal; } - // CUDA: Kernel function must have 'void' return type. - if (getLangOpts().CUDA) - if (FD->hasAttr<CUDAGlobalAttr>() && !Deduced->isVoidType()) { - Diag(FD->getLocation(), diag::err_kern_type_not_void_return) - << FD->getType() << FD->getSourceRange(); + QualType Deduced = AT->getDeducedType(); + { + // Otherwise, [...] deduce a value for U using the rules of template + // argument deduction. + TemplateDeductionInfo Info(RetExpr->getExprLoc()); + TemplateDeductionResult Res = + DeduceAutoType(OrigResultType, RetExpr, Deduced, Info); + if (Res != TDK_Success && FD->isInvalidDecl()) return true; - } - - // If a function with a declared return type that contains a placeholder type - // has multiple return statements, the return type is deduced for each return - // statement. [...] if the type deduced is not the same in each deduction, - // the program is ill-formed. - QualType DeducedT = AT->getDeducedType(); - if (!DeducedT.isNull() && !FD->isInvalidDecl()) { - AutoType *NewAT = Deduced->getContainedAutoType(); - // It is possible that NewAT->getDeducedType() is null. When that happens, - // we should not crash, instead we ignore this deduction. - if (NewAT->getDeducedType().isNull()) - return false; - - CanQualType OldDeducedType = Context.getCanonicalFunctionResultType( - DeducedT); - CanQualType NewDeducedType = Context.getCanonicalFunctionResultType( - NewAT->getDeducedType()); - if (!FD->isDependentContext() && OldDeducedType != NewDeducedType) { + switch (Res) { + case TDK_Success: + break; + case TDK_AlreadyDiagnosed: + return true; + case TDK_Inconsistent: { + // If a function with a declared return type that contains a placeholder + // type has multiple return statements, the return type is deduced for + // each return statement. [...] if the type deduced is not the same in + // each deduction, the program is ill-formed. const LambdaScopeInfo *LambdaSI = getCurLambda(); - if (LambdaSI && LambdaSI->HasImplicitReturnType) { + if (LambdaSI && LambdaSI->HasImplicitReturnType) Diag(ReturnLoc, diag::err_typecheck_missing_return_type_incompatible) - << NewAT->getDeducedType() << DeducedT - << true /*IsLambda*/; - } else { + << Info.SecondArg << Info.FirstArg << true /*IsLambda*/; + else Diag(ReturnLoc, diag::err_auto_fn_different_deductions) - << (AT->isDecltypeAuto() ? 1 : 0) - << NewAT->getDeducedType() << DeducedT; - } + << (AT->isDecltypeAuto() ? 1 : 0) << Info.SecondArg + << Info.FirstArg; + return true; + } + default: + Diag(RetExpr->getExprLoc(), diag::err_auto_fn_deduction_failure) + << OrigResultType.getType() << Info.SecondArg; return true; } - } else if (!FD->isInvalidDecl()) { + } + + // If a local type is part of the returned type, mark its fields as + // referenced. + LocalTypedefNameReferencer(*this).TraverseType(RetExpr->getType()); + + // CUDA: Kernel function must have 'void' return type. + if (getLangOpts().CUDA && FD->hasAttr<CUDAGlobalAttr>() && + !Deduced->isVoidType()) { + Diag(FD->getLocation(), diag::err_kern_type_not_void_return) + << FD->getType() << FD->getSourceRange(); + return true; + } + + if (!FD->isInvalidDecl() && AT->getDeducedType() != Deduced) // Update all declarations of the function to have the deduced return type. Context.adjustDeducedFunctionResultType(FD, Deduced); - } return false; } Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -684,6 +684,7 @@ case Sema::TDK_Success: case Sema::TDK_NonDependentConversionFailure: + case Sema::TDK_AlreadyDiagnosed: llvm_unreachable("not a deduction failure"); } @@ -733,6 +734,7 @@ // Unhandled case Sema::TDK_MiscellaneousDeductionFailure: + case Sema::TDK_AlreadyDiagnosed: break; } } @@ -770,6 +772,7 @@ // Unhandled case Sema::TDK_MiscellaneousDeductionFailure: + case Sema::TDK_AlreadyDiagnosed: break; } @@ -805,6 +808,7 @@ // Unhandled case Sema::TDK_MiscellaneousDeductionFailure: + case Sema::TDK_AlreadyDiagnosed: break; } @@ -836,6 +840,7 @@ // Unhandled case Sema::TDK_MiscellaneousDeductionFailure: + case Sema::TDK_AlreadyDiagnosed: break; } @@ -867,6 +872,7 @@ // Unhandled case Sema::TDK_MiscellaneousDeductionFailure: + case Sema::TDK_AlreadyDiagnosed: break; } @@ -11406,6 +11412,7 @@ switch ((Sema::TemplateDeductionResult)DFI.Result) { case Sema::TDK_Success: case Sema::TDK_NonDependentConversionFailure: + case Sema::TDK_AlreadyDiagnosed: llvm_unreachable("non-deduction failure while diagnosing bad deduction"); case Sema::TDK_Invalid: Index: clang/lib/Sema/SemaExprCXX.cpp =================================================================== --- clang/lib/Sema/SemaExprCXX.cpp +++ clang/lib/Sema/SemaExprCXX.cpp @@ -1506,7 +1506,10 @@ Diag(Deduce->getBeginLoc(), diag::err_auto_expr_init_paren_braces) << ListInitialization << Ty << FullRange); QualType DeducedType; - if (DeduceAutoType(TInfo, Deduce, DeducedType) == DAR_Failed) + TemplateDeductionInfo Info(Deduce->getExprLoc()); + TemplateDeductionResult Result = + DeduceAutoType(TInfo->getTypeLoc(), Deduce, DeducedType, Info); + if (Result != TDK_Success && Result != TDK_AlreadyDiagnosed) return ExprError(Diag(TyBeginLoc, diag::err_auto_expr_deduction_failure) << Ty << Deduce->getType() << FullRange << Deduce->getSourceRange()); @@ -2045,10 +2048,13 @@ Diag(Deduce->getBeginLoc(), diag::err_auto_expr_init_paren_braces) << Braced << AllocType << TypeRange); QualType DeducedType; - if (DeduceAutoType(AllocTypeInfo, Deduce, DeducedType) == DAR_Failed) + TemplateDeductionInfo Info(Deduce->getExprLoc()); + TemplateDeductionResult Result = + DeduceAutoType(AllocTypeInfo->getTypeLoc(), Deduce, DeducedType, Info); + if (Result != TDK_Success && Result != TDK_AlreadyDiagnosed) return ExprError(Diag(StartLoc, diag::err_auto_new_deduction_failure) - << AllocType << Deduce->getType() - << TypeRange << Deduce->getSourceRange()); + << AllocType << Deduce->getType() << TypeRange + << Deduce->getSourceRange()); if (DeducedType.isNull()) return ExprError(); AllocType = DeducedType; Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -4006,10 +4006,9 @@ // The old declaration provided a function prototype, but the // new declaration does not. Merge in the prototype. assert(!OldProto->hasExceptionSpec() && "Exception spec in C"); - SmallVector<QualType, 16> ParamTypes(OldProto->param_types()); - NewQType = - Context.getFunctionType(NewFuncType->getReturnType(), ParamTypes, - OldProto->getExtProtoInfo()); + NewQType = Context.getFunctionType(NewFuncType->getReturnType(), + OldProto->getParamTypes(), + OldProto->getExtProtoInfo()); New->setType(NewQType); New->setHasInheritedPrototype(); @@ -12165,7 +12164,10 @@ Type.getQualifiers()); QualType DeducedType; - if (DeduceAutoType(TSI, DeduceInit, DeducedType) == DAR_Failed) { + TemplateDeductionInfo Info(DeduceInit->getExprLoc()); + TemplateDeductionResult Result = + DeduceAutoType(TSI->getTypeLoc(), DeduceInit, DeducedType, Info); + if (Result != TDK_Success && Result != TDK_AlreadyDiagnosed) { if (!IsInitCapture) DiagnoseAutoDeductionFailure(VDecl, DeduceInit); else if (isa<InitListExpr>(Init)) Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -3179,9 +3179,9 @@ QualType ASTContext::getFunctionTypeWithoutPtrSizes(QualType T) { if (const auto *Proto = T->getAs<FunctionProtoType>()) { QualType RetTy = removePtrSizeAddrSpace(Proto->getReturnType()); - SmallVector<QualType, 16> Args(Proto->param_types()); + SmallVector<QualType, 16> Args(Proto->param_types().size()); for (unsigned i = 0, n = Args.size(); i != n; ++i) - Args[i] = removePtrSizeAddrSpace(Args[i]); + Args[i] = removePtrSizeAddrSpace(Proto->param_types()[i]); return getFunctionType(RetTy, Args, Proto->getExtProtoInfo()); } @@ -12016,6 +12016,427 @@ return (*AddrSpaceMap)[(unsigned)AS]; } +static auto getCommonTypeArray(ASTContext &Ctx, ArrayRef<QualType> Xs, + ArrayRef<QualType> Ys, + bool Unqualified = false) { + assert(Xs.size() == Ys.size()); + SmallVector<QualType, 8> Rs(Xs.size()); + for (size_t I = 0; I < Rs.size(); ++I) { + QualType X = Xs[I], Y = Ys[I]; + if (Unqualified && !Ctx.hasSameType(X, Y)) { + assert(Ctx.hasSameUnqualifiedType(X, Y)); + auto XQuals = X.getCVRQualifiers(), YQuals = Y.getCVRQualifiers(); + X.addFastQualifiers(YQuals & ~XQuals); + Y.addFastQualifiers(XQuals & ~YQuals); + } + Rs[I] = Ctx.getCommonSugar(X, Y); + } + return Rs; +} + +template <class T> +static SourceLocation getCommonAttrLoc(const T *X, const T *Y) { + return X->getAttributeLoc() == Y->getAttributeLoc() ? X->getAttributeLoc() + : SourceLocation(); +} + +static TemplateArgument getCommonTemplateArgument(ASTContext &Ctx, + const TemplateArgument &X, + const TemplateArgument &Y) { + assert(X.getKind() == Y.getKind()); + switch (X.getKind()) { + case TemplateArgument::ArgKind::Type: + return TemplateArgument(Ctx.getCommonSugar(X.getAsType(), Y.getAsType())); + case TemplateArgument::ArgKind::NullPtr: + return TemplateArgument( + Ctx.getCommonSugar(X.getNullPtrType(), Y.getNullPtrType()), true); + default: + return X; + } + llvm_unreachable(""); +} + +static auto getCommonTemplateArguments(ASTContext &Ctx, + ArrayRef<TemplateArgument> X, + ArrayRef<TemplateArgument> Y) { + SmallVector<TemplateArgument, 8> R(X.size()); + for (size_t I = 0; I < R.size(); ++I) + R[I] = getCommonTemplateArgument(Ctx, X[I], Y[I]); + return R; +} + +template <class T> +static ElaboratedTypeKeyword getCommonTypeKeyword(const T *X, const T *Y) { + return X->getKeyword() == Y->getKeyword() ? X->getKeyword() + : ElaboratedTypeKeyword::ETK_None; +} + +template <class T> +static NestedNameSpecifier *getCommonNNS(ASTContext &Ctx, const T *X, + const T *Y) { + return X->getQualifier() == Y->getQualifier() + ? X->getQualifier() + : Ctx.getCanonicalNestedNameSpecifier(X->getQualifier()); +} + +template <class T> +static QualType getCommonElementType(ASTContext &Ctx, const T *X, const T *Y) { + return Ctx.getCommonSugar(X->getElementType(), Y->getElementType()); +} + +template <class T> +static QualType getCommonPointeeType(ASTContext &Ctx, const T *X, const T *Y) { + return Ctx.getCommonSugar(X->getPointeeType(), Y->getPointeeType()); +} + +template <class T> static auto *getCommonSizeExpr(T *X, T *Y) { + assert(X->getSizeExpr() == Y->getSizeExpr()); + return X->getSizeExpr(); +} + +static auto getCommonSizeModifier(const ArrayType *X, const ArrayType *Y) { + assert(X->getSizeModifier() == Y->getSizeModifier()); + return X->getSizeModifier(); +} + +static auto getCommonIndexTypeCVRQualifiers(const ArrayType *X, + const ArrayType *Y) { + assert(X->getIndexTypeCVRQualifiers() == Y->getIndexTypeCVRQualifiers()); + return X->getIndexTypeCVRQualifiers(); +} + +static QualType getCommonCanonicalType(ASTContext &Ctx, const Type *X, + const Type *Y) { + Type::TypeClass TC = X->getTypeClass(); + assert(TC == Y->getTypeClass()); + switch (TC) { +#define UNEXPECTED_TYPE(Class, Kind) \ + case Type::Class: \ + llvm_unreachable("Unexpected " Kind ": " #Class); + +#define NON_CANONICAL_TYPE(Class, Base) UNEXPECTED_TYPE(Class, "non-canonical") +#define TYPE(Class, Base) +#include "clang/AST/TypeNodes.inc" + +#define SUGAR_FREE_TYPE(Class) UNEXPECTED_TYPE(Class, "sugar-free") + SUGAR_FREE_TYPE(Builtin) + SUGAR_FREE_TYPE(Decltype) + SUGAR_FREE_TYPE(DeducedTemplateSpecialization) + SUGAR_FREE_TYPE(DependentBitInt) + SUGAR_FREE_TYPE(Enum) + SUGAR_FREE_TYPE(BitInt) + SUGAR_FREE_TYPE(ObjCInterface) + SUGAR_FREE_TYPE(Record) + SUGAR_FREE_TYPE(SubstTemplateTypeParmPack) + SUGAR_FREE_TYPE(TemplateTypeParm) + SUGAR_FREE_TYPE(UnresolvedUsing) +#undef SUGAR_FREE_TYPE +#define NON_UNIQUE_TYPE(Class) UNEXPECTED_TYPE(Class, "non-unique") + NON_UNIQUE_TYPE(TypeOfExpr) + NON_UNIQUE_TYPE(VariableArray) +#undef NON_UNIQUE_TYPE + + UNEXPECTED_TYPE(TypeOf, "sugar") + +#undef UNEXPECTED_TYPE + + case Type::Auto: { + const auto *AX = cast<AutoType>(X), *AY = cast<AutoType>(Y); + assert(AX->getDeducedType().isNull()); + assert(AY->getDeducedType().isNull()); + assert(AX->getKeyword() == AY->getKeyword()); + assert(AX->isInstantiationDependentType() == + AY->isInstantiationDependentType()); + assert(AX->getTypeConstraintConcept() == AY->getTypeConstraintConcept()); + auto As = getCommonTemplateArguments(Ctx, AX->getTypeConstraintArguments(), + AY->getTypeConstraintArguments()); + return Ctx.getAutoType(QualType(), AX->getKeyword(), + AX->isInstantiationDependentType(), + AX->containsUnexpandedParameterPack(), + AX->getTypeConstraintConcept(), As); + } + case Type::IncompleteArray: { + const auto *AX = cast<IncompleteArrayType>(X), + *AY = cast<IncompleteArrayType>(Y); + return Ctx.getIncompleteArrayType(getCommonElementType(Ctx, AX, AY), + getCommonSizeModifier(AX, AY), + getCommonIndexTypeCVRQualifiers(AX, AY)); + } + case Type::DependentSizedArray: { + const auto *AX = cast<DependentSizedArrayType>(X), + *AY = cast<DependentSizedArrayType>(Y); + return Ctx.getDependentSizedArrayType( + getCommonElementType(Ctx, AX, AY), getCommonSizeExpr(AX, AY), + getCommonSizeModifier(AX, AY), getCommonIndexTypeCVRQualifiers(AX, AY), + AX->getBracketsRange() == AY->getBracketsRange() + ? AX->getBracketsRange() + : SourceRange()); + } + case Type::ConstantArray: { + const auto *AX = cast<ConstantArrayType>(X), + *AY = cast<ConstantArrayType>(Y); + assert(AX->getSize() == AY->getSize()); + return Ctx.getConstantArrayType(getCommonElementType(Ctx, AX, AY), + AX->getSize(), getCommonSizeExpr(AX, AY), + getCommonSizeModifier(AX, AY), + getCommonIndexTypeCVRQualifiers(AX, AY)); + } + case Type::Atomic: { + const auto *AX = cast<AtomicType>(X), *AY = cast<AtomicType>(Y); + return Ctx.getAtomicType( + Ctx.getCommonSugar(AX->getValueType(), AY->getValueType())); + } + case Type::Complex: { + const auto *CX = cast<ComplexType>(X), *CY = cast<ComplexType>(Y); + return Ctx.getComplexType(getCommonElementType(Ctx, CX, CY)); + } + case Type::Pointer: { + const auto *PX = cast<PointerType>(X), *PY = cast<PointerType>(Y); + return Ctx.getPointerType(getCommonPointeeType(Ctx, PX, PY)); + } + case Type::BlockPointer: { + const auto *PX = cast<BlockPointerType>(X), *PY = cast<BlockPointerType>(Y); + return Ctx.getBlockPointerType(getCommonPointeeType(Ctx, PX, PY)); + } + case Type::ObjCObjectPointer: { + const auto *PX = cast<ObjCObjectPointerType>(X), + *PY = cast<ObjCObjectPointerType>(Y); + return Ctx.getObjCObjectPointerType(getCommonPointeeType(Ctx, PX, PY)); + } + case Type::MemberPointer: { + const auto *PX = cast<MemberPointerType>(X), + *PY = cast<MemberPointerType>(Y); + return Ctx.getMemberPointerType( + getCommonPointeeType(Ctx, PX, PY), + Ctx.getCommonSugar(QualType(PX->getClass(), 0), + QualType(PY->getClass(), 0)) + .getTypePtr()); + } + case Type::LValueReference: { + const auto *PX = cast<LValueReferenceType>(X), + *PY = cast<LValueReferenceType>(Y); + return Ctx.getLValueReferenceType(getCommonPointeeType(Ctx, PX, PY), + PX->isSpelledAsLValue() && + PY->isSpelledAsLValue()); + } + case Type::RValueReference: { + const auto *PX = cast<RValueReferenceType>(X), + *PY = cast<RValueReferenceType>(Y); + return Ctx.getRValueReferenceType(getCommonPointeeType(Ctx, PX, PY)); + } + case Type::DependentAddressSpace: { + const auto *PX = cast<DependentAddressSpaceType>(X), + *PY = cast<DependentAddressSpaceType>(Y); + return Ctx.getDependentAddressSpaceType(getCommonPointeeType(Ctx, PX, PY), + PX->getAddrSpaceExpr(), + getCommonAttrLoc(PX, PY)); + } + case Type::FunctionNoProto: { + const auto *FX = cast<FunctionNoProtoType>(X), + *FY = cast<FunctionNoProtoType>(Y); + assert(FX->getExtInfo() == FY->getExtInfo()); + return Ctx.getFunctionNoProtoType( + Ctx.getCommonSugar(FX->getReturnType(), FY->getReturnType()), + FX->getExtInfo()); + } + case Type::FunctionProto: { + const auto *FX = cast<FunctionProtoType>(X), + *FY = cast<FunctionProtoType>(Y); + FunctionProtoType::ExtProtoInfo EPIX = FX->getExtProtoInfo(), + EPIY = FY->getExtProtoInfo(); + assert(EPIX.ExceptionSpec.Type == EPIY.ExceptionSpec.Type); + assert(EPIX.ExceptionSpec.NoexceptExpr == EPIY.ExceptionSpec.NoexceptExpr); + assert(EPIX.ExceptionSpec.SourceDecl == EPIY.ExceptionSpec.SourceDecl); + assert(EPIX.ExceptionSpec.SourceTemplate == + EPIY.ExceptionSpec.SourceTemplate); + assert(EPIX.ExtInfo == EPIY.ExtInfo); + assert(EPIX.ExtParameterInfos == EPIY.ExtParameterInfos); + assert(EPIX.RefQualifier == EPIY.RefQualifier); + assert(EPIX.TypeQuals == EPIY.TypeQuals); + assert(EPIX.Variadic == EPIY.Variadic); + + if (EPIX.EllipsisLoc != EPIY.EllipsisLoc) + EPIX.EllipsisLoc = SourceLocation(); + EPIX.HasTrailingReturn = EPIX.HasTrailingReturn && EPIY.HasTrailingReturn; + + QualType R = Ctx.getCommonSugar(FX->getReturnType(), FY->getReturnType()); + auto P = getCommonTypeArray(Ctx, FX->param_types(), FY->param_types(), + /*Unqualified=*/true); + + if (EPIX.ExceptionSpec.Exceptions.size() == + EPIY.ExceptionSpec.Exceptions.size()) { + auto E = getCommonTypeArray(Ctx, EPIX.ExceptionSpec.Exceptions, + EPIY.ExceptionSpec.Exceptions); + EPIX.ExceptionSpec.Exceptions = E; + return Ctx.getFunctionType(R, P, EPIX); + } else { + if (EPIY.ExceptionSpec.Exceptions.size() > + EPIX.ExceptionSpec.Exceptions.size()) + EPIX.ExceptionSpec.Exceptions = EPIY.ExceptionSpec.Exceptions; + return Ctx.getFunctionType(R, P, EPIX); + } + } + case Type::ObjCObject: { + const auto *OX = cast<ObjCObjectType>(X), *OY = cast<ObjCObjectType>(Y); + assert(llvm::equal(OX->getProtocols(), OY->getProtocols())); + auto TAs = getCommonTypeArray(Ctx, OX->getTypeArgsAsWritten(), + OY->getTypeArgsAsWritten()); + return Ctx.getObjCObjectType( + Ctx.getCommonSugar(OX->getBaseType(), OY->getBaseType()), TAs, + OX->getProtocols(), + OX->isKindOfTypeAsWritten() && OY->isKindOfTypeAsWritten()); + } + case Type::ConstantMatrix: { + const auto *MX = cast<ConstantMatrixType>(X), + *MY = cast<ConstantMatrixType>(Y); + assert(MX->getNumRows() == MY->getNumRows()); + assert(MX->getNumColumns() == MY->getNumColumns()); + return Ctx.getConstantMatrixType(getCommonElementType(Ctx, MX, MY), + MX->getNumRows(), MX->getNumColumns()); + } + case Type::DependentSizedMatrix: { + const auto *MX = cast<DependentSizedMatrixType>(X), + *MY = cast<DependentSizedMatrixType>(Y); + assert(MX->getRowExpr() == MY->getRowExpr()); + assert(MX->getColumnExpr() == MY->getColumnExpr()); + return Ctx.getDependentSizedMatrixType( + getCommonElementType(Ctx, MX, MY), MX->getRowExpr(), + MX->getColumnExpr(), getCommonAttrLoc(MX, MY)); + } + case Type::Vector: { + const auto *VX = cast<VectorType>(X), *VY = cast<VectorType>(Y); + assert(VX->getNumElements() == VY->getNumElements()); + assert(VX->getVectorKind() == VY->getVectorKind()); + return Ctx.getVectorType(getCommonElementType(Ctx, VX, VY), + VX->getNumElements(), VX->getVectorKind()); + } + case Type::ExtVector: { + const auto *VX = cast<ExtVectorType>(X), *VY = cast<ExtVectorType>(Y); + assert(VX->getNumElements() == VY->getNumElements()); + return Ctx.getExtVectorType(getCommonElementType(Ctx, VX, VY), + VX->getNumElements()); + } + case Type::DependentSizedExtVector: { + const auto *VX = cast<DependentSizedExtVectorType>(X), + *VY = cast<DependentSizedExtVectorType>(Y); + return Ctx.getDependentSizedExtVectorType(getCommonElementType(Ctx, VX, VY), + getCommonSizeExpr(VX, VY), + getCommonAttrLoc(VX, VY)); + } + case Type::DependentVector: { + const auto *VX = cast<DependentVectorType>(X), + *VY = cast<DependentVectorType>(Y); + assert(VX->getVectorKind() == VY->getVectorKind()); + return Ctx.getDependentVectorType( + getCommonElementType(Ctx, VX, VY), getCommonSizeExpr(VX, VY), + getCommonAttrLoc(VX, VY), VX->getVectorKind()); + } + case Type::InjectedClassName: { + const auto *IX = cast<InjectedClassNameType>(X), + *IY = cast<InjectedClassNameType>(Y); + assert(IX->getDecl() == IY->getDecl()); + return Ctx.getInjectedClassNameType( + IX->getDecl(), Ctx.getCommonSugar(IX->getInjectedSpecializationType(), + IY->getInjectedSpecializationType())); + } + case Type::TemplateSpecialization: { + const auto *TX = cast<TemplateSpecializationType>(X), + *TY = cast<TemplateSpecializationType>(Y); + assert(TX->getTemplateName().getAsVoidPointer() == + TY->getTemplateName().getAsVoidPointer()); + auto As = getCommonTemplateArguments(Ctx, TX->template_arguments(), + TY->template_arguments()); + return Ctx.getTemplateSpecializationType(TX->getTemplateName(), As, + QualType(TX, 0)); + } + case Type::DependentName: { + const auto *NX = cast<DependentNameType>(X), + *NY = cast<DependentNameType>(Y); + assert(NX->getIdentifier() == NY->getIdentifier()); + return Ctx.getDependentNameType( + getCommonTypeKeyword(NX, NY), getCommonNNS(Ctx, NX, NY), + NX->getIdentifier(), NX->getCanonicalTypeInternal()); + } + case Type::DependentTemplateSpecialization: { + const auto *TX = cast<DependentTemplateSpecializationType>(X), + *TY = cast<DependentTemplateSpecializationType>(Y); + assert(TX->getIdentifier() == TY->getIdentifier()); + auto As = getCommonTemplateArguments(Ctx, TX->template_arguments(), + TY->template_arguments()); + return Ctx.getDependentTemplateSpecializationType( + getCommonTypeKeyword(TX, TY), getCommonNNS(Ctx, TX, TY), + TX->getIdentifier(), As); + } + case Type::UnaryTransform: { + const auto *TX = cast<UnaryTransformType>(X), + *TY = cast<UnaryTransformType>(Y); + assert(TX->getUTTKind() == TY->getUTTKind()); + return Ctx.getUnaryTransformType( + Ctx.getCommonSugar(TX->getBaseType(), TY->getBaseType()), + Ctx.getCommonSugar(TX->getUnderlyingType(), TY->getUnderlyingType()), + TX->getUTTKind()); + } + case Type::PackExpansion: { + const auto *PX = cast<PackExpansionType>(X), + *PY = cast<PackExpansionType>(Y); + return Ctx.getPackExpansionType( + Ctx.getCommonSugar(PX->getPattern(), PY->getPattern()), + PX->getNumExpansions(), false); + } + case Type::Pipe: { + const auto *PX = cast<PipeType>(X), *PY = cast<PipeType>(Y); + assert(PX->isReadOnly() == PY->isReadOnly()); + auto MP = PX->isReadOnly() ? &ASTContext::getReadPipeType + : &ASTContext::getWritePipeType; + return (Ctx.*MP)(getCommonElementType(Ctx, PX, PY)); + } + } + llvm_unreachable("Unknown Type Class"); +} + +static auto unwrapSugar(QualType &T) { + SmallVector<QualType, 8> R; + while (true) { + QualType NT = T->getLocallyUnqualifiedSingleStepDesugaredType(); + NT.addFastQualifiers(T.getLocalFastQualifiers()); + if (T == NT) + break; + R.push_back(T); + T = NT; + } + return R; +} + +QualType ASTContext::getCommonSugar(QualType X, QualType Y) { + assert(hasSameType(X, Y)); + if (X == Y || X.isCanonical()) + return X; + if (Y.isCanonical()) + return Y; + + QualType Orig = X; + (void)Orig; + { + auto Xs = ::unwrapSugar(X), Ys = ::unwrapSugar(Y); + if (X == Y) { + while (!Xs.empty() && !Ys.empty() && Xs.back() == Ys.back()) { + X = Xs.pop_back_val(); + Y = Ys.pop_back_val(); + } + assert(hasSameType(X, Orig)); + return X; + } + } + + auto Quals = X.getLocalFastQualifiers(); + assert(Quals == Y.getLocalFastQualifiers()); + + X = ::getCommonCanonicalType(*this, X.getTypePtr(), Y.getTypePtr()); + X.addFastQualifiers(Quals); + assert(hasSameType(X, Orig)); + return X; +} + QualType ASTContext::getCorrespondingSaturatedType(QualType Ty) const { assert(Ty->isFixedPointType()); Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -8623,7 +8623,9 @@ /// Deduction failed; that's all we know. TDK_MiscellaneousDeductionFailure, /// CUDA Target attributes do not match. - TDK_CUDATargetMismatch + TDK_CUDATargetMismatch, + /// Some error which was already diagnosed. + TDK_AlreadyDiagnosed }; TemplateDeductionResult @@ -8714,21 +8716,11 @@ TypeSourceInfo *ReplaceAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto, QualType Replacement); - /// Result type of DeduceAutoType. - enum DeduceAutoResult { - DAR_Succeeded, - DAR_Failed, - DAR_FailedAlreadyDiagnosed - }; - - DeduceAutoResult - DeduceAutoType(TypeSourceInfo *AutoType, Expr *&Initializer, QualType &Result, - Optional<unsigned> DependentDeductionDepth = None, - bool IgnoreConstraints = false); - DeduceAutoResult - DeduceAutoType(TypeLoc AutoTypeLoc, Expr *&Initializer, QualType &Result, - Optional<unsigned> DependentDeductionDepth = None, - bool IgnoreConstraints = false); + TemplateDeductionResult DeduceAutoType(TypeLoc AutoTypeLoc, Expr *Initializer, + QualType &Result, + sema::TemplateDeductionInfo &Info, + bool DependentDeduction = false, + bool IgnoreConstraints = false); void DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init); bool DeduceReturnType(FunctionDecl *FD, SourceLocation Loc, bool Diagnose = true); @@ -8750,8 +8742,8 @@ TypeLoc getReturnTypeLoc(FunctionDecl *FD) const; bool DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, - SourceLocation ReturnLoc, - Expr *&RetExpr, const AutoType *AT); + SourceLocation ReturnLoc, Expr *RetExpr, + const AutoType *AT); FunctionTemplateDecl *getMoreSpecializedTemplate( FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc, Index: clang/include/clang/AST/Type.h =================================================================== --- clang/include/clang/AST/Type.h +++ clang/include/clang/AST/Type.h @@ -4258,10 +4258,9 @@ } using param_type_iterator = const QualType *; - using param_type_range = llvm::iterator_range<param_type_iterator>; - param_type_range param_types() const { - return param_type_range(param_type_begin(), param_type_end()); + ArrayRef<QualType> param_types() const { + return llvm::makeArrayRef(param_type_begin(), param_type_end()); } param_type_iterator param_type_begin() const { Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -2786,6 +2786,10 @@ return AddrSpaceMapMangling || isTargetAddressSpace(AS); } + // For two canonically equal types, return a type which has + // the common sugar between them. + QualType getCommonSugar(QualType X, QualType Y); + private: // Helper for integer ordering unsigned getIntegerRank(const Type *T) const;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits