Skip to content

Commit

Permalink
analysis of restrictions
Browse files Browse the repository at this point in the history
  • Loading branch information
julbinb committed May 30, 2022
1 parent 26cfdf4 commit 22fed56
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 33 deletions.
8 changes: 6 additions & 2 deletions src/types-analysis/data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
93 changes: 81 additions & 12 deletions src/types-analysis/type-analyze.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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
"""
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 == :(<:)
Expand All @@ -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
Expand Down
89 changes: 70 additions & 19 deletions test/types-analysis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

0 comments on commit 22fed56

Please sign in to comment.