-
Notifications
You must be signed in to change notification settings - Fork 0
Fix for nested types #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -46,24 +46,23 @@ function check_field_type_fully_specified( | |||||
| mod::Module, struct_name, field_name, typevars, field_type_expr; | ||||||
| report_error, location, | ||||||
| ) | ||||||
| print("TypeVars: ") | ||||||
| dump(typevars) | ||||||
| @show mod | ||||||
| @debug "Module:" mod | ||||||
| @debug "TypeVars:" typevars | ||||||
| TypeObj = Base.eval(mod, quote | ||||||
| $(field_type_expr) where {$(typevars...)} | ||||||
| end) | ||||||
| @show TypeObj | ||||||
| @debug "Type:" TypeObj | ||||||
| @assert TypeObj isa Type | ||||||
|
|
||||||
| if isconcretetype(TypeObj) | ||||||
| # The type is concrete, so it is fully specified. | ||||||
| @info "Type is concrete: $(TypeObj)" | ||||||
| @debug "Type is concrete: $(TypeObj)" | ||||||
| return true | ||||||
| end | ||||||
| if typeof(TypeObj) == DataType | ||||||
| # The type is a DataType, so it is fully specified. | ||||||
| # Presumably, it is an abstract type like `Number` or `Any` | ||||||
| @info "Type is a DataType: $(TypeObj)" | ||||||
| @debug "Type is a DataType: $(TypeObj)" | ||||||
| return true | ||||||
| end | ||||||
| if typeof(TypeObj) == Union | ||||||
|
|
@@ -77,10 +76,10 @@ function check_field_type_fully_specified( | |||||
| end | ||||||
| @assert typeof(TypeObj) === UnionAll "$(TypeObj) is not a UnionAll. Got $(typeof(TypeObj))." | ||||||
|
|
||||||
| num_type_params = _count_unionall_parameters(TypeObj) | ||||||
| num_expr_args = _count_type_expr_params(field_type_expr) | ||||||
| # "Less than or equal to" in order to support literal values in the type expression. | ||||||
| # E.g.: The UnionAll `Array{<:Int, 1}` has 1 type arg but 2 params in the expression. | ||||||
| num_type_params = _count_unionall_free_parameters(TypeObj) | ||||||
| num_expr_args = _count_type_expr_params(mod, field_type_expr) | ||||||
| # "Less than or equal to" in order to support fully constrained parameters in the expr. | ||||||
| # E.g.: `Vector{T} where T<:Int` has 0 free type params but 1 param in the expression. | ||||||
| success = num_type_params <= num_expr_args | ||||||
| if report_error | ||||||
| @assert success field_type_not_complete_message( | ||||||
|
|
@@ -101,7 +100,7 @@ function field_type_not_complete_message( | |||||
| typename = nameof(TypeObj) | ||||||
| typevars = join(["T$i" for i in 1:(num_type_params - num_expr_args)], ", ") | ||||||
| typestr = "$(field_type_expr)" | ||||||
| @show typestr | ||||||
| @debug "Type string:" typestr | ||||||
| if occursin("}", typestr) | ||||||
| typestr = replace(typestr, "}" => ", $(typevars)}") | ||||||
| else | ||||||
|
|
@@ -132,23 +131,30 @@ function field_type_not_complete_message( | |||||
| """ | ||||||
| end | ||||||
|
|
||||||
| function _count_unionall_parameters(TypeObj::UnionAll) | ||||||
| _count_unionall_free_parameters(@nospecialize(::Any)) = 0 | ||||||
| function _count_unionall_free_parameters(TypeObj::UnionAll) | ||||||
| count = 0 | ||||||
| while typeof(TypeObj) === UnionAll | ||||||
| count += 1 | ||||||
| # in `T<:ConcreteType` we don't consider `T` as a free parameter | ||||||
| count += !isconcretetype(TypeObj.var.ub) | ||||||
| TypeObj = TypeObj.body | ||||||
| end | ||||||
| # The parameters might themselves be `UnionAll` | ||||||
| for param in TypeObj.parameters | ||||||
| count += _count_unionall_free_parameters(param) | ||||||
| end | ||||||
| return count | ||||||
| end | ||||||
| _count_type_expr_params(s::Symbol) = 0 | ||||||
| function _count_type_expr_params(expr::Expr) | ||||||
|
|
||||||
| _count_type_expr_params(mod::Module, @nospecialize(x)) = 0 | ||||||
| _count_type_expr_params(mod::Module, s::Symbol) = Int(!isdefined(mod, s)) | ||||||
| function _count_type_expr_params(mod::Module, expr::Expr) | ||||||
| count = 0 | ||||||
| while expr.head === :where | ||||||
| count += length(expr.args) - 1 | ||||||
| if expr.head === :where | ||||||
| expr = expr.args[1] | ||||||
| end | ||||||
| if expr.head == :curly | ||||||
| count += length(expr.args) - 1 | ||||||
| count = _count_type_expr_params(mod, expr) | ||||||
| elseif expr.head == :curly | ||||||
| count += sum(_count_type_expr_params(mod, arg) for arg in expr.args) | ||||||
|
||||||
| count += sum(_count_type_expr_params(mod, arg) for arg in expr.args) | |
| count += sum(_count_type_expr_params(mod, arg) for arg in expr.args[2:end]) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| using FullySpecifiedFieldTypesStaticTests | ||
| using StructFieldParamsTesting | ||
| using ReTestItems | ||
|
|
||
| ReTestItems.runtests(FullySpecifiedFieldTypesStaticTests) | ||
| runtests(StructFieldParamsTesting; nworkers=1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic
Int(!isdefined(mod, s))is unclear without explanation. Consider adding a comment explaining why undefined symbols count as 1 parameter while defined symbols count as 0, or using a more explicit conditional expression likeisdefined(mod, s) ? 0 : 1for better readability.