diff --git a/kclvm/sema/src/resolver/calculation.rs b/kclvm/sema/src/resolver/calculation.rs index c056e8038..a3715eebb 100644 --- a/kclvm/sema/src/resolver/calculation.rs +++ b/kclvm/sema/src/resolver/calculation.rs @@ -2,8 +2,9 @@ use std::sync::Arc; use crate::resolver::Resolver; use crate::ty::{ - has_any_type, is_upper_bound, sup, Type, TypeInferMethods, TypeRef, ZERO_LIT_TYPES, + has_any_type, is_upper_bound, sup, Type, TypeInferMethods, TypeKind, TypeRef, ZERO_LIT_TYPES, }; +use indexmap::IndexMap; use kclvm_ast::ast; use kclvm_error::diagnostic::Range; @@ -162,12 +163,23 @@ impl<'ctx> Resolver<'ctx> { true, Type::list_ref(sup(&[t1.list_item_ty(), t2.list_item_ty()])), ) - } else if t1.is_dict() && t2.is_dict() { - let (t1_key_ty, t1_val_ty) = t1.dict_entry_ty(); - let (t2_key_ty, t2_val_ty) = t2.dict_entry_ty(); + } else if let (TypeKind::Dict(t1_dict_ty), TypeKind::Dict(t2_dict_ty)) = + (&t1.kind, &t2.kind) + { + let mut attrs = IndexMap::new(); + for (k, v) in &t1_dict_ty.attrs { + attrs.insert(k.to_string(), v.clone()); + } + for (k, v) in &t2_dict_ty.attrs { + attrs.insert(k.to_string(), v.clone()); + } ( true, - Type::dict_ref(sup(&[t1_key_ty, t2_key_ty]), sup(&[t1_val_ty, t2_val_ty])), + Arc::new(Type::dict_with_attrs( + sup(&[t1_dict_ty.key_ty.clone(), t2_dict_ty.key_ty.clone()]), + sup(&[t1_dict_ty.val_ty.clone(), t2_dict_ty.val_ty.clone()]), + attrs, + )), ) } else if t1.is_schema() && (t2.is_schema() || t2.is_dict()) { (true, t1) diff --git a/kclvm/sema/src/resolver/ty.rs b/kclvm/sema/src/resolver/ty.rs index f37e38182..7f58a1c13 100644 --- a/kclvm/sema/src/resolver/ty.rs +++ b/kclvm/sema/src/resolver/ty.rs @@ -297,21 +297,44 @@ impl<'ctx> Resolver<'ctx> { } _ => val_ty, }; - - if !self.check_type(val_ty.clone(), index_signature.val_ty.clone(), range) { - self.handler.add_type_error( - &format!( - "expected schema index signature value type {}, got {}", - index_signature.val_ty.ty_str(), - val_ty.ty_str() - ), - range.clone(), - ); - } - - if index_signature.any_other { - return self.check_type(key_ty, index_signature.key_ty.clone(), range) - && self.check_type(val_ty, index_signature.val_ty.clone(), range); + if dict_ty.attrs.is_empty() { + if !self.check_type(val_ty.clone(), index_signature.val_ty.clone(), range) { + self.handler.add_type_error( + &format!( + "expected schema index signature value type {}, got {}", + index_signature.val_ty.ty_str(), + val_ty.ty_str() + ), + range.clone(), + ); + } + } else { + for (name, attr) in &dict_ty.attrs { + if index_signature.any_other { + if let Some(attr_obj) = schema_ty.attrs.get(name) { + self.must_assignable_to( + attr.ty.clone(), + attr_obj.ty.clone(), + range.clone(), + Some(attr_obj.range.clone()), + ); + } else { + self.must_assignable_to( + attr.ty.clone(), + index_signature.val_ty.clone(), + attr.range.clone(), + None, + ); + } + } else { + self.must_assignable_to( + attr.ty.clone(), + index_signature.val_ty.clone(), + attr.range.clone(), + None, + ); + } + } } true } else { @@ -319,12 +342,14 @@ impl<'ctx> Resolver<'ctx> { // check whether the type of key value pair in dict matches the attribute type in the schema. if let TypeKind::StrLit(key_name) = &key_ty.kind { if let Some(attr_obj) = schema_ty.attrs.get(key_name) { - self.must_assignable_to( - val_ty.clone(), - attr_obj.ty.clone(), - range.clone(), - Some(attr_obj.range.clone()), - ); + if let Some(attr) = dict_ty.attrs.get(key_name) { + self.must_assignable_to( + attr.ty.clone(), + attr_obj.ty.clone(), + range.clone(), + Some(attr_obj.range.clone()), + ); + } return true; } } diff --git a/test/grammar/lambda/type_annotation/schema/invalid_1/stderr.golden b/test/grammar/lambda/type_annotation/schema/invalid_1/stderr.golden index 800a02d77..f3b53e884 100644 --- a/test/grammar/lambda/type_annotation/schema/invalid_1/stderr.golden +++ b/test/grammar/lambda/type_annotation/schema/invalid_1/stderr.golden @@ -17,55 +17,3 @@ error[E2G22]: TypeError 10 | hello: "world" | ^ expected int, got str(world) | - - --> ${CWD}/main.k:7:5 - | -7 | { - | ^ variable is defined here, its type is int, but got str(world) - | - -error[E2G22]: TypeError - --> ${CWD}/main.k:15:5 - | -15 | version: 1 - | ^ expected str, got int(1) - | - - --> ${CWD}/main.k:3:5 - | -3 | version: str - | ^ variable is defined here, its type is str, but got int(1) - | - -error[E2G22]: TypeError - --> ${CWD}/main.k:16:5 - | -16 | hello: "world" - | ^ expected int, got str(world) - | - - --> ${CWD}/main.k:14:20 - | -14 | v = providerFamily({ - | ^ variable is defined here, its type is int, but got str(world) - | - -error[E2G22]: TypeError - --> ${CWD}/main.k:14:20 - | -14 | v = providerFamily({ - | ^ expected schema index signature value type int, got str(world) - | - -error[E2G22]: TypeError - --> ${CWD}/main.k:14:20 - | -14 | v = providerFamily({ - | ^ expected ProviderFamily, got {str(version) | str(hello):int(1) | str(world)} - | - - --> ${CWD}/main.k:6:25 - | -6 | providerFamily = lambda family: ProviderFamily -> ProviderFamily { - | ^ variable is defined here, its type is ProviderFamily, but got {str(version) | str(hello):int(1) | str(world)} - | diff --git a/test/grammar/schema/type/config_expr_index_signature_fail/stderr.golden b/test/grammar/schema/type/config_expr_index_signature_fail/stderr.golden index 2f845e457..5fa973e24 100644 --- a/test/grammar/schema/type/config_expr_index_signature_fail/stderr.golden +++ b/test/grammar/schema/type/config_expr_index_signature_fail/stderr.golden @@ -9,20 +9,3 @@ error[E2G22]: TypeError 6 | name: Name | ^ variable is defined here, its type is int, but got str(aa) | -error[E2G22]: TypeError - --> ${CWD}/main.k:9:5 - | -9 | name: { - | ^ expected schema index signature value type int, got str(aa) - | -error[E2G22]: TypeError - --> ${CWD}/main.k:9:5 - | -9 | name: { - | ^ expected Name, got {str(classID):str(aa)} - | - --> ${CWD}/main.k:6:5 - | -6 | name: Name - | ^ variable is defined here, its type is Name, but got {str(classID):str(aa)} - | \ No newline at end of file diff --git a/test/grammar/schema/type/config_expr_type_fail_1/stderr.golden b/test/grammar/schema/type/config_expr_type_fail_1/stderr.golden index b31f9c66b..3315d2496 100644 --- a/test/grammar/schema/type/config_expr_type_fail_1/stderr.golden +++ b/test/grammar/schema/type/config_expr_type_fail_1/stderr.golden @@ -6,28 +6,6 @@ error[E2G22]: TypeError | --> ${CWD}/main.k:2:5 | -2 | name: str = "Kiki" - | ^ variable is defined here, its type is str, but got int(123) - | -error[E2G22]: TypeError - --> ${CWD}/main.k:12:10 - | -12 | name.name0.name = 123 - | ^ expected str, got int(123) - | - --> ${CWD}/main.k:2:5 - | -2 | name: str = "Kiki" - | ^ variable is defined here, its type is str, but got int(123) - | -error[E2G22]: TypeError - --> ${CWD}/main.k:12:5 - | -12 | name.name0.name = 123 - | ^ expected str, got int(123) - | - --> ${CWD}/main.k:2:5 - | 2 | name: str = "Kiki" | ^ variable is defined here, its type is str, but got int(123) | \ No newline at end of file diff --git a/test/grammar/schema/type/type_fail_24/stderr.golden b/test/grammar/schema/type/type_fail_24/stderr.golden index fa614fe86..5e8afcec9 100644 --- a/test/grammar/schema/type/type_fail_24/stderr.golden +++ b/test/grammar/schema/type/type_fail_24/stderr.golden @@ -35,10 +35,10 @@ error[E2G22]: TypeError --> ${CWD}/main.k:4:5 | 4 | name: { - | ^ expected [Container], got {str(image) | str(name):str(image) | str(name)} + | ^ expected [Container], got {str:str} | --> ${CWD}/pkg/person.k:3:5 | 3 | spec: [Container] - | ^ variable is defined here, its type is [Container], but got {str(image) | str(name):str(image) | str(name)} + | ^ variable is defined here, its type is [Container], but got {str:str} | \ No newline at end of file diff --git a/test/grammar/types/union_expr/union_expr_0/main.k b/test/grammar/types/union_expr/union_expr_0/main.k new file mode 100644 index 000000000..02b6375c7 --- /dev/null +++ b/test/grammar/types/union_expr/union_expr_0/main.k @@ -0,0 +1,14 @@ +schema Container: + name: str + image: str + volumeMounts: [{str:}] + +config = { + image = "test/test-container:test-cluster" + volumeMounts = [{ + name = "config" + mountPath = "/app/config" + }] +} + +expected: Container = config | {name = "test-repo/test-image:test-tag"} diff --git a/test/grammar/types/union_expr/union_expr_0/stdout.golden b/test/grammar/types/union_expr/union_expr_0/stdout.golden new file mode 100644 index 000000000..d06053bb0 --- /dev/null +++ b/test/grammar/types/union_expr/union_expr_0/stdout.golden @@ -0,0 +1,11 @@ +config: + image: test/test-container:test-cluster + volumeMounts: + - name: config + mountPath: /app/config +expected: + name: test-repo/test-image:test-tag + image: test/test-container:test-cluster + volumeMounts: + - name: config + mountPath: /app/config diff --git a/test/grammar/types/union_expr/union_expr_1/main.k b/test/grammar/types/union_expr/union_expr_1/main.k new file mode 100644 index 000000000..e4d638054 --- /dev/null +++ b/test/grammar/types/union_expr/union_expr_1/main.k @@ -0,0 +1,16 @@ +schema Container: + name: str + image: str + volumeMounts: [{str:}] + +schema Config: + _config = { + image = "test/test-container:test-cluster" + volumeMounts = [{ + name = "config" + mountPath = "/app/config" + }] + } + expected: Container = _config | {name = "test-repo/test-image:test-tag"} + +config = Config {} diff --git a/test/grammar/types/union_expr/union_expr_1/stdout.golden b/test/grammar/types/union_expr/union_expr_1/stdout.golden new file mode 100644 index 000000000..6918159dd --- /dev/null +++ b/test/grammar/types/union_expr/union_expr_1/stdout.golden @@ -0,0 +1,7 @@ +config: + expected: + name: test-repo/test-image:test-tag + image: test/test-container:test-cluster + volumeMounts: + - name: config + mountPath: /app/config diff --git a/test/grammar/types/union_expr/union_expr_fail_0/main.k b/test/grammar/types/union_expr/union_expr_fail_0/main.k new file mode 100644 index 000000000..5366ff5ea --- /dev/null +++ b/test/grammar/types/union_expr/union_expr_fail_0/main.k @@ -0,0 +1,14 @@ +schema Container: + name: str + image: str + volumeMounts: [{str:}] + +config = { + image = "test/test-container:test-cluster" + volumeMounts = [{ + name = "config" + mountPath = "/app/config" + }] +} + +expected: Container = config | {name = 1} diff --git a/test/grammar/types/union_expr/union_expr_fail_0/stderr.golden b/test/grammar/types/union_expr/union_expr_fail_0/stderr.golden new file mode 100644 index 000000000..343df5335 --- /dev/null +++ b/test/grammar/types/union_expr/union_expr_fail_0/stderr.golden @@ -0,0 +1,6 @@ +error[E2G22]: TypeError + --> ${CWD}/main.k:14:33 + | +14 | expected: Container = config | {name = 1} + | ^ expected str, got int(1) + | diff --git a/test/grammar/types/union_expr/union_expr_fail_1/main.k b/test/grammar/types/union_expr/union_expr_fail_1/main.k new file mode 100644 index 000000000..a99710fe2 --- /dev/null +++ b/test/grammar/types/union_expr/union_expr_fail_1/main.k @@ -0,0 +1,16 @@ +schema Container: + name: str + image: str + volumeMounts: [{str:}] + +schema Config: + _config = { + image = "test/test-container:test-cluster" + volumeMounts = [{ + name = "config" + mountPath = "/app/config" + }] + } + expected: Container = _config | {name = 1} + +config = Config {} diff --git a/test/grammar/types/union_expr/union_expr_fail_1/stderr.golden b/test/grammar/types/union_expr/union_expr_fail_1/stderr.golden new file mode 100644 index 000000000..b945f754e --- /dev/null +++ b/test/grammar/types/union_expr/union_expr_fail_1/stderr.golden @@ -0,0 +1,6 @@ +error[E2G22]: TypeError + --> ${CWD}/main.k:14:38 + | +14 | expected: Container = _config | {name = 1} + | ^ expected str, got int(1) + | diff --git a/test/grammar/types/union_expr/union_expr_fail_2/main.k b/test/grammar/types/union_expr/union_expr_fail_2/main.k new file mode 100644 index 000000000..ee5d194d4 --- /dev/null +++ b/test/grammar/types/union_expr/union_expr_fail_2/main.k @@ -0,0 +1,17 @@ +schema Container: + name: str + image: str + volumeMounts: [{str:}] + +schema Config: + _config = { + name = 1 + image = "test/test-container:test-cluster" + volumeMounts = [{ + name = "config" + mountPath = "/app/config" + }] + } + expected: Container = _config | {} + +config = Config {} diff --git a/test/grammar/types/union_expr/union_expr_fail_2/stderr.golden b/test/grammar/types/union_expr/union_expr_fail_2/stderr.golden new file mode 100644 index 000000000..5ef591674 --- /dev/null +++ b/test/grammar/types/union_expr/union_expr_fail_2/stderr.golden @@ -0,0 +1,6 @@ +error[E3M38]: EvaluationError + --> ${CWD}/main.k:2:1 + | +2 | name: str + | expect str, got int + |