[generic models] Implement sample generic models, add code to handle them

Reviewed By: mbouaziz

Differential Revision: D5376185

fbshipit-source-id: e3e7cfc
master
Andrzej Kotulski 7 years ago committed by Facebook Github Bot
parent 3ed48e3196
commit b8e679dfeb

@ -0,0 +1,34 @@
/*
* Copyright (c) 2017 - 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.
*/
#include <vector>
namespace folly {
#define TYPEVAR(Name) \
struct __attribute__((annotate("__infer_type_var"))) Name {};
TYPEVAR(a1);
TYPEVAR(a2);
TYPEVAR(a3);
template <class Delim, class String, class OutputType>
void split(const Delim& delimiter,
const String& input,
std::vector<OutputType>& out,
bool ignoreEmpty = false) {
out.resize(1);
return;
}
template __attribute__((annotate("__infer_generic_model"))) void
split<a1, a2, a3>(const a1& delimiter,
const a2& input,
std::vector<a3>& out,
const bool ignoreEmpty);
}

@ -1084,6 +1084,61 @@ module Procname = {
to_generic_filename pname to_generic_filename pname
| _ => to_concrete_filename pname | _ => to_concrete_filename pname
}; };
let get_template_args_mapping generic_procname concrete_procname => {
/** given two template arguments, try to generate mapping from generic ones to concrete ones. */
let mapping_for_template_args (generic_name, generic_args) (concrete_name, concrete_args) =>
switch (generic_args, concrete_args) {
| (Template generic_typs, Template concrete_typs)
when QualifiedCppName.equal generic_name concrete_name =>
try (
`Valid (
List.fold2_exn
generic_typs
concrete_typs
init::[]
f::(
/* result will be reversed list. Ordering in template mapping doesn't matter so it's ok */
fun result gtyp ctyp =>
switch (gtyp, ctyp) {
| (Some {desc: TVar name}, Some concrete) => [(name, concrete), ...result]
| _ => result
}
)
)
) {
| Invalid_argument _ =>
/* fold2_exn throws on length mismatch, we need to handle it */ `Invalid
}
| (NoTemplate, NoTemplate) => `NoTemplate
| _ => `Invalid
};
let combine_mappings mapping1 mapping2 =>
switch (mapping1, mapping2) {
| (`Valid m1, `Valid m2) => `Valid (List.append m1 m2)
| (`NoTemplate, a)
| (a, `NoTemplate) => /* no template is no-op state, simply return the other state */ a
| _ => /* otherwise there is no valid mapping */ `Invalid
};
let extract_mapping =
fun
| `Invalid
| `NoTemplate => None
| `Valid m => Some m;
let empty_qual = QualifiedCppName.of_qual_string "FIXME" /* TODO we should look at procedure names */;
switch (generic_procname, concrete_procname) {
| (C {template_args: args1}, C {template_args: args2}) /* template function */ =>
mapping_for_template_args (empty_qual, args1) (empty_qual, args2) |> extract_mapping
| (
ObjC_Cpp {template_args: args1, class_name: CppClass name1 class_args1},
ObjC_Cpp {template_args: args2, class_name: CppClass name2 class_args2}
) /* template methods/template classes/both */ =>
combine_mappings
(mapping_for_template_args (name1, class_args1) (name2, class_args2))
(mapping_for_template_args (empty_qual, args1) (empty_qual, args2)) |> extract_mapping
| _ => None
}
};
}; };

@ -481,6 +481,14 @@ module Procname: {
/** get qualifiers of a class owning objc/C++ method */ /** get qualifiers of a class owning objc/C++ method */
let objc_cpp_get_class_qualifiers: objc_cpp => QualifiedCppName.t; let objc_cpp_get_class_qualifiers: objc_cpp => QualifiedCppName.t;
/** Return type substitution that would produce concrete procname from generic procname. Returns None if
such substitution doesn't exist
NOTE: this function doesn't check if such substitution is correct in terms of return
type/function parameters.
NOTE: this function doesn't deal with nested template classes, it only extracts mapping for function
and/or direct parent (class that defines the method) if it exists. */
let get_template_args_mapping: t => t => option type_subst_t;
}; };

@ -110,6 +110,11 @@ and get_record_declaration_type tenv decl =
| None -> get_record_struct_type tenv definition_decl | None -> get_record_struct_type tenv definition_decl
and get_record_custom_type tenv definition_decl = and get_record_custom_type tenv definition_decl =
let result = get_record_friend_decl_type tenv definition_decl in
let result = if Option.is_none result then get_record_as_typevar definition_decl else result in
result
and get_record_friend_decl_type tenv definition_decl =
let open Clang_ast_t in let open Clang_ast_t in
match definition_decl with match definition_decl with
| ClassTemplateSpecializationDecl (_, _, _, _, decl_list, _, _, _, _) | ClassTemplateSpecializationDecl (_, _, _, _, decl_list, _, _, _, _)
@ -117,6 +122,21 @@ and get_record_custom_type tenv definition_decl =
Option.map ~f:(qual_type_to_sil_type tenv) (get_translate_as_friend_decl decl_list) Option.map ~f:(qual_type_to_sil_type tenv) (get_translate_as_friend_decl decl_list)
| _ -> None | _ -> None
and get_record_as_typevar (definition_decl : Clang_ast_t.decl) =
let open Clang_ast_t in
match definition_decl with
| CXXRecordDecl (decl_info, name_info, _, _, _, _, _, _) ->
let is_infer_typevar = function
| AnnotateAttr {ai_parameters=[_; name; _]}
when String.equal name "__infer_type_var" -> true
| _ -> false in
if List.exists ~f:is_infer_typevar decl_info.di_attributes then
let tname = CAst_utils.get_qualified_name name_info |> QualifiedCppName.to_qual_string in
Some (Typ.mk (TVar tname))
else
None
| _ -> None
(* We need to take the name out of the type as the struct can be anonymous (* We need to take the name out of the type as the struct can be anonymous
If tenv is not passed, then template instantiaion information may be incorrect, If tenv is not passed, then template instantiaion information may be incorrect,
as it defaults to Typ.NoTemplate *) as it defaults to Typ.NoTemplate *)

Loading…
Cancel
Save