From 2b505f0966de5fa7b8e10ece84a489c198da5b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 4 Feb 2021 16:49:57 +0200 Subject: [PATCH] Don't derive Copy in FFI structs/unions that contain pointers Clone is still derived to as explicitly cloning the structs is less of a problem, but implicitly copying them can easily cause double frees. Fixes https://github.com/gtk-rs/gir/issues/1048 --- src/analysis/types.rs | 89 ++++++++++++++++++++++++++++++++++++++- src/codegen/sys/fields.rs | 9 ++++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/src/analysis/types.rs b/src/analysis/types.rs index b10eafd54..9a9518a1b 100644 --- a/src/analysis/types.rs +++ b/src/analysis/types.rs @@ -233,10 +233,95 @@ pub trait DerivesCopy { fn derives_copy(&self, lib: &Library) -> bool; } -impl DerivesCopy for T { +impl DerivesCopy for Fundamental { fn derives_copy(&self, lib: &Library) -> bool { - // Copy is derived for all complete types. !self.is_incomplete(lib) + && !matches!( + self, + Fundamental::Pointer + | Fundamental::VarArgs + | Fundamental::Utf8 + | Fundamental::Filename + | Fundamental::IntPtr + | Fundamental::UIntPtr + | Fundamental::OsString + | Fundamental::Unsupported + ) + } +} + +impl DerivesCopy for Alias { + fn derives_copy(&self, lib: &Library) -> bool { + !self.is_incomplete(lib) && !self.is_ptr() + } +} + +impl DerivesCopy for Field { + fn derives_copy(&self, lib: &Library) -> bool { + !self.is_incomplete(lib) && !self.is_ptr() + } +} + +impl<'a> DerivesCopy for &'a [Field] { + fn derives_copy(&self, lib: &Library) -> bool { + !self.is_incomplete(lib) && !self.iter().any(|field| field.is_ptr()) + } +} + +impl DerivesCopy for Class { + fn derives_copy(&self, lib: &Library) -> bool { + !self.is_incomplete(lib) && self.fields.as_slice().derives_copy(lib) + } +} + +impl DerivesCopy for Record { + #[allow(clippy::if_same_then_else)] + fn derives_copy(&self, lib: &Library) -> bool { + !self.is_incomplete(lib) && self.fields.as_slice().derives_copy(lib) + } +} + +impl DerivesCopy for Union { + fn derives_copy(&self, lib: &Library) -> bool { + !self.is_incomplete(lib) && self.fields.as_slice().derives_copy(lib) + } +} + +impl DerivesCopy for Function { + fn derives_copy(&self, lib: &Library) -> bool { + !self.is_incomplete(lib) + } +} + +impl DerivesCopy for TypeId { + fn derives_copy(&self, lib: &Library) -> bool { + !self.is_incomplete(lib) && lib.type_(*self).derives_copy(lib) + } +} + +impl DerivesCopy for Type { + fn derives_copy(&self, lib: &Library) -> bool { + if self.is_incomplete(lib) { + return false; + } + + match *self { + Type::Fundamental(ref fundamental) => fundamental.derives_copy(lib), + Type::Alias(ref alias) => alias.derives_copy(lib), + Type::FixedArray(tid, ..) => tid.derives_copy(lib), + Type::Class(ref klass) => klass.derives_copy(lib), + Type::Record(ref record) => record.derives_copy(lib), + Type::Union(ref union) => union.derives_copy(lib), + Type::Function(ref function) => function.derives_copy(lib), + Type::Enumeration(..) | Type::Bitfield(..) | Type::Interface(..) => true, + Type::Custom(..) + | Type::Array(..) + | Type::CArray(..) + | Type::PtrArray(..) + | Type::HashTable(..) + | Type::List(..) + | Type::SList(..) => false, + } } } diff --git a/src/codegen/sys/fields.rs b/src/codegen/sys/fields.rs index fd8cb2e05..5fbcd8769 100644 --- a/src/codegen/sys/fields.rs +++ b/src/codegen/sys/fields.rs @@ -13,6 +13,7 @@ pub struct Fields { pub external: bool, /// Reason for truncating the representation, if any. pub truncated: Option, + derives_clone: bool, derives_copy: bool, /// "struct" or "union" pub kind: &'static str, @@ -38,6 +39,8 @@ impl Fields { let mut traits = Vec::new(); if self.derives_copy { traits.push("Copy"); + } + if self.derives_clone { traits.push("Clone"); } traits @@ -58,11 +61,13 @@ impl FieldInfo { pub fn from_record(env: &Env, record: &Record) -> Fields { let (fields, truncated) = analyze_fields(env, false, &record.fields); let derives_copy = truncated.is_none() && record.derives_copy(&env.library); + let derives_clone = truncated.is_none() && !record.is_incomplete(&env.library); Fields { name: record.c_type.clone(), external: record.is_external(&env.library), truncated, derives_copy, + derives_clone, kind: "struct", cfg_condition: get_gobject_cfg_condition(env, &record.name), fields, @@ -72,11 +77,13 @@ pub fn from_record(env: &Env, record: &Record) -> Fields { pub fn from_class(env: &Env, klass: &Class) -> Fields { let (fields, truncated) = analyze_fields(env, false, &klass.fields); let derives_copy = truncated.is_none() && klass.derives_copy(&env.library); + let derives_clone = truncated.is_none() && !klass.is_incomplete(&env.library); Fields { name: klass.c_type.clone(), external: klass.is_external(&env.library), truncated, derives_copy, + derives_clone, kind: "struct", cfg_condition: get_gobject_cfg_condition(env, &klass.name), fields, @@ -86,11 +93,13 @@ pub fn from_class(env: &Env, klass: &Class) -> Fields { pub fn from_union(env: &Env, union: &Union) -> Fields { let (fields, truncated) = analyze_fields(env, true, &union.fields); let derives_copy = truncated.is_none() && union.derives_copy(&env.library); + let derives_clone = truncated.is_none() && !union.is_incomplete(&env.library); Fields { name: union.c_type.as_ref().unwrap().clone(), external: union.is_external(&env.library), truncated, derives_copy, + derives_clone, kind: "union", cfg_condition: None, fields,