14
14
* limitations under the License.
15
15
*/
16
16
17
+ use std:: collections:: BTreeMap ;
18
+
17
19
use super :: {
18
20
schematype_of_restricted_expr, EntityTypeDescription , GetSchemaTypeError ,
19
21
HeterogeneousSetError , Schema , SchemaType , TypeMismatchError ,
@@ -291,6 +293,60 @@ pub fn typecheck_value_against_schematype(
291
293
}
292
294
}
293
295
296
+ /// Check whether the given `RestrictedExpr` is a valid instance of `SchemaType`
297
+ pub fn does_restricted_expr_implement_schematype (
298
+ expr : BorrowedRestrictedExpr < ' _ > ,
299
+ expected_ty : & SchemaType ,
300
+ ) -> bool {
301
+ use SchemaType :: * ;
302
+
303
+ match expected_ty {
304
+ Bool => expr. as_bool ( ) . is_some ( ) ,
305
+ Long => expr. as_long ( ) . is_some ( ) ,
306
+ String => expr. as_string ( ) . is_some ( ) ,
307
+ EmptySet => expr. as_set_elements ( ) . is_some_and ( |e| e. count ( ) == 0 ) ,
308
+ Set { .. } if expr. as_set_elements ( ) . is_some_and ( |e| e. count ( ) == 0 ) => true ,
309
+ Set { element_ty : elty } => match expr. as_set_elements ( ) {
310
+ Some ( mut els) => els. all ( |e| does_restricted_expr_implement_schematype ( e, elty) ) ,
311
+ None => false ,
312
+ } ,
313
+ Record { attrs, open_attrs } => match expr. as_record_pairs ( ) {
314
+ Some ( pairs) => {
315
+ let pairs_map: BTreeMap < & SmolStr , BorrowedRestrictedExpr < ' _ > > = pairs. collect ( ) ;
316
+ let all_req_schema_attrs_in_record = attrs. iter ( ) . all ( |( k, v) | {
317
+ !v. required
318
+ || match pairs_map. get ( k) {
319
+ Some ( inner_e) => {
320
+ does_restricted_expr_implement_schematype ( * inner_e, & v. attr_type )
321
+ }
322
+ None => false ,
323
+ }
324
+ } ) ;
325
+ let all_rec_attrs_match_schema =
326
+ pairs_map. iter ( ) . all ( |( k, inner_e) | match attrs. get ( * k) {
327
+ Some ( sch_ty) => {
328
+ does_restricted_expr_implement_schematype ( * inner_e, & sch_ty. attr_type )
329
+ }
330
+ None => * open_attrs,
331
+ } ) ;
332
+ all_rec_attrs_match_schema && all_req_schema_attrs_in_record
333
+ }
334
+ None => false ,
335
+ } ,
336
+ Extension { name } => match expr. as_extn_fn_call ( ) {
337
+ Some ( ( actual_name, _) ) => match name. id . as_ref ( ) {
338
+ "ipaddr" => actual_name. id . as_ref ( ) == "ip" ,
339
+ _ => name == actual_name,
340
+ } ,
341
+ None => false ,
342
+ } ,
343
+ Entity { ty } => match expr. as_euid ( ) {
344
+ Some ( actual_euid) => actual_euid. entity_type ( ) == ty,
345
+ None => false ,
346
+ } ,
347
+ }
348
+ }
349
+
294
350
/// Check whether the given `RestrictedExpr` typechecks with the given `SchemaType`.
295
351
/// If the typecheck passes, return `Ok(())`.
296
352
/// If the typecheck fails, return an appropriate `Err`.
@@ -299,23 +355,15 @@ pub fn typecheck_restricted_expr_against_schematype(
299
355
expected_ty : & SchemaType ,
300
356
extensions : Extensions < ' _ > ,
301
357
) -> Result < ( ) , TypecheckError > {
302
- // TODO(#440): instead of computing the `SchemaType` of `expr` and then
303
- // checking whether the schematypes are "consistent", wouldn't it be less
304
- // confusing, more efficient, and maybe even more precise to just typecheck
305
- // directly?
358
+ if does_restricted_expr_implement_schematype ( expr, expected_ty) {
359
+ return Ok ( ( ) ) ;
360
+ }
306
361
match schematype_of_restricted_expr ( expr, extensions) {
307
- Ok ( actual_ty) => {
308
- if actual_ty. is_consistent_with ( expected_ty) {
309
- // typecheck passes
310
- Ok ( ( ) )
311
- } else {
312
- Err ( TypecheckError :: TypeMismatch ( TypeMismatchError {
313
- expected : Box :: new ( expected_ty. clone ( ) ) ,
314
- actual_ty : Some ( Box :: new ( actual_ty) ) ,
315
- actual_val : Either :: Right ( Box :: new ( expr. to_owned ( ) ) ) ,
316
- } ) )
317
- }
318
- }
362
+ Ok ( actual_ty) => Err ( TypecheckError :: TypeMismatch ( TypeMismatchError {
363
+ expected : Box :: new ( expected_ty. clone ( ) ) ,
364
+ actual_ty : Some ( Box :: new ( actual_ty) ) ,
365
+ actual_val : Either :: Right ( Box :: new ( expr. to_owned ( ) ) ) ,
366
+ } ) ) ,
319
367
Err ( GetSchemaTypeError :: UnknownInsufficientTypeInfo { .. } ) => {
320
368
// in this case we just don't have the information to know whether
321
369
// the attribute value (an unknown) matches the expected type.
0 commit comments