-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[Clang] Add attribute for consteval builtin functions #91894
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
Conversation
…ins as constexpr in C++ Also support redeclaring now-constexpr builtins without constexpr
@llvm/pr-subscribers-clang Author: Mital Ashok (MitalAshok) ChangesAlso support redeclaring now-constexpr builtins without constexpr Full diff: https://github.com/llvm/llvm-project/pull/91894.diff 6 Files Affected:
diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h
index f955d21169556..e85ec5b2dca14 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -280,6 +280,11 @@ class Context {
return strchr(getRecord(ID).Attributes, 'E') != nullptr;
}
+ /// Returns true if this is an immediate (consteval) function
+ bool isImmediate(unsigned ID) const {
+ return strchr(getRecord(ID).Attributes, 'G') != nullptr;
+ }
+
private:
const Info &getRecord(unsigned ID) const;
diff --git a/clang/include/clang/Basic/BuiltinsBase.td b/clang/include/clang/Basic/BuiltinsBase.td
index 724747ec76d73..1196b9e15c10d 100644
--- a/clang/include/clang/Basic/BuiltinsBase.td
+++ b/clang/include/clang/Basic/BuiltinsBase.td
@@ -70,6 +70,8 @@ class VScanfFormat<int I> : IndexedAttribute<"S", I>;
// Builtin can be constant evaluated
def Constexpr : Attribute<"E">;
+// Builtin is immediate and must be constant evaluated. Implies Constexpr.
+def Consteval : Attribute<"EG">;
// Builtin kinds
// =============
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index fb913034bd836..6b0a04585928a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2409,10 +2409,17 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type,
Parent = CLinkageDecl;
}
- FunctionDecl *New = FunctionDecl::Create(Context, Parent, Loc, Loc, II, Type,
- /*TInfo=*/nullptr, SC_Extern,
- getCurFPFeatures().isFPConstrained(),
- false, Type->isFunctionProtoType());
+ ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified;
+ if (getLangOpts().CPlusPlus && Context.BuiltinInfo.isConstantEvaluated(ID)) {
+ ConstexprKind = ConstexprSpecKind::Constexpr;
+ if (Context.BuiltinInfo.isImmediate(ID))
+ ConstexprKind = ConstexprSpecKind::Consteval;
+ }
+
+ FunctionDecl *New = FunctionDecl::Create(
+ Context, Parent, Loc, Loc, II, Type, /*TInfo=*/nullptr, SC_Extern,
+ getCurFPFeatures().isFPConstrained(), /*isInlineSpecified=*/false,
+ Type->isFunctionProtoType(), ConstexprKind);
New->setImplicit();
New->addAttr(BuiltinAttr::CreateImplicit(Context, ID));
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 53238d355ea09..1b558d70f9b48 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -676,11 +676,19 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old,
// template has a constexpr specifier then all its declarations shall
// contain the constexpr specifier.
if (New->getConstexprKind() != Old->getConstexprKind()) {
- Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch)
- << New << static_cast<int>(New->getConstexprKind())
- << static_cast<int>(Old->getConstexprKind());
- Diag(Old->getLocation(), diag::note_previous_declaration);
- Invalid = true;
+ if (Old->getBuiltinID() &&
+ Old->getConstexprKind() == ConstexprSpecKind::Constexpr &&
+ New->getConstexprKind() == ConstexprSpecKind::Unspecified) {
+ // Except allow redeclaring a builtin as non-constexpr to match C
+ // redeclarations which will not be constexpr
+ New->setConstexprKind(ConstexprSpecKind::Constexpr);
+ } else {
+ Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch)
+ << New << static_cast<int>(New->getConstexprKind())
+ << static_cast<int>(Old->getConstexprKind());
+ Diag(Old->getLocation(), diag::note_previous_declaration);
+ Invalid = true;
+ }
} else if (!Old->getMostRecentDecl()->isInlined() && New->isInlined() &&
Old->isDefined(Def) &&
// If a friend function is inlined but does not have 'inline'
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index bb4b116fd73ca..39aa32526d2b1 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7095,8 +7095,12 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
}
// Bail out early if calling a builtin with custom type checking.
- if (BuiltinID && Context.BuiltinInfo.hasCustomTypechecking(BuiltinID))
- return CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall);
+ if (BuiltinID && Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) {
+ ExprResult E = CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall);
+ if (!E.isInvalid() && Context.BuiltinInfo.isImmediate(BuiltinID))
+ E = CheckForImmediateInvocation(E, FDecl);
+ return E;
+ }
if (getLangOpts().CUDA) {
if (Config) {
diff --git a/clang/test/Sema/builtin-redecl.cpp b/clang/test/Sema/builtin-redecl.cpp
index 323c63e202883..31409a4d46a65 100644
--- a/clang/test/Sema/builtin-redecl.cpp
+++ b/clang/test/Sema/builtin-redecl.cpp
@@ -14,13 +14,18 @@ void __builtin_va_copy(double d);
// expected-error@+2 {{cannot redeclare builtin function '__builtin_va_end'}}
// expected-note@+1 {{'__builtin_va_end' is a builtin with type}}
void __builtin_va_end(__builtin_va_list);
-// RUN: %clang_cc1 %s -fsyntax-only -verify
-// RUN: %clang_cc1 %s -fsyntax-only -verify -x c
void __va_start(__builtin_va_list*, ...);
+ void *__builtin_assume_aligned(const void *, size_t, ...);
#ifdef __cplusplus
-void *__builtin_assume_aligned(const void *, size_t, ...) noexcept;
-#else
-void *__builtin_assume_aligned(const void *, size_t, ...);
+constexpr void *__builtin_assume_aligned(const void *, size_t, ...);
+ void *__builtin_assume_aligned(const void *, size_t, ...) noexcept;
+constexpr void *__builtin_assume_aligned(const void *, size_t, ...) noexcept;
+ void *__builtin_assume_aligned(const void *, size_t, ...) throw();
+constexpr void *__builtin_assume_aligned(const void *, size_t, ...) throw();
+
+// expected-error@+1 {{constexpr declaration of '__builtin_calloc' follows non-constexpr declaration}}
+constexpr void *__builtin_calloc(size_t, size_t);
+// expected-note@-1 {{previous declaration is here}}
#endif
|
Currently, GCC's behaviour for builtin function redeclarations is to just ignore P2641R4 |
clang/test/Sema/builtin-redecl.cpp
Outdated
constexpr void *__builtin_assume_aligned(const void *, size_t, ...); | ||
void *__builtin_assume_aligned(const void *, size_t, ...) noexcept; | ||
constexpr void *__builtin_assume_aligned(const void *, size_t, ...) noexcept; | ||
void *__builtin_assume_aligned(const void *, size_t, ...) throw(); | ||
constexpr void *__builtin_assume_aligned(const void *, size_t, ...) throw(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the motivation to allow that?
I kind of understand letting users defining builtin (actually, I am not sure I do). but once the builtin is redefined once, I think should be some sort of consistency with the language rules.
Here if you were to replace __builtin_assume_aligned
with f
, it would be ill-formed, for good reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For backwards compatibility (and compatibility with the same definitions in C or with GCC), to allow declarations with or without the constexpr
. I suspect in any real code, it will always be declared without the constexpr
.
The other solutions is to keep them all as ConstexprSpecKind::Unspecified
like we currently do, but this is asymmetric with consteval
builtins which will be marked ConstexprSpecKind::Consteval
, and leads to the builtin versions of std::move
/std::forward
/constexpr cmath functions to have differing getConstexprKind()
from their non-builtin counterparts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does anyone know why we allow builtins to be redeclared at all? CC @zygoloid @rjmccall
I wonder just how much code we would break by not aiming for backwards compatibility here and requiring users to update their declarations on the assumption that declaring these at all is undefined behavior (using a reserved name) and so it's up to the user to match the compiler's behavior rather than make the compiler handle this with weird non-conforming language extension behavior.
I'm not looking to punish users in this case, but the workaround to keep things backwards compatible is pretty magical and awkward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there are particularly good non-historical reasons why we allow non-LIBBUILTIN
builtins to be redeclared -- I think it's allowed primarily for compatibility with GCC and existing code, and it's not clear to me why GCC allows it. There were some examples of code redeclaring builtins in this prior review thread.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From reading through that (and trying to search my memory): A number of our LIBBUILTIN
builtins are just replacements for things that would otherwise be implemented in the library.
So we need to allow them to be redeclared because they might be implemented in the library.
However, I would be unopposed to a patch that makes declaring a __builtin
spelled as reserved identifier ill-formed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems like that should go in a separate patch. For now, I'll change this one so ConstexprSpecKind::Constexpr
isn't added to these builtins as before
✅ With the latest revision this PR passed the C/C++ code formatter. |
You can test this locally with the following command:git-clang-format --diff 39d123f58a0e3c5f1a928940244b8dfd827fd4e5 a3aaf28d92499b1e76b49a0499be87b5b706155c -- clang/include/clang/Basic/Builtins.h clang/lib/Basic/Builtins.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaExpr.cpp View the diff from clang-format here.diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 8554cb5972..c24ebc3ef4 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2373,7 +2373,8 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type,
ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified;
if (Context.BuiltinInfo.isImmediate(ID)) {
- assert(getLangOpts().CPlusPlus20 && "consteval builtins should only be available in C++20 mode");
+ assert(getLangOpts().CPlusPlus20 &&
+ "consteval builtins should only be available in C++20 mode");
ConstexprKind = ConstexprSpecKind::Consteval;
}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
As discussed offline with @erichkeane we don't have good way to test this change until the patch to add is_within_lifetime lands
Summary: Builtins with the new `Consteval` attribute will also be marked `Constexpr` and will only be available in C++20 mode where `consteval` makes sense. Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60251624
Builtins with the new
Consteval
attribute will also be markedConstexpr
and will only be available in C++20 mode whereconsteval
makes sense.