-
Notifications
You must be signed in to change notification settings - Fork 58
[DESIGN] Fine-grained checking in StructInfo #346
Comments
Thank you for posting this issue, as I think checking The rule for checking subtyping of I agree on the point about failing in the case of kFailL1, since the type system (per our past discussions) would raise an error and request a cast in that case. |
Thanks @slyubomirsky . To dig a bit further on the FuncType and FuncStructInfo. One interesting direction is to explore possibility to not go with the function subtyping rule (that is one reason to bring up the set formalization). This is indeed controversial, so some discussions would be good. The considerations so far are mainly as follows:
The main lines of thoughts here are A0, the existence of erasure makes sticking with subtyping harder. A1(with runtime check) we can afford to relax a bit while still catching error in runtime. |
What would the rule for func types/StructInfo without subtyping look like then? Don't attempt to check any subtyping for those and insist on dynamic checks? Some simplifications might be reasonable, but I think it's important to make sure we can figure out the types/StructInfo of call results (otherwise that would greatly limit the utility of the type system). |
Agree it is important to preserve call result(and we can afford to do so because erasure of call result goes with the same direction of subtyping). The main challenge lies in erasure of function parameter, because func structure info with erased parameter structinfo is not base of the original function before erasure if we follow def of subtyping. Here is one strawman
|
It might be okay to use runtime checks more for checking function types (as long as we implement RTTI), but I think the biggest complexity is in dealing with the rule for calls (matching any shape variables in the function type). Not sure how much we would gain by simplifying the subtyping rule. If we simplify the rule for determining call results, the issues that could arise are 1) that users would have to insert more casts and 2) putting in the wrong cast could result in hiding errors until later. |
Agreed, the goal is to NOT simplify rules for determining call results -- so results are precise. But relax the rules for checking call parameters -- mainly because of the erasure complexity as being mentioned above |
Okay, I don't think there would be too much harm from simplifying the subtyping check for functions because I think the main case where it would come up is higher-order functions, which I don't expect to be very common in Relax. We could do it, though I also don't think that it would be too difficult to implement the subtyping written out in the draft spec. |
Just want to provide some context about the potential design confliction of erasing and subfunction typing. We relies on erasure when we unify the values from different branches, or when we want to simplify certain parts x0: S0
x1: S1 = erase(x0) Normally when we erase, we know that S1 is a base of S0 -- we are going towards more coarse grained information. When it comes to functions, if we encourage erasure through out the process to simplify some of our pass writing. Then in this case, x0: R.Func([R.Tensor((n, m))], R.Tensor)
x1: R.Func([R.Tensor], R.Tensor) = erase(x0) Relaxing parameter checking would avoid such confliction to occur. |
I see what you are referring to, I think you're right that erasing could cause issues in principle. Edit: Have to think about this some more, maybe it isn't as innocuous as it seems. Edit 2: Okay, yes, this is indeed a case where erasing makes the |
The unification rule I wrote in the spec actually avoids this issue in much the way you described, so I guess we are in agreement: Lines 836 to 850 in 1da54d3
I don't think we should use erasure for unification, for the reasons described. Unifying the parameters succeeds only if they can be proven equal to each other. |
StructInfo introduces a nice way to be able to do further analysis. It also creates interesting design space about checking. Specifically, consider a function call:
When looking at
sinfo_a0
andsinfo_p0
, when to report an error, when should we report a warning and when to accept a program? One interesting part about our current design is that we automatically insertmatch_cast
at function boundary, which means that if some of the errors are not caught by compiler check, we still have runtime checks.During implementation of the StructInfo, we find that it is helpful to have fine-grained checking and implemented a version of it to support the struct info analysis.
This post marks this as a point for us to think further and discuss further in future iterations.
For a given pair of lhs_struct_info(sinfo_p0), rhs_struct_info(sinfo_a0). Define following terminology:
The check result of
struct_info_base_check(lhs_struct_info, rhs_struct_info)
can have the following results:If the checking ends up in
kFailL0
, then obviously an error should be raised. We normally also raise error in kFailL1 in a strong typed system because that is normally when static type check fails.One thing that worth highlighting and discuss is the derive checking rule for functions struct info. Since this is not a normal way for function sub-typing checking. When looking at
lhs_struct_info=R.Func([R.Object], R.Tensor)
andrhs_struct_info=R.Func([R.R.Tensor], R.Tensor)
.struct_info_base_check
will return kPass. Which means LSet is superset of RSet -- this certain consistent with the definition. However, from traditional sub-typing rule's pov, passinga_func: R.Func([R.Tensor], R.Tensor)
top_func: R.Func([R.R.Object], R.Tensor)
will result an type error, because the callee function may callp_func
with an object that is not tensor, in which case cannot be handled bya_func
.In the specific case of relax. There are a few interesting observations here:
a_func
top_func
here and runtime checks will catch the error if it is being mis-used here.As such the function struct info check for now uses
struct_info_base_check
and allows such argument passing. So this is a case that is worth thinking considering the design elements of unification, runtime checking and our overall needs.It would be great to think and discuss further:
The text was updated successfully, but these errors were encountered: