14
14
* limitations under the License.
15
15
*/
16
16
17
+ use std:: collections:: BTreeMap ;
18
+
17
19
use super :: {
18
20
json:: err:: TypeMismatchError , schematype_of_restricted_expr, EntityTypeDescription ,
19
21
GetSchemaTypeError , HeterogeneousSetError , Schema , SchemaType ,
@@ -24,6 +26,7 @@ use crate::ast::{
24
26
use crate :: extensions:: { ExtensionFunctionLookupError , Extensions } ;
25
27
use either:: Either ;
26
28
use miette:: Diagnostic ;
29
+ use smol_str:: SmolStr ;
27
30
use thiserror:: Error ;
28
31
pub mod err;
29
32
@@ -176,6 +179,60 @@ pub fn typecheck_value_against_schematype(
176
179
}
177
180
}
178
181
182
+ /// Check whether the given `RestrictedExpr` is a valid instance of `SchemaType`
183
+ pub fn does_restricted_expr_implement_schematype (
184
+ expr : BorrowedRestrictedExpr < ' _ > ,
185
+ expected_ty : & SchemaType ,
186
+ ) -> bool {
187
+ use SchemaType :: * ;
188
+
189
+ match expected_ty {
190
+ Bool => expr. as_bool ( ) . is_some ( ) ,
191
+ Long => expr. as_long ( ) . is_some ( ) ,
192
+ String => expr. as_string ( ) . is_some ( ) ,
193
+ EmptySet => expr. as_set_elements ( ) . is_some_and ( |e| e. count ( ) == 0 ) ,
194
+ Set { .. } if expr. as_set_elements ( ) . is_some_and ( |e| e. count ( ) == 0 ) => true ,
195
+ Set { element_ty : elty } => match expr. as_set_elements ( ) {
196
+ Some ( mut els) => els. all ( |e| does_restricted_expr_implement_schematype ( e, elty) ) ,
197
+ None => false ,
198
+ } ,
199
+ Record { attrs, open_attrs } => match expr. as_record_pairs ( ) {
200
+ Some ( pairs) => {
201
+ let pairs_map: BTreeMap < & SmolStr , BorrowedRestrictedExpr < ' _ > > = pairs. collect ( ) ;
202
+ let all_req_schema_attrs_in_record = attrs. iter ( ) . all ( |( k, v) | {
203
+ !v. required
204
+ || match pairs_map. get ( k) {
205
+ Some ( inner_e) => {
206
+ does_restricted_expr_implement_schematype ( * inner_e, & v. attr_type )
207
+ }
208
+ None => false ,
209
+ }
210
+ } ) ;
211
+ let all_rec_attrs_match_schema =
212
+ pairs_map. iter ( ) . all ( |( k, inner_e) | match attrs. get ( * k) {
213
+ Some ( sch_ty) => {
214
+ does_restricted_expr_implement_schematype ( * inner_e, & sch_ty. attr_type )
215
+ }
216
+ None => * open_attrs,
217
+ } ) ;
218
+ all_rec_attrs_match_schema && all_req_schema_attrs_in_record
219
+ }
220
+ None => false ,
221
+ } ,
222
+ Extension { name } => match expr. as_extn_fn_call ( ) {
223
+ Some ( ( actual_name, _) ) => match name. 0 . id . as_ref ( ) {
224
+ "ipaddr" => actual_name. 0 . id . as_ref ( ) == "ip" ,
225
+ _ => name == actual_name,
226
+ } ,
227
+ None => false ,
228
+ } ,
229
+ Entity { ty } => match expr. as_euid ( ) {
230
+ Some ( actual_euid) => actual_euid. entity_type ( ) == ty,
231
+ None => false ,
232
+ } ,
233
+ }
234
+ }
235
+
179
236
/// Check whether the given `RestrictedExpr` typechecks with the given `SchemaType`.
180
237
/// If the typecheck passes, return `Ok(())`.
181
238
/// If the typecheck fails, return an appropriate `Err`.
@@ -184,23 +241,15 @@ pub fn typecheck_restricted_expr_against_schematype(
184
241
expected_ty : & SchemaType ,
185
242
extensions : & Extensions < ' _ > ,
186
243
) -> Result < ( ) , TypecheckError > {
187
- // TODO(#440): instead of computing the `SchemaType` of `expr` and then
188
- // checking whether the schematypes are "consistent", wouldn't it be less
189
- // confusing, more efficient, and maybe even more precise to just typecheck
190
- // directly?
244
+ if does_restricted_expr_implement_schematype ( expr, expected_ty) {
245
+ return Ok ( ( ) ) ;
246
+ }
191
247
match schematype_of_restricted_expr ( expr, extensions) {
192
- Ok ( actual_ty) => {
193
- if actual_ty. is_consistent_with ( expected_ty) {
194
- // typecheck passes
195
- Ok ( ( ) )
196
- } else {
197
- Err ( TypecheckError :: TypeMismatch ( TypeMismatchError {
198
- expected : Box :: new ( expected_ty. clone ( ) ) ,
199
- actual_ty : Some ( Box :: new ( actual_ty) ) ,
200
- actual_val : Either :: Right ( Box :: new ( expr. to_owned ( ) ) ) ,
201
- } ) )
202
- }
203
- }
248
+ Ok ( actual_ty) => Err ( TypecheckError :: TypeMismatch ( TypeMismatchError {
249
+ expected : Box :: new ( expected_ty. clone ( ) ) ,
250
+ actual_ty : Some ( Box :: new ( actual_ty) ) ,
251
+ actual_val : Either :: Right ( Box :: new ( expr. to_owned ( ) ) ) ,
252
+ } ) ) ,
204
253
Err ( GetSchemaTypeError :: UnknownInsufficientTypeInfo { .. } ) => {
205
254
// in this case we just don't have the information to know whether
206
255
// the attribute value (an unknown) matches the expected type.
0 commit comments