diff --git a/src/types-analysis/data.jl b/src/types-analysis/data.jl index bf305be..20cfe9c 100644 --- a/src/types-analysis/data.jl +++ b/src/types-analysis/data.jl @@ -34,8 +34,12 @@ SplitFunDef = Dict{Symbol, Any} # Analyzing type annotations #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -"Kind of type constructor" -@enum TypeConstructor TCTuple TCInvar TCUnion TCWhere TCLoBnd TCUpBnd TCVar +""" +Kind of type constructor +- TCVar means that the variable is used as a target of {}, e.g. X{Int} where X +- TCLBVar1 means that the variable has a single anonymous usage in lower bound, e.g. Ref{>:Int} +""" +@enum TypeConstructor TCTuple TCInvar TCUnion TCWhere TCLoBnd TCUpBnd TCVar TCLBVar1 TCUBVar1 "Stack of type constructors" TypeConstrStack = LinkedList{TypeConstructor} diff --git a/src/types-analysis/type-analyze.jl b/src/types-analysis/type-analyze.jl index bd4293e..f99cc37 100644 --- a/src/types-analysis/type-analyze.jl +++ b/src/types-analysis/type-analyze.jl @@ -6,7 +6,71 @@ # ####################################################################### -const ANONYMOUS_TYPE_VARIABLE = :ANON_TV +const ANONYMOUS_TY_VAR = :ANON_TV + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Analyzing type variable summaries for restrictions +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +tyVarRestrictedScopePreserved(tytvs :: TypeTyVarsSummary) = + all(tyVarRestrictedScopePreserved, tytvs) + +tyVarRestrictedScopePreserved(tvs :: TyVarSummary) = + all(tyVarRestrictedScopePreserved, map(reverse, tvs.occurrs)) + +tyVarRestrictedScopePreserved( + constrStack :: Nil{TypeConstructor}; invarCrossed :: Bool = false +) = true +tyVarRestrictedScopePreserved( + constrStack :: Cons{TypeConstructor}; invarCrossed :: Bool = false +) = begin + (hd, tl) = (head(constrStack), tail(constrStack)) + if hd == TCInvar || hd == TCLoBnd || hd == TCLBVar1 + tyVarRestrictedScopePreserved(tl; invarCrossed = true) + elseif hd == TCWhere || hd == TCLoBnd || hd == TCUpBnd || hd == TCUnion + invarCrossed ? false : tyVarRestrictedScopePreserved(tl) + else + tyVarRestrictedScopePreserved(tl) + end +end + + +tyVarOccursAsUsedSiteVariance(tytvs :: TypeTyVarsSummary) = + all(tyVarOccursAsUsedSiteVariance, tytvs) + +""" +For wildcards-like restriction, type variable can be used only once +in an invariant or cotravariant position +""" +tyVarOccursAsUsedSiteVariance(tvs :: TyVarSummary) = begin + covOccs = count(tyVarOccIsCovariant, map(reverse, tvs.occurrs)) + # either all occurrences are covariant, or there is only one + # non-covariant occurrence + covOccs == length(tvs.occurrs) || + length(tvs.occurrs) == 1 && tyVarNonCovOccIsImmediate(reverse(tvs.occurrs[1])) +end + +tyVarOccIsCovariant(constrStack :: Nil{TypeConstructor}) = true +tyVarOccIsCovariant(constrStack :: Cons{TypeConstructor}) = + head(constrStack) in [TCTuple, TCUnion, TCWhere, TCUpBnd, TCUBVar1] && + tyVarOccIsCovariant(tail(constrStack)) + +tyVarNonCovOccIsImmediate(constrStack :: Nil{TypeConstructor}) = true +tyVarNonCovOccIsImmediate(constrStack :: Cons{TypeConstructor}) = + head(constrStack) in [TCTuple, TCUnion, TCWhere, TCUpBnd, TCUBVar1] || + isempty(tail(constrStack)) + + +"No more than once" +tyVarUsedOnce(tytvs :: TypeTyVarsSummary) = + all(tyVarUsedOnce, tytvs) + +tyVarUsedOnce(tvs :: TyVarSummary) = length(tvs.occurrs) <= 1 + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Summarizing type variable usage +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% collectTyVarsSummary(ty :: JlASTTypeExpr) = collectTyVarsSummary!(ty, envempty(), TypeTyVarsSummary()) @@ -21,15 +85,6 @@ collectTyVarsSummary!( env :: TyVarEnv, tvsumm :: TypeTyVarsSummary ) = throw(TypesAnlsUnsupportedTypeAnnotation(ty)) -#= -collectTyVarsSummary!( - ty :: JlASTTypeExpr, - env :: TyVarEnv, tvsumm :: TypeTyVarsSummary -) = begin - -end -=# - """ Symbol may represent a variable occurrence """ @@ -58,6 +113,20 @@ collectTyVarsSummary!( elseif ty.head == :curly @assert length(ty.args) >= 1 "Unsupported {} $ty" collectTyVarsSummaryCurly!(ty.args, env, tvsumm) + elseif ty.head == :(<:) # Ref{<:ub} + @assert length(ty.args) == 1 "Unsupported short-hand <: $ty" + envUB = envpushconstr(env, TCUpBnd) + collectTyVarsSummary!(ty.args[1], envUB, tvsumm) + push!(tvsumm, + TyVarSummary(ANONYMOUS_TY_VAR, DEFAULT_LB, ty.args[1], [list(TCUBVar1)])) + tvsumm + elseif ty.head == :(>:) # Ref{>:lb} + @assert length(ty.args) == 1 "Unsupported short-hand >: $ty" + envLB = envpushconstr(env, TCLoBnd) + collectTyVarsSummary!(ty.args[1], envLB, tvsumm) + push!(tvsumm, + TyVarSummary(ANONYMOUS_TY_VAR, ty.args[1], DEFAULT_UB, [list(TCLBVar1)])) + tvsumm else throw(TypesAnlsUnsupportedTypeAnnotation(ty)) end @@ -88,7 +157,7 @@ splitTyVarDecl(tvDecl :: Symbol) = (tvDecl, DEFAULT_LB, DEFAULT_UB) splitTyVarDecl(tvDecl :: Expr) = begin - name = ANONYMOUS_TYPE_VARIABLE + name = ANONYMOUS_TY_VAR lb = DEFAULT_LB ub = DEFAULT_UB if tvDecl.head == :(<:) @@ -110,7 +179,7 @@ end tyVarDeclIsComparison(tvDecl :: Expr) = tvDecl.head == :comparison && length(tvDecl.args) == 5 && tvDecl.args[3] isa Symbol && - tvDecl.args[2] == :(<:) && tvDecl.args[4] == :(>:) + tvDecl.args[2] == :(<:) && tvDecl.args[4] == :(<:) #-------------------------------------------------- # ...{...} type diff --git a/test/types-analysis.jl b/test/types-analysis.jl index a80202b..522bd6a 100644 --- a/test/types-analysis.jl +++ b/test/types-analysis.jl @@ -10,9 +10,10 @@ using Main.JuliaSub: collectFunDefTypeAnnotations, collectTypeAnnotations using Main.JuliaSub: parseAndCollectTypeAnnotations using Main.JuliaSub: TyVarSummary, TypeTyVarsSummary -using Main.JuliaSub: DEFAULT_LB, DEFAULT_UB, tcsempty -using Main.JuliaSub: TCTuple, TCInvar, TCUnion, TCWhere, TCLoBnd, TCUpBnd, TCVar +using Main.JuliaSub: DEFAULT_LB, DEFAULT_UB, tcsempty, ANONYMOUS_TY_VAR +using Main.JuliaSub: TCTuple, TCInvar, TCUnion, TCWhere, TCLoBnd, TCUpBnd, TCVar, TCLBVar1, TCUBVar1 using Main.JuliaSub: collectTyVarsSummary +using Main.JuliaSub: tyVarRestrictedScopePreserved, tyVarOccursAsUsedSiteVariance, tyVarUsedOnce #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Aux values and functions @@ -213,21 +214,71 @@ end ]), ] - #= - @test parseAndCollectTypeAnnotations( - testFilePath("Multisets-cut.jl") - ) == list( - TypeAnnInfo(:push!, mtsig, :( Tuple{Multiset{T}, Any, Int} where T )), - TypeAnnInfo(:getindex, mtsig, :( Tuple{Multiset{T}, Any} where T )), - TypeAnnInfo(:clean!, mtsig, :( Tuple{Multiset} )), - TypeAnnInfo(:Multiset, mtsig, :( Tuple{Base.AbstractSet{T}} where T )), - TypeAnnInfo(:Multiset, mtsig, :(( Tuple{AbstractArray{T, d}} where d) where T )), - TypeAnnInfo(:eltype, mtsig, :( Tuple{Multiset{T}} where T )), - TypeAnnInfo(:(Base.empty!), mtsig, :( Tuple{Multiset{T}} where T )), - TypeAnnInfo(:(Base.copy), mtsig, :( Tuple{Multiset{T}} where T )), - TypeAnnInfo(:Multiset, mtsig, :( Tuple{Vararg{Any}} )), - TypeAnnInfo(:Multiset, mtsig, :( Tuple{} )), - TypeAnnInfo(:Multiset, mtsig, :( Tuple{} where T )) - ) - =# + @test collectTyVarsSummary(:(Pair{<:Number, T} where Int<:T<:Foo{>:Int})) == [ + TyVarSummary(ANONYMOUS_TY_VAR, :Int, DEFAULT_UB, [list(TCLBVar1)]), + TyVarSummary(ANONYMOUS_TY_VAR, DEFAULT_LB, :Number, [list(TCUBVar1)]), + TyVarSummary(:T, :Int, :(Foo{>:Int}), [list(TCInvar)]), + ] end + +@testset "types-analysis :: analysis of type vars usage " begin + tvsInt = collectTyVarsSummary(:(Int)) + @test tyVarUsedOnce(tvsInt) + @test tyVarOccursAsUsedSiteVariance(tvsInt) + @test tyVarRestrictedScopePreserved(tvsInt) + + tvsVector = collectTyVarsSummary(:(Vector{T} where T)) + @test tyVarUsedOnce(tvsVector) + @test tyVarOccursAsUsedSiteVariance(tvsVector) + @test tyVarRestrictedScopePreserved(tvsVector) + + tvsPair = collectTyVarsSummary(:(Pair{T, T} where T)) + @test !tyVarUsedOnce(tvsPair) + @test !tyVarOccursAsUsedSiteVariance(tvsPair) + @test tyVarRestrictedScopePreserved(tvsPair) + + tvsTuple = collectTyVarsSummary(:(Tuple{T, Tuple{T, Int}} where T)) + @test !tyVarUsedOnce(tvsTuple) + @test tyVarOccursAsUsedSiteVariance(tvsTuple) + @test tyVarRestrictedScopePreserved(tvsTuple) + + tvsTupleRef = collectTyVarsSummary(:(Tuple{T, Ref{T}} where T)) + @test !tyVarUsedOnce(tvsTupleRef) + @test !tyVarOccursAsUsedSiteVariance(tvsTupleRef) + @test tyVarRestrictedScopePreserved(tvsTupleRef) + + tvsRefUnion = collectTyVarsSummary(:(Vector{Union{T, Int}} where T)) + @test tyVarUsedOnce(tvsRefUnion) + @test !tyVarOccursAsUsedSiteVariance(tvsRefUnion) + @test !tyVarRestrictedScopePreserved(tvsRefUnion) + + tvsRefWhereRef = collectTyVarsSummary(:(Ref{Ref{T} where T} where T)) + @test tyVarUsedOnce(tvsRefWhereRef) + @test tyVarOccursAsUsedSiteVariance(tvsRefWhereRef) + @test tyVarRestrictedScopePreserved(tvsRefWhereRef) + + tvsRefWherePair = collectTyVarsSummary(:(Ref{Pair{T, S} where S} where T)) + @test tyVarUsedOnce(tvsRefWherePair) + @test !tyVarOccursAsUsedSiteVariance(tvsRefWherePair) + @test tyVarOccursAsUsedSiteVariance(tvsRefWherePair[1]) # S + @test !tyVarOccursAsUsedSiteVariance(tvsRefWherePair[2]) # T + @test !tyVarRestrictedScopePreserved(tvsRefWherePair) + @test tyVarRestrictedScopePreserved(tvsRefWherePair[1]) # S + @test !tyVarRestrictedScopePreserved(tvsRefWherePair[2]) # T + + tvsPairWherePair = collectTyVarsSummary(:(Pair{S, Pair{Int, T} where T<:S} where S)) + @test !tyVarUsedOnce(tvsPairWherePair) + @test tyVarUsedOnce(tvsPairWherePair[1]) # T + @test !tyVarUsedOnce(tvsPairWherePair[2]) # S + @test !tyVarOccursAsUsedSiteVariance(tvsPairWherePair) + @test tyVarOccursAsUsedSiteVariance(tvsPairWherePair[1]) # T + @test !tyVarOccursAsUsedSiteVariance(tvsPairWherePair[2]) # S + @test !tyVarRestrictedScopePreserved(tvsPairWherePair) + @test tyVarRestrictedScopePreserved(tvsPairWherePair[1]) # T + @test !tyVarRestrictedScopePreserved(tvsPairWherePair[2]) # S + + tvsTupleWherePair = collectTyVarsSummary(:(Tuple{S, Pair{T, S} where T<:S} where S)) + @test !tyVarUsedOnce(tvsTupleWherePair) + @test !tyVarOccursAsUsedSiteVariance(tvsTupleWherePair) + @test tyVarRestrictedScopePreserved(tvsTupleWherePair) +end \ No newline at end of file