fix: type specification bugs

This commit is contained in:
Shunsuke Shibayama 2024-10-04 15:57:22 +09:00
parent 1bfb52f801
commit ec539b013f
7 changed files with 256 additions and 120 deletions

75
Cargo.lock generated
View file

@ -43,9 +43,9 @@ checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
[[package]]
name = "autocfg"
version = "1.3.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "backtrace"
@ -99,9 +99,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.1.21"
version = "1.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0"
checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938"
dependencies = [
"shlex",
]
@ -134,7 +134,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"syn 2.0.77",
"syn 2.0.79",
]
[[package]]
@ -145,9 +145,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "els"
version = "0.1.57-nightly.3"
version = "0.1.58-nightly.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa300ad75154fdc24c02e66148c5fb15aff48a2cfe8dd18191deb239ba5bcc64"
checksum = "eed2c90d92d8be15be9e928f06d34e0cfe03c8c10e6351326859cecf3789dcac"
dependencies = [
"erg_common",
"erg_compiler",
@ -159,9 +159,9 @@ dependencies = [
[[package]]
name = "erg_common"
version = "0.6.45-nightly.3"
version = "0.6.46-nightly.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a750b2538983b9a967f1d8af9dddfc23d2a75f1c84ecaeb88e171dcb047c185"
checksum = "4758c25017a49a7f3d8cb3287360deae39c696936a6747cf9e3d9f81cb94c010"
dependencies = [
"backtrace-on-stack-overflow",
"erg_proc_macros",
@ -172,9 +172,9 @@ dependencies = [
[[package]]
name = "erg_compiler"
version = "0.6.45-nightly.3"
version = "0.6.46-nightly.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e65b36a8419d694b11effc1e123d9ff16ac50d677950d9b60f47e8bc2429be7b"
checksum = "8c5c7fad1c74774dcbc293b79bb62a024135fcde4faf13411a3490761cb71a98"
dependencies = [
"erg_common",
"erg_parser",
@ -182,9 +182,9 @@ dependencies = [
[[package]]
name = "erg_parser"
version = "0.6.45-nightly.3"
version = "0.6.46-nightly.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ec2dd2b5827961fffe7bdbc253a904de0104d0b7677a61faa3c9e47d21aac5e"
checksum = "c564e2914429af720277cb61256362762790da8c635558f77c4d6ae4c3a64f3a"
dependencies = [
"erg_common",
"erg_proc_macros",
@ -193,9 +193,9 @@ dependencies = [
[[package]]
name = "erg_proc_macros"
version = "0.6.45-nightly.3"
version = "0.6.46-nightly.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4df44f047bae2eae631309bb3f692daaa0a9724eca8e59104cdf57336c55ad45"
checksum = "3fac38f9d18406130093186708186dad6f59efc04b0eddc0a8d0364be9361a90"
dependencies = [
"quote",
"syn 1.0.109",
@ -264,7 +264,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.79",
]
[[package]]
@ -290,9 +290,9 @@ checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553"
[[package]]
name = "libc"
version = "0.2.158"
version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]]
name = "libm"
@ -464,9 +464,12 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.19.0"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1"
dependencies = [
"portable-atomic",
]
[[package]]
name = "parking_lot"
@ -541,6 +544,12 @@ dependencies = [
"siphasher",
]
[[package]]
name = "portable-atomic"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2"
[[package]]
name = "ppv-lite86"
version = "0.2.20"
@ -641,9 +650,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.4"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
dependencies = [
"bitflags 2.6.0",
]
@ -757,7 +766,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.79",
]
[[package]]
@ -780,7 +789,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.79",
]
[[package]]
@ -820,9 +829,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.77"
version = "2.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
dependencies = [
"proc-macro2",
"quote",
@ -917,9 +926,9 @@ dependencies = [
[[package]]
name = "unicode-bidi"
version = "0.3.15"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893"
[[package]]
name = "unicode-ident"
@ -1025,7 +1034,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.79",
"wasm-bindgen-shared",
]
@ -1047,7 +1056,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.79",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -1089,7 +1098,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.79",
]
[[package]]
@ -1100,7 +1109,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.79",
]
[[package]]
@ -1204,5 +1213,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.79",
]

View file

@ -24,9 +24,9 @@ edition = "2021"
repository = "https://github.com/mtshiba/pylyzer"
[workspace.dependencies]
erg_common = { version = "0.6.45-nightly.3", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.45-nightly.3", features = ["py_compat", "els"] }
els = { version = "0.1.57-nightly.3", features = ["py_compat"] }
erg_common = { version = "0.6.46-nightly.1", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.46-nightly.1", features = ["py_compat", "els"] }
els = { version = "0.1.58-nightly.1", features = ["py_compat"] }
# 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-parser = { git = "https://github.com/RustPython/Parser", version = "0.4.0", features = ["all-nodes-with-ranges", "location"] }

View file

@ -9,15 +9,16 @@ use erg_common::traits::{Locational, Stream};
use erg_common::{fmt_vec, log, set};
use erg_compiler::artifact::IncompleteArtifact;
use erg_compiler::erg_parser::ast::{
Accessor, Args, BinOp, Block, ClassAttr, ClassAttrs, ClassDef, ConstAccessor, ConstArgs,
ConstAttribute, ConstDict, ConstExpr, ConstKeyValue, ConstPosArg, Decorator, Def, DefBody,
DefId, DefaultParamSignature, Dict, Dummy, Expr, Identifier, KeyValue, KwArg, Lambda,
LambdaSignature, List, ListComprehension, Literal, Methods, Module, NonDefaultParamSignature,
NormalDict, NormalList, NormalRecord, NormalSet, NormalTuple, ParamPattern, ParamTySpec,
Params, PosArg, PreDeclTypeSpec, ReDef, Record, RecordAttrs, Set, SetComprehension, Signature,
SubrSignature, SubrTypeSpec, Tuple, TupleTypeSpec, TypeAscription, TypeBoundSpec,
TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarRecordAttr,
VarRecordAttrs, VarRecordPattern, VarSignature, VisModifierSpec,
Accessor, Args, BinOp, Block, ClassAttr, ClassAttrs, ClassDef, ConstAccessor, ConstApp,
ConstArgs, ConstAttribute, ConstBinOp, ConstBlock, ConstDict, ConstExpr, ConstKeyValue,
ConstLambda, ConstList, ConstListWithLength, ConstNormalSet, ConstPosArg, ConstSet, Decorator,
Def, DefBody, DefId, DefaultParamSignature, Dict, Dummy, Expr, Identifier, KeyValue, KwArg,
Lambda, LambdaSignature, List, ListComprehension, Literal, Methods, Module,
NonDefaultParamSignature, NormalDict, NormalList, NormalRecord, NormalSet, NormalTuple,
ParamPattern, ParamTySpec, Params, PosArg, PreDeclTypeSpec, ReDef, Record, RecordAttrs, Set,
SetComprehension, Signature, SubrSignature, SubrTypeSpec, Tuple, TupleTypeSpec, TypeAscription,
TypeBoundSpec, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern,
VarRecordAttr, VarRecordAttrs, VarRecordPattern, VarSignature, VisModifierSpec,
};
use erg_compiler::erg_parser::desugar::Desugarer;
use erg_compiler::erg_parser::token::{Token, TokenKind, COLON, DOT, EQUAL};
@ -693,6 +694,152 @@ impl ASTConverter {
))
}
#[allow(clippy::only_used_in_recursion)]
fn convert_const_expr(&self, expr: ConstExpr) -> ConstExpr {
match expr {
ConstExpr::UnaryOp(un) if un.op.is(TokenKind::Mutate) => *un.expr,
ConstExpr::App(app)
if app
.attr_name
.as_ref()
.is_some_and(|n| n.inspect() == "__getitem__") =>
{
let obj = self.convert_const_expr(*app.obj);
let mut args = app.args.map(&mut |arg| self.convert_const_expr(arg));
if args.pos_args.is_empty() {
return ConstExpr::App(ConstApp::new(obj, app.attr_name, args));
}
let mut args = match args.pos_args.remove(0).expr {
ConstExpr::Tuple(tuple) => tuple.elems,
other => {
args.pos_args.insert(0, ConstPosArg::new(other));
args
}
};
match obj.local_name() {
Some("Union") => {
if args.pos_args.len() >= 2 {
let first = args.pos_args.remove(0).expr;
let or_op = Token::dummy(TokenKind::OrOp, "or");
args.pos_args.into_iter().fold(first, |acc, expr| {
ConstExpr::BinOp(ConstBinOp::new(or_op.clone(), acc, expr.expr))
})
} else if args.pos_args.len() == 1 {
args.pos_args.remove(0).expr
} else {
ConstExpr::App(ConstApp::new(obj, app.attr_name, args))
}
}
Some("GenericDict") => {
if args.pos_args.len() == 2 {
let key = args.pos_args.remove(0).expr;
let value = args.pos_args.remove(0).expr;
let key_value = ConstKeyValue::new(key, value);
ConstExpr::Dict(ConstDict::new(
Token::DUMMY,
Token::DUMMY,
vec![key_value],
))
} else {
ConstExpr::App(ConstApp::new(obj, app.attr_name, args))
}
}
Some("GenericList") => {
if args.pos_args.len() == 2 {
let elem = args.pos_args.remove(0).expr;
let len = args.pos_args.remove(0).expr;
let l_brace = Token::dummy(TokenKind::LSqBr, "[");
let r_brace = Token::dummy(TokenKind::RSqBr, "]");
ConstExpr::List(ConstList::WithLength(ConstListWithLength::new(
l_brace, r_brace, elem, len,
)))
} else {
let obj = ConstExpr::Accessor(ConstAccessor::Local(
Identifier::private("List".into()),
));
ConstExpr::App(ConstApp::new(obj, None, args))
}
}
Some("Optional") => {
let arg = args.pos_args.remove(0).expr;
let none = ConstExpr::Accessor(ConstAccessor::Local(Identifier::private(
"NoneType".into(),
)));
let or_op = Token::dummy(TokenKind::OrOp, "or");
ConstExpr::BinOp(ConstBinOp::new(or_op, arg, none))
}
Some("Literal") => {
let set = ConstNormalSet::new(Token::DUMMY, Token::DUMMY, args);
ConstExpr::Set(ConstSet::Normal(set))
}
Some("Callable") => {
let params = match args.pos_args.remove(0).expr {
ConstExpr::List(ConstList::Normal(list)) => list.elems,
other => {
args.pos_args.insert(0, ConstPosArg::new(other));
args.clone()
}
};
let non_defaults = params
.pos_args
.into_iter()
.map(|param| {
let expr = match param.expr.downgrade() {
Expr::Literal(lit) if lit.is(TokenKind::NoneLit) => {
Expr::Accessor(Accessor::Ident(Identifier::private(
"NoneType".into(),
)))
}
other => other,
};
let ty = Parser::expr_to_type_spec(expr.clone())
.unwrap_or(TypeSpec::mono(Identifier::private("Any".into())));
let discard = Token::dummy(TokenKind::UBar, "_");
let t_spec = TypeSpecWithOp::new(
Token::dummy(TokenKind::Colon, ":"),
ty,
expr,
);
NonDefaultParamSignature::new(
ParamPattern::Discard(discard),
Some(t_spec),
)
})
.collect();
let params = Params::new(non_defaults, None, vec![], None, None);
let ret = match args.pos_args.remove(0).expr {
ConstExpr::Lit(lit) if lit.is(TokenKind::NoneLit) => {
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private(
"NoneType".into(),
)))
}
other => other,
};
let op = Token::dummy(TokenKind::ProcArrow, "=>");
let body = ConstBlock::new(vec![ret]);
let sig = LambdaSignature::new(params, None, TypeBoundSpecs::empty());
ConstExpr::Lambda(ConstLambda::new(sig, op, body, DefId(0)))
}
_ => ConstExpr::App(ConstApp::new(obj, app.attr_name, args)),
}
}
_ => expr.map(&mut |expr| self.convert_const_expr(expr)),
}
}
fn convert_expr_to_const(&mut self, expr: py_ast::Expr) -> Option<ConstExpr> {
let expr = self.convert_expr(expr);
match Parser::validate_const_expr(expr) {
Ok(expr) => Some(self.convert_const_expr(expr)),
Err(err) => {
let err =
CompileError::new(err.into(), self.cfg.input.clone(), self.cur_namespace());
self.errs.push(err);
None
}
}
}
// TODO:
fn convert_compound_type_spec(&mut self, name: String, args: py_ast::Expr) -> TypeSpec {
match &name[..] {
@ -731,19 +878,8 @@ impl ASTConverter {
};
let mut elems = vec![];
for elem in tuple.elts {
let expr = self.convert_expr(elem);
match Parser::validate_const_expr(expr) {
Ok(expr) => {
elems.push(ConstPosArg::new(expr));
}
Err(err) => {
let err = CompileError::new(
err.into(),
self.cfg.input.clone(),
self.cur_namespace(),
);
self.errs.push(err);
}
if let Some(expr) = self.convert_expr_to_const(elem) {
elems.push(ConstPosArg::new(expr));
}
}
let elems = ConstArgs::new(elems, None, vec![], None, None);
@ -789,16 +925,9 @@ impl ASTConverter {
}
"Iterable" | "Iterator" | "Collection" | "Container" | "Sequence"
| "MutableSequence" => {
let elem_t = self.convert_expr(args);
let elem_t = match Parser::validate_const_expr(elem_t) {
Ok(elem_t) => elem_t,
Err(err) => {
let err = CompileError::new(
err.into(),
self.cfg.input.clone(),
self.cur_namespace(),
);
self.errs.push(err);
let elem_t = match self.convert_expr_to_const(args) {
Some(elem_t) => elem_t,
None => {
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("Obj".into())))
}
};
@ -824,30 +953,16 @@ impl ASTConverter {
self.errs.push(err);
return Self::gen_dummy_type_spec(args.location());
};
let key_t = self.convert_expr(tuple.elts.remove(0));
let key_t = match Parser::validate_const_expr(key_t) {
Ok(key_t) => key_t,
Err(err) => {
let err = CompileError::new(
err.into(),
self.cfg.input.clone(),
self.cur_namespace(),
);
self.errs.push(err);
let key_t = match self.convert_expr_to_const(tuple.elts.remove(0)) {
Some(key_t) => key_t,
None => {
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("Obj".into())))
}
};
let key_t = ConstPosArg::new(key_t);
let value_t = self.convert_expr(tuple.elts.remove(0));
let value_t = match Parser::validate_const_expr(value_t) {
Ok(value_t) => value_t,
Err(err) => {
let err = CompileError::new(
err.into(),
self.cfg.input.clone(),
self.cur_namespace(),
);
self.errs.push(err);
let value_t = match self.convert_expr_to_const(tuple.elts.remove(0)) {
Some(value_t) => value_t,
None => {
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("Obj".into())))
}
};
@ -864,16 +979,9 @@ impl ASTConverter {
let len = ConstExpr::Accessor(ConstAccessor::Local(
self.convert_ident("_".into(), args.location()),
));
let elem_t = self.convert_expr(args);
let elem_t = match Parser::validate_const_expr(elem_t) {
Ok(elem_t) => elem_t,
Err(err) => {
let err = CompileError::new(
err.into(),
self.cfg.input.clone(),
self.cur_namespace(),
);
self.errs.push(err);
let elem_t = match self.convert_expr_to_const(args) {
Some(elem_t) => elem_t,
None => {
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("Obj".into())))
}
};
@ -895,29 +1003,15 @@ impl ASTConverter {
return Self::gen_dummy_type_spec(args.location());
};
let (l_brace, r_brace) = Self::gen_enclosure_tokens(TokenKind::LBrace, tuple.range);
let key_t = self.convert_expr(tuple.elts.remove(0));
let key_t = match Parser::validate_const_expr(key_t) {
Ok(key_t) => key_t,
Err(err) => {
let err = CompileError::new(
err.into(),
self.cfg.input.clone(),
self.cur_namespace(),
);
self.errs.push(err);
let key_t = match self.convert_expr_to_const(tuple.elts.remove(0)) {
Some(key_t) => key_t,
None => {
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("Obj".into())))
}
};
let val_t = self.convert_expr(tuple.elts.remove(0));
let val_t = match Parser::validate_const_expr(val_t) {
Ok(val_t) => val_t,
Err(err) => {
let err = CompileError::new(
err.into(),
self.cfg.input.clone(),
self.cur_namespace(),
);
self.errs.push(err);
let val_t = match self.convert_expr_to_const(tuple.elts.remove(0)) {
Some(val_t) => val_t,
None => {
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("Obj".into())))
}
};

View file

@ -78,3 +78,23 @@ class G(DoesNotExist): # ERR
g = G()
assert g.foo() == 1
class Value:
value: object
class H(Value):
value: int
def __init__(self, value):
self.value = value
def incremented(self):
return H(self.value + 1)
class MyList(list):
@staticmethod
def try_new(lis) -> "MyList" | None:
if isinstance(lis, list):
return MyList(lis)
else:
return None

View file

@ -9,7 +9,7 @@ union_arr.append(1)
union_arr.append("a") # OK
union_arr.append(None) # ERR
dic = {"a": 1}
dic: dict[Literal["a", "b"], int] = {"a": 1}
dic["b"] = 2
_ = dic["a"]
_ = dic["b"]

View file

@ -104,7 +104,7 @@ fn exec_warns() -> Result<(), String> {
#[test]
fn exec_typespec() -> Result<(), String> {
expect("tests/typespec.py", 0, 7)
expect("tests/typespec.py", 0, 13)
}
#[test]

View file

@ -9,6 +9,19 @@ o: Optional[int] = None # OK
p: Optional[int] = "a" # ERR
weekdays: Literal[1, 2, 3, 4, 5, 6, 7] = 1 # OK
weekdays: Literal[1, 2, 3, 4, 5, 6, 7] = 8 # ERR
_: dict[str, dict[str, Union[int, str]]] = {"a": {"b": 1}}
_: dict[str, dict[str, list[int]]] = {"a": {"b": [1]}}
_: dict[str, dict[str, dict[str, int]]] = {"a": {"b": {"c": 1}}}
_: dict[str, dict[str, Optional[int]]] = {"a": {"b": 1}}
_: dict[str, dict[str, Literal[1, 2]]] = {"a": {"b": 1}}
_: dict[str, dict[str, Callable[[int], int]]] = {"a": {"b": abs}}
_: dict[str, dict[str, Callable[[int], None]]] = {"a": {"b": print}}
_: dict[str, dict[str, Opional[int]]] = {"a": {"b": 1}} # ERR
_: dict[str, dict[str, Union[int, str]]] = {"a": {"b": None}} # ERR
_: dict[str, dict[str, list[int]]] = {"a": {"b": ["c"]}} # ERR
_: dict[str, dict[str, Callable[[int], int]]] = {"a": {"b": print}} # ERR
_: dict[str, dict[str, Optional[int]]] = {"a": {"b": "c"}} # ERR
_: dict[str, dict[str, Literal[1, 2]]] = {"a": {"b": 3}} # ERR
def f(x: Union[int, str]) -> None:
pass