use super::Sema;
use crate::capture::SourceRange;
use crate::ir::{Field, Layout, RecordId, RecordKind, RustField, Signature, Ty};
const REASON: &str = "binary128 has no Rust type to become (Rust's 'f128' is unstable), and \
mapping it onto 'double' would compute and pass the wrong values";
#[derive(Clone, Copy, PartialEq, Eq)]
enum StandIn {
Float128,
Complex,
}
impl Sema<'_> {
pub(super) fn float128_ty(&mut self, complex: bool, range: SourceRange) -> Ty {
let slot = usize::from(complex);
if let Some(id) = self.float128[slot] {
return Ty::Record(id);
}
let ty = if complex {
self.stand_in(
"_Complex _Float128",
"__cinrs_Complex_Float128",
32,
16,
range,
)
} else {
self.stand_in("_Float128", "__cinrs_Float128", 16, 16, range)
};
if let Ty::Record(id) = ty {
self.float128[slot] = Some(id);
}
ty
}
pub(super) fn uncomputable_complex_ty(&mut self, single: bool, range: SourceRange) -> Ty {
let slot = usize::from(!single);
if let Some(id) = self.no_complex[slot] {
return Ty::Record(id);
}
let ty = if single {
self.stand_in("float _Complex", "__cinrs_ComplexFloat", 8, 4, range)
} else {
self.stand_in("double _Complex", "__cinrs_ComplexDouble", 16, 8, range)
};
if let Ty::Record(id) = ty {
self.no_complex[slot] = Some(id);
}
ty
}
fn stand_in(
&mut self,
c_type: &'static str,
rust_name: &str,
size: u64,
align: u64,
range: SourceRange,
) -> Ty {
let id = self.declare_record(RecordKind::Struct, None, range);
let bytes = self.program.types.array(Ty::UChar, size, false);
let rust_name = self.reserve_item_name(rust_name);
let record = self.program.types.record_mut(id);
record.rust_name = rust_name;
record.anonymous = false;
record.stands_for = Some(c_type);
record.fields = vec![Field {
name: "__cinrs_bytes".to_owned(),
anonymous: false,
ty: bytes,
is_const: false,
offset: 0,
bits: None,
flexible: false,
range,
}];
record.rust_fields = vec![RustField::Member(0)];
record.complete = true;
record.layout = Some(Layout { size, align });
record.align = Some(align);
record.rust_align = align;
Ty::Record(id)
}
fn any_stand_in(&self) -> bool {
self.float128
.iter()
.chain(&self.no_complex)
.any(Option::is_some)
}
fn stand_in_of(&self, id: RecordId) -> Option<StandIn> {
if self.float128.contains(&Some(id)) {
Some(StandIn::Float128)
} else if self.no_complex.contains(&Some(id)) {
Some(StandIn::Complex)
} else {
None
}
}
fn stand_in_value(&self, ty: Ty) -> Option<StandIn> {
match ty {
Ty::Record(id) => self.stand_in_of(id),
Ty::Array(id) => self.stand_in_value(self.types().array_type(id).elem),
_ => None,
}
}
fn stand_in_mentioned(&self, ty: Ty) -> Option<StandIn> {
match ty {
Ty::Record(id) => self.stand_in_of(id),
Ty::Array(id) => self.stand_in_mentioned(self.types().array_type(id).elem),
Ty::Pointer(_) => self
.pointee(ty)
.and_then(|pointee| self.stand_in_mentioned(pointee)),
_ => None,
}
}
pub(super) fn refuse_float128_object(&mut self, name: &str, ty: Ty, range: SourceRange) {
if !self.any_stand_in() {
return;
}
match self.stand_in_value(ty) {
Some(StandIn::Float128) => {
let spelled = self.tyname(ty);
self.error(
range,
format!("'{name}' cannot be an object of type '{spelled}': {REASON}"),
);
}
Some(StandIn::Complex) => {
self.error(range, crate::COMPLEX_UNSUPPORTED.to_owned());
}
None => {}
}
}
pub(super) fn refuse_float128_cast(&mut self, spelled: &str, range: SourceRange) {
let reason = if spelled.contains("_Float128") {
REASON
} else {
crate::COMPLEX_UNSUPPORTED
};
self.error(
range,
format!("a cast to '{spelled}' is not supported: {reason}"),
);
}
pub(super) fn refuse_float128_call(
&mut self,
name: &str,
sig: &Signature,
range: SourceRange,
) -> bool {
if self.dead_code > 0 || !self.any_stand_in() {
return false;
}
let (offence, which) = if let Some(which) = self.stand_in_mentioned(sig.ret) {
(format!("returns '{}'", self.tyname(sig.ret)), which)
} else if let Some((param, which)) = sig
.params
.iter()
.find_map(|p| Some((*p, self.stand_in_mentioned(*p)?)))
{
(format!("takes '{}'", self.tyname(param)), which)
} else {
return false;
};
let reason = match which {
StandIn::Float128 => REASON,
StandIn::Complex => crate::COMPLEX_UNSUPPORTED,
};
self.error(
range,
format!("'{name}' {offence}, which cannot be called: {reason}"),
);
true
}
}