hm-inference #6

Merged
sb10q merged 136 commits from hm-inference into master 2021-08-19 11:46:50 +08:00
2 changed files with 126 additions and 17 deletions
Showing only changes of commit bc9b453b3e - Show all commits

View File

@ -1,5 +1,6 @@
use super::typedef::Type; use super::typedef::Type;
use super::location::Location; use super::location::Location;
use rustpython_parser::ast::Expr;
pub enum SymbolType { pub enum SymbolType {
TypeName(Type), TypeName(Type),
@ -16,7 +17,8 @@ pub enum SymbolValue<'a> {
} }
pub trait SymbolResolver { pub trait SymbolResolver {
fn get_symbol_type(&mut self, str: &str) -> Option<SymbolType>; fn get_symbol_type(&mut self, str: &str) -> Option<Type>;
fn parse_type_name(&mut self, expr: &Expr<()>) -> Option<Type>;
fn get_symbol_value(&mut self, str: &str) -> Option<SymbolValue>; fn get_symbol_value(&mut self, str: &str) -> Option<SymbolValue>;
fn get_symbol_location(&mut self, str: &str) -> Option<Location>; fn get_symbol_location(&mut self, str: &str) -> Option<Location>;
// handle function call etc. // handle function call etc.

View File

@ -5,7 +5,7 @@ use std::iter::once;
use std::rc::Rc; use std::rc::Rc;
use super::magic_methods::*; use super::magic_methods::*;
use super::symbol_resolver::{SymbolResolver, SymbolType}; use super::symbol_resolver::SymbolResolver;
use super::typedef::{Call, FunSignature, FuncArg, Type, TypeEnum, Unifier}; use super::typedef::{Call, FunSignature, FuncArg, Type, TypeEnum, Unifier};
use itertools::izip; use itertools::izip;
use rustpython_parser::ast::{ use rustpython_parser::ast::{
@ -44,7 +44,7 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
func, func,
args, args,
keywords, keywords,
} => unimplemented!(), } => self.fold_call(node.location, *func, args, keywords)?,
ast::ExprKind::Lambda { args, body } => { ast::ExprKind::Lambda { args, body } => {
self.fold_lambda(node.location, *args, *body)? self.fold_lambda(node.location, *args, *body)?
} }
@ -71,14 +71,15 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
ops, ops,
comparators, comparators,
} => Some(self.infer_compare(left, ops, comparators)?), } => Some(self.infer_compare(left, ops, comparators)?),
ast::ExprKind::Call { .. } => expr.custom,
ast::ExprKind::Subscript { value, slice, .. } => { ast::ExprKind::Subscript { value, slice, .. } => {
Some(self.infer_subscript(value.as_ref(), slice.as_ref())?) Some(self.infer_subscript(value.as_ref(), slice.as_ref())?)
} }
ast::ExprKind::IfExp { test, body, orelse } => { ast::ExprKind::IfExp { test, body, orelse } => {
Some(self.infer_if_expr(test, body.as_ref(), orelse.as_ref())?) Some(self.infer_if_expr(test, body.as_ref(), orelse.as_ref())?)
} }
ast::ExprKind::ListComp { .. } | ast::ExprKind::Lambda { .. } => expr.custom, // already computed ast::ExprKind::ListComp { .. }
| ast::ExprKind::Lambda { .. }
| ast::ExprKind::Call { .. } => expr.custom, // already computed
ast::ExprKind::Slice { .. } => None, // we don't need it for slice ast::ExprKind::Slice { .. } => None, // we don't need it for slice
_ => return Err("not supported yet".into()), _ => return Err("not supported yet".into()),
}; };
@ -243,21 +244,127 @@ impl<'a> Inferencer<'a> {
}) })
} }
fn fold_call(
&mut self,
location: Location,
func: ast::Expr<()>,
mut args: Vec<ast::Expr<()>>,
keywords: Vec<Located<ast::KeywordData>>,
) -> Result<ast::Expr<Option<Type>>, String> {
let func = if let Located {
location: func_location,
custom,
node: ExprKind::Name { id, ctx },
} = func
{
// handle special functions that cannot be typed in the usual way...
if id == "virtual" {
if args.is_empty() || args.len() > 2 || !keywords.is_empty() {
return Err("`virtual` can only accept 1/2 positional arguments.".to_string());
}
let arg0 = self.fold_expr(args.remove(0))?;
let ty = if let Some(arg) = args.pop() {
self.resolver
.parse_type_name(&arg)
.ok_or_else(|| "error parsing type".to_string())?
} else {
self.unifier.get_fresh_var().0
};
let custom = Some(self.unifier.add_ty(TypeEnum::TVirtual { ty }));
return Ok(Located {
location,
custom,
node: ExprKind::Call {
func: Box::new(Located {
custom: None,
location: func.location,
node: ExprKind::Name { id, ctx },
}),
args: vec![arg0],
keywords: vec![],
},
});
}
// int64 is special because its argument can be a constant larger than int32
if id == "int64" && args.len() == 1 {
if let ExprKind::Constant {
value: ast::Constant::Int(val),
..
} = &args[0].node
{
let int64: Result<i64, _> = val.try_into();
let custom;
if int64.is_ok() {
custom = Some(self.primitives.int64);
} else {
return Err("Integer out of bound".into());
}
return Ok(Located {
location,
custom,
node: ExprKind::Call {
func: Box::new(Located {
custom: None,
location: func.location,
node: ExprKind::Name { id, ctx },
}),
args: vec![self.fold_expr(args.pop().unwrap())?],
keywords: vec![],
},
});
}
}
Located {
location: func_location,
custom,
node: ExprKind::Name { id, ctx },
}
} else {
func
};
let func = Box::new(self.fold_expr(func)?);
let args = args
.into_iter()
.map(|v| self.fold_expr(v))
.collect::<Result<Vec<_>, _>>()?;
let keywords = keywords
.into_iter()
.map(|v| fold::fold_keyword(self, v))
.collect::<Result<Vec<_>, _>>()?;
let ret = self.unifier.get_fresh_var().0;
let call = Rc::new(Call {
posargs: args.iter().map(|v| v.custom.unwrap()).collect(),
kwargs: keywords
.iter()
.map(|v| (v.node.arg.as_ref().unwrap().clone(), v.custom.unwrap()))
.collect(),
fun: RefCell::new(None),
ret,
});
self.calls.push(call.clone());
let call = self.unifier.add_ty(TypeEnum::TCall { calls: vec![call] });
self.unifier.unify(func.custom.unwrap(), call)?;
Ok(Located {
location,
custom: Some(ret),
node: ExprKind::Call {
func,
args,
keywords,
},
})
}
fn infer_identifier(&mut self, id: &str) -> InferenceResult { fn infer_identifier(&mut self, id: &str) -> InferenceResult {
if let Some(ty) = self.variable_mapping.get(id) { if let Some(ty) = self.variable_mapping.get(id) {
Ok(*ty) Ok(*ty)
} else { } else {
match self.resolver.get_symbol_type(id) { Ok(self.resolver.get_symbol_type(id).unwrap_or_else(|| {
Some(SymbolType::TypeName(_)) => {
Err("Expected expression instead of type".to_string())
}
Some(SymbolType::Identifier(ty)) => Ok(ty),
None => {
let ty = self.unifier.get_fresh_var().0; let ty = self.unifier.get_fresh_var().0;
self.variable_mapping.insert(id.to_string(), ty); self.variable_mapping.insert(id.to_string(), ty);
Ok(ty) ty
} }))
}
} }
} }
@ -268,7 +375,7 @@ impl<'a> Inferencer<'a> {
let int32: Result<i32, _> = val.try_into(); let int32: Result<i32, _> = val.try_into();
// int64 would be handled separately in functions // int64 would be handled separately in functions
if int32.is_ok() { if int32.is_ok() {
Ok(self.primitives.int64) Ok(self.primitives.int32)
} else { } else {
Err("Integer out of bound".into()) Err("Integer out of bound".into())
} }