Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
60 changes: 36 additions & 24 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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<unsigned char>(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<clang::CharacterLiteral>(expr->IgnoreCasts());
Expand Down Expand Up @@ -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_));
Expand Down
9 changes: 8 additions & 1 deletion cpp2rust/converter/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
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);
Expand Down Expand Up @@ -351,7 +354,6 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

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

std::string GetEscapedCharLiteral(char character) const;
std::string GetCodeUnitArrayLiteral(const clang::StringLiteral *expr);
bool IsArrayInitContext() const;

Expand Down Expand Up @@ -424,8 +426,13 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
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);
Expand Down
63 changes: 63 additions & 0 deletions cpp2rust/converter/converter_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@

#include <clang/AST/DeclTemplate.h>
#include <clang/AST/ExprCXX.h>
#include <clang/AST/ExprConcepts.h>
#include <clang/AST/Mangle.h>
#include <clang/AST/ParentMapContext.h>
#include <clang/Basic/SourceManager.h>
#include <clang/Lex/Lexer.h>
#include <llvm/Support/Path.h>
#include <llvm/Support/raw_ostream.h>

Expand Down Expand Up @@ -108,6 +110,67 @@ bool IsBuiltinConstantP(const clang::Expr *expr) {
return false;
}

bool IsRustConstEvaluableExpr(const clang::Expr *expr) {
expr = expr->IgnoreParenImpCasts();
if (clang::isa<clang::IntegerLiteral, clang::ConceptSpecializationExpr>(
expr)) {
return true;
}
if (auto *trait = clang::dyn_cast<clang::UnaryExprOrTypeTraitExpr>(expr)) {
return trait->getKind() == clang::UnaryExprOrTypeTrait::UETT_SizeOf;
}
if (auto *unary = clang::dyn_cast<clang::UnaryOperator>(expr)) {
return unary->getOpcode() == clang::UO_LNot &&
IsRustConstEvaluableExpr(unary->getSubExpr());
}
if (auto *binary = clang::dyn_cast<clang::BinaryOperator>(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<unsigned char>(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;
Expand Down
7 changes: 7 additions & 0 deletions cpp2rust/converter/converter_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
45 changes: 45 additions & 0 deletions tests/unit/concepts.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// ADDITIONAL_COMPILE_FLAGS: -std=c++20
#include <cassert>
#include <concepts>

template <typename T>
concept Small = sizeof(T) <= 4;

static_assert(Small<int>);
static_assert(sizeof(int) == 4);

template <typename T>
concept HasSize = requires(T t) {
{ t.size() } -> std::same_as<int>;
};

struct Sized {
int size() { return 4; }
};

template <typename T> bool is_small() { return Small<T>; }

template <typename T> bool has_size() {
return requires(T t) { t.size(); };
}

template <typename T> int pick(T x) {
if (std::integral<T> && Small<T>) {
return 1;
}
return 2;
}

int main() {
static_assert(!Small<long>);
assert(is_small<char>());
assert(!is_small<double>());
assert(HasSize<Sized>);
assert(!HasSize<int>);
assert(has_size<Sized>());
assert(!has_size<int>());
assert(pick(1) == 1);
assert(pick(1L) == 2);
assert(pick(1.0f) == 2);
return 0;
}
74 changes: 74 additions & 0 deletions tests/unit/out/refcount/concepts.rs
Original file line number Diff line number Diff line change
@@ -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<int>");
const _: () = assert!(
(::std::mem::size_of::<i32>() == 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<i32> = Rc::new(RefCell::new(x));
if (true) && (true) {
return 1;
}
return 2;
}
pub fn pick_5(x: i64) -> i32 {
let x: Value<i64> = Rc::new(RefCell::new(x));
if (true) && (false) {
return 1;
}
return 2;
}
pub fn pick_6(x: f32) -> i32 {
let x: Value<f32> = 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<long>");;
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<Sized> {
fn size(&self) -> i32 {
return 4;
}
}
pub fn __cpp2rust_init_globals() {}
71 changes: 71 additions & 0 deletions tests/unit/out/unsafe/concepts.rs
Original file line number Diff line number Diff line change
@@ -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<int>");
const _: () = assert!(
((::std::mem::size_of::<i32>()) == (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<long>");;
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() {}
Loading