Skip to content

Commit a5733bd

Browse files
authored
Support C11 generics (#1788)
Add support for C11 `_Generic` generics where the result can be determined by clang at compile time so we transpile the resolved expression directly. Fixes #1786
2 parents 756b1f9 + b783d04 commit a5733bd

7 files changed

Lines changed: 141 additions & 1 deletion

File tree

c2rust-ast-exporter/src/AstExporter.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1501,7 +1501,13 @@ class TranslateASTVisitor final
15011501
}
15021502

15031503
bool VisitGenericSelectionExpr(GenericSelectionExpr *E) {
1504-
printDiag(Context, DiagnosticsEngine::Warning, "Encountered unsupported generic selection expression", E);
1504+
if (E->isResultDependent()) {
1505+
printDiag(Context, DiagnosticsEngine::Warning, "Encountered unsupported generic selection expression", E);
1506+
return true;
1507+
}
1508+
1509+
std::vector<void *> childIds{E->getResultExpr()};
1510+
encode_entry(E, TagGenericExpr, childIds);
15051511
return true;
15061512
}
15071513

c2rust-ast-exporter/src/ast_tags.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ enum ASTEntryTag {
8989

9090
TagAtomicExpr,
9191
TagTypeTraitExpr,
92+
TagGenericExpr,
9293

9394
TagIntegerLiteral = 300,
9495
TagStringLiteral,

c2rust-transpile/src/c_ast/conversion.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,6 +2001,19 @@ impl ConversionContext {
20012001
self.expr_possibly_as_stmt(expected_ty, new_id, node, e)
20022002
}
20032003

2004+
ASTEntryTag::TagGenericExpr if expected_ty & (EXPR | STMT) != 0 => {
2005+
let wrapped = node.children[0].expect("Expected generic expression");
2006+
let ty_old = node.type_id.expect("Expected expression to have type");
2007+
let ty = self.visit_qualified_type(ty_old);
2008+
2009+
// Use the existing `Paren` kind instead of adding a new one
2010+
// that would just be a no-op wrapper around the inner
2011+
// expression
2012+
let expr = CExprKind::Paren(ty, self.visit_expr(wrapped));
2013+
2014+
self.expr_possibly_as_stmt(expected_ty, new_id, node, expr);
2015+
}
2016+
20042017
// Declarations
20052018
ASTEntryTag::TagFunctionDecl if expected_ty & OTHER_DECL != 0 => {
20062019
let name = from_value::<String>(node.extras[0].clone())

c2rust-transpile/tests/snapshots.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,11 @@ fn test_fn_attrs() {
374374
transpile("fn_attrs.c").run();
375375
}
376376

377+
#[test]
378+
fn test_generics() {
379+
transpile("generics.c").run();
380+
}
381+
377382
#[test]
378383
fn test_gotos() {
379384
transpile("gotos.c").run();
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#define inc(x) (_Generic((x), \
2+
float: (x) + 1.0f, \
3+
double: (x) + 1.0, \
4+
default: (x) + 1))
5+
6+
struct s { int x; };
7+
struct t { int y; };
8+
#define cast(p) (_Generic((p), \
9+
const struct s *: (const struct t *)(p), \
10+
struct s *: (struct t *)(p)))
11+
12+
#define unqual_typeof(x) typeof(_Generic((x), \
13+
int: (int)0, const int: (int)0, default: (x)))
14+
15+
void foo() {
16+
int x = inc(42);
17+
double y = inc(42.0);
18+
float z = inc(42.0f);
19+
20+
int n = 0;
21+
_Generic((n++), int: n, default: n); // n must end up 0, not 1
22+
23+
// No default
24+
int m = 0;
25+
n = _Generic((m), int: m);
26+
27+
struct s s;
28+
struct s *p1 = &s;
29+
const struct s *p2 = &s;
30+
struct t *t1 = cast(p1);
31+
const struct t *t2 = cast(p2);
32+
33+
const int cx = n;
34+
unqual_typeof(x) ut_y = cx;
35+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
source: c2rust-transpile/tests/snapshots.rs
3+
expression: cat tests/snapshots/generics.2021.clang15.rs
4+
---
5+
#![allow(
6+
clippy::missing_safety_doc,
7+
dead_code,
8+
non_camel_case_types,
9+
non_snake_case,
10+
non_upper_case_globals,
11+
unused_assignments,
12+
unused_mut
13+
)]
14+
#![feature(raw_ref_op)]
15+
#[derive(Copy, Clone)]
16+
#[repr(C)]
17+
pub struct s {
18+
pub x: ::core::ffi::c_int,
19+
}
20+
#[derive(Copy, Clone)]
21+
#[repr(C)]
22+
pub struct t {
23+
pub y: ::core::ffi::c_int,
24+
}
25+
#[no_mangle]
26+
pub unsafe extern "C" fn foo() {
27+
let mut x: ::core::ffi::c_int = 42 as ::core::ffi::c_int + 1 as ::core::ffi::c_int;
28+
let mut y: ::core::ffi::c_double = 42.0f64 + 1.0f64;
29+
let mut z: ::core::ffi::c_float = 42.0f32 + 1.0f32;
30+
let mut n: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
31+
let mut m: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
32+
n = m;
33+
let mut s: s = s { x: 0 };
34+
let mut p1: *mut s = &raw mut s;
35+
let mut p2: *const s = &raw mut s;
36+
let mut t1: *mut t = p1 as *mut t;
37+
let mut t2: *const t = p2 as *const t;
38+
let cx: ::core::ffi::c_int = n;
39+
let mut ut_y: ::core::ffi::c_int = cx as ::core::ffi::c_int;
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
source: c2rust-transpile/tests/snapshots.rs
3+
expression: cat tests/snapshots/generics.2024.clang15.rs
4+
---
5+
#![allow(
6+
clippy::missing_safety_doc,
7+
dead_code,
8+
non_camel_case_types,
9+
non_snake_case,
10+
non_upper_case_globals,
11+
unsafe_op_in_unsafe_fn,
12+
unused_assignments,
13+
unused_mut
14+
)]
15+
#[derive(Copy, Clone)]
16+
#[repr(C)]
17+
pub struct s {
18+
pub x: ::core::ffi::c_int,
19+
}
20+
#[derive(Copy, Clone)]
21+
#[repr(C)]
22+
pub struct t {
23+
pub y: ::core::ffi::c_int,
24+
}
25+
#[unsafe(no_mangle)]
26+
pub unsafe extern "C" fn foo() {
27+
let mut x: ::core::ffi::c_int = 42 as ::core::ffi::c_int + 1 as ::core::ffi::c_int;
28+
let mut y: ::core::ffi::c_double = 42.0f64 + 1.0f64;
29+
let mut z: ::core::ffi::c_float = 42.0f32 + 1.0f32;
30+
let mut n: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
31+
let mut m: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
32+
n = m;
33+
let mut s: s = s { x: 0 };
34+
let mut p1: *mut s = &raw mut s;
35+
let mut p2: *const s = &raw mut s;
36+
let mut t1: *mut t = p1 as *mut t;
37+
let mut t2: *const t = p2 as *const t;
38+
let cx: ::core::ffi::c_int = n;
39+
let mut ut_y: ::core::ffi::c_int = cx as ::core::ffi::c_int;
40+
}

0 commit comments

Comments
 (0)