You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

608 lines
16 KiB

/*
* vim: set ft=rust:
* vim: set ft=reason:
*
* Copyright (c) 2009 - 2013 Monoidics ltd.
* Copyright (c) 2013 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
open! Utils;
/** The Smallfoot Intermediate Language: Types */
let module L = Logging;
let module F = Format;
/** Type to represent one @Annotation. */
type annotation = {
class_name: string, /** name of the annotation */
parameters: list string /** currently only one string parameter */
};
/** Compare function for annotations. */
let annotation_compare a1 a2 => {
let n = string_compare a1.class_name a2.class_name;
if (n != 0) {
n
} else {
IList.compare string_compare a1.parameters a2.parameters
}
};
/** Pretty print an annotation. */
let pp_annotation fmt annotation => F.fprintf fmt "@@%s" annotation.class_name;
let module AnnotMap = PrettyPrintable.MakePPMap {
type t = annotation;
let compare = annotation_compare;
let pp_key = pp_annotation;
};
/** Annotation for one item: a list of annotations with visibility. */
type item_annotation = list (annotation, bool);
/** Compare function for annotation items. */
let item_annotation_compare ia1 ia2 => {
let cmp (a1, b1) (a2, b2) => {
let n = annotation_compare a1 a2;
if (n != 0) {
n
} else {
bool_compare b1 b2
}
};
IList.compare cmp ia1 ia2
};
/** Pretty print an item annotation. */
let pp_item_annotation fmt item_annotation => {
let pp fmt (a, _) => pp_annotation fmt a;
F.fprintf fmt "<%a>" (pp_seq pp) item_annotation
};
let item_annotation_to_string ann => {
let pp fmt () => pp_item_annotation fmt ann;
pp_to_string pp ()
};
/** Empty item annotation. */
let item_annotation_empty = [];
/** Check if the item annodation is empty. */
let item_annotation_is_empty ia => ia == [];
let objc_class_str = "ObjC-Class";
let cpp_class_str = "Cpp-Class";
let class_annotation class_string => [({class_name: class_string, parameters: []}, true)];
let objc_class_annotation = class_annotation objc_class_str;
let cpp_class_annotation = class_annotation cpp_class_str;
/** Annotation for a method: return value and list of parameters. */
type method_annotation = (item_annotation, list item_annotation);
/** Compare function for Method annotations. */
let method_annotation_compare (ia1, ial1) (ia2, ial2) =>
IList.compare item_annotation_compare [ia1, ...ial1] [ia2, ...ial2];
/** Pretty print a method annotation. */
let pp_method_annotation s fmt (ia, ial) =>
F.fprintf fmt "%a %s(%a)" pp_item_annotation ia s (pp_seq pp_item_annotation) ial;
/** Empty method annotation. */
let method_annotation_empty = ([], []);
/** Check if the method annodation is empty. */
let method_annotation_is_empty (ia, ial) => IList.for_all item_annotation_is_empty [ia, ...ial];
/** Kinds of integers */
type ikind =
| IChar /** [char] */
| ISChar /** [signed char] */
| IUChar /** [unsigned char] */
| IBool /** [bool] */
| IInt /** [int] */
| IUInt /** [unsigned int] */
| IShort /** [short] */
| IUShort /** [unsigned short] */
| ILong /** [long] */
| IULong /** [unsigned long] */
| ILongLong /** [long long] (or [_int64] on Microsoft Visual C) */
| IULongLong /** [unsigned long long] (or [unsigned _int64] on Microsoft Visual C) */
| I128 /** [__int128_t] */
| IU128 /** [__uint128_t] */;
/** comparison for ikind */
let ikind_compare k1 k2 =>
switch (k1, k2) {
| (IChar, IChar) => 0
| (IChar, _) => (-1)
| (_, IChar) => 1
| (ISChar, ISChar) => 0
| (ISChar, _) => (-1)
| (_, ISChar) => 1
| (IUChar, IUChar) => 0
| (IUChar, _) => (-1)
| (_, IUChar) => 1
| (IBool, IBool) => 0
| (IBool, _) => (-1)
| (_, IBool) => 1
| (IInt, IInt) => 0
| (IInt, _) => (-1)
| (_, IInt) => 1
| (IUInt, IUInt) => 0
| (IUInt, _) => (-1)
| (_, IUInt) => 1
| (IShort, IShort) => 0
| (IShort, _) => (-1)
| (_, IShort) => 1
| (IUShort, IUShort) => 0
| (IUShort, _) => (-1)
| (_, IUShort) => 1
| (ILong, ILong) => 0
| (ILong, _) => (-1)
| (_, ILong) => 1
| (IULong, IULong) => 0
| (IULong, _) => (-1)
| (_, IULong) => 1
| (ILongLong, ILongLong) => 0
| (ILongLong, _) => (-1)
| (_, ILongLong) => 1
| (IULongLong, IULongLong) => 0
| (IULongLong, _) => (-1)
| (_, IULongLong) => 1
| (I128, I128) => 0
| (I128, _) => (-1)
| (_, I128) => 1
| (IU128, IU128) => 0
};
let ikind_to_string =
fun
| IChar => "char"
| ISChar => "signed char"
| IUChar => "unsigned char"
| IBool => "_Bool"
| IInt => "int"
| IUInt => "unsigned int"
| IShort => "short"
| IUShort => "unsigned short"
| ILong => "long"
| IULong => "unsigned long"
| ILongLong => "long long"
| IULongLong => "unsigned long long"
| I128 => "__int128_t"
| IU128 => "__uint128_t";
let ikind_is_char =
fun
| IChar
| ISChar
| IUChar => true
| _ => false;
let ikind_is_unsigned =
fun
| IUChar
| IUInt
| IUShort
| IULong
| IULongLong => true
| _ => false;
let int_of_int64_kind i ik => IntLit.of_int64_unsigned i (ikind_is_unsigned ik);
/** Kinds of floating-point numbers */
type fkind =
| FFloat /** [float] */
| FDouble /** [double] */
| FLongDouble /** [long double] */;
/** comparison for fkind */
let fkind_compare k1 k2 =>
switch (k1, k2) {
| (FFloat, FFloat) => 0
| (FFloat, _) => (-1)
| (_, FFloat) => 1
| (FDouble, FDouble) => 0
| (FDouble, _) => (-1)
| (_, FDouble) => 1
| (FLongDouble, FLongDouble) => 0
};
let fkind_to_string =
fun
| FFloat => "float"
| FDouble => "double"
| FLongDouble => "long double";
/** kind of pointer */
type ptr_kind =
| Pk_pointer /** C/C++, Java, Objc standard/__strong pointer */
| Pk_reference /** C++ reference */
| Pk_objc_weak /** Obj-C __weak pointer */
| Pk_objc_unsafe_unretained /** Obj-C __unsafe_unretained pointer */
| Pk_objc_autoreleasing /** Obj-C __autoreleasing pointer */;
let ptr_kind_compare pk1 pk2 =>
switch (pk1, pk2) {
| (Pk_pointer, Pk_pointer) => 0
| (Pk_pointer, _) => (-1)
| (_, Pk_pointer) => 1
| (Pk_reference, Pk_reference) => 0
| (_, Pk_reference) => (-1)
| (Pk_reference, _) => 1
| (Pk_objc_weak, Pk_objc_weak) => 0
| (Pk_objc_weak, _) => (-1)
| (_, Pk_objc_weak) => 1
| (Pk_objc_unsafe_unretained, Pk_objc_unsafe_unretained) => 0
| (Pk_objc_unsafe_unretained, _) => (-1)
| (_, Pk_objc_unsafe_unretained) => 1
| (Pk_objc_autoreleasing, Pk_objc_autoreleasing) => 0
};
let ptr_kind_string =
fun
| Pk_reference => "&"
| Pk_pointer => "*"
| Pk_objc_weak => "__weak *"
| Pk_objc_unsafe_unretained => "__unsafe_unretained *"
| Pk_objc_autoreleasing => "__autoreleasing *";
/** statically determined length of an array type, if any */
type static_length = option IntLit.t;
type struct_fields = list (Ident.fieldname, t, item_annotation)
/** Type for a structured value. */
and struct_typ = {
name: Typename.t, /** name */
instance_fields: struct_fields, /** non-static fields */
static_fields: struct_fields, /** static fields */
csu: Csu.t, /** class/struct/union */
superclasses: list Typename.t, /** list of superclasses */
def_methods: list Procname.t, /** methods defined */
struct_annotations: item_annotation /** annotations */
}
/** types for sil (structured) expressions */
and t =
| Tvar of Typename.t /** named type */
| Tint of ikind /** integer type */
| Tfloat of fkind /** float type */
| Tvoid /** void type */
| Tfun of bool /** function type with noreturn attribute */
| Tptr of t ptr_kind /** pointer type */
| Tstruct of struct_typ /** Type for a structured value */
| Tarray of t static_length /** array type with statically fixed length */;
let rec fld_typ_ann_compare fta1 fta2 =>
triple_compare Ident.fieldname_compare compare item_annotation_compare fta1 fta2
and fld_typ_ann_list_compare ftal1 ftal2 => IList.compare fld_typ_ann_compare ftal1 ftal2
and struct_typ_compare struct_typ1 struct_typ2 =>
if (struct_typ1.csu == Csu.Class Csu.Java && struct_typ2.csu == Csu.Class Csu.Java) {
Typename.compare struct_typ1.name struct_typ2.name
} else {
let n = fld_typ_ann_list_compare struct_typ1.instance_fields struct_typ2.instance_fields;
if (n != 0) {
n
} else {
let n = fld_typ_ann_list_compare struct_typ1.static_fields struct_typ2.static_fields;
if (n != 0) {
n
} else {
let n = Csu.compare struct_typ1.csu struct_typ2.csu;
if (n != 0) {
n
} else {
Typename.compare struct_typ1.name struct_typ2.name
}
}
}
}
/** Comparision for types. */
and compare t1 t2 =>
if (t1 === t2) {
0
} else {
switch (t1, t2) {
| (Tvar tn1, Tvar tn2) => Typename.compare tn1 tn2
| (Tvar _, _) => (-1)
| (_, Tvar _) => 1
| (Tint ik1, Tint ik2) => ikind_compare ik1 ik2
| (Tint _, _) => (-1)
| (_, Tint _) => 1
| (Tfloat fk1, Tfloat fk2) => fkind_compare fk1 fk2
| (Tfloat _, _) => (-1)
| (_, Tfloat _) => 1
| (Tvoid, Tvoid) => 0
| (Tvoid, _) => (-1)
| (_, Tvoid) => 1
| (Tfun noreturn1, Tfun noreturn2) => bool_compare noreturn1 noreturn2
| (Tfun _, _) => (-1)
| (_, Tfun _) => 1
| (Tptr t1' pk1, Tptr t2' pk2) =>
let n = compare t1' t2';
if (n != 0) {
n
} else {
ptr_kind_compare pk1 pk2
}
| (Tptr _, _) => (-1)
| (_, Tptr _) => 1
| (Tstruct struct_typ1, Tstruct struct_typ2) => struct_typ_compare struct_typ1 struct_typ2
| (Tstruct _, _) => (-1)
| (_, Tstruct _) => 1
| (Tarray t1 _, Tarray t2 _) => compare t1 t2
}
};
let struct_typ_equal struct_typ1 struct_typ2 => struct_typ_compare struct_typ1 struct_typ2 == 0;
let equal t1 t2 => compare t1 t2 == 0;
let rec pp_struct_typ pe pp_base f {csu, instance_fields, name} =>
if false {
/* change false to true to print the details of struct */
F.fprintf
f
"%s %a {%a} %a"
(Csu.name csu)
Typename.pp
name
(pp_seq (fun f (fld, t, _) => F.fprintf f "%a %a" (pp_full pe) t Ident.pp_fieldname fld))
instance_fields
pp_base
()
} else {
F.fprintf f "%a %a" Typename.pp name pp_base ()
}
/** Pretty print a type declaration.
pp_base prints the variable for a declaration, or can be skip to print only the type */
and pp_decl pe pp_base f =>
fun
| Tvar tname => F.fprintf f "%s %a" (Typename.to_string tname) pp_base ()
| Tint ik => F.fprintf f "%s %a" (ikind_to_string ik) pp_base ()
| Tfloat fk => F.fprintf f "%s %a" (fkind_to_string fk) pp_base ()
| Tvoid => F.fprintf f "void %a" pp_base ()
| Tfun false => F.fprintf f "_fn_ %a" pp_base ()
| Tfun true => F.fprintf f "_fn_noreturn_ %a" pp_base ()
| Tptr ((Tarray _ | Tfun _) as typ) pk => {
let pp_base' fmt () => F.fprintf fmt "(%s%a)" (ptr_kind_string pk) pp_base ();
pp_decl pe pp_base' f typ
}
| Tptr typ pk => {
let pp_base' fmt () => F.fprintf fmt "%s%a" (ptr_kind_string pk) pp_base ();
pp_decl pe pp_base' f typ
}
| Tstruct struct_typ => pp_struct_typ pe pp_base f struct_typ
| Tarray typ static_len => {
let pp_array_static_len fmt => (
fun
| Some static_len => IntLit.pp fmt static_len
| None => F.fprintf fmt "_"
);
let pp_base' fmt () => F.fprintf fmt "%a[%a]" pp_base () pp_array_static_len static_len;
pp_decl pe pp_base' f typ
}
/** Pretty print a type with all the details, using the C syntax. */
and pp_full pe => pp_decl pe (fun _ () => ())
/** Pretty print a type. Do nothing by default. */
and pp pe f te =>
if Config.print_types {
pp_full pe f te
} else {
()
};
let to_string typ => {
let pp fmt () => pp_full pe_text fmt typ;
pp_to_string pp ()
};
/** dump a type with all the details. */
let d_full (t: t) => L.add_print_action (L.PTtyp_full, Obj.repr t);
/** dump a list of types. */
let d_list (tl: list t) => L.add_print_action (L.PTtyp_list, Obj.repr tl);
/** {2 Sets and maps of types} */
let module StructSet = Set.Make {
type t = struct_typ;
let compare = struct_typ_compare;
};
let module Set = Set.Make {
type nonrec t = t;
let compare = compare;
};
let module Map = Map.Make {
type nonrec t = t;
let compare = compare;
};
let module Tbl = Hashtbl.Make {
type nonrec t = t;
let equal = equal;
let hash = Hashtbl.hash;
};
let name t =>
switch t {
| Tvar name
| Tstruct {name} => Some name
| _ => None
};
let unsome s =>
fun
| Some default_typ => default_typ
| None => {
L.err "No default typ in %s@." s;
assert false
};
/** turn a *T into a T. fails if [typ] is not a pointer type */
let strip_ptr =
fun
| Tptr t _ => t
| _ => assert false;
/** If an array type, return the type of the element.
If not, return the default type if given, otherwise raise an exception */
let array_elem default_opt =>
fun
| Tarray t_el _ => t_el
| _ => unsome "array_elem" default_opt;
/** the element typ of the final extensible array in the given typ, if any */
let rec get_extensible_array_element_typ =
fun
| Tarray typ _ => Some typ
| Tstruct {instance_fields} =>
Option.map_default
(fun (_, fld_typ, _) => get_extensible_array_element_typ fld_typ)
None
(IList.last instance_fields)
| _ => None;
/** If a struct type with field f, return the type of f.
If not, return the default type if given, otherwise raise an exception */
let struct_typ_fld default_opt f => {
let def () => unsome "struct_typ_fld" default_opt;
fun
| Tstruct struct_typ =>
try (
(fun (_, y, _) => y) (
IList.find (fun (_f, _, _) => Ident.fieldname_equal _f f) struct_typ.instance_fields
)
) {
| Not_found => def ()
}
| _ => def ()
};
let get_field_type_and_annotation fn =>
fun
| Tptr (Tstruct struct_typ) _
| Tstruct struct_typ =>
try {
let (_, t, a) =
IList.find
(fun (f, _, _) => Ident.fieldname_equal f fn)
(struct_typ.instance_fields @ struct_typ.static_fields);
Some (t, a)
} {
| Not_found => None
}
| _ => None;
/** if [struct_typ] is a class, return its class kind (Java, CPP, or Obj-C) */
let struct_typ_get_class_kind struct_typ =>
switch struct_typ.csu {
| Csu.Class class_kind => Some class_kind
| _ => None
};
/** return true if [struct_typ] is a Java class */
let struct_typ_is_java_class struct_typ =>
switch (struct_typ_get_class_kind struct_typ) {
| Some Csu.Java => true
| _ => false
};
/** return true if [struct_typ] is a C++ class. Note that this returns false for raw structs. */
let struct_typ_is_cpp_class struct_typ =>
switch (struct_typ_get_class_kind struct_typ) {
| Some Csu.CPP => true
| _ => false
};
/** return true if [struct_typ] is an Obj-C class. Note that this returns false for raw structs. */
let struct_typ_is_objc_class struct_typ =>
switch (struct_typ_get_class_kind struct_typ) {
| Some Csu.Objc => true
| _ => false
};
let is_class_of_kind typ ck =>
switch typ {
| Tstruct {csu: Csu.Class ck'} => ck == ck'
| _ => false
};
let is_objc_class typ => is_class_of_kind typ Csu.Objc;
let is_cpp_class typ => is_class_of_kind typ Csu.CPP;
let is_java_class typ => is_class_of_kind typ Csu.Java;
let rec is_array_of_cpp_class typ =>
switch typ {
| Tarray typ _ => is_array_of_cpp_class typ
| _ => is_cpp_class typ
};
let is_pointer_to_cpp_class typ =>
switch typ {
| Tptr t _ => is_cpp_class t
| _ => false
};
let has_block_prefix s =>
switch (Str.split_delim (Str.regexp_string Config.anonymous_block_prefix) s) {
| [_, _, ..._] => true
| _ => false
};
/** Check if type is a type for a block in objc */
let is_block_type typ => has_block_prefix (to_string typ);
let objc_ref_counter_annot = [({class_name: "ref_counter", parameters: []}, false)];
/** Field used for objective-c reference counting */
let objc_ref_counter_field = (Ident.fieldname_hidden, Tint IInt, objc_ref_counter_annot);
let is_objc_ref_counter_field (fld, _, a) =>
Ident.fieldname_is_hidden fld && item_annotation_compare a objc_ref_counter_annot == 0;