Skip to content

Commit

Permalink
[C++2a] Implement operator<=> CodeGen and ExprConstant
Browse files Browse the repository at this point in the history
Summary:
This patch tackles long hanging fruit for the builtin operator<=> expressions. It is currently needs some cleanup before landing, but I want to get some initial feedback.

The main changes are:

* Lookup, build, and store the required standard library types and expressions in `ASTContext`. By storing them in ASTContext we don't need to store (and duplicate) the required expressions in the BinaryOperator AST nodes. 

* Implement [expr.spaceship] checking, including diagnosing narrowing conversions. 

* Implement `ExprConstant` for builtin spaceship operators.

* Implement builitin operator<=> support in `CodeGenAgg`. Initially I emitted the required comparisons using `ScalarExprEmitter::VisitBinaryOperator`, but this caused the operand expressions to be emitted once for every required cmp.

* Implement [builtin.over] with modifications to support the intent of P0946R0. See the note on `BuiltinOperatorOverloadBuilder::addThreeWayArithmeticOverloads` for more information about the workaround.




Reviewers: rsmith, aaron.ballman, majnemer, rnk, compnerd, rjmccall

Reviewed By: rjmccall

Subscribers: rjmccall, rsmith, aaron.ballman, junbuml, mgorny, cfe-commits

Differential Revision: https://reviews.llvm.org/D45476

llvm-svn: 331677
  • Loading branch information
EricWF committed May 7, 2018
1 parent f53d9ab commit 0683c0e
Show file tree
Hide file tree
Showing 24 changed files with 3,548 additions and 392 deletions.
5 changes: 5 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Expand Up @@ -18,6 +18,7 @@
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/CanonicalType.h"
#include "clang/AST/CommentCommandTraits.h"
#include "clang/AST/ComparisonCategories.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclarationName.h"
Expand Down Expand Up @@ -1978,6 +1979,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
QualType GetBuiltinType(unsigned ID, GetBuiltinTypeError &Error,
unsigned *IntegerConstantArgs = nullptr) const;

/// \brief Types and expressions required to build C++2a three-way comparisons
/// using operator<=>, including the values return by builtin <=> operators.
ComparisonCategories CompCategories;

private:
CanQualType getFromTargetType(unsigned Type) const;
TypeInfo getTypeInfoImpl(const Type *T) const;
Expand Down
255 changes: 255 additions & 0 deletions clang/include/clang/AST/ComparisonCategories.h
@@ -0,0 +1,255 @@
//===- ComparisonCategories.h - Three Way Comparison Data -------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the Comparison Category enum and data types, which
// store the types and expressions needed to support operator<=>
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_COMPARISONCATEGORIES_H
#define LLVM_CLANG_AST_COMPARISONCATEGORIES_H

#include "clang/Basic/LLVM.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/DenseMap.h"
#include <array>
#include <cassert>

namespace llvm {
class StringRef;
class APSInt;
}

namespace clang {

class ASTContext;
class VarDecl;
class CXXRecordDecl;
class Sema;
class QualType;
class NamespaceDecl;

/// \brief An enumeration representing the different comparison categories
/// types.
///
/// C++2a [cmp.categories.pre] The types weak_equality, strong_equality,
/// partial_ordering, weak_ordering, and strong_ordering are collectively
/// termed the comparison category types.
enum class ComparisonCategoryType : unsigned char {
WeakEquality,
StrongEquality,
PartialOrdering,
WeakOrdering,
StrongOrdering,
First = WeakEquality,
Last = StrongOrdering
};

/// \brief An enumeration representing the possible results of a three-way
/// comparison. These values map onto instances of comparison category types
/// defined in the standard library. i.e. 'std::strong_ordering::less'.
enum class ComparisonCategoryResult : unsigned char {
Equal,
Equivalent,
Nonequivalent,
Nonequal,
Less,
Greater,
Unordered,
Last = Unordered
};

class ComparisonCategoryInfo {
friend class ComparisonCategories;
friend class Sema;

public:
ComparisonCategoryInfo(const ASTContext &Ctx, CXXRecordDecl *RD,
ComparisonCategoryType Kind)
: Ctx(Ctx), Record(RD), Kind(Kind) {}

struct ValueInfo {
ComparisonCategoryResult Kind;
VarDecl *VD;

ValueInfo(ComparisonCategoryResult Kind, VarDecl *VD)
: Kind(Kind), VD(VD), HasValue(false) {}

/// \brief True iff we've successfully evaluated the variable as a constant
/// expression and extracted its integer value.
bool hasValidIntValue() const { return HasValue; }

/// \brief Get the constant integer value used by this variable to represent
/// the comparison category result type.
llvm::APSInt getIntValue() const {
assert(hasValidIntValue());
return IntValue;
}

void setIntValue(llvm::APSInt Val) {
IntValue = Val;
HasValue = true;
}

private:
friend class ComparisonCategoryInfo;
llvm::APSInt IntValue;
bool HasValue : 1;
};
private:
const ASTContext &Ctx;

/// \brief A map containing the comparison category result decls from the
/// standard library. The key is a value of ComparisonCategoryResult.
mutable llvm::SmallVector<
ValueInfo, static_cast<unsigned>(ComparisonCategoryResult::Last) + 1>
Objects;

/// \brief Lookup the ValueInfo struct for the specified ValueKind. If the
/// VarDecl for the value cannot be found, nullptr is returned.
///
/// If the ValueInfo does not have a valid integer value the variable
/// is evaluated as a constant expression to determine that value.
ValueInfo *lookupValueInfo(ComparisonCategoryResult ValueKind) const;

public:
/// \brief The declaration for the comparison category type from the
/// standard library.
// FIXME: Make this const
CXXRecordDecl *Record = nullptr;

/// \brief The Kind of the comparison category type
ComparisonCategoryType Kind;

public:
QualType getType() const;

const ValueInfo *getValueInfo(ComparisonCategoryResult ValueKind) const {
ValueInfo *Info = lookupValueInfo(ValueKind);
assert(Info &&
"comparison category does not contain the specified result kind");
assert(Info->hasValidIntValue() &&
"couldn't determine the integer constant for this value");
return Info;
}

/// \brief True iff the comparison category is an equality comparison.
bool isEquality() const { return !isOrdered(); }

/// \brief True iff the comparison category is a relational comparison.
bool isOrdered() const {
using CCK = ComparisonCategoryType;
return Kind == CCK::PartialOrdering || Kind == CCK::WeakOrdering ||
Kind == CCK::StrongOrdering;
}

/// \brief True iff the comparison is "strong". i.e. it checks equality and
/// not equivalence.
bool isStrong() const {
using CCK = ComparisonCategoryType;
return Kind == CCK::StrongEquality || Kind == CCK::StrongOrdering;
}

/// \brief True iff the comparison is not totally ordered.
bool isPartial() const {
using CCK = ComparisonCategoryType;
return Kind == CCK::PartialOrdering;
}

/// \brief Converts the specified result kind into the the correct result kind
/// for this category. Specifically it lowers strong equality results to
/// weak equivalence if needed.
ComparisonCategoryResult makeWeakResult(ComparisonCategoryResult Res) const {
using CCR = ComparisonCategoryResult;
if (!isStrong()) {
if (Res == CCR::Equal)
return CCR::Equivalent;
if (Res == CCR::Nonequal)
return CCR::Nonequivalent;
}
return Res;
}

const ValueInfo *getEqualOrEquiv() const {
return getValueInfo(makeWeakResult(ComparisonCategoryResult::Equal));
}
const ValueInfo *getNonequalOrNonequiv() const {
assert(isEquality());
return getValueInfo(makeWeakResult(ComparisonCategoryResult::Nonequal));
}
const ValueInfo *getLess() const {
assert(isOrdered());
return getValueInfo(ComparisonCategoryResult::Less);
}
const ValueInfo *getGreater() const {
assert(isOrdered());
return getValueInfo(ComparisonCategoryResult::Greater);
}
const ValueInfo *getUnordered() const {
assert(isPartial());
return getValueInfo(ComparisonCategoryResult::Unordered);
}
};

class ComparisonCategories {
public:
static StringRef getCategoryString(ComparisonCategoryType Kind);
static StringRef getResultString(ComparisonCategoryResult Kind);

/// \brief Return the list of results which are valid for the specified
/// comparison category type.
static std::vector<ComparisonCategoryResult>
getPossibleResultsForType(ComparisonCategoryType Type);

/// \brief Return the comparison category information for the category
/// specified by 'Kind'.
const ComparisonCategoryInfo &getInfo(ComparisonCategoryType Kind) const {
const ComparisonCategoryInfo *Result = lookupInfo(Kind);
assert(Result != nullptr &&
"information for specified comparison category has not been built");
return *Result;
}

/// \brief Return the comparison category information as specified by
/// `getCategoryForType(Ty)`. If the information is not already cached,
/// the declaration is looked up and a cache entry is created.
/// NOTE: Lookup is expected to succeed. Use lookupInfo if failure is possible.
const ComparisonCategoryInfo &getInfoForType(QualType Ty) const;

public:
/// \brief Return the cached comparison category information for the
/// specified 'Kind'. If no cache entry is present the comparison category
/// type is looked up. If lookup fails nullptr is returned. Otherwise, a
/// new cache entry is created and returned
const ComparisonCategoryInfo *lookupInfo(ComparisonCategoryType Kind) const;

ComparisonCategoryInfo *lookupInfo(ComparisonCategoryType Kind) {
const auto &This = *this;
return const_cast<ComparisonCategoryInfo *>(This.lookupInfo(Kind));
}

private:
const ComparisonCategoryInfo *lookupInfoForType(QualType Ty) const;

private:
friend class ASTContext;

explicit ComparisonCategories(const ASTContext &Ctx) : Ctx(Ctx) {}

const ASTContext &Ctx;

/// A map from the ComparisonCategoryType (represented as 'char') to the
/// cached information for the specified category.
mutable llvm::DenseMap<char, ComparisonCategoryInfo> Data;
mutable NamespaceDecl *StdNS = nullptr;
};

} // namespace clang

#endif
4 changes: 3 additions & 1 deletion clang/include/clang/AST/Expr.h
Expand Up @@ -2778,7 +2778,9 @@ class CastExpr : public Expr {
public:
CastKind getCastKind() const { return (CastKind) CastExprBits.Kind; }
void setCastKind(CastKind K) { CastExprBits.Kind = K; }
const char *getCastKindName() const;

static const char *getCastKindName(CastKind CK);
const char *getCastKindName() const { return getCastKindName(getCastKind()); }

Expr *getSubExpr() { return cast<Expr>(Op); }
const Expr *getSubExpr() const { return cast<Expr>(Op); }
Expand Down
14 changes: 14 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -9424,4 +9424,18 @@ def err_multiversion_not_allowed_on_main : Error<
def err_multiversion_not_supported : Error<
"function multiversioning is not supported on the current target">;

// three-way comparison operator diagnostics
def err_implied_comparison_category_type_not_found : Error<
"cannot deduce return type of 'operator<=>' because type %0 was not found; "
"include <compare>">;
def err_spaceship_argument_narrowing : Error<
"argument to 'operator<=>' "
"%select{cannot be narrowed from type %1 to %2|"
"evaluates to %1, which cannot be narrowed to type %2}0">;
def err_std_compare_type_not_supported : Error<
"standard library implementation of %0 is not supported; "
"%select{member '%2' does not have expected form|"
"member '%2' is missing|"
"the type is not trivially copyable|"
"the type does not have the expected form}1">;
} // end of sema component.
7 changes: 4 additions & 3 deletions clang/include/clang/Sema/Overload.h
Expand Up @@ -330,9 +330,10 @@ class Sema;
}

ImplicitConversionRank getRank() const;
NarrowingKind getNarrowingKind(ASTContext &Context, const Expr *Converted,
APValue &ConstantValue,
QualType &ConstantType) const;
NarrowingKind
getNarrowingKind(ASTContext &Context, const Expr *Converted,
APValue &ConstantValue, QualType &ConstantType,
bool IgnoreFloatToIntegralConversion = false) const;
bool isPointerConversionToBool() const;
bool isPointerConversionToVoidPointer(ASTContext& Context) const;
void dump() const;
Expand Down
24 changes: 21 additions & 3 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -17,8 +17,9 @@

#include "clang/AST/Attr.h"
#include "clang/AST/Availability.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/ComparisonCategories.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExternalASTSource.h"
Expand Down Expand Up @@ -49,6 +50,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/TinyPtrVector.h"
Expand Down Expand Up @@ -4545,6 +4547,22 @@ class Sema {
CXXRecordDecl *getStdBadAlloc() const;
EnumDecl *getStdAlignValT() const;

private:
// A cache representing if we've fully checked the various comparison category
// types stored in ASTContext. The bit-index corresponds to the integer value
// of a ComparisonCategoryType enumerator.
llvm::SmallBitVector FullyCheckedComparisonCategories;

public:
/// \brief Lookup the specified comparison category types in the standard
/// library, an check the VarDecls possibly returned by the operator<=>
/// builtins for that type.
///
/// \return The type of the comparison category type corresponding to the
/// specified Kind, or a null type if an error occurs
QualType CheckComparisonCategoryType(ComparisonCategoryType Kind,
SourceLocation Loc);

/// \brief Tests whether Ty is an instance of std::initializer_list and, if
/// it is and Element is not NULL, assigns the element type to Element.
bool isStdInitializerList(QualType Ty, QualType *Element);
Expand Down Expand Up @@ -9574,8 +9592,8 @@ class Sema {
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
BinaryOperatorKind Opc, bool IsCompAssign = false);
QualType CheckCompareOperands( // C99 6.5.8/9
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
BinaryOperatorKind Opc, bool isRelational);
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
BinaryOperatorKind Opc);
QualType CheckBitwiseOperands( // C99 6.5.[10...12]
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
BinaryOperatorKind Opc);
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/ASTContext.cpp
Expand Up @@ -792,7 +792,8 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
LangOpts.XRayAttrListFiles, SM)),
PrintingPolicy(LOpts), Idents(idents), Selectors(sels),
BuiltinInfo(builtins), DeclarationNames(*this), Comments(SM),
CommentCommandTraits(BumpAlloc, LOpts.CommentOpts), LastSDM(nullptr, 0) {
CommentCommandTraits(BumpAlloc, LOpts.CommentOpts),
CompCategories(this_()), LastSDM(nullptr, 0) {
TUDecl = TranslationUnitDecl::Create(*this);
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/CMakeLists.txt
Expand Up @@ -20,6 +20,7 @@ add_clang_library(clangAST
CommentLexer.cpp
CommentParser.cpp
CommentSema.cpp
ComparisonCategories.cpp
DataCollection.cpp
Decl.cpp
DeclarationName.cpp
Expand Down

0 comments on commit 0683c0e

Please sign in to comment.