================ @@ -3058,6 +3058,141 @@ void Sema::NoteAllFoundTemplates(TemplateName Name) { } } +static std::optional<QualType> commonTypeImpl(Sema &S, + TemplateName BaseTemplate, + SourceLocation TemplateLoc, + ArrayRef<TemplateArgument> Ts) { + auto lookUpCommonType = [&](TemplateArgument T1, + TemplateArgument T2) -> std::optional<QualType> { + // Don't bother looking for other specializations if both types are + // builtins - users aren't allowed to specialize for them + if (T1.getAsType()->isBuiltinType() && T2.getAsType()->isBuiltinType()) + return commonTypeImpl(S, BaseTemplate, TemplateLoc, {T1, T2}); + + TemplateArgumentListInfo Args; + Args.addArgument(TemplateArgumentLoc( + T1, S.Context.getTrivialTypeSourceInfo(T1.getAsType()))); + Args.addArgument(TemplateArgumentLoc( + T2, S.Context.getTrivialTypeSourceInfo(T2.getAsType()))); + QualType BaseTemplateInst = + S.CheckTemplateIdType(BaseTemplate, TemplateLoc, Args); + if (S.RequireCompleteType(TemplateLoc, BaseTemplateInst, + diag::err_incomplete_type)) + return std::nullopt; + if (QualType Type = S.getTypeMember("type", BaseTemplateInst); + !Type.isNull()) { + return Type; + } + return std::nullopt; + }; + + // Note A: For the common_type trait applied to a template parameter pack T of + // types, the member type shall be either defined or not present as follows: + switch (Ts.size()) { + + // If sizeof...(T) is zero, there shall be no member type. + case 0: + return std::nullopt; + + // If sizeof...(T) is one, let T0 denote the sole type constituting the + // pack T. The member typedef-name type shall denote the same type, if any, as + // common_type_t<T0, T0>; otherwise there shall be no member type. + case 1: + return lookUpCommonType(Ts[0], Ts[0]); + + // If sizeof...(T) is two, let the first and second types constituting T be + // denoted by T1 and T2, respectively, and let D1 and D2 denote the same types + // as decay_t<T1> and decay_t<T2>, respectively. + case 2: { + QualType T1 = Ts[0].getAsType(); + QualType T2 = Ts[1].getAsType(); + QualType D1 = S.BuiltinDecay(T1, {}); + QualType D2 = S.BuiltinDecay(T2, {}); + + // If is_same_v<T1, D1> is false or is_same_v<T2, D2> is false, let C denote + // the same type, if any, as common_type_t<D1, D2>. + if (!S.Context.hasSameType(T1, D1) || !S.Context.hasSameType(T2, D2)) { + return lookUpCommonType(D1, D2); + } + + // Otherwise, if decay_t<decltype(false ? declval<D1>() : declval<D2>())> + // denotes a valid type, let C denote that type. + { + auto CheckConditionalOperands = + [&](bool ConstRefQual) -> std::optional<QualType> { + EnterExpressionEvaluationContext UnevaluatedContext( + S, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true); + Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl()); + + // false + OpaqueValueExpr CondExpr({}, S.Context.BoolTy, + ExprValueKind::VK_PRValue); + ExprResult Cond = &CondExpr; + + auto EVK = + ConstRefQual ? ExprValueKind::VK_LValue : ExprValueKind::VK_PRValue; + if (ConstRefQual) { + D1.addConst(); + D2.addConst(); + } + + // declval<D1>() + OpaqueValueExpr LHSExpr(TemplateLoc, D1, EVK); + ExprResult LHS = &LHSExpr; + + // declval<D2>() + OpaqueValueExpr RHSExpr(TemplateLoc, D2, EVK); + ExprResult RHS = &RHSExpr; + + ExprValueKind VK = VK_PRValue; + ExprObjectKind OK = OK_Ordinary; + + // decltype(false ? declval<D1>() : declval<D2>()) + QualType Result = + S.CheckConditionalOperands(Cond, LHS, RHS, VK, OK, TemplateLoc); + + if (Result.isNull() || SFINAE.hasErrorOccurred()) + return std::nullopt; + + // decay_t<decltype(false ? declval<D1>() : declval<D2>())> + return S.BuiltinDecay(Result, TemplateLoc); + }; + + if (auto Res = CheckConditionalOperands(false)) + return Res; + + // Let: + // CREF(A) be add_lvalue_reference_t<const remove_reference_t<A>>, + // COND-RES(X, Y) be + // decltype(false ? declval<X(&)()>()() : declval<Y(&)()>()()). + + // C++20 only + // Otherwise, if COND-RES(CREF(D1), CREF(D2)) denotes a type, let C denote + // the type decay_t<COND-RES(CREF(D1), CREF(D2))>. + if (!S.Context.getLangOpts().CPlusPlus20) + return std::nullopt; + return CheckConditionalOperands(true); + } + } + + // If sizeof...(T) is greater than two, let T1, T2, and R, respectively, + // denote the first, second, and (pack of) remaining types constituting T. Let + // C denote the same type, if any, as common_type_t<T1, T2>. If there is such + // a type C, the member typedef-name type shall denote the same type, if any, + // as common_type_t<C, R...>. Otherwise, there shall be no member type. + default: { + std::optional<QualType> Result = Ts[Ts.size() - 1].getAsType(); + for (size_t i = Ts.size() - 1; i != 0; --i) { + Result = lookUpCommonType(Ts[i - 1].getAsType(), *Result); ---------------- Sirraide wrote:
I might be missing something obvious, but is there a reason why we’re iterating in reverse here? I don’t think it *should* matter, but still... https://github.com/llvm/llvm-project/pull/99473 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits