diff options
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/Sema.cpp | 11 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 129 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 333 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 57 |
4 files changed, 463 insertions, 67 deletions
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 5c0026cd374..b964cb2f0ef 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -137,10 +137,13 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, ValueWithBytesObjCTypeMethod(nullptr), NSArrayDecl(nullptr), ArrayWithObjectsMethod(nullptr), NSDictionaryDecl(nullptr), DictionaryWithObjectsMethod(nullptr), GlobalNewDeleteDeclared(false), - TUKind(TUKind), NumSFINAEErrors(0), AccessCheckingSFINAE(false), - InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0), - ArgumentPackSubstitutionIndex(-1), CurrentInstantiationScope(nullptr), - DisableTypoCorrection(false), TyposCorrected(0), AnalysisWarnings(*this), + TUKind(TUKind), NumSFINAEErrors(0), + FullyCheckedComparisonCategories( + static_cast<unsigned>(ComparisonCategoryType::Last) + 1), + AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false), + NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1), + CurrentInstantiationScope(nullptr), DisableTypoCorrection(false), + TyposCorrected(0), AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr), VarDataSharingAttributesStack(nullptr), CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) { TUScope = nullptr; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 9126f27b4de..fb168957bd7 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17,6 +17,7 @@ #include "clang/AST/ASTMutationListener.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/ComparisonCategories.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecordLayout.h" @@ -8891,6 +8892,134 @@ NamespaceDecl *Sema::lookupStdExperimentalNamespace() { return StdExperimentalNamespaceCache; } +namespace { + +enum UnsupportedSTLSelect { + USS_InvalidMember, + USS_MissingMember, + USS_NonTrivial, + USS_Other +}; + +struct InvalidSTLDiagnoser { + Sema &S; + SourceLocation Loc; + QualType TyForDiags; + + QualType operator()(UnsupportedSTLSelect Sel = USS_Other, StringRef Name = "", + const VarDecl *VD = nullptr) { + { + auto D = S.Diag(Loc, diag::err_std_compare_type_not_supported) + << TyForDiags << ((int)Sel); + if (Sel == USS_InvalidMember || Sel == USS_MissingMember) { + assert(!Name.empty()); + D << Name; + } + } + if (Sel == USS_InvalidMember) { + S.Diag(VD->getLocation(), diag::note_var_declared_here) + << VD << VD->getSourceRange(); + } + return QualType(); + } +}; +} // namespace + +QualType Sema::CheckComparisonCategoryType(ComparisonCategoryType Kind, + SourceLocation Loc) { + assert(getLangOpts().CPlusPlus && + "Looking for comparison category type outside of C++."); + + // Check if we've already successfully checked the comparison category type + // before. If so, skip checking it again. + ComparisonCategoryInfo *Info = Context.CompCategories.lookupInfo(Kind); + if (Info && FullyCheckedComparisonCategories[static_cast<unsigned>(Kind)]) + return Info->getType(); + + // If lookup failed + if (!Info) { + Diag(Loc, diag::err_implied_comparison_category_type_not_found) + << ComparisonCategories::getCategoryString(Kind); + return QualType(); + } + + assert(Info->Kind == Kind); + assert(Info->Record); + + // Update the Record decl in case we encountered a forward declaration on our + // first pass. FIXME(EricWF): This is a bit of a hack. + if (Info->Record->hasDefinition()) + Info->Record = Info->Record->getDefinition(); + + // Use an elaborated type for diagnostics which has a name containing the + // prepended 'std' namespace but not any inline namespace names. + QualType TyForDiags = [&]() { + auto *NNS = + NestedNameSpecifier::Create(Context, nullptr, getStdNamespace()); + return Context.getElaboratedType(ETK_None, NNS, Info->getType()); + }(); + + if (RequireCompleteType(Loc, TyForDiags, diag::err_incomplete_type)) + return QualType(); + + InvalidSTLDiagnoser UnsupportedSTLError{*this, Loc, TyForDiags}; + + if (!Info->Record->isTriviallyCopyable()) + return UnsupportedSTLError(USS_NonTrivial); + + for (const CXXBaseSpecifier &BaseSpec : Info->Record->bases()) { + CXXRecordDecl *Base = BaseSpec.getType()->getAsCXXRecordDecl(); + // Tolerate empty base classes. + if (Base->isEmpty()) + continue; + // Reject STL implementations which have at least one non-empty base. + return UnsupportedSTLError(); + } + + // Check that the STL has implemented the types using a single integer field. + // This expectation allows better codegen for builtin operators. We require: + // (1) The class has exactly one field. + // (2) The field is an integral or enumeration type. + auto FIt = Info->Record->field_begin(), FEnd = Info->Record->field_end(); + if (std::distance(FIt, FEnd) != 1 || + !FIt->getType()->isIntegralOrEnumerationType()) { + return UnsupportedSTLError(); + } + + // Build each of the require values and store them in Info. + for (ComparisonCategoryResult CCR : + ComparisonCategories::getPossibleResultsForType(Kind)) { + StringRef MemName = ComparisonCategories::getResultString(CCR); + ComparisonCategoryInfo::ValueInfo *ValInfo = Info->lookupValueInfo(CCR); + + if (!ValInfo) + return UnsupportedSTLError(USS_MissingMember, MemName); + + VarDecl *VD = ValInfo->VD; + assert(VD && "should not be null!"); + + // Attempt to diagnose reasons why the STL definition of this type + // might be foobar, including it failing to be a constant expression. + // TODO Handle more ways the lookup or result can be invalid. + if (!VD->isStaticDataMember() || !VD->isConstexpr() || !VD->hasInit() || + !VD->checkInitIsICE()) + return UnsupportedSTLError(USS_InvalidMember, MemName, VD); + + // Attempt to evaluate the var decl as a constant expression and extract + // the value of its first field as a ICE. If this fails, the STL + // implementation is not supported. + if (!ValInfo->hasValidIntValue()) + return UnsupportedSTLError(); + + MarkVariableReferenced(Loc, VD); + } + + // We've successfully built the required types and expressions. Update + // the cache and return the newly cached value. + FullyCheckedComparisonCategories[static_cast<unsigned>(Kind)] = true; + return Info->getType(); +} + /// \brief Retrieve the special "std" namespace, which may require us to /// implicitly define the namespace. NamespaceDecl *Sema::getOrCreateStdNamespace() { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 58e70a4ceaa..177cfd7354a 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -37,6 +37,7 @@ #include "clang/Sema/Designator.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/Overload.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" @@ -9619,12 +9620,18 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc, Expr *RHSStripped = RHS->IgnoreParenImpCasts(); QualType LHSType = LHS->getType(); + QualType RHSType = RHS->getType(); if (LHSType->hasFloatingRepresentation() || (LHSType->isBlockPointerType() && !BinaryOperator::isEqualityOp(Opc)) || LHS->getLocStart().isMacroID() || RHS->getLocStart().isMacroID() || S.inTemplateInstantiation()) return; + // Comparisons between two array types are ill-formed for operator<=>, so + // we shouldn't emit any additional warnings about it. + if (Opc == BO_Cmp && LHSType->isArrayType() && RHSType->isArrayType()) + return; + // For non-floating point types, check for self-comparisons of the form // x == x, x != x, x < x, etc. These always evaluate to a constant, and // often indicate logic errors in the program. @@ -9708,10 +9715,183 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc, } } +static ImplicitConversionKind castKindToImplicitConversionKind(CastKind CK) { + switch (CK) { + default: { +#ifndef NDEBUG + llvm::errs() << "unhandled cast kind: " << CastExpr::getCastKindName(CK) + << "\n"; +#endif + llvm_unreachable("unhandled cast kind"); + } + case CK_UserDefinedConversion: + return ICK_Identity; + case CK_LValueToRValue: + return ICK_Lvalue_To_Rvalue; + case CK_ArrayToPointerDecay: + return ICK_Array_To_Pointer; + case CK_FunctionToPointerDecay: + return ICK_Function_To_Pointer; + case CK_IntegralCast: + return ICK_Integral_Conversion; + case CK_FloatingCast: + return ICK_Floating_Conversion; + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + return ICK_Floating_Integral; + case CK_IntegralComplexCast: + case CK_FloatingComplexCast: + case CK_FloatingComplexToIntegralComplex: + case CK_IntegralComplexToFloatingComplex: + return ICK_Complex_Conversion; + case CK_FloatingComplexToReal: + case CK_FloatingRealToComplex: + case CK_IntegralComplexToReal: + case CK_IntegralRealToComplex: + return ICK_Complex_Real; + } +} + +static bool checkThreeWayNarrowingConversion(Sema &S, QualType ToType, Expr *E, + QualType FromType, + SourceLocation Loc) { + // Check for a narrowing implicit conversion. + StandardConversionSequence SCS; + SCS.setToType(0, FromType); + SCS.setToType(1, ToType); + if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) { + auto CastK = ICE->getCastKind(); + SCS.Second = castKindToImplicitConversionKind(CastK); + } + APValue PreNarrowingValue; + QualType PreNarrowingType; + switch (SCS.getNarrowingKind(S.Context, E, PreNarrowingValue, + PreNarrowingType, + /*IgnoreFloatToIntegralConversion*/ true)) { + case NK_Dependent_Narrowing: + // Implicit conversion to a narrower type, but the expression is + // value-dependent so we can't tell whether it's actually narrowing. + case NK_Not_Narrowing: + return false; + + case NK_Constant_Narrowing: + // Implicit conversion to a narrower type, and the value is not a constant + // expression. + S.Diag(E->getLocStart(), diag::err_spaceship_argument_narrowing) + << /*Constant*/ 1 + << PreNarrowingValue.getAsString(S.Context, PreNarrowingType) << ToType; + return true; + + case NK_Variable_Narrowing: + // Implicit conversion to a narrower type, and the value is not a constant + // expression. + case NK_Type_Narrowing: + S.Diag(E->getLocStart(), diag::err_spaceship_argument_narrowing) + << /*Constant*/ 0 << FromType << ToType; + // TODO: It's not a constant expression, but what if the user intended it + // to be? Can we produce notes to help them figure out why it isn't? + return true; + } + llvm_unreachable("unhandled case in switch"); +} + +static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S, + ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc) { + using CCT = ComparisonCategoryType; + + QualType LHSType = LHS.get()->getType(); + QualType RHSType = RHS.get()->getType(); + // Dig out the original argument type and expression before implicit casts + // were applied. These are the types/expressions we need to check the + // [expr.spaceship] requirements against. + ExprResult LHSStripped = LHS.get()->IgnoreParenImpCasts(); + ExprResult RHSStripped = RHS.get()->IgnoreParenImpCasts(); + QualType LHSStrippedType = LHSStripped.get()->getType(); + QualType RHSStrippedType = RHSStripped.get()->getType(); + + // C++2a [expr.spaceship]p3: If one of the operands is of type bool and the + // other is not, the program is ill-formed. + if (LHSStrippedType->isBooleanType() != RHSStrippedType->isBooleanType()) { + S.InvalidOperands(Loc, LHSStripped, RHSStripped); + return QualType(); + } + + int NumEnumArgs = (int)LHSStrippedType->isEnumeralType() + + RHSStrippedType->isEnumeralType(); + if (NumEnumArgs == 1) { + bool LHSIsEnum = LHSStrippedType->isEnumeralType(); + QualType OtherTy = LHSIsEnum ? RHSStrippedType : LHSStrippedType; + if (OtherTy->hasFloatingRepresentation()) { + S.InvalidOperands(Loc, LHSStripped, RHSStripped); + return QualType(); + } + } + if (NumEnumArgs == 2) { + // C++2a [expr.spaceship]p5: If both operands have the same enumeration + // type E, the operator yields the result of converting the operands + // to the underlying type of E and applying <=> to the converted operands. + if (!S.Context.hasSameUnqualifiedType(LHSStrippedType, RHSStrippedType)) { + S.InvalidOperands(Loc, LHSStripped, RHSStripped); + return QualType(); + } + QualType IntType = + LHSStrippedType->getAs<EnumType>()->getDecl()->getIntegerType(); + assert(IntType->isArithmeticType()); + + // We can't use `CK_IntegralCast` when the underlying type is 'bool', so we + // promote the boolean type, and all other promotable integer types, to + // avoid this. + if (IntType->isPromotableIntegerType()) + IntType = S.Context.getPromotedIntegerType(IntType); + + LHS = S.ImpCastExprToType(LHS.get(), IntType, CK_IntegralCast); + RHS = S.ImpCastExprToType(RHS.get(), IntType, CK_IntegralCast); + LHSType = RHSType = IntType; + } + + // C++2a [expr.spaceship]p4: If both operands have arithmetic types, the + // usual arithmetic conversions are applied to the operands. + QualType Type = S.UsualArithmeticConversions(LHS, RHS); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); + if (Type.isNull()) + return S.InvalidOperands(Loc, LHS, RHS); + assert(Type->isArithmeticType() || Type->isEnumeralType()); + + bool HasNarrowing = checkThreeWayNarrowingConversion( + S, Type, LHS.get(), LHSType, LHS.get()->getLocStart()); + HasNarrowing |= checkThreeWayNarrowingConversion( + S, Type, RHS.get(), RHSType, RHS.get()->getLocStart()); + if (HasNarrowing) + return QualType(); + + assert(!Type.isNull() && "composite type for <=> has not been set"); + + auto TypeKind = [&]() { + if (const ComplexType *CT = Type->getAs<ComplexType>()) { + if (CT->getElementType()->hasFloatingRepresentation()) + return CCT::WeakEquality; + return CCT::StrongEquality; + } + if (Type->isIntegralOrEnumerationType()) + return CCT::StrongOrdering; + if (Type->hasFloatingRepresentation()) + return CCT::PartialOrdering; + llvm_unreachable("other types are unimplemented"); + }(); + + return S.CheckComparisonCategoryType(TypeKind, Loc); +} + static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, BinaryOperatorKind Opc) { + if (Opc == BO_Cmp) + return checkArithmeticOrEnumeralThreeWayCompare(S, LHS, RHS, Loc); + // C99 6.5.8p3 / C99 6.5.9p4 QualType Type = S.UsualArithmeticConversions(LHS, RHS); if (LHS.isInvalid() || RHS.isInvalid()) @@ -9722,15 +9902,7 @@ static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS, checkEnumComparison(S, Loc, LHS.get(), RHS.get()); - enum { StrongEquality, PartialOrdering, StrongOrdering } Ordering; - if (Type->isAnyComplexType()) - Ordering = StrongEquality; - else if (Type->isFloatingType()) - Ordering = PartialOrdering; - else - Ordering = StrongOrdering; - - if (Ordering == StrongEquality && BinaryOperator::isRelationalOp(Opc)) + if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc)) return S.InvalidOperands(Loc, LHS, RHS); // Check for comparisons of floating point operands using != and ==. @@ -9738,22 +9910,40 @@ static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS, S.CheckFloatComparison(Loc, LHS.get(), RHS.get()); // The result of comparisons is 'bool' in C++, 'int' in C. - // FIXME: For BO_Cmp, return the relevant comparison category type. return S.Context.getLogicalOperationType(); } // C99 6.5.8, C++ [expr.rel] QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, BinaryOperatorKind Opc, - bool IsRelational) { - // 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()) - return QualType(); + SourceLocation Loc, + BinaryOperatorKind Opc) { + bool IsRelational = BinaryOperator::isRelationalOp(Opc); + bool IsThreeWay = Opc == BO_Cmp; + auto IsAnyPointerType = [](ExprResult E) { + QualType Ty = E.get()->getType(); + return Ty->isPointerType() || Ty->isMemberPointerType(); + }; + + // C++2a [expr.spaceship]p6: If at least one of the operands is of pointer + // type, array-to-pointer, ..., conversions are performed on both operands to + // bring them to their composite type. + // Otherwise, all comparisons expect an rvalue, so convert to rvalue before + // any type-related checks. + if (!IsThreeWay || IsAnyPointerType(LHS) || IsAnyPointerType(RHS)) { + LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + } else { + LHS = DefaultLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + RHS = DefaultLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + } checkArithmeticNull(*this, LHS, RHS, Loc, /*isCompare=*/true); @@ -9771,8 +9961,6 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, (RHSType->isArithmeticType() || RHSType->isEnumeralType())) return checkArithmeticOrEnumeralCompare(*this, LHS, RHS, Loc, Opc); - QualType ResultTy = Context.getLogicalOperationType(); - const Expr::NullPointerConstantKind LHSNullKind = LHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); const Expr::NullPointerConstantKind RHSNullKind = @@ -9780,6 +9968,44 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, bool LHSIsNull = LHSNullKind != Expr::NPCK_NotNull; bool RHSIsNull = RHSNullKind != Expr::NPCK_NotNull; + auto computeResultTy = [&]() { + if (Opc != BO_Cmp) + return Context.getLogicalOperationType(); + assert(getLangOpts().CPlusPlus); + assert(Context.hasSameType(LHS.get()->getType(), RHS.get()->getType())); + + QualType CompositeTy = LHS.get()->getType(); + assert(!CompositeTy->isReferenceType()); + + auto buildResultTy = [&](ComparisonCategoryType Kind) { + return CheckComparisonCategoryType(Kind, Loc); + }; + + // 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 + if (CompositeTy->isFunctionPointerType() || + CompositeTy->isMemberPointerType() || CompositeTy->isNullPtrType()) + // FIXME: consider making the function pointer case produce + // strong_ordering not strong_equality, per P0946R0-Jax18 discussion + // and direction polls + return buildResultTy(ComparisonCategoryType::StrongEquality); + + // C++2a [expr.spaceship]p8: If the composite pointer type is an object + // pointer type, p <=> q is of type std::strong_ordering. + if (CompositeTy->isPointerType()) { + // P0946R0: Comparisons between a null pointer constant and an object + // pointer result in std::strong_equality + if (LHSIsNull != RHSIsNull) + return buildResultTy(ComparisonCategoryType::StrongEquality); + return buildResultTy(ComparisonCategoryType::StrongOrdering); + } + // C++2a [expr.spaceship]p9: Otherwise, the program is ill-formed. + // TODO: Extend support for operator<=> to ObjC types. + return InvalidOperands(Loc, LHS, RHS); + }; + + if (!IsRelational && LHSIsNull != RHSIsNull) { bool IsEquality = Opc == BO_EQ; if (RHSIsNull) @@ -9807,29 +10033,30 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, // conformance with the C++ standard. diagnoseFunctionPointerToVoidComparison( *this, Loc, LHS, RHS, /*isError*/ (bool)isSFINAEContext()); - + if (isSFINAEContext()) return QualType(); - + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return ResultTy; + return computeResultTy(); } // C++ [expr.eq]p2: // If at least one operand is a pointer [...] bring them to their // composite pointer type. + // C++ [expr.spaceship]p6 + // If at least one of the operands is of pointer type, [...] bring them + // to their composite pointer type. // C++ [expr.rel]p2: // If both operands are pointers, [...] bring them to their composite // pointer type. if ((int)LHSType->isPointerType() + (int)RHSType->isPointerType() >= (IsRelational ? 2 : 1) && - (!LangOpts.ObjCAutoRefCount || - !(LHSType->isObjCObjectPointerType() || - RHSType->isObjCObjectPointerType()))) { + (!LangOpts.ObjCAutoRefCount || !(LHSType->isObjCObjectPointerType() || + RHSType->isObjCObjectPointerType()))) { if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) return QualType(); - else - return ResultTy; + return computeResultTy(); } } else if (LHSType->isPointerType() && RHSType->isPointerType()) { // C99 6.5.8p2 @@ -9880,7 +10107,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, else RHS = ImpCastExprToType(RHS.get(), LHSType, Kind); } - return ResultTy; + return computeResultTy(); } if (getLangOpts().CPlusPlus) { @@ -9890,11 +10117,11 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, if (!IsRelational && LHSIsNull && RHSIsNull) { if (LHSType->isNullPtrType()) { RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } if (RHSType->isNullPtrType()) { LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } } @@ -9903,12 +10130,12 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, if (!IsRelational && RHSType->isNullPtrType() && (LHSType->isObjCObjectPointerType() || LHSType->isBlockPointerType())) { RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } if (!IsRelational && LHSType->isNullPtrType() && (RHSType->isObjCObjectPointerType() || RHSType->isBlockPointerType())) { LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } if (IsRelational && @@ -9931,7 +10158,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); else LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } } } @@ -9944,7 +10171,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) return QualType(); else - return ResultTy; + return computeResultTy(); } } @@ -9961,7 +10188,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, << RHS.get()->getSourceRange(); } RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return ResultTy; + return computeResultTy(); } // Allow block pointers to be compared with null pointer constants. @@ -9985,7 +10212,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, RHS = ImpCastExprToType(RHS.get(), LHSType, LHSType->isPointerType() ? CK_BitCast : CK_AnyPointerToBlockPointerCast); - return ResultTy; + return computeResultTy(); } if (LHSType->isObjCObjectPointerType() || @@ -10018,7 +10245,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, RHS = ImpCastExprToType(E, LHSType, LPT ? CK_BitCast :CK_CPointerToObjCPointerCast); } - return ResultTy; + return computeResultTy(); } if (LHSType->isObjCObjectPointerType() && RHSType->isObjCObjectPointerType()) { @@ -10032,20 +10259,20 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); else RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return ResultTy; + return computeResultTy(); } if (!IsRelational && LHSType->isBlockPointerType() && RHSType->isBlockCompatibleObjCPointerType(Context)) { LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BlockPointerToObjCPointerCast); - return ResultTy; + return computeResultTy(); } else if (!IsRelational && LHSType->isBlockCompatibleObjCPointerType(Context) && RHSType->isBlockPointerType()) { RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BlockPointerToObjCPointerCast); - return ResultTy; + return computeResultTy(); } } if ((LHSType->isAnyPointerType() && RHSType->isIntegerType()) || @@ -10085,30 +10312,30 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, else RHS = ImpCastExprToType(RHS.get(), LHSType, RHSIsNull ? CK_NullToPointer : CK_IntegralToPointer); - return ResultTy; + return computeResultTy(); } // Handle block pointers. if (!IsRelational && RHSIsNull && LHSType->isBlockPointerType() && RHSType->isIntegerType()) { RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } if (!IsRelational && LHSIsNull && LHSType->isIntegerType() && RHSType->isBlockPointerType()) { LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } if (getLangOpts().OpenCLVersion >= 200) { if (LHSIsNull && RHSType->isQueueT()) { LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } if (LHSType->isQueueT() && RHSIsNull) { RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } } @@ -11761,19 +11988,17 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, case BO_GE: case BO_GT: ConvertHalfVec = true; - ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, true); + ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); break; case BO_EQ: case BO_NE: ConvertHalfVec = true; - ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, false); + ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); break; case BO_Cmp: - // FIXME: Implement proper semantic checking of '<=>'. ConvertHalfVec = true; - ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, true); - if (!ResultTy.isNull()) - ResultTy = Context.VoidTy; + ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); + assert(ResultTy.isNull() || ResultTy->getAsCXXRecordDecl()); break; case BO_And: checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index f534589c507..362fe71f3ca 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -288,11 +288,11 @@ static const Expr *IgnoreNarrowingConversion(const Expr *Converted) { /// value of the expression prior to the narrowing conversion. /// \param ConstantType If this is an NK_Constant_Narrowing conversion, the /// type of the expression prior to the narrowing conversion. -NarrowingKind -StandardConversionSequence::getNarrowingKind(ASTContext &Ctx, - const Expr *Converted, - APValue &ConstantValue, - QualType &ConstantType) const { +/// \param IgnoreFloatToIntegralConversion If true type-narrowing conversions +/// from floating point types to integral types should be ignored. +NarrowingKind StandardConversionSequence::getNarrowingKind( + ASTContext &Ctx, const Expr *Converted, APValue &ConstantValue, + QualType &ConstantType, bool IgnoreFloatToIntegralConversion) const { assert(Ctx.getLangOpts().CPlusPlus && "narrowing check outside C++"); // C++11 [dcl.init.list]p7: @@ -329,6 +329,8 @@ StandardConversionSequence::getNarrowingKind(ASTContext &Ctx, return NK_Type_Narrowing; } else if (FromType->isIntegralOrUnscopedEnumerationType() && ToType->isRealFloatingType()) { + if (IgnoreFloatToIntegralConversion) + return NK_Not_Narrowing; llvm::APSInt IntConstantValue; const Expr *Initializer = IgnoreNarrowingConversion(Converted); assert(Initializer && "Unknown conversion expression"); @@ -7988,7 +7990,8 @@ public: // bool operator>=(T, T); // bool operator==(T, T); // bool operator!=(T, T); - void addRelationalPointerOrEnumeralOverloads() { + // R operator<=>(T, T) + void addGenericBinaryPointerOrEnumeralOverloads() { // C++ [over.match.oper]p3: // [...]the built-in candidates include all of the candidate operator // functions defined in 13.6 that, compared to the given operator, [...] @@ -8061,7 +8064,6 @@ public: UserDefinedBinaryOperators.count(std::make_pair(CanonType, CanonType))) continue; - QualType ParamTypes[2] = { *Enum, *Enum }; S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet); } @@ -8179,6 +8181,41 @@ public: } } + // C++2a [over.built]p14: + // + // For every integral type T there exists a candidate operator function + // of the form + // + // std::strong_ordering operator<=>(T, T) + // + // C++2a [over.built]p15: + // + // For every pair of floating-point types L and R, there exists a candidate + // operator function of the form + // + // std::partial_ordering operator<=>(L, R); + // + // FIXME: The current specification for integral types doesn't play nice with + // the direction of p0946r0, which allows mixed integral and unscoped-enum + // comparisons. Under the current spec this can lead to ambiguity during + // overload resolution. For example: + // + // enum A : int {a}; + // auto x = (a <=> (long)42); + // + // error: call is ambiguous for arguments 'A' and 'long'. + // note: candidate operator<=>(int, int) + // note: candidate operator<=>(long, long) + // + // To avoid this error, this function deviates from the specification and adds + // the mixed overloads `operator<=>(L, R)` where L and R are promoted + // arithmetic types (the same as the generic relational overloads). + // + // For now this function acts as a placeholder. + void addThreeWayArithmeticOverloads() { + addGenericBinaryArithmeticOverloads(); + } + // C++ [over.built]p17: // // For every pair of promoted integral types L and R, there @@ -8747,12 +8784,14 @@ void Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, case OO_Greater: case OO_LessEqual: case OO_GreaterEqual: - OpBuilder.addRelationalPointerOrEnumeralOverloads(); + OpBuilder.addGenericBinaryPointerOrEnumeralOverloads(); OpBuilder.addGenericBinaryArithmeticOverloads(); break; case OO_Spaceship: - llvm_unreachable("<=> expressions not supported yet"); + OpBuilder.addGenericBinaryPointerOrEnumeralOverloads(); + OpBuilder.addThreeWayArithmeticOverloads(); + break; case OO_Percent: case OO_Caret: |