Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4272,6 +4272,12 @@ def MoveIfNsoexcept : CxxLibBuiltin<"utility"> {
let Namespace = "std";
}

def Invoke : Builtin {
let Spellings = ["__builtin_invoke"];
let Attributes = [CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}

def Annotation : Builtin {
let Spellings = ["__builtin_annotation"];
let Attributes = [NoThrow, CustomTypeChecking];
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2594,6 +2594,8 @@ class Sema final : public SemaBase {
SourceLocation BuiltinLoc,
SourceLocation RParenLoc);

ExprResult BuiltinInvoke(CallExpr *TheCall);

static StringRef GetFormatStringTypeName(FormatStringType FST);
static FormatStringType GetFormatStringType(StringRef FormatFlavor);
static FormatStringType GetFormatStringType(const FormatAttr *Format);
Expand Down Expand Up @@ -15220,11 +15222,18 @@ class Sema final : public SemaBase {
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 BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT, QualType RhsT);

/// 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
24 changes: 1 addition & 23 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1611,29 +1611,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
Tok.isOneOf(
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) tok::kw___##Trait,
#include "clang/Basic/TransformTypeTraits.def"
tok::kw___is_abstract,
tok::kw___is_aggregate,
tok::kw___is_arithmetic,
tok::kw___is_array,
tok::kw___is_assignable,
tok::kw___is_base_of,
tok::kw___is_bounded_array,
tok::kw___is_class,
tok::kw___is_complete_type,
tok::kw___is_compound,
tok::kw___is_const,
tok::kw___is_constructible,
tok::kw___is_convertible,
tok::kw___is_convertible_to,
tok::kw___is_destructible,
tok::kw___is_empty,
tok::kw___is_enum,
tok::kw___is_floating_point,
tok::kw___is_final,
tok::kw___is_function,
tok::kw___is_fundamental,
tok::kw___is_integral,
tok::kw___is_interface_class,
tok::kw___is_convertible, // Last use in libc++ was removed in 925a11a
tok::kw___is_literal,
tok::kw___is_lvalue_expr,
tok::kw___is_lvalue_reference,
Expand Down
97 changes: 97 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2368,6 +2368,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
return BuiltinShuffleVector(TheCall);
// TheCall will be freed by the smart pointer here, but that's fine, since
// BuiltinShuffleVector guts it, but then doesn't release it.
case Builtin::BI__builtin_invoke:
return BuiltinInvoke(TheCall);
case Builtin::BI__builtin_prefetch:
if (BuiltinPrefetch(TheCall))
return ExprError();
Expand Down Expand Up @@ -5406,6 +5408,101 @@ ExprResult Sema::ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
RParenLoc, CurFPFeatureOverrides());
}

ExprResult Sema::BuiltinInvoke(CallExpr *TheCall) {
auto Loc = TheCall->getBeginLoc();
auto Args = MutableArrayRef(TheCall->getArgs(), TheCall->getNumArgs());
assert(llvm::none_of(Args,
[](Expr *Arg) { return Arg->isTypeDependent(); }));

if (Args.size() == 0) {
Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_few_args_at_least)
<< 0 << 1 << 0 << 0 << TheCall->getSourceRange();
return ExprError();
}

auto FuncT = Args[0]->getType();

if (auto *MPT = FuncT->getAs<MemberPointerType>()) {
if (Args.size() < 2) {
Diag(TheCall->getBeginLoc(),
diag::err_typecheck_call_too_few_args_at_least)
<< 0 << 2 << 1 << 0 << TheCall->getSourceRange();
return ExprError();
}

auto *MemPtrClass = MPT->getQualifier()->getAsType();
auto ObjectT = Args[1]->getType();


if (MPT->isMemberDataPointer() && Args.size() != 2) {
Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args)
<< 0 << 2 << Args.size() << 0 << TheCall->getSourceRange();
return ExprError();
}

ExprResult ObjectArg = [&]() -> ExprResult {
// (1.1): (t1.*f)(t2, …, tN) when f is a pointer to a member function of a
// class T and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
// (1.4): t1.*f when N=1 and f is a pointer to data member of a class T
// and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
if (Context.hasSameType(QualType(MemPtrClass, 0),
BuiltinRemoveCVRef(ObjectT, Loc)) ||
BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0),
BuiltinRemoveCVRef(ObjectT, Loc))) {
return Args[1];
}

// (t1.get().*f)(t2, …, tN) when f is a pointer to a member function of
// a class T and remove_cvref_t<decltype(t1)> is a specialization of
// reference_wrapper;
if (auto *RD = ObjectT->getAsCXXRecordDecl()) {
if (RD->isInStdNamespace() &&
RD->getDeclName().getAsString() == "reference_wrapper") {
CXXScopeSpec SS;
IdentifierInfo *GetName = &Context.Idents.get("get");
UnqualifiedId GetID;
GetID.setIdentifier(GetName, Loc);

auto MemExpr = ActOnMemberAccessExpr(
getCurScope(), Args[1], Loc, tok::period, SS,
/*TemplateKWLoc=*/SourceLocation(), GetID, nullptr);

if (MemExpr.isInvalid())
return ExprError();

return ActOnCallExpr(getCurScope(), MemExpr.get(), Loc, {}, Loc);
}
}

// ((*t1).*f)(t2, …, tN) when f is a pointer to a member function of a
// class T and t1 does not satisfy the previous two items;

return ActOnUnaryOp(getCurScope(), Loc, tok::star, Args[1]);
}();

if (ObjectArg.isInvalid())
return ExprError();

auto BinOp = ActOnBinOp(getCurScope(), TheCall->getBeginLoc(),
tok::periodstar, ObjectArg.get(), Args[0]);
if (BinOp.isInvalid())
return ExprError();

if (MPT->isMemberDataPointer())
return BinOp;

auto *MemCall = new (Context)
ParenExpr(SourceLocation(), SourceLocation(), BinOp.get());

return ActOnCallExpr(getCurScope(), MemCall, TheCall->getBeginLoc(),
Args.drop_front(2), TheCall->getRParenLoc());
}
return ActOnCallExpr(getCurScope(), Args.front(), TheCall->getBeginLoc(),
Args.drop_front(), TheCall->getRParenLoc());
}

bool Sema::BuiltinPrefetch(CallExpr *TheCall) {
unsigned NumArgs = TheCall->getNumArgs();

Expand Down
105 changes: 54 additions & 51 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6540,67 +6540,70 @@ ExprResult Sema::ActOnTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
return BuildTypeTrait(Kind, KWLoc, ConvertedArgs, RParenLoc);
}

static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
QualType LhsT = Lhs->getType();
QualType RhsT = Rhs->getType();
bool Sema::BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT,
QualType RhsT) {
// C++0x [meta.rel]p2
// Base is a base class of Derived without regard to cv-qualifiers or
// Base and Derived are not unions and name the same class type without
// regard to cv-qualifiers.

const RecordType *lhsRecord = LhsT->getAs<RecordType>();
const RecordType *rhsRecord = RhsT->getAs<RecordType>();
if (!rhsRecord || !lhsRecord) {
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
if (!LHSObjTy || !RHSObjTy)
return false;

assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
"Cannot evaluate traits of dependent types");
ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
if (!BaseInterface || !DerivedInterface)
return false;

switch(BTT) {
case BTT_IsBaseOf: {
// C++0x [meta.rel]p2
// Base is a base class of Derived without regard to cv-qualifiers or
// Base and Derived are not unions and name the same class type without
// regard to cv-qualifiers.

const RecordType *lhsRecord = LhsT->getAs<RecordType>();
const RecordType *rhsRecord = RhsT->getAs<RecordType>();
if (!rhsRecord || !lhsRecord) {
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
if (!LHSObjTy || !RHSObjTy)
return false;
if (RequireCompleteType(RhsTLoc, RhsT,
diag::err_incomplete_type_used_in_type_trait_expr))
return false;

ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
if (!BaseInterface || !DerivedInterface)
return false;
return BaseInterface->isSuperClassOf(DerivedInterface);
}

if (Self.RequireCompleteType(
Rhs->getTypeLoc().getBeginLoc(), RhsT,
diag::err_incomplete_type_used_in_type_trait_expr))
return false;
assert(Context.hasSameUnqualifiedType(LhsT, RhsT) ==
(lhsRecord == rhsRecord));

return BaseInterface->isSuperClassOf(DerivedInterface);
}
// Unions are never base classes, and never have base classes.
// It doesn't matter if they are complete or not. See PR#41843
if (lhsRecord && lhsRecord->getDecl()->isUnion())
return false;
if (rhsRecord && rhsRecord->getDecl()->isUnion())
return false;

if (lhsRecord == rhsRecord)
return true;

assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT)
== (lhsRecord == rhsRecord));
// C++0x [meta.rel]p2:
// If Base and Derived are class types and are different types
// (ignoring possible cv-qualifiers) then Derived shall be a
// complete type.
if (RequireCompleteType(RhsTLoc, RhsT,
diag::err_incomplete_type_used_in_type_trait_expr))
return false;

// Unions are never base classes, and never have base classes.
// It doesn't matter if they are complete or not. See PR#41843
if (lhsRecord && lhsRecord->getDecl()->isUnion())
return false;
if (rhsRecord && rhsRecord->getDecl()->isUnion())
return false;
return cast<CXXRecordDecl>(rhsRecord->getDecl())
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
}

if (lhsRecord == rhsRecord)
return true;
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
Copy link
Contributor

Choose a reason for hiding this comment

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

Were these refactors necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think so. This allows me to use them in BuiltinInvoke above and be very close to the wording in the standard. I guess it would be possible to call BuildTypeTrait instead, but that seems much less readable to me (and would require me to find a TypeSourceInfo somewhere).

const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
QualType LhsT = Lhs->getType();
QualType RhsT = Rhs->getType();

// C++0x [meta.rel]p2:
// If Base and Derived are class types and are different types
// (ignoring possible cv-qualifiers) then Derived shall be a
// complete type.
if (Self.RequireCompleteType(
Rhs->getTypeLoc().getBeginLoc(), RhsT,
diag::err_incomplete_type_used_in_type_trait_expr))
return false;
assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
"Cannot evaluate traits of dependent types");

switch(BTT) {
case BTT_IsBaseOf:
return Self.BuiltinIsBaseOf(Rhs->getTypeLoc().getBeginLoc(), LhsT, RhsT);

return cast<CXXRecordDecl>(rhsRecord->getDecl())
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
}
case BTT_IsVirtualBaseOf: {
const RecordType *BaseRecord = LhsT->getAs<RecordType>();
const RecordType *DerivedRecord = RhsT->getAs<RecordType>();
Expand Down
61 changes: 61 additions & 0 deletions clang/test/CodeGenCXX/builtin-invoke.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s

extern "C" void* memcpy(void*, const void*, decltype(sizeof(int)));
void func();

namespace std {
template <class T>
class reference_wrapper {
T* ptr;

public:
T& get() { return *ptr; }
};
} // namespace std

struct Callable {
void operator()() {}

void func();
};

extern "C" void call1() {
__builtin_invoke(func);
__builtin_invoke(Callable{});
__builtin_invoke(memcpy, nullptr, nullptr, 0);

// CHECK: define dso_local void @call1
// CHECK-NEXT: entry:
// CHECK-NEXT: %ref.tmp = alloca %struct.Callable, align 1
// CHECK-NEXT: call void @_Z4funcv()
// CHECK-NEXT: call void @_ZN8CallableclEv(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp)
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 null, ptr align 1 null, i64 0, i1 false)
// CHECK-NEXT: ret void
}

extern "C" void call_memptr(std::reference_wrapper<Callable> wrapper) {
__builtin_invoke(&Callable::func, wrapper);

// CHECK: define dso_local void @call_memptr
// CHECK-NEXT: entry:
// CHECK-NEXT: %wrapper = alloca %"class.std::reference_wrapper", align 8
// CHECK-NEXT: %coerce.dive = getelementptr inbounds nuw %"class.std::reference_wrapper", ptr %wrapper, i32 0, i32 0
// CHECK-NEXT: store ptr %wrapper.coerce, ptr %coerce.dive, align 8
// CHECK-NEXT: %call = call noundef nonnull align 1 dereferenceable(1) ptr @_ZNSt17reference_wrapperI8CallableE3getEv(ptr noundef nonnull align 8 dereferenceable(8) %wrapper)
// CHECK-NEXT: %0 = getelementptr inbounds i8, ptr %call, i64 0
// CHECK-NEXT: br i1 false, label %memptr.virtual, label %memptr.nonvirtual
// CHECK-EMPTY:
// CHECK-NEXT: memptr.virtual:
// CHECK-NEXT: %vtable = load ptr, ptr %0, align 8
// CHECK-NEXT: %1 = getelementptr i8, ptr %vtable, i64 sub (i64 ptrtoint (ptr @_ZN8Callable4funcEv to i64), i64 1), !nosanitize !2
// CHECK-NEXT: %memptr.virtualfn = load ptr, ptr %1, align 8, !nosanitize !2
// CHECK-NEXT: br label %memptr.end
// CHECK-EMPTY:
// CHECK-NEXT: memptr.nonvirtual:
// CHECK-NEXT: br label %memptr.end
// CHECK-EMPTY:
// CHECK-NEXT: memptr.end:
// CHECK-NEXT: %2 = phi ptr [ %memptr.virtualfn, %memptr.virtual ], [ @_ZN8Callable4funcEv, %memptr.nonvirtual ]
// CHECK-NEXT: call void %2(ptr noundef nonnull align 1 dereferenceable(1) %0)
// CHECK-NEXT: ret void
}
Loading
Loading