Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Addresses !60 and introduces a test which fails on
master
but passes on this branch.The fundamental issue this PR addresses is the following:
We can apply
Positive{Trait}
to an abstract type, then applyNegative{Trait}
to a subtype. We then implement a default@traitfn
forPositive{Trait}
but require a manual method implementation for theNegative{Trait}
. If we run@check
on the subtype, it can incorrectly show that the interface is satisfied.To solve this problem, there are two things that we need to do.
Positive/Negative
version of the trait.To do this, we first call
traits(::Module, T::Assignable)
and traverse the type tree to find all traits which are applied to theT
in question. We now sort the tree beforehand, proceeding down the tree fromAny
rather than in an unordered fashion. This allows us to determine whether the last instance of a trait applied to a type wasPositive
orNegative
If we define
foo(::Any) = foo(::Negative{Trait}, ::Any)
, then we requirefoo(::MyType)
for thePositive{Trait}
interface, the checker can give a false positive that the interface is satisfied becausefoo(::Any)
will passhasmethod(foo, Tuple{MyType})
. We need to introduce a second check whenhasmethod
passes which verifies that the types in the locations where the trait is substituted for the type in question that the resulting type in the method signature also possesses the trait. I call this extra checkmethod_traits_match
This PR assumes that any given type cannot satisfy both
Positive{Trait}
andNegative{Trait}
for any fixedTrait
. This is technically permissible according toBinaryTraits.jl
(assigning both to a type doesn't throw an error), so perhaps this PR is a wash from the start. However, I think this is a very useful capability. For example, it allows us to define a default interface for a type hierarchy viaNegative{Trait}
, and then we can special-case subsets of the type tree which satisfy a separate interface forPositive{Trait}
.