================ @@ -3231,6 +3241,230 @@ static QualType builtinCommonTypeImpl(Sema &S, TemplateName BaseTemplate, } } +static QualType CopyCV(QualType From, QualType To) { + if (From.isConstQualified()) + To.addConst(); + if (From.isVolatileQualified()) + To.addVolatile(); + return To; +} + +// COND-RES(X, Y) be decltype(false ? declval<X(&)()>()() : declval<Y(&)()>()()) +static QualType CondRes(Sema &S, QualType X, QualType Y, SourceLocation Loc) { + EnterExpressionEvaluationContext UnevaluatedContext( + S, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true); + Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl()); + + // false + OpaqueValueExpr CondExpr(SourceLocation(), S.Context.BoolTy, VK_PRValue); + ExprResult Cond = &CondExpr; + + // declval<X(&)()>()() + OpaqueValueExpr LHSExpr(Loc, X.getNonLValueExprType(S.Context), + Expr::getValueKindForType(X)); + ExprResult LHS = &LHSExpr; + + // declval<Y(&)()>()() + OpaqueValueExpr RHSExpr(Loc, Y.getNonLValueExprType(S.Context), + Expr::getValueKindForType(Y)); + ExprResult RHS = &RHSExpr; + + ExprValueKind VK = VK_PRValue; + ExprObjectKind OK = OK_Ordinary; + + // decltype(false ? declval<X(&)()>()() : declval<Y(&)()>()()) + QualType Result = S.CheckConditionalOperands(Cond, LHS, RHS, VK, OK, Loc); + + if (SFINAE.hasErrorOccurred()) + return QualType(); + if (VK == VK_LValue) + return S.BuiltinAddLValueReference(Result, Loc); + if (VK == VK_XValue) + return S.BuiltinAddRValueReference(Result, Loc); + return Result; +} + +static QualType CommonRef(Sema &S, QualType A, QualType B, SourceLocation Loc) { + // Given types A and B, let X be remove_reference_t<A>, let Y be + // remove_reference_t<B>, and let COMMON-REF(A, B) be: + assert(A->isReferenceType() && B->isReferenceType() && + "A and B have to be ref qualified for a COMMON-REF"); + auto X = A.getNonReferenceType(); + auto Y = B.getNonReferenceType(); + + // If A and B are both lvalue reference types, COMMON-REF(A, B) is + // COND-RES(COPYCV(X, Y) &, COPYCV(Y, X) &) if that type exists and is a + // reference type. + if (A->isLValueReferenceType() && B->isLValueReferenceType()) { + auto CR = CondRes(S, S.BuiltinAddLValueReference(CopyCV(X, Y), Loc), + S.BuiltinAddLValueReference(CopyCV(Y, X), Loc), Loc); + if (CR.isNull() || !CR->isReferenceType()) + return QualType(); + return CR; + } + + // Otherwise, let C be remove_reference_t<COMMON-REF(X&, Y&)>&&. If A and B + // are both rvalue reference types, C is well-formed, and + // is_convertible_v<A, C> && is_convertible_v<B, C> is true, then + // COMMON-REF(A, B) is C. + if (A->isRValueReferenceType() && B->isRValueReferenceType()) { + auto C = CommonRef(S, S.BuiltinAddLValueReference(X, Loc), + S.BuiltinAddLValueReference(Y, Loc), Loc); + if (C.isNull()) + return QualType(); + + C = C.getNonReferenceType(); + + if (S.BuiltinIsConvertible(A, C, Loc) && S.BuiltinIsConvertible(B, C, Loc)) + return S.BuiltinAddRValueReference(C, Loc); + return QualType(); + } + + // Otherwise, if A is an lvalue reference and B is an rvalue reference, then + // COMMON-REF(A, B) is COMMON-REF(B, A). + if (A->isLValueReferenceType() && B->isRValueReferenceType()) + std::swap(A, B); + + // Otherwise, let D be COMMON-REF(const X&, Y&). If A is an rvalue reference + // and B is an lvalue reference and D is well-formed and + // is_convertible_v<A, D> is true, then COMMON-REF(A, B) is D. + if (A->isRValueReferenceType() && B->isLValueReferenceType()) { + auto X2 = X; + X2.addConst(); + auto D = CommonRef(S, S.BuiltinAddLValueReference(X2, Loc), + S.BuiltinAddLValueReference(Y, Loc), Loc); + if (!D.isNull() && S.BuiltinIsConvertible(A, D, Loc)) + return D; + return QualType(); + } + + // Otherwise, COMMON-REF(A, B) is ill-formed. + // This is implemented by returning from the individual branches above. + + llvm_unreachable("The above cases should be exhaustive"); +} + +static QualType builtinCommonReferenceImpl(Sema &S, + TemplateName CommonReference, + TemplateName CommonType, + SourceLocation TemplateLoc, + ArrayRef<TemplateArgument> Ts) { + switch (Ts.size()) { + // If sizeof...(T) is zero, there shall be no member type. + case 0: + return QualType(); + + // Otherwise, if sizeof...(T) is one, let T0 denote the sole type in the + // pack T. The member typedef type shall denote the same type as T0. + case 1: + return Ts[0].getAsType(); + + // Otherwise, if sizeof...(T) is two, let T1 and T2 denote the two types in + // the pack T. Then + case 2: { + auto T1 = Ts[0].getAsType(); + auto T2 = Ts[1].getAsType(); + + // Let R be COMMON-REF(T1, T2). If T1 and T2 are reference types, R is + // well-formed, and is_convertible_v<add_pointer_t<T1>, add_pointer_t<R>> && + // is_convertible_v<add_pointer_t<T2>, add_pointer_t<R>> is true, then the + // member typedef type denotes R. + if (T1->isReferenceType() && T2->isReferenceType()) { + QualType R = CommonRef(S, T1, T2, TemplateLoc); + if (!R.isNull()) { + if (S.BuiltinIsConvertible(S.BuiltinAddPointer(T1, TemplateLoc), ---------------- huixie90 wrote:
sure. but my concern is that your builtin implementation seems to be based on the wording after P2655. However, the original implementation of common_reference.h is pre P2655. So depending on wether the builtin is available, the behaviour of the library will be different. I would prefer to updating the common_reference.h 's non-builtin branch to match the builtin branch https://github.com/llvm/llvm-project/pull/121199 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits