feat: support `TypeVar` & type parameter syntax

User class generics are not yet supported
This commit is contained in:
Shunsuke Shibayama 2024-08-18 13:30:22 +09:00
parent 582906ed92
commit ce12285143
6 changed files with 227 additions and 90 deletions

20
Cargo.lock generated
View File

@ -139,9 +139,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]] [[package]]
name = "els" name = "els"
version = "0.1.54-nightly.2" version = "0.1.54-nightly.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d774d511ed129b8438a9633b17f3303768fa7a3941372c1650e61a7bc7b00c" checksum = "cfe2679dec933507f4d9abfa4e6468d3b312790e933b3269ce2a2a2bc910bd3d"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_compiler", "erg_compiler",
@ -153,9 +153,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_common" name = "erg_common"
version = "0.6.42-nightly.2" version = "0.6.42-nightly.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b084d80afdb59d10d300595b2860868e52d1a0a72ad98ac995c9f5abfba9acd8" checksum = "1c5582717e4cd56c2015263641441c7f3c19ae030bd804565f0b93ad0a8c536e"
dependencies = [ dependencies = [
"backtrace-on-stack-overflow", "backtrace-on-stack-overflow",
"erg_proc_macros", "erg_proc_macros",
@ -165,9 +165,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_compiler" name = "erg_compiler"
version = "0.6.42-nightly.2" version = "0.6.42-nightly.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "805b009c668055c8d72d11f8b26853c9a65a2744548909beda8b32bd4058a375" checksum = "299406202ab6dfe28be95c3d6ceae19d9821624ef666a978b42112f658b528c4"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_parser", "erg_parser",
@ -175,9 +175,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_parser" name = "erg_parser"
version = "0.6.42-nightly.2" version = "0.6.42-nightly.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec4f2cc69904baae639ff154323d1cc0e100ca2785bfb6d8c392c88e0560771" checksum = "cc13d9b7c3342e1a4ccd4159e39c99769a30733ac6cf44c22593d0aecb169af9"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_proc_macros", "erg_proc_macros",
@ -186,9 +186,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_proc_macros" name = "erg_proc_macros"
version = "0.6.42-nightly.2" version = "0.6.42-nightly.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6234abaef2fed929391add7520409890b2b7ee7029f2a5dcb9c2f4905bb7556b" checksum = "b8506a5228f462df55923b4a6ca8617ce90a84b52a4b603cedf3dda6beb3243f"
dependencies = [ dependencies = [
"quote", "quote",
"syn 1.0.109", "syn 1.0.109",

View File

@ -22,9 +22,9 @@ edition = "2021"
repository = "https://github.com/mtshiba/pylyzer" repository = "https://github.com/mtshiba/pylyzer"
[workspace.dependencies] [workspace.dependencies]
erg_common = { version = "0.6.42-nightly.2", features = ["py_compat", "els"] } erg_common = { version = "0.6.42-nightly.3", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.42-nightly.2", features = ["py_compat", "els"] } erg_compiler = { version = "0.6.42-nightly.3", features = ["py_compat", "els"] }
els = { version = "0.1.54-nightly.2", features = ["py_compat"] } els = { version = "0.1.54-nightly.3", features = ["py_compat"] }
# rustpython-parser = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] } # rustpython-parser = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] }
# rustpython-ast = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] } # rustpython-ast = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] }
rustpython-parser = { git = "https://github.com/RustPython/Parser", version = "0.4.0", features = ["all-nodes-with-ranges", "location"] } rustpython-parser = { git = "https://github.com/RustPython/Parser", version = "0.4.0", features = ["all-nodes-with-ranges", "location"] }

View File

@ -1,3 +1,4 @@
use core::fmt;
use std::path::Path; use std::path::Path;
use erg_common::config::ErgConfig; use erg_common::config::ErgConfig;
@ -14,9 +15,9 @@ use erg_compiler::erg_parser::ast::{
LambdaSignature, List, ListComprehension, Literal, Methods, Module, NonDefaultParamSignature, LambdaSignature, List, ListComprehension, Literal, Methods, Module, NonDefaultParamSignature,
NormalDict, NormalList, NormalRecord, NormalSet, NormalTuple, ParamPattern, ParamTySpec, NormalDict, NormalList, NormalRecord, NormalSet, NormalTuple, ParamPattern, ParamTySpec,
Params, PosArg, PreDeclTypeSpec, ReDef, Record, RecordAttrs, Set, SetComprehension, Signature, Params, PosArg, PreDeclTypeSpec, ReDef, Record, RecordAttrs, Set, SetComprehension, Signature,
SubrSignature, SubrTypeSpec, Tuple, TupleTypeSpec, TypeAscription, TypeBoundSpecs, TypeSpec, SubrSignature, SubrTypeSpec, Tuple, TupleTypeSpec, TypeAscription, TypeBoundSpec,
TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarRecordAttr, VarRecordAttrs, VarRecordPattern, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarRecordAttr,
VarSignature, VisModifierSpec, VarRecordAttrs, VarRecordPattern, VarSignature, VisModifierSpec,
}; };
use erg_compiler::erg_parser::desugar::Desugarer; use erg_compiler::erg_parser::desugar::Desugarer;
use erg_compiler::erg_parser::token::{Token, TokenKind, COLON, DOT, EQUAL}; use erg_compiler::erg_parser::token::{Token, TokenKind, COLON, DOT, EQUAL};
@ -24,7 +25,7 @@ use erg_compiler::erg_parser::Parser;
use erg_compiler::error::{CompileError, CompileErrors}; use erg_compiler::error::{CompileError, CompileErrors};
use rustpython_parser::ast::located::{ use rustpython_parser::ast::located::{
self as py_ast, Alias, Arg, Arguments, BoolOp, CmpOp, ExprConstant, Keyword, Located, self as py_ast, Alias, Arg, Arguments, BoolOp, CmpOp, ExprConstant, Keyword, Located,
ModModule, Operator, Stmt, String, Suite, UnaryOp as UnOp, ModModule, Operator, Stmt, String, Suite, TypeParam, UnaryOp as UnOp,
}; };
use rustpython_parser::source_code::{ use rustpython_parser::source_code::{
OneIndexed, SourceLocation as PyLocation, SourceRange as PySourceRange, OneIndexed, SourceLocation as PyLocation, SourceRange as PySourceRange,
@ -217,6 +218,49 @@ pub enum ShadowingMode {
Visible, Visible,
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TypeVarInfo {
name: String,
bound: Option<Expr>,
}
impl fmt::Display for TypeVarInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(bound) = &self.bound {
write!(f, "TypeVarInfo({} bound={})", self.name, bound)
} else {
write!(f, "TypeVarInfo({})", self.name)
}
}
}
impl TypeVarInfo {
pub const fn new(name: String, bound: Option<Expr>) -> Self {
Self { name, bound }
}
}
#[derive(Debug)]
pub struct LocalContext {
name: String,
/// Erg does not allow variables to be defined multiple times, so rename them using this
names: HashMap<String, NameInfo>,
type_vars: HashMap<String, TypeVarInfo>,
// e.g. def id(x: T) -> T: ... => appeared_types = {T}
appeared_type_names: HashSet<String>,
}
impl LocalContext {
pub fn new(name: String) -> Self {
Self {
name,
names: HashMap::new(),
type_vars: HashMap::new(),
appeared_type_names: HashSet::new(),
}
}
}
/// AST must be converted in the following order: /// AST must be converted in the following order:
/// ///
/// Params -> Block -> Signature /// Params -> Block -> Signature
@ -245,11 +289,9 @@ pub enum ShadowingMode {
pub struct ASTConverter { pub struct ASTConverter {
cfg: ErgConfig, cfg: ErgConfig,
shadowing: ShadowingMode, shadowing: ShadowingMode,
namespace: Vec<String>,
block_id_counter: usize, block_id_counter: usize,
block_ids: Vec<usize>, block_ids: Vec<usize>,
/// Erg does not allow variables to be defined multiple times, so rename them using this contexts: Vec<LocalContext>,
names: Vec<HashMap<String, NameInfo>>,
warns: CompileErrors, warns: CompileErrors,
errs: CompileErrors, errs: CompileErrors,
} }
@ -259,18 +301,17 @@ impl ASTConverter {
Self { Self {
shadowing, shadowing,
cfg, cfg,
namespace: vec![String::from("<module>")],
block_id_counter: 0, block_id_counter: 0,
block_ids: vec![0], block_ids: vec![0],
names: vec![HashMap::new()], contexts: vec![LocalContext::new("<module>".into())],
warns: CompileErrors::empty(), warns: CompileErrors::empty(),
errs: CompileErrors::empty(), errs: CompileErrors::empty(),
} }
} }
fn get_name(&self, name: &str) -> Option<&NameInfo> { fn get_name(&self, name: &str) -> Option<&NameInfo> {
for ns in self.names.iter().rev() { for ctx in self.contexts.iter().rev() {
if let Some(ni) = ns.get(name) { if let Some(ni) = ctx.names.get(name) {
return Some(ni); return Some(ni);
} }
} }
@ -278,38 +319,67 @@ impl ASTConverter {
} }
fn get_mut_name(&mut self, name: &str) -> Option<&mut NameInfo> { fn get_mut_name(&mut self, name: &str) -> Option<&mut NameInfo> {
for ns in self.names.iter_mut().rev() { for ctx in self.contexts.iter_mut().rev() {
if let Some(ni) = ns.get_mut(name) { if let Some(ni) = ctx.names.get_mut(name) {
return Some(ni); return Some(ni);
} }
} }
None None
} }
fn get_type_var(&self, name: &str) -> Option<&TypeVarInfo> {
for ctx in self.contexts.iter().rev() {
if let Some(tv) = ctx.type_vars.get(name) {
return Some(tv);
}
}
None
}
fn define_name(&mut self, name: String, info: NameInfo) { fn define_name(&mut self, name: String, info: NameInfo) {
self.names.last_mut().unwrap().insert(name, info); self.contexts.last_mut().unwrap().names.insert(name, info);
} }
fn declare_name(&mut self, name: String, info: NameInfo) { fn declare_name(&mut self, name: String, info: NameInfo) {
self.names.first_mut().unwrap().insert(name, info); self.contexts.first_mut().unwrap().names.insert(name, info);
}
fn define_type_var(&mut self, name: String, info: TypeVarInfo) {
self.contexts
.last_mut()
.unwrap()
.type_vars
.insert(name, info);
} }
fn grow(&mut self, namespace: String) { fn grow(&mut self, namespace: String) {
self.namespace.push(namespace); self.contexts.push(LocalContext::new(namespace));
self.names.push(HashMap::new());
} }
fn pop(&mut self) { fn pop(&mut self) {
self.namespace.pop(); self.contexts.pop();
self.names.pop();
} }
fn cur_block_id(&self) -> usize { fn cur_block_id(&self) -> usize {
*self.block_ids.last().unwrap() *self.block_ids.last().unwrap()
} }
/// foo.bar.baz
fn cur_namespace(&self) -> String { fn cur_namespace(&self) -> String {
self.namespace.join(".") self.contexts
.iter()
.map(|ctx| &ctx.name[..])
.collect::<Vec<_>>()
.join(".")
}
// baz
fn cur_name(&self) -> &str {
&self.contexts.last().unwrap().name
}
fn cur_appeared_type_names(&self) -> &HashSet<String> {
&self.contexts.last().unwrap().appeared_type_names
} }
fn register_name_info(&mut self, name: &str, kind: NameKind) -> CanShadow { fn register_name_info(&mut self, name: &str, kind: NameKind) -> CanShadow {
@ -869,6 +939,11 @@ impl ASTConverter {
#[allow(clippy::collapsible_match)] #[allow(clippy::collapsible_match)]
match expr { match expr {
py_ast::Expr::Name(name) => { py_ast::Expr::Name(name) => {
self.contexts
.last_mut()
.unwrap()
.appeared_type_names
.insert(name.id.to_string());
self.convert_ident_type_spec(name.id.to_string(), name.location()) self.convert_ident_type_spec(name.id.to_string(), name.location())
} }
py_ast::Expr::Constant(cons) => { py_ast::Expr::Constant(cons) => {
@ -1306,7 +1381,7 @@ impl ASTConverter {
self.errs.push(self_not_found_error( self.errs.push(self_not_found_error(
self.cfg.input.clone(), self.cfg.input.clone(),
subr.loc(), subr.loc(),
self.namespace.join("."), self.cur_namespace(),
)); ));
Some(()) Some(())
} }
@ -1314,7 +1389,7 @@ impl ASTConverter {
self.errs.push(init_var_error( self.errs.push(init_var_error(
self.cfg.input.clone(), self.cfg.input.clone(),
var.loc(), var.loc(),
self.namespace.join("."), self.cur_namespace(),
)); ));
None None
} }
@ -1418,13 +1493,17 @@ impl ASTConverter {
); );
let class_ident = Identifier::public_with_line( let class_ident = Identifier::public_with_line(
DOT, DOT,
self.namespace.last().unwrap().into(), self.cur_name().to_string().into(),
sig.ln_begin().unwrap_or(0), sig.ln_begin().unwrap_or(0),
); );
let class_ident_expr = Expr::Accessor(Accessor::Ident(class_ident.clone())); let class_ident_expr = Expr::Accessor(Accessor::Ident(class_ident.clone()));
let class_spec = TypeSpecWithOp::new(COLON, TypeSpec::mono(class_ident), class_ident_expr); let class_spec = TypeSpecWithOp::new(COLON, TypeSpec::mono(class_ident), class_ident_expr);
let mut params = sig.params.clone(); let mut params = sig.params.clone();
if params.non_defaults.first().is_some_and(|param| param.inspect().map(|s| &s[..]) == Some("self")) { if params
.non_defaults
.first()
.is_some_and(|param| param.inspect().map(|s| &s[..]) == Some("self"))
{
params.non_defaults.remove(0); params.non_defaults.remove(0);
} }
let sig = Signature::Subr(SubrSignature::new( let sig = Signature::Subr(SubrSignature::new(
@ -1449,7 +1528,7 @@ impl ASTConverter {
); );
let params = Params::empty(); let params = Params::empty();
let class_ident = let class_ident =
Identifier::public_with_line(DOT, self.namespace.last().unwrap().into(), line as u32); Identifier::public_with_line(DOT, self.cur_name().to_string().into(), line as u32);
let class_ident_expr = Expr::Accessor(Accessor::Ident(class_ident.clone())); let class_ident_expr = Expr::Accessor(Accessor::Ident(class_ident.clone()));
let class_spec = TypeSpecWithOp::new(COLON, TypeSpec::mono(class_ident), class_ident_expr); let class_spec = TypeSpecWithOp::new(COLON, TypeSpec::mono(class_ident), class_ident_expr);
let sig = Signature::Subr(SubrSignature::new( let sig = Signature::Subr(SubrSignature::new(
@ -1566,15 +1645,49 @@ impl ASTConverter {
(base_type, vec![methods]) (base_type, vec![methods])
} }
fn convert_funcdef( fn get_type_bounds(&mut self, type_params: Vec<TypeParam>) -> TypeBoundSpecs {
&mut self, let mut bounds = TypeBoundSpecs::empty();
name: String, if type_params.is_empty() {
params: Arguments, for ty in self.cur_appeared_type_names() {
body: Vec<py_ast::Stmt>, let name = VarName::from_str(ty.clone().into());
decorator_list: Vec<py_ast::Expr>, let op = Token::dummy(TokenKind::SubtypeOf, "<:");
returns: Option<py_ast::Expr>, if let Some(tv_info) = self.get_type_var(ty) {
range: PySourceRange, let bound = if let Some(bound) = &tv_info.bound {
) -> Expr { let t_spec = Parser::expr_to_type_spec(bound.clone())
.unwrap_or(TypeSpec::Infer(name.token().clone()));
let spec = TypeSpecWithOp::new(op, t_spec, bound.clone());
TypeBoundSpec::non_default(name, spec)
} else {
TypeBoundSpec::Omitted(name)
};
bounds.push(bound);
}
}
}
for tp in type_params {
// TODO:
let Some(tv) = tp.as_type_var() else {
continue;
};
let name = VarName::from_str(tv.name.to_string().into());
let spec = if let Some(bound) = &tv.bound {
let op = Token::dummy(TokenKind::SubtypeOf, "<:");
let spec = self.convert_type_spec(*bound.clone());
let expr = self.convert_expr(*bound.clone());
let spec = TypeSpecWithOp::new(op, spec, expr);
TypeBoundSpec::non_default(name, spec)
} else {
TypeBoundSpec::Omitted(name)
};
bounds.push(spec);
}
bounds
}
fn convert_funcdef(&mut self, func_def: py_ast::StmtFunctionDef) -> Expr {
let name = func_def.name.to_string();
let params = *func_def.args;
let returns = func_def.returns.map(|x| *x);
// if reassigning of a function referenced by other functions is occurred, it is an error // if reassigning of a function referenced by other functions is occurred, it is an error
if self.get_name(&name).is_some_and(|info| { if self.get_name(&name).is_some_and(|info| {
info.defined_times > 0 info.defined_times > 0
@ -1583,15 +1696,16 @@ impl ASTConverter {
}) { }) {
let err = reassign_func_error( let err = reassign_func_error(
self.cfg.input.clone(), self.cfg.input.clone(),
pyloc_to_ergloc(range), pyloc_to_ergloc(func_def.range),
self.namespace.join("."), self.cur_namespace(),
&name, &name,
); );
self.errs.push(err); self.errs.push(err);
Expr::Dummy(Dummy::new(None, vec![])) Expr::Dummy(Dummy::new(None, vec![]))
} else { } else {
let loc = range.start; let loc = func_def.range.start;
let decos = decorator_list let decos = func_def
.decorator_list
.into_iter() .into_iter()
.map(|ex| Decorator(self.convert_expr(ex))) .map(|ex| Decorator(self.convert_expr(ex)))
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
@ -1613,14 +1727,9 @@ impl ASTConverter {
); );
TypeSpecWithOp::new(colon, t_spec, self.convert_expr(ret)) TypeSpecWithOp::new(colon, t_spec, self.convert_expr(ret))
}); });
let sig = Signature::Subr(SubrSignature::new( let bounds = self.get_type_bounds(func_def.type_params);
decos, let sig = Signature::Subr(SubrSignature::new(decos, ident, bounds, params, return_t));
ident, let block = self.convert_block(func_def.body, BlockKind::Function);
TypeBoundSpecs::empty(),
params,
return_t,
));
let block = self.convert_block(body, BlockKind::Function);
let body = DefBody::new(EQUAL, block, DefId(0)); let body = DefBody::new(EQUAL, block, DefId(0));
let def = Def::new(sig, body); let def = Def::new(sig, body);
self.pop(); self.pop();
@ -1642,19 +1751,16 @@ impl ASTConverter {
/// ```erg /// ```erg
/// Foo = Inherit Bar /// Foo = Inherit Bar
/// ``` /// ```
fn convert_classdef( fn convert_classdef(&mut self, class_def: py_ast::StmtClassDef) -> Expr {
&mut self, let loc = class_def.location();
name: String, let name = class_def.name.to_string();
body: Vec<py_ast::Stmt>, let _decos = class_def
bases: Vec<py_ast::Expr>, .decorator_list
decorator_list: Vec<py_ast::Expr>,
loc: PyLocation,
) -> Expr {
let _decos = decorator_list
.into_iter() .into_iter()
.map(|deco| self.convert_expr(deco)) .map(|deco| self.convert_expr(deco))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut bases = bases let mut bases = class_def
.bases
.into_iter() .into_iter()
.map(|base| self.convert_expr(base)) .map(|base| self.convert_expr(base))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -1667,7 +1773,7 @@ impl ASTConverter {
let ident = self.convert_ident(name, class_name_loc); let ident = self.convert_ident(name, class_name_loc);
let sig = Signature::Var(VarSignature::new(VarPattern::Ident(ident.clone()), None)); let sig = Signature::Var(VarSignature::new(VarPattern::Ident(ident.clone()), None));
self.grow(ident.inspect().to_string()); self.grow(ident.inspect().to_string());
let (base_type, methods) = self.extract_method_list(ident, body, inherit); let (base_type, methods) = self.extract_method_list(ident, class_def.body, inherit);
let classdef = if inherit { let classdef = if inherit {
// TODO: multiple inheritance // TODO: multiple inheritance
let pos_args = vec![PosArg::new(bases.remove(0))]; let pos_args = vec![PosArg::new(bases.remove(0))];
@ -1763,6 +1869,20 @@ impl ASTConverter {
match lhs { match lhs {
py_ast::Expr::Name(name) => { py_ast::Expr::Name(name) => {
let expr = self.convert_expr(*assign.value); let expr = self.convert_expr(*assign.value);
if let Expr::Call(call) = &expr {
if let Some("TypeVar") = call.obj.get_name().map(|s| &s[..]) {
let arg = if let Some(Expr::Literal(lit)) =
call.args.get_left_or_key("arg")
{
lit.token.content.trim_matches('\"').to_string()
} else {
name.id.to_string()
};
let bound = call.args.nth_or_key(1, "bound").cloned();
let info = TypeVarInfo::new(arg, bound);
self.define_type_var(name.id.to_string(), info);
}
}
let can_shadow = self.register_name_info(&name.id, NameKind::Variable); let can_shadow = self.register_name_info(&name.id, NameKind::Variable);
let ident = self.convert_ident(name.id.to_string(), name.location()); let ident = self.convert_ident(name.id.to_string(), name.location());
if can_shadow.is_yes() { if can_shadow.is_yes() {
@ -1918,24 +2038,8 @@ impl ASTConverter {
} }
} }
} }
py_ast::Stmt::FunctionDef(func_def) => self.convert_funcdef( py_ast::Stmt::FunctionDef(func_def) => self.convert_funcdef(func_def),
func_def.name.to_string(), py_ast::Stmt::ClassDef(class_def) => self.convert_classdef(class_def),
*func_def.args,
func_def.body,
func_def.decorator_list,
func_def.returns.map(|x| *x),
func_def.range,
),
py_ast::Stmt::ClassDef(class_def) => {
let class_loc = class_def.location();
self.convert_classdef(
class_def.name.to_string(),
class_def.body,
class_def.bases,
class_def.decorator_list,
class_loc,
)
}
py_ast::Stmt::For(for_) => { py_ast::Stmt::For(for_) => {
let loc = for_.location(); let loc = for_.location();
let iter = self.convert_expr(*for_.iter); let iter = self.convert_expr(*for_.iter);
@ -1991,7 +2095,7 @@ impl ASTConverter {
value value
} else { } else {
let func_acc = Expr::Accessor(Accessor::Ident( let func_acc = Expr::Accessor(Accessor::Ident(
self.convert_ident(self.namespace.last().unwrap().clone(), loc), self.convert_ident(self.cur_name().to_string(), loc),
)); ));
let return_acc = self.convert_ident("return".to_string(), loc); let return_acc = self.convert_ident("return".to_string(), loc);
let return_acc = Expr::Accessor(Accessor::attr(func_acc, return_acc)); let return_acc = Expr::Accessor(Accessor::attr(func_acc, return_acc));

View File

@ -71,7 +71,10 @@ impl DeclFileGenerator {
// e.g. `x: foo.Bar` => `foo = pyimport "foo"; x: foo.Bar` // e.g. `x: foo.Bar` => `foo = pyimport "foo"; x: foo.Bar`
fn prepare_using_type(&mut self, typ: &Type) { fn prepare_using_type(&mut self, typ: &Type) {
let namespace = Str::rc(typ.namespace().split('.').next().unwrap()); let namespace = Str::rc(typ.namespace().split('.').next().unwrap());
if namespace != self.namespace && !namespace.is_empty() && self.imported.insert(namespace.clone()) { if namespace != self.namespace
&& !namespace.is_empty()
&& self.imported.insert(namespace.clone())
{
self.code += &format!("{namespace} = pyimport \"{namespace}\"\n"); self.code += &format!("{namespace} = pyimport \"{namespace}\"\n");
} }
} }

View File

@ -137,6 +137,11 @@ fn exec_shadowing() -> Result<(), String> {
expect("tests/shadowing.py", 0, 3) expect("tests/shadowing.py", 0, 3)
} }
#[test]
fn exec_typevar() -> Result<(), String> {
expect("tests/typevar.py", 0, 2)
}
#[test] #[test]
fn exec_widening() -> Result<(), String> { fn exec_widening() -> Result<(), String> {
expect("tests/widening.py", 0, 1) expect("tests/widening.py", 0, 1)

25
tests/typevar.py Normal file
View File

@ -0,0 +1,25 @@
from typing import TypeVar
T = TypeVar("T")
U = TypeVar("U", int)
def id(x: T) -> T:
return x
def id_int(x: U) -> U:
return x
_ = id(1) + 1 # OK
_ = id("a") + "b" # OK
_ = id_int(1) # OK
_ = id_int("a") # ERR
def id2[T](x: T) -> T:
return x
def id_int2[T: int](x: T) -> T:
return x
_ = id2(1) + 1 # OK
_ = id2("a") + "b" # OK
_ = id_int2(1) # OK
_ = id_int2("a") # ERR