hm-inference #6
|
@ -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.
|
||||||
|
|
|
@ -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(_)) => {
|
let ty = self.unifier.get_fresh_var().0;
|
||||||
Err("Expected expression instead of type".to_string())
|
self.variable_mapping.insert(id.to_string(), ty);
|
||||||
}
|
ty
|
||||||
Some(SymbolType::Identifier(ty)) => Ok(ty),
|
}))
|
||||||
None => {
|
|
||||||
let ty = self.unifier.get_fresh_var().0;
|
|
||||||
self.variable_mapping.insert(id.to_string(), ty);
|
|
||||||
Ok(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())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue