mizvekov created this revision. mizvekov published this revision for review. Herald added a project: clang. Herald added a subscriber: cfe-commits.
This patch implements deducing the common sugar between two types, and uses it to establish the result when checking deduced template arguments. This carries over to auto return type deduction as well, which is reworked to update the deduced type. The common sugar is calculated by stripping of top-level sugar which differs between types, crossing over and rebuilding non-sugar type nodes if needed. TODO: - Handle more types. - Maybe in another patch: Add this logic to conditional operator. Depends on D110216 <https://reviews.llvm.org/D110216> Signed-off-by: Matheus Izvekov <mizve...@gmail.com> Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D111283 Files: clang/include/clang/AST/ASTContext.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/SemaStmt.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateDeduction.cpp clang/test/SemaCXX/sugared-auto.cpp
Index: clang/test/SemaCXX/sugared-auto.cpp =================================================================== --- clang/test/SemaCXX/sugared-auto.cpp +++ clang/test/SemaCXX/sugared-auto.cpp @@ -9,23 +9,92 @@ using T2 = T1 *; auto x2 = T2(); -N t2 = x2; -// expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'T2' (aka 'int *')}} +N t2 = x2; // expected-error {{lvalue of type 'T2' (aka 'int *')}} auto *x3 = T2(); -N t3 = x3; -// expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'T1 *' (aka 'int *')}} +N t3 = x3; // expected-error {{lvalue of type 'T1 *' (aka 'int *')}} auto f1() { return T1(); } auto x4 = f1(); -N t4 = x4; -// expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'T1' (aka 'int')}} +N t4 = x4; // expected-error {{lvalue of type 'T1' (aka 'int')}} decltype(auto) f2() { return T1(); } auto x5 = f2(); -N t5 = x5; -// expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'T1' (aka 'int')}} +N t5 = x5; // expected-error {{lvalue of type 'T1' (aka 'int')}} auto x6 = [a = T1()] { return a; }(); -N t6 = x6; -// expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'T1' (aka 'int')}} +N t6 = x6; // expected-error {{lvalue of type 'T1' (aka 'int')}} + +using Animal = int; + +using Man = Animal; +using Dog = Animal; + +// Each variable deduces separately. +auto x7 = Man(), x8 = Dog(); +N t7 = x7; // expected-error {{lvalue of type 'Man' (aka 'int')}} +N t8 = x8; // expected-error {{lvalue of type 'Dog' (aka 'int')}} + +N t9 = [] { // expected-error {{rvalue of type 'Animal' (aka 'int')}} + if (true) + return Man(); + return Dog(); +}(); + +N t10 = []() -> decltype(auto) { // expected-error {{rvalue of type 'Animal' (aka 'int')}} + if (true) + return Man(); + return Dog(); +}(); + +N t11 = [] { // expected-error {{rvalue of type 'Animal' (aka 'int')}} + if (true) + return Dog(); + auto x = Man(); + return x; +}(); + +N t12 = [] { // expected-error {{rvalue of type 'int'}} + if (true) + return Dog(); + return 1; +}(); + +using Virus = void; + +N t13 = [] { // expected-error {{rvalue of type 'void'}} + if (true) + return Virus(); + return; +}(); + +void walk(Man); +void run(Dog); +N t14 = [] { // expected-error {{rvalue of type 'void (*)(Animal)' (aka 'void (*)(int)')}} + if (true) + return walk; + return run; +}(); + +using ManPtr = Man*; +using DogPtr = Dog*; + +N t15 = [] { // expected-error {{rvalue of type 'Animal *' (aka 'int *')}} + if (true) + return ManPtr(); + return DogPtr(); +}(); + +N t16 = [] { // expected-error {{rvalue of type 'int *'}} + if (true) + return ManPtr(); + return (int*)0; +}(); + +using SocratesPtr = ManPtr; + +N t17 = [] { // expected-error {{rvalue of type 'ManPtr' (aka 'int *')}} + if (true) + return SocratesPtr(); + return ManPtr(); +}(); 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 @@ -4516,42 +4519,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(), @@ -4566,11 +4536,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); @@ -4584,11 +4554,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) @@ -4607,177 +4577,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_MiscellaneousDeductionFailure; + + 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_MiscellaneousDeductionFailure; 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_MiscellaneousDeductionFailure; } - 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_MiscellaneousDeductionFailure; + } - 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_MiscellaneousDeductionFailure); + } + 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_MiscellaneousDeductionFailure; + } + 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); + } - if (const auto *AT = Type.getType()->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_MiscellaneousDeductionFailure; + 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_MiscellaneousDeductionFailure; + Result = SubstituteDeducedTypeTransform(*this, DeducedType).Apply(Type); if (Result.isNull()) - return DAR_FailedAlreadyDiagnosed; + return TDK_MiscellaneousDeductionFailure; // Check that the deduced argument type is compatible with the original // argument type per C++ [temp.deduct.call]p4. @@ -4788,11 +4746,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 @@ -6807,7 +6807,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(); @@ -6823,20 +6822,28 @@ 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_MiscellaneousDeductionFailure) { + 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 @@ -2275,11 +2275,15 @@ // 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_MiscellaneousDeductionFailure) DiagnoseAutoDeductionFailure(D, DeducedInit); if (FirstType.isNull()) { D->setInvalidDecl(); @@ -2343,10 +2347,17 @@ // 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_MiscellaneousDeductionFailure) + SemaRef.Diag(Loc, DiagID) << Init->getType(); + } + if (InitType.isNull()) { Decl->setInvalidDecl(); return true; @@ -3744,9 +3755,6 @@ 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. @@ -3766,80 +3774,69 @@ return false; } - if (RetExpr) { + // In the case of a return with no operand, the initializer is considered + // to be void(). + CXXScalarValueInitExpr VoidRet(Context.VoidTy, nullptr, SourceLocation()); + if (!RetExpr) + RetExpr = &VoidRet; + + QualType Deduced = AT->getDeducedType(); + { + TypeLoc OrigResultType = getReturnTypeLoc(FD); // 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) + TemplateDeductionInfo Info(RetExpr->getExprLoc()); + switch (DeduceAutoType(OrigResultType, RetExpr, Deduced, Info)) { + case TDK_Success: + break; + case TDK_MiscellaneousDeductionFailure: 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 { - // In the case of a return with no operand, the initializer is considered - // to be void(). - // - // Deduction here can only succeed if the return type is exactly 'cv auto' - // or 'decltype(auto)', so just check for that case directly. - if (!OrigResultType.getType()->getAs<AutoType>()) { - Diag(ReturnLoc, diag::err_auto_fn_return_void_but_not_auto) - << OrigResultType.getType(); + case TDK_Inconsistent: { + if (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. + const LambdaScopeInfo *LambdaSI = getCurLambda(); + if (LambdaSI && LambdaSI->HasImplicitReturnType) + Diag(ReturnLoc, diag::err_typecheck_missing_return_type_incompatible) + << Info.SecondArg << Info.FirstArg << true /*IsLambda*/; + else + Diag(ReturnLoc, diag::err_auto_fn_different_deductions) + << (AT->isDecltypeAuto() ? 1 : 0) << Info.SecondArg + << Info.FirstArg; return true; } - // We always deduce U = void in this case. - Deduced = SubstAutoType(OrigResultType.getType(), Context.VoidTy); - if (Deduced.isNull()) + default: + if (FD->isInvalidDecl()) + return true; + if (Info.SecondArg.getAsType()->isVoidType()) + Diag(ReturnLoc, diag::err_auto_fn_return_void_but_not_auto) + << OrigResultType.getType(); + else + Diag(RetExpr->getExprLoc(), diag::err_auto_fn_deduction_failure) + << OrigResultType.getType() << Info.SecondArg; return true; + } } - // 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(); - return true; - } + // If a local type is part of the returned type, mark its fields as + // referenced. + LocalTypedefNameReferencer(*this).TraverseType(RetExpr->getType()); - // 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; + // 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; + } - CanQualType OldDeducedType = Context.getCanonicalFunctionResultType( - DeducedT); - CanQualType NewDeducedType = Context.getCanonicalFunctionResultType( - NewAT->getDeducedType()); - if (!FD->isDependentContext() && OldDeducedType != NewDeducedType) { - const LambdaScopeInfo *LambdaSI = getCurLambda(); - if (LambdaSI && LambdaSI->HasImplicitReturnType) { - Diag(ReturnLoc, diag::err_typecheck_missing_return_type_incompatible) - << NewAT->getDeducedType() << DeducedT - << true /*IsLambda*/; - } else { - Diag(ReturnLoc, diag::err_auto_fn_different_deductions) - << (AT->isDecltypeAuto() ? 1 : 0) - << NewAT->getDeducedType() << DeducedT; - } - return true; - } - } else if (!FD->isInvalidDecl()) { + 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/SemaExprCXX.cpp =================================================================== --- clang/lib/Sema/SemaExprCXX.cpp +++ clang/lib/Sema/SemaExprCXX.cpp @@ -2002,10 +2002,13 @@ << AllocType << TypeRange; Expr *Deduce = Inits[0]; 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_MiscellaneousDeductionFailure) 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 @@ -11816,7 +11816,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_MiscellaneousDeductionFailure) { 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 @@ -11532,6 +11532,108 @@ return (*AddrSpaceMap)[(unsigned)AS]; } +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 unwrap = [](QualType &T) { + SmallVector<QualType, 8> R; + while (true) { + QualType NextT = T->getLocallyUnqualifiedSingleStepDesugaredType(); + NextT.addFastQualifiers(T.getLocalFastQualifiers()); + if (T == NextT) + break; + R.push_back(T); + T = NextT; + } + return R; + }; + auto Xs = unwrap(X), Ys = unwrap(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; + } + } + + Type::TypeClass TC = X->getTypeClass(); + assert(TC == Y->getTypeClass()); + auto Quals = X.getLocalFastQualifiers(); + assert(Quals == Y.getLocalFastQualifiers()); + + switch (TC) { + case Type::Pointer: { + const auto *PX = cast<PointerType>(X), *PY = cast<PointerType>(Y); + X = getPointerType( + getCommonSugar(PX->getPointeeType(), PY->getPointeeType())); + break; + } + case Type::LValueReference: { + const auto *PX = cast<LValueReferenceType>(X), + *PY = cast<LValueReferenceType>(Y); + X = getLValueReferenceType( + getCommonSugar(PX->getPointeeType(), PY->getPointeeType()), + PX->isSpelledAsLValue() && PY->isSpelledAsLValue()); + break; + } + case Type::RValueReference: { + const auto *PX = cast<RValueReferenceType>(X), + *PY = cast<RValueReferenceType>(Y); + X = getRValueReferenceType( + getCommonSugar(PX->getPointeeType(), PY->getPointeeType())); + break; + } + case Type::FunctionProto: { + const auto *FX = cast<FunctionProtoType>(X), + *FY = cast<FunctionProtoType>(Y); + assert(FX->getNumParams() == FY->getNumParams()); + FunctionProtoType::ExtProtoInfo EPIX = FX->getExtProtoInfo(), + EPIY = FY->getExtProtoInfo(); + assert(EPIX.ExceptionSpec.Type == EPIY.ExceptionSpec.Type); + assert(EPIX.ExceptionSpec.Exceptions.size() == + EPIY.ExceptionSpec.Exceptions.size()); + 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); + + QualType R = getCommonSugar(FX->getReturnType(), FY->getReturnType()); + SmallVector<QualType, 8> P(FX->getNumParams()); + for (size_t I = 0; I < P.size(); ++I) + P[I] = getCommonSugar(FX->getParamType(I), FY->getParamType(I)); + SmallVector<QualType, 8> E(EPIX.ExceptionSpec.Exceptions.size()); + for (size_t I = 0; I < E.size(); ++I) + E[I] = getCommonSugar(EPIX.ExceptionSpec.Exceptions[I], + EPIY.ExceptionSpec.Exceptions[I]); + EPIX.ExceptionSpec.Exceptions = E; + if (EPIX.EllipsisLoc != EPIY.EllipsisLoc) + EPIX.EllipsisLoc = SourceLocation(); + EPIX.HasTrailingReturn = EPIX.HasTrailingReturn && EPIY.HasTrailingReturn; + X = getFunctionType(R, P, EPIX); + } + // FIXME: Handle all the other types. + default: + break; + } + 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 @@ -8564,21 +8564,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); Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -2735,6 +2735,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