It depends on how the language is defined.
Language might be statically typed (like e.g. C, Java, Haskell, ...) or dynamically typed (like e.g. Python, Common Lisp, ...). In a statically typed language every expression has associated value and the semantic analyser checks whether the types match just after when the expressions are parsed.
In a statically typed language if you have variable
a defined to contain integer and variable
b defined to contain integer between 0 and 10, assignment
b = a is not valid at compile. Of course such language needs a way of saying "please check whether the value will fit and set it if it does and do that if it does not". Usually via some kind of type cast that throws an exception, but might be anything.
In a dynamically typed language expressions don't have any type specified and whether the value can be assigned is always checked at runtime when specific type is needed.
Combination is possible where expressions may have associated type and if they don't, they are considered to be of universal "any" type and runtime check is omitted if it is possible to statically ensure the value will fit.
Then there are strongly typed languages (e.g. Java, Python, Haskell, ...) and weakly typed languages (C, C++, Pascal). In a strongly typed language each value has associated type and this type may be checked at runtime if needed. On the other hand in weakly typed languages the value is just a bit pattern and gets interpreted according to the static type of the expression. Note that dynamically typed languages are strongly typed as their values carry their type around with them.
Since your language has variable types, it is at least partially statically typed. So you can do some static type checking. If you have assignment of variable that can hold positive numbers to variable that can only hold negative numbers, you can reject such statement straight away without waiting for the actual values, because the ranges are disjoint (the types are incompatible).
If the type on the right hand is a subtype of the one on left hand (the range is smaller), you know the statement will always be ok. You don't need to do a check at runtime.
If the types have some overlap, but the type on right hand is not a subtype, i.e. may have values that don't fit in the variable on the left side, you may either require explicit check or you may automatically inject a check that will yield error or exception if at runtime the actual value does not fit. Which you do depends on the specification of the language.
Last for constants you handle them as special case of the subtype rule. A constant 42 is an integer in range [42, 42]. This is a subtype of all ranges that can contain 42 and you can do the check at compile-time.