Skip to content

Commit c08d280

Browse files
committed
Translate static assert to rust const assert
1 parent 5a471be commit c08d280

7 files changed

Lines changed: 99 additions & 26 deletions

File tree

‎cpp2rust/converter/converter.cpp‎

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,7 +1268,22 @@ bool Converter::VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *) {
12681268
return false;
12691269
}
12701270

1271-
bool Converter::VisitStaticAssertDecl(clang::StaticAssertDecl *) {
1271+
bool Converter::VisitStaticAssertDecl(clang::StaticAssertDecl *decl) {
1272+
auto *assert_expr = decl->getAssertExpr();
1273+
if (assert_expr->isValueDependent()) {
1274+
return false;
1275+
}
1276+
std::string condition;
1277+
if (assert_expr->getType()->isBooleanType() &&
1278+
IsRustConstEvaluableExpr(assert_expr)) {
1279+
condition = ToString(assert_expr);
1280+
} else {
1281+
bool value = false;
1282+
ENSURE(assert_expr->EvaluateAsBooleanCondition(value, ctx_));
1283+
condition = value ? keyword::kTrue : keyword::kFalse;
1284+
}
1285+
StrCat(std::format("const _: () = assert!({}{});", condition,
1286+
GetAssertMessageAsString(assert_expr, ctx_)));
12721287
return false;
12731288
}
12741289

@@ -2273,30 +2288,6 @@ bool Converter::VisitCharacterLiteral(clang::CharacterLiteral *expr) {
22732288
return false;
22742289
}
22752290

2276-
std::string Converter::GetEscapedCharLiteral(char character) const {
2277-
switch (character) {
2278-
case '"':
2279-
return "\\\"";
2280-
case '\'':
2281-
return "\\'";
2282-
case '\\':
2283-
return "\\\\";
2284-
case '\n':
2285-
return "\\n";
2286-
case '\r':
2287-
return "\\r";
2288-
case '\t':
2289-
return "\\t";
2290-
case '\0':
2291-
return "\\0";
2292-
}
2293-
auto uc = static_cast<unsigned char>(character);
2294-
if (uc < 0x20 || uc >= 0x7F) {
2295-
return std::format("\\x{:02x}", uc);
2296-
}
2297-
return std::string(1, character);
2298-
}
2299-
23002291
std::string Converter::GetEscapedUTF8CharLiteral(clang::Expr *expr) const {
23012292
auto char_expr =
23022293
clang::dyn_cast<clang::CharacterLiteral>(expr->IgnoreCasts());

‎cpp2rust/converter/converter.h‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,6 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
354354

355355
virtual bool VisitCharacterLiteral(clang::CharacterLiteral *expr);
356356

357-
std::string GetEscapedCharLiteral(char character) const;
358357
std::string GetCodeUnitArrayLiteral(const clang::StringLiteral *expr);
359358
bool IsArrayInitContext() const;
360359

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55

66
#include <clang/AST/DeclTemplate.h>
77
#include <clang/AST/ExprCXX.h>
8+
#include <clang/AST/ExprConcepts.h>
89
#include <clang/AST/Mangle.h>
910
#include <clang/AST/ParentMapContext.h>
1011
#include <clang/Basic/SourceManager.h>
12+
#include <clang/Lex/Lexer.h>
1113
#include <llvm/Support/Path.h>
1214
#include <llvm/Support/raw_ostream.h>
1315

@@ -108,6 +110,67 @@ bool IsBuiltinConstantP(const clang::Expr *expr) {
108110
return false;
109111
}
110112

113+
bool IsRustConstEvaluableExpr(const clang::Expr *expr) {
114+
expr = expr->IgnoreParenImpCasts();
115+
if (clang::isa<clang::IntegerLiteral, clang::ConceptSpecializationExpr>(
116+
expr)) {
117+
return true;
118+
}
119+
if (auto *trait = clang::dyn_cast<clang::UnaryExprOrTypeTraitExpr>(expr)) {
120+
return trait->getKind() == clang::UnaryExprOrTypeTrait::UETT_SizeOf;
121+
}
122+
if (auto *unary = clang::dyn_cast<clang::UnaryOperator>(expr)) {
123+
return unary->getOpcode() == clang::UO_LNot &&
124+
IsRustConstEvaluableExpr(unary->getSubExpr());
125+
}
126+
if (auto *binary = clang::dyn_cast<clang::BinaryOperator>(expr)) {
127+
return (binary->isEqualityOp() || binary->isRelationalOp()) &&
128+
IsRustConstEvaluableExpr(binary->getLHS()) &&
129+
IsRustConstEvaluableExpr(binary->getRHS());
130+
}
131+
return false;
132+
}
133+
134+
std::string GetEscapedCharLiteral(char character) {
135+
switch (character) {
136+
case '"':
137+
return "\\\"";
138+
case '\'':
139+
return "\\'";
140+
case '\\':
141+
return "\\\\";
142+
case '\n':
143+
return "\\n";
144+
case '\r':
145+
return "\\r";
146+
case '\t':
147+
return "\\t";
148+
case '\0':
149+
return "\\0";
150+
}
151+
auto uc = static_cast<unsigned char>(character);
152+
if (uc < 0x20 || uc >= 0x7F) {
153+
return std::format("\\x{:02x}", uc);
154+
}
155+
return std::string(1, character);
156+
}
157+
158+
std::string GetAssertMessageAsString(const clang::Expr *expr,
159+
const clang::ASTContext &ctx) {
160+
auto text = clang::Lexer::getSourceText(
161+
clang::CharSourceRange::getTokenRange(expr->getSourceRange()),
162+
ctx.getSourceManager(), ctx.getLangOpts());
163+
std::string message = R"(, ")";
164+
for (char c : text) {
165+
// Doubled so that assert! does not read them as a format placeholder.
166+
if (c == '{' || c == '}') {
167+
message += c;
168+
}
169+
message += GetEscapedCharLiteral(c);
170+
}
171+
return message + '"';
172+
}
173+
111174
bool IsComparisonWithNullOp(const clang::BinaryOperator *expr) {
112175
if (!expr->isComparisonOp()) {
113176
return false;

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ bool IsGlobalVar(const clang::VarDecl *decl);
3838

3939
bool IsGlobalVar(const clang::Expr *expr);
4040

41+
bool IsRustConstEvaluableExpr(const clang::Expr *expr);
42+
43+
std::string GetEscapedCharLiteral(char character);
44+
45+
std::string GetAssertMessageAsString(const clang::Expr *expr,
46+
const clang::ASTContext &ctx);
47+
4148
bool IsComparisonWithNullOp(const clang::BinaryOperator *expr);
4249

4350
bool IsInMainFile(const clang::Decl *decl);

‎tests/unit/concepts.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ template <typename T>
66
concept Small = sizeof(T) <= 4;
77

88
static_assert(Small<int>);
9+
static_assert(sizeof(int) == 4);
910

1011
template <typename T>
1112
concept HasSize = requires(T t) {

‎tests/unit/out/refcount/concepts.rs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ use std::io::prelude::*;
66
use std::io::{Read, Seek, Write};
77
use std::os::fd::AsFd;
88
use std::rc::{Rc, Weak};
9+
const _: () = assert!(true, "Small<int>");
10+
const _: () = assert!(
11+
(::std::mem::size_of::<i32>() == 4_usize),
12+
"sizeof(int) == 4"
13+
);
914
#[derive(Clone, ByteRepr, Default)]
1015
pub struct Sized {}
1116
pub fn is_small_0() -> bool {
@@ -46,6 +51,7 @@ pub fn main() {
4651
std::process::exit(main_0());
4752
}
4853
fn main_0() -> i32 {
54+
const _: () = assert!(!(false), "!Small<long>");;
4955
assert!(({ is_small_0() }));
5056
assert!(!({ is_small_1() }));
5157
assert!(true);

‎tests/unit/out/unsafe/concepts.rs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ use std::collections::BTreeMap;
66
use std::io::{Read, Seek, Write};
77
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
88
use std::rc::Rc;
9+
const _: () = assert!(true, "Small<int>");
10+
const _: () = assert!(
11+
((::std::mem::size_of::<i32>()) == (4_usize)),
12+
"sizeof(int) == 4"
13+
);
914
#[repr(C)]
1015
#[derive(Copy, Clone, Default)]
1116
pub struct Sized {}
@@ -51,6 +56,7 @@ pub fn main() {
5156
}
5257
}
5358
unsafe fn main_0() -> i32 {
59+
const _: () = assert!(!(false), "!Small<long>");;
5460
assert!((unsafe { is_small_0() }));
5561
assert!(!(unsafe { is_small_1() }));
5662
assert!(true);

0 commit comments

Comments
 (0)