[clang] [Clang] Add attribute for consteval builtin functions (PR #91894)
Mital Ashok via cfe-commits
cfe-commits at lists.llvm.org
Tue May 14 11:07:02 PDT 2024
https://github.com/MitalAshok updated https://github.com/llvm/llvm-project/pull/91894
>From 56aed689dc5825fc5bacc6dfdff58ee0eaf71f82 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Sun, 12 May 2024 19:48:24 +0100
Subject: [PATCH 1/4] [Clang] Add attribute for consteval builtins; Declare
constexpr builtins as constexpr in C++
Also support redeclaring now-constexpr builtins without constexpr
---
clang/include/clang/Basic/Builtins.h | 5 +++++
clang/include/clang/Basic/BuiltinsBase.td | 2 ++
clang/lib/Sema/SemaDecl.cpp | 15 +++++++++++----
clang/lib/Sema/SemaDeclCXX.cpp | 18 +++++++++++++-----
clang/lib/Sema/SemaExpr.cpp | 8 ++++++--
clang/test/Sema/builtin-redecl.cpp | 15 ++++++++++-----
6 files changed, 47 insertions(+), 16 deletions(-)
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 at +2 {{cannot redeclare builtin function '__builtin_va_end'}}
// expected-note at +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 at +1 {{constexpr declaration of '__builtin_calloc' follows non-constexpr declaration}}
+constexpr void *__builtin_calloc(size_t, size_t);
+// expected-note at -1 {{previous declaration is here}}
#endif
>From 88c68cdffe8bf94d8ecca651d6162e8b601cdc38 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Tue, 14 May 2024 18:29:48 +0100
Subject: [PATCH 2/4] No longer declare constexpr builtins as constexpr
---
clang/lib/Sema/SemaDecl.cpp | 6 ++----
clang/lib/Sema/SemaDeclCXX.cpp | 18 +++++-------------
clang/test/Sema/builtin-redecl.cpp | 15 +++++----------
3 files changed, 12 insertions(+), 27 deletions(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 6b0a04585928a..7ca02b5bbf624 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2410,10 +2410,8 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type,
}
ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified;
- if (getLangOpts().CPlusPlus && Context.BuiltinInfo.isConstantEvaluated(ID)) {
- ConstexprKind = ConstexprSpecKind::Constexpr;
- if (Context.BuiltinInfo.isImmediate(ID))
- ConstexprKind = ConstexprSpecKind::Consteval;
+ if (getLangOpts().CPlusPlus && Context.BuiltinInfo.isImmediate(ID)) {
+ ConstexprKind = ConstexprSpecKind::Consteval;
}
FunctionDecl *New = FunctionDecl::Create(
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 1b558d70f9b48..53238d355ea09 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -676,19 +676,11 @@ 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()) {
- 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;
- }
+ 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/test/Sema/builtin-redecl.cpp b/clang/test/Sema/builtin-redecl.cpp
index 31409a4d46a65..323c63e202883 100644
--- a/clang/test/Sema/builtin-redecl.cpp
+++ b/clang/test/Sema/builtin-redecl.cpp
@@ -14,18 +14,13 @@ void __builtin_va_copy(double d);
// expected-error at +2 {{cannot redeclare builtin function '__builtin_va_end'}}
// expected-note at +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
-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 at +1 {{constexpr declaration of '__builtin_calloc' follows non-constexpr declaration}}
-constexpr void *__builtin_calloc(size_t, size_t);
-// expected-note at -1 {{previous declaration is here}}
+void *__builtin_assume_aligned(const void *, size_t, ...) noexcept;
+#else
+void *__builtin_assume_aligned(const void *, size_t, ...);
#endif
>From ec88839cb34bf2a5f57edd376e30d8c08740f855 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Tue, 14 May 2024 18:57:09 +0100
Subject: [PATCH 3/4] Prevent consteval builtins from being available when
consteval doesn't make sense (!CPlusPlus20)
---
clang/include/clang/Basic/Builtins.def | 1 +
clang/include/clang/Basic/BuiltinsBase.td | 2 +-
clang/lib/Basic/Builtins.cpp | 3 +++
clang/lib/Sema/SemaDecl.cpp | 3 ++-
4 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def
index f356f881d5ef9..f3d7642573380 100644
--- a/clang/include/clang/Basic/Builtins.def
+++ b/clang/include/clang/Basic/Builtins.def
@@ -100,3 +100,4 @@
// M_0, ..., M_k as payload
// z -> this is a function in (possibly-versioned) namespace std
// E -> this function can be constant evaluated by Clang frontend
+// G -> this is a C++20 consteval function
diff --git a/clang/include/clang/Basic/BuiltinsBase.td b/clang/include/clang/Basic/BuiltinsBase.td
index 1196b9e15c10d..58dee22fc0a45 100644
--- a/clang/include/clang/Basic/BuiltinsBase.td
+++ b/clang/include/clang/Basic/BuiltinsBase.td
@@ -70,7 +70,7 @@ 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.
+// Builtin is immediate and must be constant evaluated. Implies Constexpr, and will only be supported in C++20 mode.
def Consteval : Attribute<"EG">;
// Builtin kinds
diff --git a/clang/lib/Basic/Builtins.cpp b/clang/lib/Basic/Builtins.cpp
index b116abbe034f7..7116e27cd9546 100644
--- a/clang/lib/Basic/Builtins.cpp
+++ b/clang/lib/Basic/Builtins.cpp
@@ -119,6 +119,9 @@ static bool builtinIsSupported(const Builtin::Info &BuiltinInfo,
/* CPlusPlus Unsupported */
if (!LangOpts.CPlusPlus && BuiltinInfo.Langs == CXX_LANG)
return false;
+ /* consteval Unsupported */
+ if (!LangOpts.CPlusPlus20 && strchr(BuiltinInfo.Attributes, 'G') != nullptr)
+ return false;
return true;
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7ca02b5bbf624..d651ee2f502b1 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2410,7 +2410,8 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type,
}
ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified;
- if (getLangOpts().CPlusPlus && Context.BuiltinInfo.isImmediate(ID)) {
+ if (Context.BuiltinInfo.isImmediate(ID)) {
+ assert(getLangOpts().CPlusPlus20 && "consteval builtins should only be available in C++20 mode");
ConstexprKind = ConstexprSpecKind::Consteval;
}
>From b498e90f6bd72e2b9044aef9a4c049845a68bc0d Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Tue, 14 May 2024 19:06:46 +0100
Subject: [PATCH 4/4] clang-format
---
clang/lib/Sema/SemaDecl.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 8554cb59729ff..c24ebc3ef4ebb 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;
}
More information about the cfe-commits
mailing list