Not directly in type parameters because while a struct type’s structure cannot change, N+1 depends on what the dispatched method of + does, which is allowed to change at runtime. Even though changing the behavior of +(::Int, ::Int) specifically would break Julia, it can’t recognize an exception for a particular call signature. This would be hypothetically possible for a function that can’t change, but we can only define the changing generic functions, and none of the built-ins are supported. For now (and probably very far into the future because of practical issues I haven’t mentioned), the language doesn’t do type parameter computation.
However, you can enforce a relation between type parameters at runtime by checking it in all constructor methods (or rather the inner ones that all the others use), throw an (informative) error instead of allowing instantiation to complete. (EDIT: see next commenter’s example.) There are unsafe ways to get around it to make invalid instances, but undefined behavior is not your problem.
If you don’t enforce that relation, you can also indirectly dispatch to a different method given that relation by mapping subsets of the parametric type to Holy traits. I don’t know a good name so I’ll just go for a typical stand-in:
struct Foo{d} end
isfoo(::mystruct{N,M}) where {N,M} = Foo{M==N+1}()
bar(x::mystruct) = bar(isfoo(x), x)
bar(::Foo{true}, x) = "M==N+1 here!"
bar(::Foo{false}, x) = "uh oh"
So all mystruct{N,M} where M==N+1 are mapped to Foo{true}(), and the rest are mapped to Foo{false}(). We can dispatch over those values’ different types to different methods:
julia> bar(mystruct(zeros(3), zeros(2,3)))
"M==N+1 here!"
julia> bar(mystruct(zeros(3,3), zeros(2,3)))
"uh oh"