rsmith added a comment. Thanks! This is looking pretty close.
================ Comment at: include/clang/AST/ComparisonCategories.h:78 +public: + /// \brief Wether Sema has fully checked the type and result values for this + /// comparison category types before. ---------------- Typo "Wether". ================ Comment at: include/clang/Basic/DiagnosticSemaKinds.td:9377 +def err_implied_comparison_category_type_not_found : Error< + "cannot deduce return type of operator<=> because type %0 was not found; " + "include <compare>">; ---------------- Nit: you have inconsistent quoting of 'operator<=>' between this diagnostic and the next. ================ Comment at: include/clang/Basic/DiagnosticSemaKinds.td:9387 + "standard library implementation of comparison category %0 is not supported; " + "failed to build reference to member '%1'">; } // end of sema component. ---------------- This seems a bit too implementation-detaily -- how about something vaguer like "[...] not supported; member '%1' does not have expected form"? (I suppose it doesn't matter too much; only standard library implementers are likely to ever see this diagnostic anyway.) ================ Comment at: lib/AST/ComparisonCategories.cpp:51 + if (!StdNS) { + StdNS = NamespaceDecl::Create( + const_cast<ASTContext &>(Ctx), Ctx.getTranslationUnitDecl(), ---------------- We shouldn't be creating a 'namespace std' here. If there is no existing such namespace, our lookups should just fail. ================ Comment at: lib/AST/ExprConstant.cpp:3784 +static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD, + APValue *Dest = nullptr) { // We don't need to evaluate the initializer for a static local. ---------------- This `Dest` parameter seems to be unused, is it left behind from a previous direction? ================ Comment at: lib/AST/ExprConstant.cpp:8824 + return EvaluateComparisonBinaryOperator(Info, E, OnSuccess, []() -> bool { + llvm_unreachable("operator<=> should have been evaluated to a result"); + }); ---------------- I'm not convinced this is a legitimate use of `llvm_unreachable` -- it seems to me that there could be all sorts of types for which Sema can form a `<=>` expression but that we don't handle here, such as vector types. Maybe issue an `FFDiag` here, or just call the base class version of this function, which I think should do it for you? ================ Comment at: lib/CodeGen/CGExprAgg.cpp:947-954 + return CGF.ErrorUnsupported( + E, "aggregate binary expression with complex arguments"); + if (ArgTy->isVectorType()) + return CGF.ErrorUnsupported( + E, "aggregate binary expression with vector arguments"); + if (!ArgTy->isIntegralOrEnumerationType() && !ArgTy->isRealFloatingType() && + !ArgTy->isPointerType() && !ArgTy->isMemberPointerType()) ---------------- Instead of "aggregate binary expression" in all of these errors, how about "three-way comparison"? ================ Comment at: lib/CodeGen/CGExprAgg.cpp:1000-1001 + + return EmitFinalDestCopy( + E->getType(), CGF.MakeNaturalAlignAddrLValue(Select, E->getType())); +} ---------------- Hm, I wonder whether it's worthwhile to try to generate a select between the comparison result values rather than their addresses. (Maybe not, since they could in general be an arbitrary aggregate type, and a select on a first-class aggregate value is unlikely to produce anything useful at -O0). ================ Comment at: lib/Sema/SemaDeclCXX.cpp:8903-8916 + // Build the initial category information + RecordDecl *CCDecl = nullptr; + // Lookup the record for the category type + if (auto Std = getStdNamespace()) { + LookupResult Result(*this, &PP.getIdentifierTable().get(Name), + SourceLocation(), Sema::LookupTagName); + if (LookupQualifiedName(Result, Std)) ---------------- I don't think this makes sense: `CompCategories::lookupInfo` already did the lookup we wanted here; checking some other declaration just invites the two lookups being inconsistent in some way. I think you should just check whether `CachedInfo` is null here, and if so, produce the "type not found" error. ================ Comment at: lib/Sema/SemaDeclCXX.cpp:8936-8948 + // Build each of the require values and store them in Info. + for (CCVT CCV : Values) { + StringRef ValueName = ComparisonCategories::getResultString(CCV); + QualType Ty(CCDecl->getTypeForDecl(), 0); + DeclContext *LookupCtx = computeDeclContext(Ty); + LookupResult Found(*this, &PP.getIdentifierTable().get(ValueName), Loc, + Sema::LookupOrdinaryName); ---------------- Likewise here, you should query the `CachedInfo` object for these values and check them, rather than looking them up again in a different way. ================ Comment at: lib/Sema/SemaDeclCXX.cpp:8966 + assert(CachedInfo->Kind == Kind); + CachedInfo->IsFullyChecked = true; + return QualType(CachedInfo->CCDecl->getTypeForDecl(), 0); ---------------- I would somewhat prefer that we keep (say) a `DenseSet` of checked types on `Sema` rather than having `Sema` effectively own this flag that's held by `ASTContext`. ================ Comment at: lib/Sema/SemaExpr.cpp:9727-9728 + +static bool checkNarrowingConversion(Sema &S, QualType ToType, Expr *E, + QualType FromType, SourceLocation Loc) { + // Check for a narrowing implicit conversion. ---------------- This should have a name that has something to do with three-way comparisons (that is, assuming that duplicating this is the best way to customize the diagnostic behavior). ================ Comment at: lib/Sema/SemaExpr.cpp:9761-9776 + if (E->isValueDependent()) + return false; + + // Check the expression is a constant expression. + SmallVector<PartialDiagnosticAt, 8> Notes; + Expr::EvalResult Eval; + Eval.Diag = &Notes; ---------------- Delete all of this? Once we get this far, we know it's a narrowing conversion, so should just be diagnosing it. ================ Comment at: lib/Sema/SemaExpr.cpp:9794 + + // C++2a [expr.spaceship]p3 + if (int Count = (LHSType->isBooleanType() + RHSType->isBooleanType())) { ---------------- This is not especially useful: C++2a is a moving target, so p3 might mean something else later. We usually include an excerpt describing what we're checking, if it's useful to do so. ================ Comment at: lib/Sema/SemaExpr.cpp:9795 + // C++2a [expr.spaceship]p3 + if (int Count = (LHSType->isBooleanType() + RHSType->isBooleanType())) { + // TODO: The spec says that if one but not both of the operands is 'bool' ---------------- Redundant parens here. ================ Comment at: lib/Sema/SemaExpr.cpp:9807-9810 + if (!S.Context.hasSameUnqualifiedType(LHSType, RHSType)) { + S.InvalidOperands(Loc, LHS, RHS); + return QualType(); + } ---------------- Please implement the "straight to CWG" resolutions from http://wiki.edg.com/bin/view/Wg21jacksonville2018/P0946R0-Jax18 directly here. Specifically, in this case, we should allow three-way comparisons between unscoped enumerations and integral types (subject to the narrowing check), but not between two unrelated enumeration types, and not between a scoped enumeration and an integral type. ================ Comment at: lib/Sema/SemaExpr.cpp:9814-9815 + + LHS = S.ImpCastExprToType(LHS.get(), Type, CK_BitCast); + RHS = S.ImpCastExprToType(RHS.get(), Type, CK_BitCast); + } else { ---------------- We use `CK_IntegralCast` for conversions between enumerations and integer types. ================ Comment at: lib/Sema/SemaExpr.cpp:9816 + RHS = S.ImpCastExprToType(RHS.get(), Type, CK_BitCast); + } else { + // C++2a [expr.spaceship]p4 ---------------- We still need to apply the usual arithmetic conversions after converting enumerations to their underlying types (eg, `<=>` on `enum E : char` converts the operands first to `char` then to `int`). You could remove the `else` here and make this stuff unconditional, but it's probably better to sidestep the extra work and convert directly to the promoted type of the enum's underlying type. ================ Comment at: lib/Sema/SemaExpr.cpp:9887-9894 // Comparisons expect an rvalue, so convert to rvalue before any // type-related checks. LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); if (LHS.isInvalid()) return QualType(); RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); if (RHS.isInvalid()) ---------------- This is wrong for the three-way comparison case, where these conversions are only performed if one of the operands is of pointer type (see [expr.spaceship]p6). You could either delay decaying here, or check whether we performed a non-permitted decay after the fact. ================ Comment at: lib/Sema/SemaExpr.cpp:9925-9927 + // C++2a [expr.spaceship]p7: If the composite pointer type is a function + // pointer type, a pointer-to-member type, or std::nullptr_t, the + // result is of type std::strong_equality ---------------- Per http://wiki.edg.com/bin/view/Wg21jacksonville2018/P0946R0-Jax18, we should also end up here if either of the operands was (a null pointer constant) of non-pointer type prior to the conversions -- that is, in the pointer comparison cases where `<` would be invalid, we should return `std::strong_equality`. ================ Comment at: lib/Sema/SemaExpr.cpp:9935-9936 + if (CompositeTy->isPointerType() && + (CompositeTy->getPointeeType()->isObjectType() || + CompositeTy->getPointeeType()->isVoidType())) + return buildResultTy(ComparisonCategoryType::StrongOrdering); ---------------- You don't need this check; you already checked for function pointer types above, which are the only kind of non-object pointer. ================ Comment at: lib/Sema/SemaExpr.cpp:9940-9941 + // C++2a [expr.spaceship]p9: Otherwise, the program is ill-formed. + // TODO: Can this case actually occur? ie we have a + // non-object/function/mem-function pointer, non-enum, and non-integral type + return InvalidOperands(Loc, LHS, RHS); ---------------- Yes, you'll get here for at least Obj-C object pointers and block pointers. ================ Comment at: lib/Sema/SemaExpr.cpp:10112 // their composite pointer type. - if (!IsRelational && + if ((!IsRelational || IsThreeWayCmp) && (LHSType->isMemberPointerType() || RHSType->isMemberPointerType())) { ---------------- Three-way compare is not a relational comparison, so this change appears redundant? (Likewise in other places where you use this check.) Maybe we're passing the wrong value for `IsRelational`? ================ Comment at: lib/Sema/SemaExpr.cpp:11942 ConvertHalfVec = true; ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, true); + assert(ResultTy.isNull() || ResultTy->getAsCXXRecordDecl()); ---------------- Ah, here it is, `true` is incorrectly being passed for `IsRelational` here. Maybe replace that `bool` with an `enum` (or remove it entirely and have the callee recompute it from `Opc`)? https://reviews.llvm.org/D45476 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits