Skip to content

Commit 8e1ac09

Browse files
committed
Initial Dlang support
closes: #866 * enable c++ compat * fix struct/enum/union (semicolon) * fix casts * C++ namespace support * typedefs to alias * attributes added (`@nogc`, `nothrow`, `@safe`) scope * D file on tests enabled * D generic instaciation (`Foo!(T, U)`, similar to `Foo<T, U>`) * disable enum typedef (like C++) * add struct-literals * build all tests/expectations
1 parent b9b8f88 commit 8e1ac09

File tree

159 files changed

+4238
-53
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

159 files changed

+4238
-53
lines changed

src/bindgen/bindings.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ impl Bindings {
204204

205205
pub fn write<F: Write>(&self, file: F) {
206206
match self.config.language {
207-
Language::Cxx | Language::C => {
207+
Language::Cxx | Language::C | Language::D => {
208208
self.write_with_backend(file, &mut CLikeLanguageBackend::new(&self.config))
209209
}
210210
Language::Cython => {

src/bindgen/cdecl.rs

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,11 @@ impl CDecl {
147147
"error generating cdecl for {:?}",
148148
t
149149
);
150-
self.type_name = p.to_repr_c(config).to_string();
150+
if config.language == Language::D {
151+
self.type_name = p.to_repr_d(config).to_string();
152+
} else {
153+
self.type_name = p.to_repr_c(config).to_string();
154+
}
151155
}
152156
Type::Ptr {
153157
ref ty,
@@ -213,7 +217,11 @@ impl CDecl {
213217
write!(out, "{}", self.type_name);
214218

215219
if !self.type_generic_args.is_empty() {
216-
out.write("<");
220+
if config.language == Language::D {
221+
out.write("!(");
222+
} else {
223+
out.write("<");
224+
}
217225
out.write_horizontal_source_list(
218226
language_backend,
219227
&self.type_generic_args,
@@ -223,7 +231,11 @@ impl CDecl {
223231
GenericArgument::Const(ref expr) => write!(out, "{}", expr.as_str()),
224232
},
225233
);
226-
out.write(">");
234+
if config.language == Language::D {
235+
out.write(")");
236+
} else {
237+
out.write(">");
238+
}
227239
}
228240

229241
// When we have an identifier, put a space between the type and the declarators
@@ -233,6 +245,7 @@ impl CDecl {
233245

234246
// Write the left part of declarators before the identifier
235247
let mut iter_rev = self.declarators.iter().rev().peekable();
248+
let mut is_functors = false;
236249

237250
#[allow(clippy::while_let_on_iterator)]
238251
while let Some(declarator) = iter_rev.next() {
@@ -244,7 +257,17 @@ impl CDecl {
244257
is_nullable,
245258
is_ref,
246259
} => {
247-
out.write(if is_ref { "&" } else { "*" });
260+
if config.language == Language::D {
261+
// out.write(if is_ref { "ref " } else { "*" });
262+
if is_ref {
263+
out.write("ref ");
264+
} else if is_functors {
265+
} else {
266+
out.write("*");
267+
}
268+
} else {
269+
out.write(if is_ref { "&" } else { "*" });
270+
}
248271
if is_const {
249272
out.write("const ");
250273
}
@@ -254,22 +277,34 @@ impl CDecl {
254277
}
255278
}
256279
}
257-
CDeclarator::Array(..) => {
280+
CDeclarator::Array(ref constant) => {
258281
if next_is_pointer {
259282
out.write("(");
260283
}
284+
if config.language == Language::D {
285+
write!(out, "[{}] ", constant);
286+
}
261287
}
262288
CDeclarator::Func { .. } => {
263289
if next_is_pointer {
264-
out.write("(");
290+
if config.language == Language::D {
291+
out.write(" function");
292+
is_functors = true;
293+
} else {
294+
out.write("(");
295+
}
265296
}
266297
}
267298
}
268299
}
269300

270301
// Write the identifier
271302
if let Some(ident) = ident {
272-
write!(out, "{}", ident);
303+
if is_functors {
304+
// out.write(" ");
305+
} else {
306+
write!(out, "{}", ident);
307+
}
273308
}
274309

275310
// Write the right part of declarators after the identifier
@@ -286,7 +321,9 @@ impl CDecl {
286321
if last_was_pointer {
287322
out.write(")");
288323
}
289-
write!(out, "[{}]", constant);
324+
if config.language != Language::D {
325+
write!(out, "[{}]", constant);
326+
}
290327

291328
last_was_pointer = false;
292329
}
@@ -296,7 +333,9 @@ impl CDecl {
296333
never_return,
297334
} => {
298335
if last_was_pointer {
299-
out.write(")");
336+
if config.language != Language::D {
337+
out.write(")");
338+
}
300339
}
301340

302341
out.write("(");
@@ -363,6 +402,12 @@ impl CDecl {
363402
out.write_fmt(format_args!(" {}", no_return_attr));
364403
}
365404
}
405+
if config.language == Language::D && is_functors {
406+
if let Some(ident) = ident {
407+
write!(out, " {}", ident);
408+
}
409+
}
410+
is_functors = false;
366411

367412
last_was_pointer = true;
368413
}

src/bindgen/config.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub enum Language {
2323
Cxx,
2424
C,
2525
Cython,
26+
D,
2627
}
2728

2829
impl FromStr for Language {
@@ -42,6 +43,9 @@ impl FromStr for Language {
4243
"C" => Ok(Language::C),
4344
"cython" => Ok(Language::Cython),
4445
"Cython" => Ok(Language::Cython),
46+
"d" => Ok(Language::D),
47+
"dlang" => Ok(Language::D),
48+
"D" => Ok(Language::D),
4549
_ => Err(format!("Unrecognized Language: '{}'.", s)),
4650
}
4751
}
@@ -54,6 +58,7 @@ impl Language {
5458
match self {
5559
Language::Cxx | Language::C => "typedef",
5660
Language::Cython => "ctypedef",
61+
Language::D => "alias",
5762
}
5863
}
5964
}
@@ -166,6 +171,7 @@ pub enum DocumentationStyle {
166171
C99,
167172
Doxy,
168173
Cxx,
174+
D,
169175
Auto,
170176
}
171177

@@ -179,6 +185,7 @@ impl FromStr for DocumentationStyle {
179185
"cxx" => Ok(DocumentationStyle::Cxx),
180186
"c++" => Ok(DocumentationStyle::Cxx),
181187
"doxy" => Ok(DocumentationStyle::Doxy),
188+
"dlang" => Ok(DocumentationStyle::D),
182189
"auto" => Ok(DocumentationStyle::Auto),
183190
_ => Err(format!("Unrecognized documentation style: '{}'.", s)),
184191
}

src/bindgen/ir/constant.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,7 @@ impl Constant {
615615
) {
616616
debug_assert!(self.associated_to.is_some());
617617
debug_assert!(config.language == Language::Cxx);
618+
debug_assert!(config.language == Language::D);
618619
debug_assert!(!associated_to_struct.is_transparent);
619620
debug_assert!(config.structure.associated_constants_in_body);
620621
debug_assert!(config.constant.allow_static_const);
@@ -721,6 +722,11 @@ impl Constant {
721722
write!(out, " {} # = ", name);
722723
language_backend.write_literal(out, value);
723724
}
725+
Language::D => {
726+
write!(out, "enum {} = ", name);
727+
language_backend.write_literal(out, value);
728+
out.write(";");
729+
}
724730
}
725731

726732
condition.write_after(config, out);

src/bindgen/ir/enumeration.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,27 @@ impl Enum {
732732
write!(out, "{}enum {}", config.style.cython_def(), tag_name);
733733
}
734734
}
735+
Language::D => {
736+
out.write("enum");
737+
738+
if self.annotations.must_use(config) {
739+
if let Some(ref anno) = config.enumeration.must_use {
740+
write!(out, " {}", anno)
741+
}
742+
}
743+
744+
if let Some(note) = self
745+
.annotations
746+
.deprecated_note(config, DeprecatedNoteKind::Enum)
747+
{
748+
write!(out, " {}", note);
749+
}
750+
751+
write!(out, " {}", tag_name);
752+
if let Some(prim) = size {
753+
write!(out, " : {}", prim);
754+
}
755+
}
735756
}
736757
out.open_brace();
737758

@@ -748,7 +769,11 @@ impl Enum {
748769
out.close_brace(false);
749770
write!(out, " {};", tag_name);
750771
} else {
751-
out.close_brace(true);
772+
if config.language != Language::D {
773+
out.close_brace(true);
774+
} else {
775+
out.close_brace(false);
776+
}
752777
}
753778

754779
// Emit typedef specifying the tag enum's size if necessary.
@@ -760,7 +785,7 @@ impl Enum {
760785
out.write("#ifndef __cplusplus");
761786
}
762787

763-
if config.language != Language::Cxx {
788+
if config.language != Language::Cxx && config.language != Language::D {
764789
out.new_line();
765790
write!(out, "{} {} {};", config.language.typedef(), prim, tag_name);
766791
}
@@ -784,7 +809,7 @@ impl Enum {
784809
) {
785810
match config.language {
786811
Language::C if config.style.generate_typedef() => out.write("typedef "),
787-
Language::C | Language::Cxx => {}
812+
Language::C | Language::Cxx | Language::D => {}
788813
Language::Cython => out.write(config.style.cython_def()),
789814
}
790815

src/bindgen/ir/generic_path.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,15 +145,23 @@ impl GenericParams {
145145
out: &mut SourceWriter<F>,
146146
with_default: bool,
147147
) {
148-
if !self.0.is_empty() && config.language == Language::Cxx {
149-
out.write("template<");
148+
if (!self.0.is_empty() && config.language == Language::Cxx) || (!self.0.is_empty() && config.language == Language::D) {
149+
out.write(if config.language == Language::D {
150+
"("
151+
} else {
152+
"template<"
153+
});
150154
for (i, item) in self.0.iter().enumerate() {
151155
if i != 0 {
152156
out.write(", ");
153157
}
154158
match item.ty {
155159
GenericParamType::Type => {
156-
write!(out, "typename {}", item.name);
160+
if config.language == Language::D {
161+
write!(out, "{}", item.name);
162+
} else {
163+
write!(out, "typename {}", item.name);
164+
}
157165
if let Some(GenericArgument::Type(ref ty)) = item.default {
158166
write!(out, " = ");
159167
cdecl::write_type(language_backend, out, ty, config);
@@ -171,8 +179,14 @@ impl GenericParams {
171179
}
172180
}
173181
}
174-
out.write(">");
175-
out.new_line();
182+
out.write(if config.language == Language::D {
183+
")"
184+
} else {
185+
">"
186+
});
187+
if config.language != Language::D {
188+
out.new_line();
189+
}
176190
}
177191
}
178192

src/bindgen/ir/ty.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,57 @@ impl PrimitiveType {
222222
}
223223
}
224224

225+
pub fn to_repr_d(&self, config: &Config) -> &'static str {
226+
match *self {
227+
PrimitiveType::Void => "void",
228+
PrimitiveType::Bool => "bool",
229+
PrimitiveType::Char => "char",
230+
PrimitiveType::SChar => "byte",
231+
PrimitiveType::UChar => "ubyte",
232+
// NOTE: It'd be nice to use a char32_t, but:
233+
//
234+
// * uchar.h is not present on mac (see #423).
235+
//
236+
// * char32_t isn't required to be compatible with Rust's char, as
237+
// the C++ spec only requires it to be the same size as
238+
// uint_least32_t, which is _not_ guaranteed to be 4-bytes.
239+
//
240+
PrimitiveType::Char32 => "uint",
241+
PrimitiveType::Integer {
242+
kind,
243+
signed,
244+
zeroable: _,
245+
} => match (kind, signed) {
246+
(IntKind::Short, true) => "short",
247+
(IntKind::Short, false) => "ushort",
248+
(IntKind::Int, true) => "int",
249+
(IntKind::Int, false) => "uint",
250+
(IntKind::Long, true) => "long",
251+
(IntKind::Long, false) => "ulong",
252+
(IntKind::LongLong, true) => "long long",
253+
(IntKind::LongLong, false) => "ulong long",
254+
(IntKind::SizeT, true) => "long",
255+
(IntKind::SizeT, false) => "ulong",
256+
(IntKind::Size, true) if config.usize_is_size_t => "long",
257+
(IntKind::Size, false) if config.usize_is_size_t => "ulong",
258+
(IntKind::Size, true) => "long",
259+
(IntKind::Size, false) => "ulong",
260+
(IntKind::B8, true) => "byte",
261+
(IntKind::B8, false) => "ubyte",
262+
(IntKind::B16, true) => "short",
263+
(IntKind::B16, false) => "ushort",
264+
(IntKind::B32, true) => "int",
265+
(IntKind::B32, false) => "uint",
266+
(IntKind::B64, true) => "long",
267+
(IntKind::B64, false) => "ulong",
268+
},
269+
PrimitiveType::Float => "float",
270+
PrimitiveType::Double => "double",
271+
PrimitiveType::PtrDiffT => "long",
272+
PrimitiveType::VaList => "...",
273+
}
274+
}
275+
225276
fn can_cmp_order(&self) -> bool {
226277
!matches!(*self, PrimitiveType::Bool)
227278
}

0 commit comments

Comments
 (0)