use std::{cmp::Ordering, fmt,mem};
#[allow(unused_imports)]
use core_extensions::{prelude::*,matches};
use std::{
borrow::Borrow,
cell::Cell,
collections::hash_map::{HashMap,Entry},
};
use crate::{
abi_stability::{
extra_checks::{
ExtraChecksBox,ExtraChecksRef,
TypeChecker,TypeCheckerMut,
ExtraChecksError,
},
ConstGeneric,
},
sabi_types::{ParseVersionError, VersionStrings},
prefix_type::{FieldAccessibility,FieldConditionality},
sabi_types::CmpIgnored,
std_types::{
RArc,RBox,RStr,RVec,UTypeId,RBoxError,RResult,
RSome,RNone,ROk,RErr,
},
traits::IntoReprC,
type_layout::{
TypeLayout, TLData, TLDataDiscriminant, TLField,
FmtFullType, ReprAttr, TLDiscriminant,TLPrimitive,
TLEnum,IsExhaustive,IncompatibleWithNonExhaustive,TLNonExhaustive,
TLFieldOrFunction, TLFunction,
tagging::TagErrors,
},
type_level::unerasability::TU_Opaque,
utils::{max_by,min_max_by},
};
mod errors;
pub use self::errors::{
AbiInstability,
AbiInstability as AI,
ExtraCheckError,
AbiInstabilityErrors,
AbiInstabilityError,
};
#[derive(Debug,Copy,Clone, PartialEq,Eq,Ord,PartialOrd,Hash)]
#[repr(C)]
enum FieldContext{
Fields,
Subfields,
PhantomFields,
}
#[derive(Debug,Copy,Clone,PartialEq,Eq,Ord,PartialOrd,Hash)]
#[repr(C)]
struct CheckingUTypeId{
type_id:UTypeId,
}
impl CheckingUTypeId{
fn new(this: &'static TypeLayout)->Self{
Self{
type_id:this.get_utypeid(),
}
}
}
#[derive(Debug, PartialEq,Eq,Ord,PartialOrd,Hash)]
pub enum CheckingState{
Checking{layer:u32},
Compatible,
Error,
}
#[derive(Debug, PartialEq,Clone)]
#[repr(C)]
pub struct ExpectedFound<T> {
pub expected: T,
pub found: T,
}
impl<T> ExpectedFound<T> {
pub fn new<O, F>(this: O, other: O, mut field_getter: F) -> ExpectedFound<T>
where
F: FnMut(O) -> T,
{
ExpectedFound {
expected: field_getter(this),
found: field_getter(other),
}
}
pub fn as_ref(&self) -> ExpectedFound<&T> {
ExpectedFound {
expected: &self.expected,
found: &self.found,
}
}
pub fn map<F, U>(self, mut f: F) -> ExpectedFound<U>
where
F: FnMut(T) -> U,
{
ExpectedFound {
expected: f(self.expected),
found: f(self.found),
}
}
pub fn display_str(&self) -> Option<ExpectedFound<String>>
where
T: fmt::Display,
{
Some(self.as_ref().map(|x| format!("{:#}", x)))
}
pub fn debug_str(&self) -> Option<ExpectedFound<String>>
where
T: fmt::Debug,
{
Some(self.as_ref().map(|x| format!("{:#?}", x)))
}
}
#[derive(Debug)]
#[repr(C)]
pub struct CheckedPrefixTypes{
this:&'static TypeLayout,
this_prefix:__PrefixTypeMetadata,
other:&'static TypeLayout,
other_prefix:__PrefixTypeMetadata,
}
#[derive(Debug,Copy,Clone)]
#[repr(C)]
pub struct NonExhaustiveEnumWithContext{
layout:&'static TypeLayout,
enum_:TLEnum,
nonexhaustive:&'static TLNonExhaustive,
}
#[derive(Debug,Clone)]
#[repr(C)]
pub struct ExtraChecksBoxWithContext{
t_lay:&'static TypeLayout,
o_lay:&'static TypeLayout,
extra_checks:ExtraChecksBox,
}
#[derive(Debug,Copy,Clone)]
#[repr(C)]
pub struct CheckedNonExhaustiveEnums{
this:NonExhaustiveEnumWithContext,
other:NonExhaustiveEnumWithContext,
}
struct AbiChecker {
stack_trace: RVec<ExpectedFound<TLFieldOrFunction>>,
checked_prefix_types: RVec<CheckedPrefixTypes>,
checked_nonexhaustive_enums: RVec<CheckedNonExhaustiveEnums>,
checked_extra_checks: RVec<ExtraChecksBoxWithContext>,
visited: HashMap<(CheckingUTypeId,CheckingUTypeId),CheckingState>,
errors: RVec<AbiInstabilityError>,
current_layer:u32,
error_index: usize,
}
impl AbiChecker {
fn new() -> Self {
Self {
stack_trace: RVec::new(),
checked_prefix_types:RVec::new(),
checked_nonexhaustive_enums:RVec::new(),
checked_extra_checks:RVec::new(),
visited: HashMap::default(),
errors: RVec::new(),
current_layer: 0,
error_index: 0,
}
}
#[inline]
fn check_fields<I,F>(
&mut self,
errs: &mut RVec<AbiInstability>,
t_lay: &'static TypeLayout,
o_lay: &'static TypeLayout,
ctx:FieldContext,
t_fields: I,
o_fields: I,
)
where
I:ExactSizeIterator<Item=F>,
F:Borrow<TLField>,
{
if t_fields.len()==0&&o_fields.len()==0 {
return;
}
let t_data=t_lay.data();
let is_prefix= match &t_data {
TLData::PrefixType{..}=>true,
TLData::Enum(enum_)=>!enum_.exhaustiveness.is_exhaustive(),
_=>false,
};
match (t_fields.len().cmp(&o_fields.len()), is_prefix) {
(Ordering::Greater, _) | (Ordering::Less, false) => {
push_err(
errs,
&t_fields,
&o_fields,
|x| x.len(),
AI::FieldCountMismatch,
);
}
(Ordering::Equal, _) | (Ordering::Less, true) => {}
}
let acc_fields:Option<(FieldAccessibility,FieldAccessibility)>=
match (&t_data,&o_lay.data()) {
(TLData::PrefixType(t_prefix), TLData::PrefixType(o_prefix))=>
Some((t_prefix.accessible_fields, o_prefix.accessible_fields)),
_=>None,
};
for (field_i,(this_f,other_f)) in t_fields.zip(o_fields).enumerate() {
let this_f=this_f.borrow();
let other_f=other_f.borrow();
if this_f.name() != other_f.name() {
push_err(errs, this_f, other_f, |x| *x, AI::UnexpectedField);
continue;
}
let t_field_abi=this_f.layout();
let o_field_abi=other_f.layout();
let is_accessible=match (ctx,acc_fields) {
(FieldContext::Fields,Some((l,r))) => {
l.is_accessible(field_i)&&r.is_accessible(field_i)
},
_ => true,
};
if is_accessible {
if this_f.lifetime_indices() != other_f.lifetime_indices() {
push_err(errs, this_f, other_f, |x| *x, AI::FieldLifetimeMismatch);
}
self.stack_trace.push(ExpectedFound{
expected:(*this_f).into(),
found:(*other_f).into(),
});
let sf_ctx=FieldContext::Subfields;
let func_ranges=this_f.function_range().iter().zip(other_f.function_range());
for (t_func,o_func) in func_ranges {
self.error_index += 1;
let errs_index = self.error_index;
let mut errs_ = RVec::<AbiInstability>::new();
let errs=&mut errs_;
self.stack_trace.push(ExpectedFound{
expected:t_func.into(),
found:o_func.into(),
});
if t_func.paramret_lifetime_indices != o_func.paramret_lifetime_indices {
push_err(errs, t_func, o_func, |x| x, AI::FnLifetimeMismatch);
}
self.check_fields(
errs,
t_lay,
o_lay,
sf_ctx,
t_func.get_params_ret_iter(),
o_func.get_params_ret_iter(),
);
if !errs_.is_empty() {
self.errors.push(AbiInstabilityError {
stack_trace: self.stack_trace.clone(),
errs: errs_,
index: errs_index,
_priv:(),
});
}
self.stack_trace.pop();
}
let _=self.check_inner(t_field_abi, o_field_abi);
self.stack_trace.pop();
}else{
self.stack_trace.push(ExpectedFound{
expected:(*this_f).into(),
found:(*other_f).into(),
});
let t_field_layout=&t_field_abi;
let o_field_layout=&o_field_abi;
if t_field_layout.size()!=o_field_layout.size() {
push_err(errs, t_field_layout, o_field_layout, |x| x.size(), AI::Size);
}
if t_field_layout.alignment() != o_field_layout.alignment() {
push_err(
errs,
t_field_layout,
o_field_layout,
|x| x.alignment(),
AI::Alignment
);
}
self.stack_trace.pop();
}
}
}
fn check_inner(
&mut self,
this: &'static TypeLayout,
other: &'static TypeLayout
) ->Result<(),()> {
let t_cuti=CheckingUTypeId::new(this );
let o_cuti=CheckingUTypeId::new(other);
let cuti_pair=(t_cuti,o_cuti);
self.error_index += 1;
let errs_index = self.error_index;
let mut errs_ = RVec::<AbiInstability>::new();
let mut top_level_errs_ = RVec::<AbiInstabilityError>::new();
let t_lay = &this;
let o_lay = &other;
let start_errors=self.errors.len();
match self.visited.entry(cuti_pair) {
Entry::Occupied(mut entry)=>{
match entry.get_mut() {
CheckingState::Checking{layer}if self.current_layer==*layer =>{
return Ok(())
}
cs@CheckingState::Checking{..}=>{
*cs=CheckingState::Error;
self.errors.push(AbiInstabilityError {
stack_trace: self.stack_trace.clone(),
errs: rvec![AbiInstability::CyclicTypeChecking{
interface:this,
implementation:other,
}],
index: errs_index,
_priv:(),
});
return Err(());
}
CheckingState::Compatible=>{
return Ok(());
}
CheckingState::Error=>{
return Err(());
}
}
}
Entry::Vacant(entry)=>{
entry.insert(CheckingState::Checking{layer:self.current_layer});
}
}
(|| {
let errs = &mut errs_;
let top_level_errs= &mut top_level_errs_;
if t_lay.name() != o_lay.name() {
push_err(errs, t_lay, o_lay, |x| x.full_type(), AI::Name);
return;
}
let (t_package,t_ver_str)=t_lay.package_and_version();
let (o_package,o_ver_str)=o_lay.package_and_version();
if t_package != o_package {
push_err(errs, t_lay, o_lay, |x| x.package(), AI::Package);
return;
}
if this.is_nonzero() != other.is_nonzero() {
push_err(errs, this, other, |x| x.is_nonzero(), AI::NonZeroness);
}
if t_lay.repr_attr() != o_lay.repr_attr() {
push_err(errs, t_lay, o_lay, |x| x.repr_attr(), AI::ReprAttr);
}
{
let x = (|| {
let l = t_ver_str.parsed()?;
let r = o_ver_str.parsed()?;
Ok(l.is_loosely_compatible(r))
})();
match x {
Ok(false) => {
push_err(
errs,
t_lay,
o_lay,
|x| x.package_version(),
AI::PackageVersion,
);
}
Ok(true) => {}
Err(parse_error) => {
errs.push(AI::PackageVersionParseError(parse_error));
return;
}
}
}
{
let t_gens = t_lay.generics();
let o_gens = o_lay.generics();
let t_consts=t_gens.const_params();
let o_consts=o_gens.const_params();
if t_gens.lifetime_count() != o_gens.lifetime_count()
|| t_gens.const_params().len() != o_gens.const_params().len()
{
push_err(errs, t_lay, o_lay, |x| x.full_type(), AI::GenericParamCount);
}
let mut ty_checker=TypeCheckerMut::from_ptr(&mut *self,TU_Opaque);
for (l,r) in t_consts.iter().zip(o_consts.iter()) {
match l.is_equal(r,ty_checker.sabi_reborrow_mut()) {
Ok(false)|Err(_)=>{
push_err(errs, l, r, |x| *x, AI::MismatchedConstParam);
}
Ok(true)=>{}
}
}
}
self.check_fields(
errs,
this,
other,
FieldContext::PhantomFields,
this.phantom_fields().iter(),
other.phantom_fields().iter(),
);
match (t_lay.size().cmp(&o_lay.size()), this.is_prefix_kind()) {
(Ordering::Greater, _) | (Ordering::Less, false) => {
push_err(errs, t_lay, o_lay, |x| x.size(), AI::Size);
}
(Ordering::Equal, _) | (Ordering::Less, true) => {}
}
if t_lay.alignment() != o_lay.alignment() {
push_err(errs, t_lay, o_lay, |x| x.alignment(), AI::Alignment);
}
let t_discr = t_lay.data_discriminant();
let o_discr = o_lay.data_discriminant();
if t_discr != o_discr {
errs.push(AI::TLDataDiscriminant(ExpectedFound {
expected: t_discr,
found: o_discr,
}));
}
let t_tag=t_lay.tag().to_checkable();
let o_tag=o_lay.tag().to_checkable();
if let Err(tag_err)=t_tag.check_compatible(&o_tag) {
errs.push(AI::TagError{
err:tag_err,
});
}
match (t_lay.extra_checks(),o_lay.extra_checks()) {
(None,_)=>{}
(Some(_),None)=>{
errs.push(AI::NoneExtraChecks);
}
(Some(t_extra_checks),Some(o_extra_checks))=>{
let mut ty_checker=TypeCheckerMut::from_ptr(&mut *self,TU_Opaque);
let res=handle_extra_checks_ret(
t_extra_checks.clone(),
o_extra_checks.clone(),
errs,
top_level_errs,
move||{
let ty_checker_=ty_checker.sabi_reborrow_mut();
rtry!( t_extra_checks.check_compatibility(t_lay,o_lay,ty_checker_) );
let ty_checker_=ty_checker.sabi_reborrow_mut();
let opt=rtry!(
t_extra_checks.combine(o_extra_checks,ty_checker_)
);
opt.map(|combined|{
ExtraChecksBoxWithContext{
t_lay,
o_lay,
extra_checks:combined
}
})
.piped(ROk)
}
);
if let Ok(RSome(x))=res {
self.checked_extra_checks.push(x);
}
}
}
match (t_lay.data(), o_lay.data()) {
(TLData::Opaque{..}, _) => {
}
(TLData::Primitive(t_prim), TLData::Primitive(o_prim)) => {
if t_prim != o_prim {
errs.push(AI::MismatchedPrimitive(ExpectedFound {
expected: t_prim,
found: o_prim,
}));
}
}
(TLData::Primitive{..}, _) => {}
(TLData::Struct { fields: t_fields }, TLData::Struct { fields: o_fields }) => {
self.check_fields(
errs,
this,
other,
FieldContext::Fields,
t_fields.iter(),
o_fields.iter()
);
}
(TLData::Struct { .. }, _) => {}
(TLData::Union { fields: t_fields }, TLData::Union { fields: o_fields }) => {
self.check_fields(
errs,
this,
other,
FieldContext::Fields,
t_fields.iter(),
o_fields.iter()
);
}
(TLData::Union { .. }, _) => {}
( TLData::Enum(t_enum),TLData::Enum(o_enum) ) => {
self.check_enum(errs,this,other,t_enum,o_enum);
let t_as_ne=t_enum.exhaustiveness.as_nonexhaustive();
let o_as_ne=o_enum.exhaustiveness.as_nonexhaustive();
if let (Some(this_ne),Some(other_ne))=(t_as_ne,o_as_ne) {
self.checked_nonexhaustive_enums.push(CheckedNonExhaustiveEnums{
this:NonExhaustiveEnumWithContext{
layout:this,
enum_:t_enum,
nonexhaustive:this_ne,
},
other:NonExhaustiveEnumWithContext{
layout:other,
enum_:o_enum,
nonexhaustive:other_ne,
},
});
}
}
(TLData::Enum { .. }, _) => {}
(
TLData::PrefixType (t_prefix),
TLData::PrefixType (o_prefix),
) => {
let this_prefix =__PrefixTypeMetadata::with_prefix_layout(t_prefix,t_lay);
let other_prefix=__PrefixTypeMetadata::with_prefix_layout(o_prefix,o_lay);
self.check_prefix_types(errs,&this_prefix,&other_prefix);
self.checked_prefix_types.push(
CheckedPrefixTypes{this,this_prefix,other,other_prefix}
)
}
( TLData::PrefixType {..}, _ ) => {}
}
})();
self.errors.extend(top_level_errs_);
let check_st=self.visited.get_mut(&cuti_pair).unwrap();
if errs_.is_empty() &&
self.errors.len()==start_errors &&
*check_st!=CheckingState::Error
{
*check_st=CheckingState::Compatible;
Ok(())
}else{
*check_st=CheckingState::Error;
self.errors.push(AbiInstabilityError {
stack_trace: self.stack_trace.clone(),
errs: errs_,
index: errs_index,
_priv:(),
});
Err(())
}
}
fn check_enum(
&mut self,
errs: &mut RVec<AbiInstability>,
this: &'static TypeLayout,other: &'static TypeLayout,
t_enum:TLEnum,o_enum:TLEnum,
){
let TLEnum{ fields: t_fields,.. }=t_enum;
let TLEnum{ fields: o_fields,.. }=o_enum;
let t_fcount = t_enum.field_count.as_slice();
let o_fcount = o_enum.field_count.as_slice();
let t_exhaus=t_enum.exhaustiveness;
let o_exhaus=o_enum.exhaustiveness;
match (t_exhaus.as_nonexhaustive(),o_exhaus.as_nonexhaustive()) {
(Some(this_ne),Some(other_ne))=>{
if let Err(e)=this_ne.check_compatible(this){
errs.push(AI::IncompatibleWithNonExhaustive(e))
}
if let Err(e)=other_ne.check_compatible(other){
errs.push(AI::IncompatibleWithNonExhaustive(e))
}
}
(Some(_),None)|(None,Some(_))=>{
push_err(
errs,
t_enum,
o_enum,
|x| x.exhaustiveness,
AI::MismatchedExhaustiveness
);
}
(None,None)=>{}
}
if t_exhaus.is_exhaustive()&&t_fcount.len()!=o_fcount.len() ||
t_exhaus.is_nonexhaustive()&&t_fcount.len() >o_fcount.len()
{
push_err(errs, t_fcount, o_fcount, |x| x.len(), AI::TooManyVariants);
}
if let Err(d_errs)=t_enum.discriminants.compare(&o_enum.discriminants) {
errs.extend(d_errs);
}
let mut t_names=t_enum.variant_names.as_str().split(';');
let mut o_names=o_enum.variant_names.as_str().split(';');
let mut total_field_count=0;
for (t_field_count, o_field_count) in t_fcount.iter().zip(o_fcount) {
let t_name = t_names.next().unwrap_or("<this unavailable>");
let o_name = o_names.next().unwrap_or("<other unavailable>");
total_field_count+=usize::from(*t_field_count);
if t_field_count!=o_field_count {
push_err(
errs,
*t_field_count,
*o_field_count,
|x| x as usize,
AI::FieldCountMismatch
);
}
if t_name != o_name {
push_err(errs, t_name, o_name,RStr::from_str, AI::UnexpectedVariant);
continue;
}
}
let min_field_count=t_fields.len().min(o_fields.len());
if total_field_count!=min_field_count {
push_err(errs, total_field_count, min_field_count,|x|x, AI::FieldCountMismatch);
}
self.check_fields(
errs,
this,
other,
FieldContext::Fields,
t_fields.iter(),
o_fields.iter()
);
}
fn check_prefix_types(
&mut self,
errs: &mut RVec<AbiInstability>,
this: &__PrefixTypeMetadata,
other: &__PrefixTypeMetadata,
){
if this.prefix_field_count != other.prefix_field_count {
push_err(
errs,
this,
other,
|x| x.prefix_field_count ,
AI::MismatchedPrefixSize
);
}
if this.conditional_prefix_fields != other.conditional_prefix_fields {
push_err(
errs,
this,
other,
|x| x.conditional_prefix_fields,
AI::MismatchedPrefixConditionality
);
}
self.check_fields(
errs,
this.layout,
other.layout,
FieldContext::Fields,
this.fields.iter(),
other.fields.iter()
);
}
fn final_prefix_type_checks(
&mut self,
globals:&CheckingGlobals
)->Result<(),AbiInstabilityError>{
self.error_index += 1;
let mut errs_ = RVec::<AbiInstability>::new();
let errs =&mut errs_;
let mut prefix_type_map=globals.prefix_type_map.lock().unwrap();
for pair in mem::take(&mut self.checked_prefix_types) {
let errors_before=self.errors.len();
let t_utid=pair.this .get_utypeid();
let o_utid=pair.other.get_utypeid();
let t_index=prefix_type_map.get_index(&t_utid);
let mut o_index=prefix_type_map.get_index(&o_utid);
if t_index==o_index{
o_index=None;
}
let (min_prefix,mut max_prefix)=pair.this_prefix.min_max(pair.other_prefix);
match (t_index,o_index) {
(None,None)=>{
max_prefix.combine_fields_from(&min_prefix);
let i=prefix_type_map
.get_or_insert(t_utid,max_prefix)
.into_inner()
.index;
prefix_type_map.associate_key(o_utid,i);
}
(Some(im_index),None)|(None,Some(im_index))=>{
max_prefix.combine_fields_from(&min_prefix);
let im_prefix=prefix_type_map.get_mut_with_index(im_index).unwrap();
let im_prefix_addr=im_prefix as *const _ as usize;
let (min_prefix,max_prefix)=
min_max_by(im_prefix,&mut max_prefix,|x|x.fields.len());
self.check_prefix_types(errs,min_prefix,max_prefix);
if !errs.is_empty() || errors_before!=self.errors.len() { break; }
max_prefix.combine_fields_from(&*min_prefix);
if im_prefix_addr != (max_prefix as *mut _ as usize) {
mem::swap(min_prefix,max_prefix);
}
prefix_type_map.associate_key(t_utid,im_index);
prefix_type_map.associate_key(o_utid,im_index);
}
(Some(l_index),Some(r_index))=>{
let (l_prefix,r_prefix)=
prefix_type_map.get2_mut_with_index(l_index,r_index);
let l_prefix=l_prefix.unwrap();
let r_prefix=r_prefix.unwrap();
let (replace,with)=if l_prefix.fields.len() < r_prefix.fields.len() {
(l_index,r_index)
}else{
(r_index,l_index)
};
let (min_prefix,max_prefix)=min_max_by(l_prefix,r_prefix,|x|x.fields.len());
self.check_prefix_types(errs,min_prefix,max_prefix);
if !errs.is_empty() || errors_before!=self.errors.len() { break; }
max_prefix.combine_fields_from(&*min_prefix);
prefix_type_map.replace_with_index(replace,with);
}
}
}
if errs_.is_empty() {
Ok(())
}else{
Err(AbiInstabilityError {
stack_trace: self.stack_trace.clone(),
errs: errs_,
index: self.error_index,
_priv:(),
})
}
}
fn final_non_exhaustive_enum_checks(
&mut self,
globals:&CheckingGlobals
)->Result<(),AbiInstabilityError>{
self.error_index += 1;
let mut errs_ = RVec::<AbiInstability>::new();
let errs =&mut errs_;
let mut nonexhaustive_map=globals.nonexhaustive_map.lock().unwrap();
for pair in mem::take(&mut self.checked_nonexhaustive_enums) {
let CheckedNonExhaustiveEnums{this,other}=pair;
let errors_before=self.errors.len();
let t_utid=this .layout.get_utypeid();
let o_utid=other.layout.get_utypeid();
let t_index=nonexhaustive_map.get_index(&t_utid);
let mut o_index=nonexhaustive_map.get_index(&o_utid);
if t_index==o_index{
o_index=None;
}
let mut max_=max_by(this,other,|x|x.enum_.variant_count());
match (t_index,o_index) {
(None,None)=>{
let i=nonexhaustive_map
.get_or_insert(t_utid,max_)
.into_inner()
.index;
nonexhaustive_map.associate_key(o_utid,i);
}
(Some(im_index),None)|(None,Some(im_index))=>{
let im_nonexh=nonexhaustive_map.get_mut_with_index(im_index).unwrap();
let im_nonexh_addr=im_nonexh as *const _ as usize;
let (min_nonexh,max_nonexh)=
min_max_by(im_nonexh,&mut max_,|x|x.enum_.variant_count());
self.check_enum(
errs,
min_nonexh.layout,max_nonexh.layout,
min_nonexh.enum_ ,max_nonexh.enum_ ,
);
if !errs.is_empty() || errors_before!=self.errors.len() { break; }
if im_nonexh_addr != (max_nonexh as *mut _ as usize) {
mem::swap(min_nonexh,max_nonexh);
}
nonexhaustive_map.associate_key(t_utid,im_index);
nonexhaustive_map.associate_key(o_utid,im_index);
}
(Some(l_index),Some(r_index))=>{
let (l_nonexh,r_nonexh)=
nonexhaustive_map.get2_mut_with_index(l_index,r_index);
let l_nonexh=l_nonexh.unwrap();
let r_nonexh=r_nonexh.unwrap();
let (replace,with)=
if l_nonexh.enum_.variant_count() < r_nonexh.enum_.variant_count() {
(l_index,r_index)
}else{
(r_index,l_index)
};
let (min_nonexh,max_nonexh)=
min_max_by(l_nonexh,r_nonexh,|x|x.enum_.variant_count());
self.check_enum(
errs,
min_nonexh.layout,max_nonexh.layout,
min_nonexh.enum_ ,max_nonexh.enum_ ,
);
if !errs.is_empty() || errors_before!=self.errors.len() { break; }
nonexhaustive_map.replace_with_index(replace,with);
}
}
}
if errs_.is_empty() {
Ok(())
}else{
Err(AbiInstabilityError {
stack_trace: self.stack_trace.clone(),
errs: errs_,
index: self.error_index,
_priv:(),
})
}
}
fn final_extra_checks(
&mut self,
globals:&CheckingGlobals
)->Result<(),RVec<AbiInstabilityError>>{
self.error_index += 1;
let mut top_level_errs_ = RVec::<AbiInstabilityError>::new();
let mut errs_ = RVec::<AbiInstability>::new();
let errs =&mut errs_;
let top_level_errs =&mut top_level_errs_;
let mut extra_checker_map=globals.extra_checker_map.lock().unwrap();
for with_context in mem::take(&mut self.checked_extra_checks) {
let ExtraChecksBoxWithContext{t_lay,o_lay,extra_checks}=with_context;
let errors_before=self.errors.len();
let type_checker=TypeCheckerMut::from_ptr(&mut *self,TU_Opaque);
let t_utid=t_lay.get_utypeid();
let o_utid=o_lay.get_utypeid();
let t_index=extra_checker_map.get_index(&t_utid);
let mut o_index=extra_checker_map.get_index(&o_utid);
if t_index==o_index{
o_index=None;
}
match (t_index,o_index) {
(None,None)=>{
let i=extra_checker_map
.get_or_insert(t_utid,extra_checks)
.into_inner()
.index;
extra_checker_map.associate_key(o_utid,i);
}
(Some(im_index),None)|(None,Some(im_index))=>{
let other_checks=extra_checker_map.get_mut_with_index(im_index).unwrap();
combine_extra_checks(
errs,
top_level_errs,
type_checker,
other_checks,
&[extra_checks.sabi_reborrow()]
);
if !errs.is_empty() || errors_before!=self.errors.len() { break; }
extra_checker_map.associate_key(t_utid,im_index);
extra_checker_map.associate_key(o_utid,im_index);
}
(Some(l_index),Some(r_index))=>{
let (l_extra_checks,r_extra_checks)=
extra_checker_map.get2_mut_with_index(l_index,r_index);
let l_extra_checks=l_extra_checks.unwrap();
let r_extra_checks=r_extra_checks.unwrap();
combine_extra_checks(
errs,
top_level_errs,
type_checker,
l_extra_checks,
&[ r_extra_checks.sabi_reborrow(), extra_checks.sabi_reborrow() ]
);
if !errs.is_empty() || errors_before!=self.errors.len() { break; }
extra_checker_map.replace_with_index(r_index,l_index);
}
}
}
if errs_.is_empty() {
Ok(())
}else{
top_level_errs.push(AbiInstabilityError {
stack_trace: self.stack_trace.clone(),
errs: errs_,
index: self.error_index,
_priv:(),
});
Err(top_level_errs_)
}
}
}
pub fn check_layout_compatibility(
interface: &'static TypeLayout,
implementation: &'static TypeLayout,
) -> Result<(), AbiInstabilityErrors> {
check_layout_compatibility_with_globals(
interface,
implementation,
get_checking_globals(),
)
}
#[inline(never)]
pub fn check_layout_compatibility_with_globals(
interface: &'static TypeLayout,
implementation: &'static TypeLayout,
globals:&CheckingGlobals,
) -> Result<(), AbiInstabilityErrors> {
let mut errors: RVec<AbiInstabilityError>;
if interface.is_prefix_kind() || implementation.is_prefix_kind() {
let mut errs=RVec::with_capacity(1);
push_err(
&mut errs,
interface,
implementation,
|x| x.data_discriminant() ,
AI::TLDataDiscriminant
);
errors = vec![AbiInstabilityError {
stack_trace: vec![].into(),
errs,
index: 0,
_priv:(),
}]
.into();
} else {
let mut checker = AbiChecker::new();
let _=checker.check_inner(interface, implementation);
if checker.errors.is_empty() {
if let Err(e)=checker.final_prefix_type_checks(globals) {
checker.errors.push(e);
}
if let Err(e)=checker.final_non_exhaustive_enum_checks(globals) {
checker.errors.push(e);
}
if let Err(e)=checker.final_extra_checks(globals) {
checker.errors.extend(e);
}
}
errors = checker.errors;
}
if errors.is_empty() {
Ok(())
} else {
errors.sort_by_key(|x| x.index);
Err(AbiInstabilityErrors {
interface,
implementation,
errors,
_priv:()
})
}
}
pub(crate) extern "C" fn check_layout_compatibility_for_ffi(
interface: &'static TypeLayout,
implementation: &'static TypeLayout,
) -> RResult<(), RBoxError> {
extern_fn_panic_handling!{
let mut is_already_inside=false;
INSIDE_LAYOUT_CHECKER.with(|inside|{
is_already_inside=inside.get();
inside.set(true);
});
let _guard=LayoutCheckerGuard;
if is_already_inside {
let errors =
vec![AbiInstabilityError {
stack_trace: vec![].into(),
errs:vec![AbiInstability::ReentrantLayoutCheckingCall].into(),
index: 0,
_priv:(),
}].into_c();
Err(AbiInstabilityErrors{ interface, implementation, errors, _priv:() })
}else{
check_layout_compatibility(interface,implementation)
}.map_err(RBoxError::new)
.into_c()
}
}
pub extern "C" fn exported_check_layout_compatibility(
interface: &'static TypeLayout,
implementation: &'static TypeLayout,
) -> RResult<(), RBoxError> {
extern_fn_panic_handling!{
(crate::globals::initialized_globals().layout_checking)
(interface,implementation)
}
}
impl AbiChecker{
fn check_compatibility_inner(
&mut self,
interface:&'static TypeLayout,
implementation:&'static TypeLayout,
)->RResult<(),()>{
let error_count_before=self.errors.len();
self.current_layer+=1;
let res=self.check_inner(interface,implementation);
self.current_layer-=1;
if error_count_before==self.errors.len() && res.is_ok() {
ROk(())
}else{
RErr(())
}
}
}
unsafe impl TypeChecker for AbiChecker{
fn check_compatibility(
&mut self,
interface:&'static TypeLayout,
implementation:&'static TypeLayout,
)->RResult<(), ExtraChecksError> {
self.check_compatibility_inner(interface,implementation)
.map_err(|_| ExtraChecksError::TypeChecker )
}
fn local_check_compatibility(
&mut self,
interface:&'static TypeLayout,
implementation:&'static TypeLayout,
)->RResult<(), ExtraChecksError> {
let error_count_before=self.errors.len();
dbg!(error_count_before);
println!(
"interface:{} implementation:{}",
interface.full_type(),
implementation.full_type()
);
self.check_compatibility_inner(interface,implementation)
.map_err(|_|{
AbiInstabilityErrors {
interface,
implementation,
errors: self.errors.drain(error_count_before..).collect(),
_priv:(),
}
.piped(RBoxError::new)
.piped(ExtraChecksError::TypeCheckerErrors)
})
}
}
thread_local!{
static INSIDE_LAYOUT_CHECKER:Cell<bool>=Cell::new(false);
}
struct LayoutCheckerGuard;
impl Drop for LayoutCheckerGuard{
fn drop(&mut self){
INSIDE_LAYOUT_CHECKER.with(|inside|{
inside.set(false);
});
}
}
use std::sync::Mutex;
use crate::{
sabi_types::LateStaticRef,
multikey_map::MultiKeyMap,
prefix_type::__PrefixTypeMetadata,
utils::leak_value,
};
#[derive(Debug)]
pub struct CheckingGlobals{
pub prefix_type_map:Mutex<MultiKeyMap<UTypeId,__PrefixTypeMetadata>>,
pub nonexhaustive_map:Mutex<MultiKeyMap<UTypeId,NonExhaustiveEnumWithContext>>,
pub extra_checker_map:Mutex<MultiKeyMap<UTypeId,ExtraChecksBox>>,
}
#[allow(clippy::new_without_default)]
impl CheckingGlobals{
pub fn new()->Self{
CheckingGlobals{
prefix_type_map:MultiKeyMap::new().piped(Mutex::new),
nonexhaustive_map:MultiKeyMap::new().piped(Mutex::new),
extra_checker_map:MultiKeyMap::new().piped(Mutex::new),
}
}
}
static CHECKING_GLOBALS:LateStaticRef<&CheckingGlobals>=LateStaticRef::new();
pub fn get_checking_globals()->&'static CheckingGlobals{
CHECKING_GLOBALS.init(||{
CheckingGlobals::new().piped(leak_value)
})
}
pub(crate) fn push_err<O, U, FG, VC>(
errs: &mut RVec<AbiInstability>,
this: O,
other: O,
field_getter: FG,
mut variant_constructor: VC,
) where
FG: FnMut(O) -> U,
VC: FnMut(ExpectedFound<U>) -> AbiInstability,
{
let x = ExpectedFound::new(this, other, field_getter);
let x = variant_constructor(x);
errs.push(x);
}
fn handle_extra_checks_ret<F,R>(
expected_extra_checks:ExtraChecksRef<'_>,
found_extra_checks:ExtraChecksRef<'_>,
errs: &mut RVec<AbiInstability>,
top_level_errs: &mut RVec<AbiInstabilityError>,
f:F
)->Result<R,()>
where
F:FnOnce()->RResult<R, ExtraChecksError>
{
let make_extra_check_error=move|e:RBoxError|->AbiInstability{
ExtraCheckError{
err: RArc::new(e),
expected_err: ExpectedFound{
expected:expected_extra_checks
.piped_ref(RBoxError::from_fmt)
.piped(RArc::new),
found:found_extra_checks
.piped_ref(RBoxError::from_fmt)
.piped(RArc::new),
}
}.piped(CmpIgnored::new)
.piped(AI::ExtraCheckError)
};
match f() {
ROk(x)=>{
Ok(x)
}
RErr(ExtraChecksError::TypeChecker)=>{
Err(())
}
RErr(ExtraChecksError::TypeCheckerErrors(e))=>{
match e.downcast::<AbiInstabilityErrors>() {
Ok(e)=>top_level_errs.extend(RBox::into_inner(e).errors),
Err(e)=>errs.push(make_extra_check_error(e)),
}
Err(())
}
RErr(ExtraChecksError::NoneExtraChecks)=>{
errs.push(AI::NoneExtraChecks);
Err(())
}
RErr(ExtraChecksError::ExtraChecks(e))=>{
errs.push(make_extra_check_error(e));
Err(())
}
}
}
fn combine_extra_checks(
errs: &mut RVec<AbiInstability>,
top_level_errs: &mut RVec<AbiInstabilityError>,
mut ty_checker:TypeCheckerMut<'_>,
extra_checks:&mut ExtraChecksBox,
slic:&[ExtraChecksRef<'_>]
){
for other in slic {
let other_ref=other.sabi_reborrow();
let ty_checker=ty_checker.sabi_reborrow_mut();
let opt_ret=handle_extra_checks_ret(
extra_checks.sabi_reborrow(),
other.sabi_reborrow(),
errs,
top_level_errs,
|| extra_checks.sabi_reborrow().combine( other_ref , ty_checker )
);
match opt_ret {
Ok(RSome(new))=>{ *extra_checks=new; },
Ok(RNone)=>{},
Err(_)=>break,
}
}
}