#![allow(dead_code)]
use super::obj::{LangObj, ObjSet};
use super::objects::{ObjKey, ScopeKey, TCObjects, TypeKey};
use std::cell::{Ref, RefCell, RefMut};
use std::collections::HashSet;
use std::fmt;
use std::fmt::Debug;
use std::fmt::Write;
use std::mem::size_of as std_size_of;
use std::rc::Rc;
macro_rules! compare_by_method_name {
($objs:expr) => {{
|a, b| {
let oa = &$objs.lobjs[*a];
let ob = &$objs.lobjs[*b];
oa.id($objs).as_ref().cmp(ob.id($objs).as_ref())
}
}};
}
macro_rules! compare_by_type_name {
($objs:expr) => {{
|a, b| {
let sort_name = |t: &Type| match t {
Type::Named(n) => {
let typ = &$objs.lobjs[n.obj().unwrap()];
typ.id($objs)
}
_ => "".into(),
};
let ta = sort_name(&$objs.types[*a]);
let tb = sort_name(&$objs.types[*b]);
ta.as_ref().cmp(tb.as_ref())
}
}};
}
#[derive(Debug)]
pub enum Type {
Basic(BasicDetail),
Array(ArrayDetail),
Slice(SliceDetail),
Struct(StructDetail),
Pointer(PointerDetail),
Tuple(TupleDetail),
Signature(SignatureDetail),
Interface(InterfaceDetail),
Map(MapDetail),
Chan(ChanDetail),
Named(NamedDetail),
}
impl Type {
pub fn try_as_basic(&self) -> Option<&BasicDetail> {
match &self {
Type::Basic(b) => Some(b),
_ => None,
}
}
pub fn try_as_array(&self) -> Option<&ArrayDetail> {
match &self {
Type::Array(a) => Some(a),
_ => None,
}
}
pub fn try_as_array_mut(&mut self) -> Option<&mut ArrayDetail> {
match self {
Type::Array(a) => Some(a),
_ => None,
}
}
pub fn try_as_slice(&self) -> Option<&SliceDetail> {
match &self {
Type::Slice(s) => Some(s),
_ => None,
}
}
pub fn try_as_struct(&self) -> Option<&StructDetail> {
match &self {
Type::Struct(s) => Some(s),
_ => None,
}
}
pub fn try_as_pointer(&self) -> Option<&PointerDetail> {
match &self {
Type::Pointer(p) => Some(p),
_ => None,
}
}
pub fn try_as_tuple(&self) -> Option<&TupleDetail> {
match self {
Type::Tuple(t) => Some(t),
_ => None,
}
}
pub fn try_as_tuple_mut(&mut self) -> Option<&mut TupleDetail> {
match self {
Type::Tuple(t) => Some(t),
_ => None,
}
}
pub fn try_as_signature(&self) -> Option<&SignatureDetail> {
match self {
Type::Signature(s) => Some(s),
_ => None,
}
}
pub fn try_as_signature_mut(&mut self) -> Option<&mut SignatureDetail> {
match self {
Type::Signature(s) => Some(s),
_ => None,
}
}
pub fn try_as_interface(&self) -> Option<&InterfaceDetail> {
match &self {
Type::Interface(i) => Some(i),
_ => None,
}
}
pub fn try_as_interface_mut(&mut self) -> Option<&mut InterfaceDetail> {
match self {
Type::Interface(i) => Some(i),
_ => None,
}
}
pub fn try_as_map(&self) -> Option<&MapDetail> {
match &self {
Type::Map(m) => Some(m),
_ => None,
}
}
pub fn try_as_chan(&self) -> Option<&ChanDetail> {
match self {
Type::Chan(c) => Some(c),
_ => None,
}
}
pub fn try_as_chan_mut(&mut self) -> Option<&mut ChanDetail> {
match self {
Type::Chan(c) => Some(c),
_ => None,
}
}
pub fn try_as_named(&self) -> Option<&NamedDetail> {
match self {
Type::Named(n) => Some(n),
_ => None,
}
}
pub fn try_as_named_mut(&mut self) -> Option<&mut NamedDetail> {
match self {
Type::Named(n) => Some(n),
_ => None,
}
}
pub fn underlying(&self) -> Option<TypeKey> {
match self {
Type::Named(detail) => detail.underlying,
_ => None,
}
}
pub fn underlying_val<'a>(&'a self, objs: &'a TCObjects) -> &'a Type {
if let Some(k) = self.underlying() {
&objs.types[k]
} else {
&self
}
}
pub fn is_named(&self) -> bool {
match self {
Type::Basic(_) | Type::Named(_) => true,
_ => false,
}
}
pub fn is_invalid(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.info() == BasicInfo::IsInvalid,
_ => false,
}
}
pub fn is_boolean(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.info() == BasicInfo::IsBoolean,
_ => false,
}
}
pub fn is_integer(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.info() == BasicInfo::IsInteger,
_ => false,
}
}
pub fn is_unsigned(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.typ().is_unsigned(),
_ => false,
}
}
pub fn is_float(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.info() == BasicInfo::IsFloat,
_ => false,
}
}
pub fn is_complex(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.info() == BasicInfo::IsComplex,
_ => false,
}
}
pub fn is_numeric(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.info().is_numeric(),
_ => false,
}
}
pub fn is_string(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.info() == BasicInfo::IsString,
_ => false,
}
}
pub fn is_typed(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => !b.typ().is_untyped(),
_ => true,
}
}
pub fn is_untyped(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.typ().is_untyped(),
_ => false,
}
}
pub fn is_ordered(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.info().is_ordered(),
_ => false,
}
}
pub fn is_const_type(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.info().is_const_type(),
_ => false,
}
}
pub fn is_interface(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Interface(_) => true,
_ => false,
}
}
pub fn has_nil(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.typ() == BasicType::UnsafePointer,
Type::Slice(_)
| Type::Pointer(_)
| Type::Signature(_)
| Type::Interface(_)
| Type::Map(_)
| Type::Chan(_) => true,
_ => false,
}
}
pub fn comparable(&self, objs: &TCObjects) -> bool {
match self.underlying_val(objs) {
Type::Basic(b) => b.typ() != BasicType::UntypedNil,
Type::Pointer(_) | Type::Interface(_) | Type::Chan(_) => true,
Type::Struct(s) => !s
.fields()
.iter()
.any(|f| !comparable(objs.lobjs[*f].typ().unwrap(), objs)),
Type::Array(a) => comparable(a.elem(), objs),
_ => false,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum BasicType {
Invalid,
Bool,
Int,
Int8,
Int16,
Int32,
Int64,
Uint,
Uint8,
Uint16,
Uint32,
Uint64,
Uintptr,
Float32,
Float64,
Complex64,
Complex128,
Str,
UnsafePointer,
UntypedBool,
UntypedInt,
UntypedRune,
UntypedFloat,
UntypedComplex,
UntypedString,
UntypedNil,
Byte, Rune, }
impl BasicType {
pub fn is_unsigned(&self) -> bool {
match self {
BasicType::Uint
| BasicType::Uint8
| BasicType::Uint16
| BasicType::Uint32
| BasicType::Uint64
| BasicType::Byte
| BasicType::Rune
| BasicType::Uintptr => true,
_ => false,
}
}
pub fn is_untyped(&self) -> bool {
match self {
BasicType::UntypedBool
| BasicType::UntypedInt
| BasicType::UntypedRune
| BasicType::UntypedFloat
| BasicType::UntypedComplex
| BasicType::UntypedString
| BasicType::UntypedNil => true,
_ => false,
}
}
pub fn real_type(&self) -> BasicType {
match self {
BasicType::Byte => BasicType::Uint8,
BasicType::Rune => BasicType::Int32,
_ => *self,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BasicInfo {
IsInvalid,
IsBoolean,
IsInteger,
IsFloat,
IsComplex,
IsString,
}
impl BasicInfo {
pub fn is_ordered(&self) -> bool {
match self {
BasicInfo::IsInteger | BasicInfo::IsFloat | BasicInfo::IsString => true,
_ => false,
}
}
pub fn is_numeric(&self) -> bool {
match self {
BasicInfo::IsInteger | BasicInfo::IsFloat | BasicInfo::IsComplex => true,
_ => false,
}
}
pub fn is_const_type(&self) -> bool {
match self {
BasicInfo::IsBoolean
| BasicInfo::IsInteger
| BasicInfo::IsFloat
| BasicInfo::IsComplex
| BasicInfo::IsString => true,
_ => false,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct BasicDetail {
typ: BasicType,
info: BasicInfo,
name: &'static str,
}
impl BasicDetail {
pub fn new(typ: BasicType, info: BasicInfo, name: &'static str) -> BasicDetail {
BasicDetail {
typ: typ,
info: info,
name: name,
}
}
pub fn typ(&self) -> BasicType {
self.typ
}
pub fn info(&self) -> BasicInfo {
self.info
}
pub fn name(&self) -> &str {
self.name
}
pub fn size_of(&self) -> usize {
match self.typ {
BasicType::Bool | BasicType::Byte | BasicType::Uint8 | BasicType::Int8 => 1,
BasicType::Int16 | BasicType::Uint16 => 2,
BasicType::Int32 | BasicType::Uint32 | BasicType::Rune | BasicType::Float32 => 4,
BasicType::Int64 | BasicType::Uint64 | BasicType::Float64 | BasicType::Complex64 => 8,
BasicType::Int | BasicType::Uint | BasicType::Uintptr | BasicType::UnsafePointer => {
std_size_of::<usize>()
}
BasicType::Complex128 => 16,
BasicType::Str => std_size_of::<String>(),
_ => unreachable!(),
}
}
}
#[derive(Debug)]
pub struct ArrayDetail {
len: Option<u64>,
elem: TypeKey,
}
impl ArrayDetail {
pub fn new(elem: TypeKey, len: Option<u64>) -> ArrayDetail {
ArrayDetail {
len: len,
elem: elem,
}
}
pub fn len(&self) -> Option<u64> {
self.len
}
pub fn set_len(&mut self, len: u64) {
self.len = Some(len);
}
pub fn elem(&self) -> TypeKey {
self.elem
}
}
#[derive(Debug)]
pub struct SliceDetail {
elem: TypeKey,
}
impl SliceDetail {
pub fn new(elem: TypeKey) -> SliceDetail {
SliceDetail { elem: elem }
}
pub fn elem(&self) -> TypeKey {
self.elem
}
}
#[derive(Debug)]
pub struct StructDetail {
fields: Vec<ObjKey>, tags: Option<Vec<Option<String>>>, }
impl StructDetail {
pub fn new(
fields: Vec<ObjKey>,
tags: Option<Vec<Option<String>>>,
objs: &TCObjects,
) -> StructDetail {
if cfg!(debug_assertions) {
let set = ObjSet::new();
let result = fields.iter().try_fold(set, |s, x| {
if !objs.lobjs[*x].entity_type().is_var() {
Err(())
} else if s.insert(*x, objs).is_some() {
Err(())
} else {
Ok::<ObjSet, ()>(s)
}
});
assert!(result.is_ok());
assert!(tags.is_none() || fields.len() >= tags.as_ref().unwrap().len());
}
StructDetail {
fields: fields,
tags: tags,
}
}
pub fn fields(&self) -> &Vec<ObjKey> {
&self.fields
}
pub fn tag(&self, i: usize) -> Option<&String> {
self.tags
.as_ref()
.map(|x| if i < x.len() { x[i].as_ref() } else { None })
.flatten()
}
}
#[derive(Debug)]
pub struct PointerDetail {
base: TypeKey, }
impl PointerDetail {
pub fn new(base: TypeKey) -> PointerDetail {
PointerDetail { base: base }
}
pub fn base(&self) -> TypeKey {
self.base
}
}
#[derive(Debug)]
pub struct TupleDetail {
vars: Vec<ObjKey>, }
impl TupleDetail {
pub fn new(vars: Vec<ObjKey>) -> TupleDetail {
TupleDetail { vars: vars }
}
pub fn vars(&self) -> &Vec<ObjKey> {
&self.vars
}
pub fn vars_mut(&mut self) -> &mut Vec<ObjKey> {
&mut self.vars
}
}
#[derive(Copy, Clone, Debug)]
pub struct SignatureDetail {
scope: Option<ScopeKey>, recv: Option<ObjKey>, params: TypeKey,
results: TypeKey,
variadic: bool,
}
impl SignatureDetail {
pub fn new(
scope: Option<ScopeKey>,
recv: Option<ObjKey>,
params: TypeKey,
results: TypeKey,
variadic: bool,
objs: &TCObjects,
) -> SignatureDetail {
if false && cfg!(debug_assertions) {
if variadic {
let typ = &objs.types[params];
match typ {
Type::Tuple(t) => {
assert!(t.vars.len() > 0);
let okey = t.vars[t.vars.len() - 1];
let tkey = &objs.lobjs[okey].typ().unwrap();
assert!(objs.types[*tkey].try_as_slice().is_some());
}
_ => unreachable!(),
}
}
}
SignatureDetail {
scope: scope,
recv: recv,
params: params,
results: results,
variadic: variadic,
}
}
pub fn scope(&self) -> Option<ScopeKey> {
self.scope
}
pub fn recv(&self) -> &Option<ObjKey> {
&self.recv
}
pub fn set_recv(&mut self, r: Option<ObjKey>) {
self.recv = r
}
pub fn params(&self) -> TypeKey {
self.params
}
pub fn set_params(&mut self, p: TypeKey) {
self.params = p;
}
pub fn results(&self) -> TypeKey {
self.results
}
pub fn variadic(&self) -> bool {
self.variadic
}
pub fn params_count(&self, objs: &TCObjects) -> usize {
let l = objs.types[self.params].try_as_tuple().unwrap().vars().len();
if self.variadic {
l - 1
} else {
l
}
}
pub fn results_count(&self, objs: &TCObjects) -> usize {
objs.types[self.results]
.try_as_tuple()
.unwrap()
.vars()
.len()
}
}
#[derive(Debug)]
pub struct InterfaceDetail {
methods: Vec<ObjKey>,
embeddeds: Vec<TypeKey>,
all_methods: Rc<RefCell<Option<Vec<ObjKey>>>>,
}
impl InterfaceDetail {
pub fn new(
mut methods: Vec<ObjKey>,
mut embeddeds: Vec<TypeKey>,
objs: &mut TCObjects,
) -> InterfaceDetail {
let set = ObjSet::new();
let result = methods.iter().try_fold(set, |s, x| {
let mobj = &objs.lobjs[*x];
if !mobj.entity_type().is_func() {
Err(())
} else if s.insert(*x, &objs).is_some() {
Err(())
} else {
let signature = objs.types[mobj.typ().unwrap()].try_as_signature_mut();
if let Some(sig) = signature {
if sig.recv.is_none() {
let var =
LangObj::new_var(mobj.pos(), mobj.pkg(), "".to_string(), mobj.typ());
sig.recv = Some(objs.lobjs.insert(var));
}
Ok(s)
} else {
Err(())
}
}
});
assert!(result.is_ok());
methods.sort_by(compare_by_method_name!(&objs));
embeddeds.sort_by(compare_by_type_name!(&objs));
InterfaceDetail {
methods: methods,
embeddeds: embeddeds,
all_methods: Rc::new(RefCell::new(None)),
}
}
pub fn new_empty() -> InterfaceDetail {
InterfaceDetail {
methods: Vec::new(),
embeddeds: Vec::new(),
all_methods: Rc::new(RefCell::new(Some(Vec::new()))),
}
}
pub fn methods(&self) -> &Vec<ObjKey> {
&self.methods
}
pub fn methods_mut(&mut self) -> &mut Vec<ObjKey> {
&mut self.methods
}
pub fn embeddeds(&self) -> &Vec<TypeKey> {
&self.embeddeds
}
pub fn embeddeds_mut(&mut self) -> &mut Vec<TypeKey> {
&mut self.embeddeds
}
pub fn all_methods(&self) -> Ref<Option<Vec<ObjKey>>> {
self.all_methods.borrow()
}
pub fn all_methods_mut(&self) -> RefMut<Option<Vec<ObjKey>>> {
self.all_methods.borrow_mut()
}
pub fn all_methods_push(&self, t: ObjKey) {
if self.all_methods.borrow_mut().is_none() {
*self.all_methods.borrow_mut() = Some(vec![t]);
} else {
self.all_methods.borrow_mut().as_mut().unwrap().push(t);
}
}
pub fn is_empty(&self) -> bool {
self.all_methods().as_ref().unwrap().len() == 0
}
pub fn set_empty_complete(&self) {
*self.all_methods.borrow_mut() = Some(vec![]);
}
pub fn complete(&self, objs: &TCObjects) {
if self.all_methods.borrow().is_some() {
return;
}
let mut all = self.methods.clone();
for tkey in self.embeddeds.iter() {
let embeded = &objs.types[*tkey].try_as_interface().unwrap();
embeded.complete(objs);
all.append(&mut embeded.all_methods().as_ref().unwrap().clone());
}
all.sort_by(compare_by_method_name!(&objs));
*self.all_methods.borrow_mut() = Some(all);
}
}
#[derive(Debug)]
pub struct MapDetail {
key: TypeKey,
elem: TypeKey,
}
impl MapDetail {
pub fn new(key: TypeKey, elem: TypeKey) -> MapDetail {
MapDetail {
key: key,
elem: elem,
}
}
pub fn key(&self) -> TypeKey {
self.key
}
pub fn elem(&self) -> TypeKey {
self.elem
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ChanDir {
SendRecv,
SendOnly,
RecvOnly,
}
#[derive(Debug)]
pub struct ChanDetail {
dir: ChanDir,
elem: TypeKey,
}
impl ChanDetail {
pub fn new(dir: ChanDir, elem: TypeKey) -> ChanDetail {
ChanDetail {
dir: dir,
elem: elem,
}
}
pub fn dir(&self) -> ChanDir {
self.dir
}
pub fn elem(&self) -> TypeKey {
self.elem
}
}
#[derive(Debug)]
pub struct NamedDetail {
obj: Option<ObjKey>, underlying: Option<TypeKey>, methods: Vec<ObjKey>, }
impl NamedDetail {
pub fn new(
obj: Option<ObjKey>,
underlying: Option<TypeKey>,
methods: Vec<ObjKey>,
objs: &TCObjects,
) -> NamedDetail {
if cfg!(debug_assertions) && underlying.is_some() {
let t = &objs.types[underlying.unwrap()];
assert!(t.try_as_named().is_none());
}
NamedDetail {
obj: obj,
underlying: underlying,
methods: methods,
}
}
pub fn obj(&self) -> &Option<ObjKey> {
&self.obj
}
pub fn set_obj(&mut self, obj: ObjKey) {
self.obj = Some(obj)
}
pub fn methods(&self) -> &Vec<ObjKey> {
&self.methods
}
pub fn methods_mut(&mut self) -> &mut Vec<ObjKey> {
&mut self.methods
}
pub fn underlying(&self) -> TypeKey {
self.underlying.unwrap()
}
pub fn set_underlying(&mut self, t: TypeKey) {
self.underlying = Some(t);
}
}
pub fn size_of(t: &TypeKey, objs: &TCObjects) -> usize {
let typ = &objs.types[*t].underlying_val(objs);
match typ {
Type::Basic(detail) => detail.size_of(),
_ => std_size_of::<usize>(),
}
}
pub fn underlying_type(t: TypeKey, objs: &TCObjects) -> TypeKey {
let typ = &objs.types[t];
match typ.underlying() {
Some(ut) => ut,
None => t,
}
}
pub fn deep_underlying_type(t: TypeKey, objs: &TCObjects) -> TypeKey {
let mut typ = &objs.types[t];
let mut ret = t;
loop {
match typ.underlying() {
Some(ut) => {
typ = &objs.types[ut];
ret = ut;
continue;
}
None => return ret,
}
}
}
pub fn is_named(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_named()
}
pub fn is_boolean(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_boolean(objs)
}
pub fn is_integer(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_integer(objs)
}
pub fn is_unsigned(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_unsigned(objs)
}
pub fn is_float(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_float(objs)
}
pub fn is_complex(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_complex(objs)
}
pub fn is_numeric(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_numeric(objs)
}
pub fn is_string(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_string(objs)
}
pub fn is_typed(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_typed(objs)
}
pub fn is_untyped(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_untyped(objs)
}
pub fn is_ordered(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_ordered(objs)
}
pub fn is_const_type(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_const_type(objs)
}
pub fn is_interface(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].is_interface(objs)
}
pub fn has_nil(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].has_nil(objs)
}
pub fn comparable(t: TypeKey, objs: &TCObjects) -> bool {
objs.types[t].comparable(objs)
}
pub fn untyped_default_type(t: TypeKey, objs: &TCObjects) -> TypeKey {
objs.types[t].try_as_basic().map_or(t, |bt| match bt.typ() {
BasicType::UntypedBool => objs.universe().types()[&BasicType::Bool],
BasicType::UntypedInt => objs.universe().types()[&BasicType::Int],
BasicType::UntypedRune => *objs.universe().rune(),
BasicType::UntypedFloat => objs.universe().types()[&BasicType::Float64],
BasicType::UntypedComplex => objs.universe().types()[&BasicType::Complex128],
BasicType::UntypedString => objs.universe().types()[&BasicType::Str],
_ => t,
})
}
pub fn identical(x: TypeKey, y: TypeKey, objs: &TCObjects) -> bool {
identical_impl(x, y, true, &mut HashSet::new(), objs)
}
pub fn identical_option(x: Option<TypeKey>, y: Option<TypeKey>, objs: &TCObjects) -> bool {
identical_impl_o(x, y, true, &mut HashSet::new(), objs)
}
pub fn identical_ignore_tags(x: Option<TypeKey>, y: Option<TypeKey>, objs: &TCObjects) -> bool {
identical_impl_o(x, y, false, &mut HashSet::new(), objs)
}
fn identical_impl_o(
x: Option<TypeKey>,
y: Option<TypeKey>,
cmp_tags: bool,
dup: &mut HashSet<(TypeKey, TypeKey)>,
objs: &TCObjects,
) -> bool {
if x == y {
true
} else if x.is_none() || y.is_none() {
false
} else {
identical_impl(x.unwrap(), y.unwrap(), cmp_tags, dup, objs)
}
}
fn identical_impl(
x: TypeKey,
y: TypeKey,
cmp_tags: bool,
dup: &mut HashSet<(TypeKey, TypeKey)>,
objs: &TCObjects,
) -> bool {
if x == y {
return true;
}
let tx = &objs.types[x];
let ty = &objs.types[y];
match (tx, ty) {
(Type::Basic(bx), Type::Basic(by)) => bx.typ().real_type() == by.typ().real_type(),
(Type::Array(ax), Type::Array(ay)) => {
ax.len() == ay.len() && identical_impl(ax.elem(), ay.elem(), cmp_tags, dup, objs)
}
(Type::Slice(sx), Type::Slice(sy)) => {
identical_impl(sx.elem(), sy.elem(), cmp_tags, dup, objs)
}
(Type::Struct(sx), Type::Struct(sy)) => {
if sx.fields().len() == sy.fields().len() {
!sx.fields().iter().enumerate().any(|(i, f)| {
let of = &objs.lobjs[*f];
let og = &objs.lobjs[sy.fields()[i]];
(of.var_embedded() != og.var_embedded())
|| (cmp_tags && sx.tag(i) != sy.tag(i))
|| (!of.same_id(og.pkg(), og.name(), objs))
|| (!identical_impl_o(of.typ(), og.typ(), cmp_tags, dup, objs))
})
} else {
false
}
}
(Type::Pointer(px), Type::Pointer(py)) => {
identical_impl(px.base(), py.base(), cmp_tags, dup, objs)
}
(Type::Tuple(tx), Type::Tuple(ty)) => {
if tx.vars().len() == ty.vars().len() {
!tx.vars().iter().enumerate().any(|(i, v)| {
let ov = &objs.lobjs[*v];
let ow = &objs.lobjs[ty.vars()[i]];
!identical_impl_o(ov.typ(), ow.typ(), cmp_tags, dup, objs)
})
} else {
false
}
}
(Type::Signature(sx), Type::Signature(sy)) => {
sx.variadic() == sy.variadic()
&& identical_impl(sx.params(), sy.params(), cmp_tags, dup, objs)
&& identical_impl(sx.results(), sy.results(), cmp_tags, dup, objs)
}
(Type::Interface(ix), Type::Interface(iy)) => {
match (ix.all_methods().as_ref(), iy.all_methods().as_ref()) {
(None, None) => true,
(Some(a), Some(b)) => {
if a.len() == b.len() {
let pair = (x, y);
if dup.get(&pair).is_some() {
true
} else {
dup.insert(pair);
!a.iter().enumerate().any(|(i, k)| {
let ox = &objs.lobjs[*k];
let oy = &objs.lobjs[b[i]];
ox.id(objs) != oy.id(objs)
|| !identical_impl_o(ox.typ(), oy.typ(), cmp_tags, dup, objs)
})
}
} else {
false
}
}
_ => false,
}
}
(Type::Map(mx), Type::Map(my)) => {
identical_impl(mx.key(), my.key(), cmp_tags, dup, objs)
&& identical_impl(mx.elem(), my.elem(), cmp_tags, dup, objs)
}
(Type::Chan(cx), Type::Chan(cy)) => {
cx.dir() == cy.dir() && identical_impl(cx.elem(), cy.elem(), cmp_tags, dup, objs)
}
(Type::Named(nx), Type::Named(ny)) => nx.obj() == ny.obj(),
_ => false,
}
}
pub fn fmt_type(t: Option<TypeKey>, f: &mut fmt::Formatter<'_>, objs: &TCObjects) -> fmt::Result {
fmt_type_impl(t, f, &mut HashSet::new(), objs)
}
pub fn fmt_signature(t: TypeKey, f: &mut fmt::Formatter<'_>, objs: &TCObjects) -> fmt::Result {
fmt_signature_impl(t, f, &mut HashSet::new(), objs)
}
fn fmt_type_impl(
t: Option<TypeKey>,
f: &mut fmt::Formatter<'_>,
visited: &mut HashSet<TypeKey>,
objs: &TCObjects,
) -> fmt::Result {
if t.is_none() {
return f.write_str("<nil>");
}
let tkey = t.unwrap();
if visited.get(&tkey).is_some() {
return tkey.fmt(f);
}
visited.insert(tkey);
let typ = &objs.types[tkey];
match typ {
Type::Basic(detail) => {
if detail.typ == BasicType::UnsafePointer {
f.write_str("unsafe.")?;
}
write!(f, "{}", detail.name)?;
}
Type::Array(detail) => {
match detail.len {
Some(i) => write!(f, "[{}]", i)?,
None => f.write_str("[unknown]")?,
};
fmt_type_impl(Some(detail.elem), f, visited, objs)?;
}
Type::Slice(detail) => {
f.write_str("[]")?;
fmt_type_impl(Some(detail.elem), f, visited, objs)?;
}
Type::Struct(detail) => {
f.write_str("struct{")?;
for (i, key) in detail.fields().iter().enumerate() {
if i > 0 {
f.write_str("; ")?;
}
let field = &objs.lobjs[*key];
if !field.var_embedded() {
write!(f, "{} ", field.name())?;
}
fmt_type_impl(field.typ(), f, visited, objs)?;
if let Some(tag) = detail.tag(i) {
write!(f, " {}", tag)?;
}
}
f.write_str("}")?;
}
Type::Pointer(detail) => {
f.write_char('*')?;
fmt_type_impl(Some(detail.base()), f, visited, objs)?;
}
Type::Tuple(_) => {
fmt_tuple(tkey, false, f, visited, objs)?;
}
Type::Signature(_) => {
f.write_str("func")?;
fmt_signature_impl(t.unwrap(), f, visited, objs)?;
}
Type::Interface(detail) => {
f.write_str("interface{")?;
for (i, k) in detail.methods().iter().enumerate() {
if i > 0 {
f.write_str("; ")?;
}
let mobj = &objs.lobjs[*k];
f.write_str(mobj.name())?;
fmt_signature_impl(mobj.typ().unwrap(), f, visited, objs)?;
}
for (i, k) in detail.embeddeds().iter().enumerate() {
if i > 0 || detail.methods().len() > 0 {
f.write_str("; ")?;
}
fmt_type_impl(Some(*k), f, visited, objs)?;
}
if detail.all_methods().is_none() {
f.write_str(" /* incomplete */")?;
}
f.write_char('}')?;
}
Type::Map(detail) => {
f.write_str("map[")?;
fmt_type_impl(Some(detail.key()), f, visited, objs)?;
f.write_char(']')?;
fmt_type_impl(Some(detail.elem()), f, visited, objs)?;
}
Type::Chan(detail) => {
let (s, paren) = match detail.dir() {
ChanDir::SendRecv => ("chan ", {
let elm = &objs.types[detail.elem()];
if let Some(c) = elm.try_as_chan() {
c.dir() == ChanDir::RecvOnly
} else {
false
}
}),
ChanDir::SendOnly => ("chan<- ", false),
ChanDir::RecvOnly => ("<-chan ", false),
};
f.write_str(s)?;
if paren {
f.write_char('(')?;
}
fmt_type_impl(Some(detail.elem()), f, visited, objs)?;
if paren {
f.write_char(')')?;
}
}
Type::Named(detail) => {
if let Some(okey) = detail.obj() {
let o = &objs.lobjs[*okey];
if let Some(pkg) = o.pkg() {
objs.pkgs[pkg].fmt_with_qualifier(f, objs.fmt_qualifier.as_ref())?;
}
f.write_str(o.name())?;
} else {
f.write_str("<Named w/o object>")?;
}
}
}
Ok(())
}
fn fmt_signature_impl(
t: TypeKey,
f: &mut fmt::Formatter<'_>,
visited: &mut HashSet<TypeKey>,
objs: &TCObjects,
) -> fmt::Result {
let sig = &objs.types[t].try_as_signature().unwrap();
fmt_tuple(sig.params(), sig.variadic(), f, visited, &objs)?;
f.write_char(' ')?;
let results = &objs.types[sig.results()].try_as_tuple().unwrap();
if results.vars().len() == 1 {
let obj = &objs.lobjs[results.vars()[0]];
if obj.name().is_empty() {
return fmt_type_impl(obj.typ(), f, visited, objs);
}
}
fmt_tuple(sig.results(), false, f, visited, objs)
}
fn fmt_tuple(
tkey: TypeKey,
variadic: bool,
f: &mut fmt::Formatter<'_>,
visited: &mut HashSet<TypeKey>,
objs: &TCObjects,
) -> fmt::Result {
f.write_char('(')?;
let tuple = &objs.types[tkey].try_as_tuple().unwrap();
for (i, v) in tuple.vars().iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
let obj = &objs.lobjs[*v];
if !obj.name().is_empty() {
write!(f, "{} ", obj.name())?;
}
let tkey = obj.typ();
if variadic && i == tuple.vars().len() - 1 {
let utype = underlying_type(tkey.unwrap(), &objs);
let typ = &objs.types[utype];
match typ {
Type::Slice(detail) => {
f.write_str("...")?;
fmt_type_impl(Some(detail.elem()), f, visited, objs)?;
}
Type::Basic(detail) => {
assert!(detail.typ() == BasicType::Str);
fmt_type_impl(tkey, f, visited, objs)?;
f.write_str("...")?;
}
_ => unreachable!(),
}
} else {
fmt_type_impl(tkey, f, visited, objs)?;
}
}
f.write_char(')')?;
Ok(())
}