diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 86440281f..c4088c017 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1268,6 +1268,27 @@ bool Converter::VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *) { return false; } +bool Converter::VisitStaticAssertDecl(clang::StaticAssertDecl *decl) { + auto *assert_expr = decl->getAssertExpr(); + if (assert_expr->isValueDependent()) { + return false; + } + std::string condition; + if (assert_expr->getType()->isBooleanType() && + IsRustConstEvaluableExpr(assert_expr)) { + condition = ToString(assert_expr); + } else { + bool value = false; + ENSURE(assert_expr->EvaluateAsBooleanCondition(value, ctx_)); + condition = value ? keyword::kTrue : keyword::kFalse; + } + StrCat(std::format("const _: () = assert!({}{});", condition, + GetAssertMessageAsString(assert_expr, ctx_))); + return false; +} + +bool Converter::VisitConceptDecl(clang::ConceptDecl *) { return false; } + static bool IsaSemiColonStmt(const clang::Stmt *stmt) { switch (stmt->getStmtClass()) { case clang::Stmt::IfStmtClass: @@ -2267,30 +2288,6 @@ bool Converter::VisitCharacterLiteral(clang::CharacterLiteral *expr) { return false; } -std::string Converter::GetEscapedCharLiteral(char character) const { - switch (character) { - case '"': - return "\\\""; - case '\'': - return "\\'"; - case '\\': - return "\\\\"; - case '\n': - return "\\n"; - case '\r': - return "\\r"; - case '\t': - return "\\t"; - case '\0': - return "\\0"; - } - auto uc = static_cast(character); - if (uc < 0x20 || uc >= 0x7F) { - return std::format("\\x{:02x}", uc); - } - return std::string(1, character); -} - std::string Converter::GetEscapedUTF8CharLiteral(clang::Expr *expr) const { auto char_expr = clang::dyn_cast(expr->IgnoreCasts()); @@ -3703,6 +3700,21 @@ bool Converter::VisitUnaryExprOrTypeTraitExpr( return false; } +bool Converter::VisitConceptSpecializationExpr( + clang::ConceptSpecializationExpr *expr) { + assert(!expr->isValueDependent()); + StrCat(expr->isSatisfied() ? keyword::kTrue : keyword::kFalse); + computed_expr_type_ = ComputedExprType::FreshValue; + return false; +} + +bool Converter::VisitRequiresExpr(clang::RequiresExpr *expr) { + assert(!expr->isValueDependent()); + StrCat(expr->isSatisfied() ? keyword::kTrue : keyword::kFalse); + computed_expr_type_ = ComputedExprType::FreshValue; + return false; +} + bool Converter::VisitTypeTraitExpr(clang::TypeTraitExpr *expr) { clang::Expr::EvalResult result; ENSURE(expr->EvaluateAsInt(result, ctx_)); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 5e8465c70..87281dba3 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -164,6 +164,9 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool VisitTypeAliasDecl(clang::TypeAliasDecl *decl); virtual bool VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *decl); + bool VisitStaticAssertDecl(clang::StaticAssertDecl *decl); + bool VisitConceptDecl(clang::ConceptDecl *decl); + virtual bool VisitCompoundStmt(clang::CompoundStmt *stmt); virtual bool VisitDeclStmt(clang::DeclStmt *stmt); @@ -351,7 +354,6 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool VisitCharacterLiteral(clang::CharacterLiteral *expr); - std::string GetEscapedCharLiteral(char character) const; std::string GetCodeUnitArrayLiteral(const clang::StringLiteral *expr); bool IsArrayInitContext() const; @@ -424,8 +426,13 @@ class Converter : public clang::RecursiveASTVisitor { VisitUnaryExprOrTypeTraitExpr(clang::UnaryExprOrTypeTraitExpr *expr); virtual bool VisitTypeTraitExpr(clang::TypeTraitExpr *expr); + virtual bool VisitSizeOfPackExpr(clang::SizeOfPackExpr *expr); + virtual bool + VisitConceptSpecializationExpr(clang::ConceptSpecializationExpr *expr); + virtual bool VisitRequiresExpr(clang::RequiresExpr *expr); + virtual bool VisitOffsetOfExpr(clang::OffsetOfExpr *expr); virtual bool VisitEnumDecl(clang::EnumDecl *decl); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 82acf2fd0..7b5c08282 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -5,9 +5,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -108,6 +110,67 @@ bool IsBuiltinConstantP(const clang::Expr *expr) { return false; } +bool IsRustConstEvaluableExpr(const clang::Expr *expr) { + expr = expr->IgnoreParenImpCasts(); + if (clang::isa( + expr)) { + return true; + } + if (auto *trait = clang::dyn_cast(expr)) { + return trait->getKind() == clang::UnaryExprOrTypeTrait::UETT_SizeOf; + } + if (auto *unary = clang::dyn_cast(expr)) { + return unary->getOpcode() == clang::UO_LNot && + IsRustConstEvaluableExpr(unary->getSubExpr()); + } + if (auto *binary = clang::dyn_cast(expr)) { + return (binary->isEqualityOp() || binary->isRelationalOp()) && + IsRustConstEvaluableExpr(binary->getLHS()) && + IsRustConstEvaluableExpr(binary->getRHS()); + } + return false; +} + +std::string GetEscapedCharLiteral(char character) { + switch (character) { + case '"': + return "\\\""; + case '\'': + return "\\'"; + case '\\': + return "\\\\"; + case '\n': + return "\\n"; + case '\r': + return "\\r"; + case '\t': + return "\\t"; + case '\0': + return "\\0"; + } + auto uc = static_cast(character); + if (uc < 0x20 || uc >= 0x7F) { + return std::format("\\x{:02x}", uc); + } + return std::string(1, character); +} + +std::string GetAssertMessageAsString(const clang::Expr *expr, + const clang::ASTContext &ctx) { + auto text = clang::Lexer::getSourceText( + clang::CharSourceRange::getTokenRange(expr->getSourceRange()), + ctx.getSourceManager(), ctx.getLangOpts()); + std::string message = R"(, ")"; + for (char c : text) { + // Doubled so that assert! does not read them as a format placeholder. + if (c == '{' || c == '}') { + message += c; + } + message += GetEscapedCharLiteral(c); + } + return message + '"'; +} + bool IsComparisonWithNullOp(const clang::BinaryOperator *expr) { if (!expr->isComparisonOp()) { return false; diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index cec83b2b2..5f09e0d2f 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -38,6 +38,13 @@ bool IsGlobalVar(const clang::VarDecl *decl); bool IsGlobalVar(const clang::Expr *expr); +bool IsRustConstEvaluableExpr(const clang::Expr *expr); + +std::string GetEscapedCharLiteral(char character); + +std::string GetAssertMessageAsString(const clang::Expr *expr, + const clang::ASTContext &ctx); + bool IsComparisonWithNullOp(const clang::BinaryOperator *expr); bool IsInMainFile(const clang::Decl *decl); diff --git a/tests/unit/concepts.cpp b/tests/unit/concepts.cpp new file mode 100644 index 000000000..aeb57b20c --- /dev/null +++ b/tests/unit/concepts.cpp @@ -0,0 +1,45 @@ +// ADDITIONAL_COMPILE_FLAGS: -std=c++20 +#include +#include + +template +concept Small = sizeof(T) <= 4; + +static_assert(Small); +static_assert(sizeof(int) == 4); + +template +concept HasSize = requires(T t) { + { t.size() } -> std::same_as; +}; + +struct Sized { + int size() { return 4; } +}; + +template bool is_small() { return Small; } + +template bool has_size() { + return requires(T t) { t.size(); }; +} + +template int pick(T x) { + if (std::integral && Small) { + return 1; + } + return 2; +} + +int main() { + static_assert(!Small); + assert(is_small()); + assert(!is_small()); + assert(HasSize); + assert(!HasSize); + assert(has_size()); + assert(!has_size()); + assert(pick(1) == 1); + assert(pick(1L) == 2); + assert(pick(1.0f) == 2); + return 0; +} diff --git a/tests/unit/out/refcount/concepts.rs b/tests/unit/out/refcount/concepts.rs new file mode 100644 index 000000000..42486c596 --- /dev/null +++ b/tests/unit/out/refcount/concepts.rs @@ -0,0 +1,74 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +const _: () = assert!(true, "Small"); +const _: () = assert!( + (::std::mem::size_of::() == 4_usize), + "sizeof(int) == 4" +); +#[derive(Clone, ByteRepr, Default)] +pub struct Sized {} +pub fn is_small_0() -> bool { + return true; +} +pub fn is_small_1() -> bool { + return false; +} +pub fn has_size_2() -> bool { + return true; +} +pub fn has_size_3() -> bool { + return false; +} +pub fn pick_4(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + if (true) && (true) { + return 1; + } + return 2; +} +pub fn pick_5(x: i64) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + if (true) && (false) { + return 1; + } + return 2; +} +pub fn pick_6(x: f32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + if (false) && (true) { + return 1; + } + return 2; +} +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + const _: () = assert!(!(false), "!Small");; + assert!(({ is_small_0() })); + assert!(!({ is_small_1() })); + assert!(true); + assert!(!(false)); + assert!(({ has_size_2() })); + assert!(!({ has_size_3() })); + assert!((({ pick_4(1,) }) == 1)); + assert!((({ pick_5(1_i64,) }) == 2)); + assert!((({ pick_6(1.0E+0,) }) == 2)); + return 0; +} +pub trait SizedImpl { + fn size(&self) -> i32; +} +impl SizedImpl for Ptr { + fn size(&self) -> i32 { + return 4; + } +} +pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/concepts.rs b/tests/unit/out/unsafe/concepts.rs new file mode 100644 index 000000000..bfb3757e7 --- /dev/null +++ b/tests/unit/out/unsafe/concepts.rs @@ -0,0 +1,71 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +const _: () = assert!(true, "Small"); +const _: () = assert!( + ((::std::mem::size_of::()) == (4_usize)), + "sizeof(int) == 4" +); +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Sized {} +impl Sized { + pub unsafe fn size(&mut self) -> i32 { + return 4; + } +} +pub unsafe fn is_small_0() -> bool { + return true; +} +pub unsafe fn is_small_1() -> bool { + return false; +} +pub unsafe fn has_size_2() -> bool { + return true; +} +pub unsafe fn has_size_3() -> bool { + return false; +} +pub unsafe fn pick_4(mut x: i32) -> i32 { + if (true) && (true) { + return 1; + } + return 2; +} +pub unsafe fn pick_5(mut x: i64) -> i32 { + if (true) && (false) { + return 1; + } + return 2; +} +pub unsafe fn pick_6(mut x: f32) -> i32 { + if (false) && (true) { + return 1; + } + return 2; +} +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + const _: () = assert!(!(false), "!Small");; + assert!((unsafe { is_small_0() })); + assert!(!(unsafe { is_small_1() })); + assert!(true); + assert!(!(false)); + assert!((unsafe { has_size_2() })); + assert!(!(unsafe { has_size_3() })); + assert!(((unsafe { pick_4(1,) }) == (1))); + assert!(((unsafe { pick_5(1_i64,) }) == (2))); + assert!(((unsafe { pick_6(1.0E+0,) }) == (2))); + return 0; +} +pub unsafe fn __cpp2rust_init_globals() {}