Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Const generics: handle bool and char literals #760

Merged
merged 4 commits into from May 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/bindgen/cdecl.rs
Expand Up @@ -5,7 +5,7 @@
use std::io::Write;

use crate::bindgen::declarationtyperesolver::DeclarationType;
use crate::bindgen::ir::{ArrayLength, Function, GenericArgument, Type};
use crate::bindgen::ir::{ConstExpr, Function, GenericArgument, Type};
use crate::bindgen::writer::{ListType, SourceWriter};
use crate::bindgen::{Config, Language};

Expand Down Expand Up @@ -70,7 +70,7 @@ impl CDecl {
t
),
};
let ptr_as_array = Type::Array(ty.clone(), ArrayLength::Value(length.to_string()));
let ptr_as_array = Type::Array(ty.clone(), ConstExpr::Value(length.to_string()));
cdecl.build_type(&ptr_as_array, *is_const, config);
cdecl
}
Expand Down
6 changes: 3 additions & 3 deletions src/bindgen/ir/generic_path.rs
Expand Up @@ -6,7 +6,7 @@ use syn::ext::IdentExt;
use crate::bindgen::cdecl;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::declarationtyperesolver::{DeclarationType, DeclarationTypeResolver};
use crate::bindgen::ir::{ArrayLength, Path, Type};
use crate::bindgen::ir::{ConstExpr, Path, Type};
use crate::bindgen::utilities::IterHelpers;
use crate::bindgen::writer::{Source, SourceWriter};

Expand Down Expand Up @@ -153,7 +153,7 @@ impl Source for GenericParams {
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum GenericArgument {
Type(Type),
Const(ArrayLength),
Const(ConstExpr),
}

impl GenericArgument {
Expand Down Expand Up @@ -275,7 +275,7 @@ impl GenericPath {
syn::GenericArgument::Type(ref x) => Ok(Type::load(x)?.map(GenericArgument::Type)),
syn::GenericArgument::Lifetime(_) => Ok(None),
syn::GenericArgument::Const(ref x) => {
Ok(Some(GenericArgument::Const(ArrayLength::load(x)?)))
Ok(Some(GenericArgument::Const(ConstExpr::load(x)?)))
}
_ => Err(format!("can't handle generic argument {:?}", x)),
})?,
Expand Down
43 changes: 26 additions & 17 deletions src/bindgen/ir/ty.rs
Expand Up @@ -311,43 +311,52 @@ impl PrimitiveType {
}
}

// The `U` part of `[T; U]`
/// Constant expressions.
///
/// Used for the `U` part of `[T; U]` and const generics. We support a very
/// limited vocabulary here: only identifiers and literals.
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ArrayLength {
pub enum ConstExpr {
Name(String),
Value(String),
}

impl ArrayLength {
impl ConstExpr {
pub fn as_str(&self) -> &str {
match *self {
ArrayLength::Name(ref string) | ArrayLength::Value(ref string) => string,
ConstExpr::Name(ref string) | ConstExpr::Value(ref string) => string,
}
}

pub fn rename_for_config(&mut self, config: &Config) {
if let ArrayLength::Name(ref mut name) = self {
if let ConstExpr::Name(ref mut name) = self {
config.export.rename(name);
}
}

pub fn load(expr: &syn::Expr) -> Result<Self, String> {
match *expr {
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Int(ref len),
..
}) => Ok(ArrayLength::Value(len.base10_digits().to_string())),
syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
let val = match *lit {
syn::Lit::Bool(syn::LitBool { value, .. }) => value.to_string(),
syn::Lit::Int(ref len) => len.base10_digits().to_string(),
syn::Lit::Byte(ref byte) => u8::to_string(&byte.value()),
syn::Lit::Char(ref ch) => u32::to_string(&ch.value().into()),
_ => return Err(format!("can't handle const expression {:?}", lit)),
};
Ok(ConstExpr::Value(val))
}
syn::Expr::Path(ref path) => {
let generic_path = GenericPath::load(&path.path)?;
Ok(ArrayLength::Name(generic_path.export_name().to_owned()))
Ok(ConstExpr::Name(generic_path.export_name().to_owned()))
}
_ => Err(format!("can't handle const expression {:?}", expr)),
}
}

pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> ArrayLength {
pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> ConstExpr {
match *self {
ArrayLength::Name(ref name) => {
ConstExpr::Name(ref name) => {
let path = Path::new(name);
for &(param, value) in mappings {
if path == *param {
Expand All @@ -356,7 +365,7 @@ impl ArrayLength {
if path.is_single_identifier() =>
{
// This happens when the generic argument is a path.
return ArrayLength::Name(path.name().to_string());
return ConstExpr::Name(path.name().to_string());
}
GenericArgument::Const(ref expr) => {
return expr.clone();
Expand All @@ -368,13 +377,13 @@ impl ArrayLength {
}
}
}
ArrayLength::Value(_) => {}
ConstExpr::Value(_) => {}
}
self.clone()
}
}

impl Source for ArrayLength {
impl Source for ConstExpr {
fn write<F: Write>(&self, _config: &Config, out: &mut SourceWriter<F>) {
write!(out, "{}", self.as_str());
}
Expand All @@ -393,7 +402,7 @@ pub enum Type {
},
Path(GenericPath),
Primitive(PrimitiveType),
Array(Box<Type>, ArrayLength),
Array(Box<Type>, ConstExpr),
FuncPtr {
ret: Box<Type>,
args: Vec<(Option<String>, Type)>,
Expand Down Expand Up @@ -472,7 +481,7 @@ impl Type {
None => return Err("Cannot have an array of zero sized types.".to_owned()),
};

let len = ArrayLength::load(len)?;
let len = ConstExpr::load(len)?;
Type::Array(Box::new(converted), len)
}
syn::Type::BareFn(ref function) => {
Expand Down
12 changes: 6 additions & 6 deletions src/bindgen/mangle.rs
Expand Up @@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::bindgen::config::MangleConfig;
use crate::bindgen::ir::{ArrayLength, GenericArgument, GenericPath, Path, Type};
use crate::bindgen::ir::{ConstExpr, GenericArgument, GenericPath, Path, Type};
use crate::bindgen::rename::IdentifierType;

pub fn mangle_path(path: &Path, generic_values: &[GenericArgument], config: &MangleConfig) -> Path {
Expand Down Expand Up @@ -71,14 +71,14 @@ impl<'a> Mangler<'a> {
fn append_mangled_argument(&mut self, arg: &GenericArgument, last: bool) {
match *arg {
GenericArgument::Type(ref ty) => self.append_mangled_type(ty, last),
GenericArgument::Const(ArrayLength::Name(ref name)) => {
GenericArgument::Const(ConstExpr::Name(ref name)) => {
// This must behave the same as a GenericArgument::Type,
// because const arguments are commonly represented as Types;
// see the comment on `enum GenericArgument`.
let fake_ty = Type::Path(GenericPath::new(Path::new(name), vec![]));
self.append_mangled_type(&fake_ty, last);
}
GenericArgument::Const(ArrayLength::Value(ref val)) => self.output.push_str(val),
GenericArgument::Const(ConstExpr::Value(ref val)) => self.output.push_str(val),
}
}

Expand Down Expand Up @@ -310,7 +310,7 @@ fn generics() {
assert_eq!(
mangle_path(
&Path::new("Top"),
&[GenericArgument::Const(ArrayLength::Value("40".to_string()))],
&[GenericArgument::Const(ConstExpr::Value("40".to_string()))],
&MangleConfig::default(),
),
Path::new("Top_40")
Expand All @@ -319,7 +319,7 @@ fn generics() {
assert_eq!(
mangle_path(
&Path::new("Top"),
&[GenericArgument::Const(ArrayLength::Name("N".to_string()))],
&[GenericArgument::Const(ConstExpr::Name("N".to_string()))],
&MangleConfig::default(),
),
Path::new("Top_N")
Expand All @@ -339,7 +339,7 @@ fn generics() {
&Path::new("Foo"),
&[
float(),
GenericArgument::Const(ArrayLength::Value("40".to_string()))
GenericArgument::Const(ConstExpr::Value("40".to_string()))
],
&MangleConfig::default(),
),
Expand Down
36 changes: 36 additions & 0 deletions tests/expectations/const_generics_bool.both.c
@@ -0,0 +1,36 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef const char *Str;

typedef struct HashTable_Str__c_char__false {
uintptr_t num_buckets;
uintptr_t capacity;
uint8_t *occupied;
Str *keys;
char *vals;
} HashTable_Str__c_char__false;

typedef struct HashTable_Str__c_char__false MySet;

typedef void (*SetCallback)(Str key);

typedef struct HashTable_Str__u64__true {
uintptr_t num_buckets;
uintptr_t capacity;
uint8_t *occupied;
Str *keys;
uint64_t *vals;
} HashTable_Str__u64__true;

typedef void (*MapCallback)(Str key, uint64_t val);

MySet *new_set(void);

void set_for_each(const MySet *set, SetCallback callback);

struct HashTable_Str__u64__true *new_map(void);

void map_for_each(const struct HashTable_Str__u64__true *map, MapCallback callback);
44 changes: 44 additions & 0 deletions tests/expectations/const_generics_bool.both.compat.c
@@ -0,0 +1,44 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef const char *Str;

typedef struct HashTable_Str__c_char__false {
uintptr_t num_buckets;
uintptr_t capacity;
uint8_t *occupied;
Str *keys;
char *vals;
} HashTable_Str__c_char__false;

typedef struct HashTable_Str__c_char__false MySet;

typedef void (*SetCallback)(Str key);

typedef struct HashTable_Str__u64__true {
uintptr_t num_buckets;
uintptr_t capacity;
uint8_t *occupied;
Str *keys;
uint64_t *vals;
} HashTable_Str__u64__true;

typedef void (*MapCallback)(Str key, uint64_t val);

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

MySet *new_set(void);

void set_for_each(const MySet *set, SetCallback callback);

struct HashTable_Str__u64__true *new_map(void);

void map_for_each(const struct HashTable_Str__u64__true *map, MapCallback callback);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
36 changes: 36 additions & 0 deletions tests/expectations/const_generics_bool.c
@@ -0,0 +1,36 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef const char *Str;

typedef struct {
uintptr_t num_buckets;
uintptr_t capacity;
uint8_t *occupied;
Str *keys;
char *vals;
} HashTable_Str__c_char__false;

typedef HashTable_Str__c_char__false MySet;

typedef void (*SetCallback)(Str key);

typedef struct {
uintptr_t num_buckets;
uintptr_t capacity;
uint8_t *occupied;
Str *keys;
uint64_t *vals;
} HashTable_Str__u64__true;

typedef void (*MapCallback)(Str key, uint64_t val);

MySet *new_set(void);

void set_for_each(const MySet *set, SetCallback callback);

HashTable_Str__u64__true *new_map(void);

void map_for_each(const HashTable_Str__u64__true *map, MapCallback callback);
44 changes: 44 additions & 0 deletions tests/expectations/const_generics_bool.compat.c
@@ -0,0 +1,44 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef const char *Str;

typedef struct {
uintptr_t num_buckets;
uintptr_t capacity;
uint8_t *occupied;
Str *keys;
char *vals;
} HashTable_Str__c_char__false;

typedef HashTable_Str__c_char__false MySet;

typedef void (*SetCallback)(Str key);

typedef struct {
uintptr_t num_buckets;
uintptr_t capacity;
uint8_t *occupied;
Str *keys;
uint64_t *vals;
} HashTable_Str__u64__true;

typedef void (*MapCallback)(Str key, uint64_t val);

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

MySet *new_set(void);

void set_for_each(const MySet *set, SetCallback callback);

HashTable_Str__u64__true *new_map(void);

void map_for_each(const HashTable_Str__u64__true *map, MapCallback callback);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus