Skip to content

[Clang] Add __builtin_common_reference #121199

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1677,6 +1677,23 @@ Builtin type aliases

Clang provides a few builtin aliases to improve the throughput of certain metaprogramming facilities.

__builtin_common_reference
--------------------------

.. code-block:: c++

template <template <class, class, template <class> class, template <class> class> class BasicCommonReferenceT,
template <class... Args> CommonTypeT,
template <class> HasTypeMember,
class HasNoTypeMember,
class... Ts>
using __builtin_common_reference = ...;

This alias is used for implementing ``std::common_reference``. If ``std::common_reference`` should contain a ``type``
member, it is an alias to ``HasTypeMember<TheCommonReference>``. Otherwse it is an alias to ``HasNoTypeMember``. The
``CommonTypeT`` is usually ``std::common_type_t``. ``BasicCommonReferenceT`` is usually an alias template to
``basic_common_reference<T, U, TX, UX>::type``.

__builtin_common_type
---------------------

Expand Down
30 changes: 28 additions & 2 deletions clang/include/clang/Basic/BuiltinTemplates.td
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ class TemplateArg<string name> {
string Name = name;
}

class Template<list<TemplateArg> args, string name> : TemplateArg<name> {
class Template<list<TemplateArg> args, string name = ""> : TemplateArg<name> {
list<TemplateArg> Args = args;
}

class Class<string name, bit is_variadic = 0> : TemplateArg<name> {
class Class<string name = "", bit is_variadic = 0> : TemplateArg<name> {
bit IsVariadic = is_variadic;
}

Expand Down Expand Up @@ -50,3 +50,29 @@ def __builtin_common_type : BuiltinTemplate<
Template<[Class<"TypeMember">], "HasTypeMember">,
Class<"HasNoTypeMember">,
Class<"Ts", /*is_variadic=*/1>]>;

// template <template <class,"
// class,"
// template <class> class,"
// template <class> class> class BasicCommonReferenceT,"
// template <class... Args> class CommonTypeT,"
// template <class> class HasTypeMember,"
// class HasNoTypeMember,"
// class... Ts>"
def __builtin_common_reference : BuiltinTemplate<
[Template<[Class<>,
Class<>,
Template<[Class<>]>,
Template<[Class<>]>], "BasicCommonReferenceT">,
Template<[Class<"Args", /*is_variadic=*/1>], "CommonTypeT">,
Template<[Class<>], "HasTypeMember">,
Class<"HasNoTypeMember">,
Class<"Ts", /*is_variadic=*/1>]>;

foreach Ref = ["", "lvalue", "rvalue"] in {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if I'm being a little dense here... what is going on for the forloop?

foreach Const = ["", "const"] in {
foreach Volatile = ["", "volatile"] in {
def __clang_internal_xref_#Ref#Const#Volatile : BuiltinTemplate<[Class<>]>;
}
}
}
19 changes: 19 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -15076,15 +15076,34 @@ class Sema final : public SemaBase {
QualType BuiltinDecay(QualType BaseType, SourceLocation Loc);
QualType BuiltinAddReference(QualType BaseType, UTTKind UKind,
SourceLocation Loc);

QualType BuiltinAddRValueReference(QualType BaseType, SourceLocation Loc) {
return BuiltinAddReference(BaseType, UnaryTransformType::AddRvalueReference,
Loc);
}

QualType BuiltinAddLValueReference(QualType BaseType, SourceLocation Loc) {
return BuiltinAddReference(BaseType, UnaryTransformType::AddLvalueReference,
Loc);
}

QualType BuiltinRemoveExtent(QualType BaseType, UTTKind UKind,
SourceLocation Loc);
QualType BuiltinRemoveReference(QualType BaseType, UTTKind UKind,
SourceLocation Loc);

QualType BuiltinRemoveCVRef(QualType BaseType, SourceLocation Loc) {
return BuiltinRemoveReference(BaseType, UTTKind::RemoveCVRef, Loc);
}

QualType BuiltinChangeCVRQualifiers(QualType BaseType, UTTKind UKind,
SourceLocation Loc);
QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
SourceLocation Loc);

bool BuiltinIsConvertible(QualType From, QualType To, SourceLocation Loc,
bool CheckNothrow = false);

/// Ensure that the type T is a literal type.
///
/// This routine checks whether the type @p T is a literal type. If @p T is an
Expand Down
92 changes: 5 additions & 87 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5739,76 +5739,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
const TypeSourceInfo *Rhs, SourceLocation KeyLoc);

static ExprResult CheckConvertibilityForTypeTraits(
Sema &Self, const TypeSourceInfo *Lhs, const TypeSourceInfo *Rhs,
SourceLocation KeyLoc, llvm::BumpPtrAllocator &OpaqueExprAllocator) {

QualType LhsT = Lhs->getType();
QualType RhsT = Rhs->getType();

// C++0x [meta.rel]p4:
// Given the following function prototype:
//
// template <class T>
// typename add_rvalue_reference<T>::type create();
//
// the predicate condition for a template specialization
// is_convertible<From, To> shall be satisfied if and only if
// the return expression in the following code would be
// well-formed, including any implicit conversions to the return
// type of the function:
//
// To test() {
// return create<From>();
// }
//
// Access checking is performed as if in a context unrelated to To and
// From. Only the validity of the immediate context of the expression
// of the return-statement (including conversions to the return type)
// is considered.
//
// We model the initialization as a copy-initialization of a temporary
// of the appropriate type, which for this expression is identical to the
// return statement (since NRVO doesn't apply).

// Functions aren't allowed to return function or array types.
if (RhsT->isFunctionType() || RhsT->isArrayType())
return ExprError();

// A function definition requires a complete, non-abstract return type.
if (!Self.isCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT) ||
Self.isAbstractType(Rhs->getTypeLoc().getBeginLoc(), RhsT))
return ExprError();

// Compute the result of add_rvalue_reference.
if (LhsT->isObjectType() || LhsT->isFunctionType())
LhsT = Self.Context.getRValueReferenceType(LhsT);

// Build a fake source and destination for initialization.
InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT));
Expr *From = new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
OpaqueValueExpr(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
Expr::getValueKindForType(LhsT));
InitializationKind Kind =
InitializationKind::CreateCopy(KeyLoc, SourceLocation());

// Perform the initialization in an unevaluated context within a SFINAE
// trap at translation unit scope.
EnterExpressionEvaluationContext Unevaluated(
Self, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true);
Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
InitializationSequence Init(Self, To, Kind, From);
if (Init.Failed())
return ExprError();

ExprResult Result = Init.Perform(Self, To, Kind, From);
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
return ExprError();

return Result;
}

static APValue EvaluateSizeTTypeTrait(Sema &S, TypeTrait Kind,
SourceLocation KWLoc,
ArrayRef<TypeSourceInfo *> Args,
Expand Down Expand Up @@ -5958,9 +5888,8 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
S.Context.getPointerType(T.getNonReferenceType()));
TypeSourceInfo *UPtr = S.Context.CreateTypeSourceInfo(
S.Context.getPointerType(U.getNonReferenceType()));
return !CheckConvertibilityForTypeTraits(S, UPtr, TPtr, RParenLoc,
OpaqueExprAllocator)
.isInvalid();
return S.BuiltinIsConvertible(UPtr->getType(), TPtr->getType(),
RParenLoc);
}

if (Kind == clang::TT_IsNothrowConstructible)
Expand Down Expand Up @@ -6200,20 +6129,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
}
case BTT_IsConvertible:
case BTT_IsConvertibleTo:
case BTT_IsNothrowConvertible: {
if (RhsT->isVoidType())
return LhsT->isVoidType();
llvm::BumpPtrAllocator OpaqueExprAllocator;
ExprResult Result = CheckConvertibilityForTypeTraits(Self, Lhs, Rhs, KeyLoc,
OpaqueExprAllocator);
if (Result.isInvalid())
return false;

if (BTT != BTT_IsNothrowConvertible)
return true;

return Self.canThrow(Result.get()) == CT_Cannot;
}
case BTT_IsNothrowConvertible:
return Self.BuiltinIsConvertible(LhsT, RhsT, KeyLoc,
BTT == BTT_IsNothrowConvertible);

case BTT_IsAssignable:
case BTT_IsNothrowAssignable:
Expand Down
Loading