Commit 73576bb
committed
Implement mechanism for
This implements support for the `strict mode` mechanism proposed in #54903.
There was a desire to see an implementation of this along side #60018
since that are conceptually adjacent. However, other than some shared
infrastructure, this version is farther from #60018 than the original
proposal for reasons mentioned below. The motivation and basic idea
are largely unchanged.
# Implemented semantics
The user facing interface to this mechanism is `@strict` (currently
in `Base.Experimental`, but expected to be moved out in the course
of the 1.14 release process. Rather than paraphrase, let me just
provide the first part of the docstring that describes the semantics:
```
Set the `strict` mode for the current module to all flags specified in `flags`.
Each `strict` mode flag is an independent option that disallows certain syntax or
semantics that may be undesirable in *some* contexts. Package authors should
consider which flags are appropriate for their package and set them at the top
of the applicable module.
# General semantics and philosophy
Note that additional strict mode flags are not necessarily *safer* or *better*
in any way; they are a reflection of of the reality that different users have
different tradeoffs for their codebases and what may be a sensible restriction
in a mature package could be very annoying in the REPL.
As designed, there are several general guidelines that apply to strict mode flags.
To the extent possible, they should be kept for future flag additions.
1. Code that evaluates without error in strict mode should also evaluate without
error under the ordinary julia execution semantics.
2. Strict mode should not affect parsing. If it is desirable to disallow a
particular syntax pattern, it should be recognized at the lowering stage.
If this is currently not possible, the parser should be modified to emit an
appropriate marker that can be checked at lowering time.
3. Strict mode is not intended for for issues that are clearly bugs.
Those should instead use the syntax versioning mechanism (see
[`Base.Experimental.@set_syntax_version`](@ref)). However, `strict` mode flags
that gain widespread adoption may eventually be considered as candidates for
syntax evolution.
Strict mode flags are automatically inherited by submodules, but can be
overriden by an explicit `@strict` invocation in the submodule. Strict mode
flags are partitioned by world age.
# Specifying flags
The `flags` expression is runtime-evaluated and should evaluate to a collection
of `Symbols` as specified below. In addition, nested collections of symbols are
allowed and will be flattened. This is intended to support specifying strict
mode flags in a central location and enforcing them across multiple dependents.
```
# Module vs Project.toml opt-ins
Of particular note is that I ended up deciding on a per-module opt-in
rather than a Project.toml opt in (like was originally proposed
in #54903, and is implemented for syntax evolution in #60018).
This is for the following reasons:
1. The #60018 experience has shown that project.toml opt ins are
semantically somewhat awkward and need to be implemented both in the
language and the package manager. This was fine for the syntax
version, but strict mode is richer (and potentially much richer in
the future) and adding this complexity into code loading seems
undesirable.
2. One of the design objectives is to allow user-defined collections of
strict mode flags enforced centrally across multiple packages. In
this design this is easy by having a MyOrganizationBase package that
defines a variable with the set of flags to enable. Doing something
like this in Project.toml opens a whole can of worms on how to
represent that.
3. I believe the concern about wanting to enable parse-time strict mode
can be adequately addressed by having the parser emit a special
marker that can then get picked up and checked against the strict
mode by lowering (such marker addition possibly making use of the
syntax evolution mechanism). If this is not how it works, the parser
would need additional input state specifying the strict mode flags.
#60018 has shown that changing parser state flags dynamically is
undesirable, because people don't have a good sense of what the
parse unit is. As such, I don't want the parser to look at strict
mode flags at all.
4. As implemented here `@strict` inherits binding-world-age semantics.
Since these are now well defined as of 1.12, this addresses a lot of
the ordering concerns that were brought up in the discussion of
#54903.
5. I think it may be useful to opt into certain strict mode flags for
some modules in a package only (unlike the syntax version, where I
don't expect this to be common). E.g. packages may define modules
that define their core API or segregate their core algorithms from
support code and may want more strict coding styles for such core
modules.
There remains a bit of a concern that this is less friendly to IDEs.
I'm sympathetic to that, but the analysis required to compute the
strict mode is a lot simpler than other analyses (so a language server
should easily be able to do that) and I think it's outweighed
particularly by the desire for user-definable collections (which
requires the IDE to do some sort of analysis of however that is
specified anyway). Given that, I think this mechanism is as IDE friendly
as it gets, since the required capability is simply to compute the
value of a constant obtained from a macro expansion (so no special
strict-mode specific analysis required).
# Implementation details
The core of the implementation is simple, to determine the active
strict mode flags, we simply look up the
`_internal_module_strict_flags` binding in the appropriate module
and see which flags are set. The exact types and values of this
binding are explicitly and intentionally implementation details
and `@strict` decides how to set it. This is inteded to allow
flexibility of implementation in the future here.
To faciliate the above described semantics of `@strict`, this binding
has a couple of special features:
1. It gets automatically imported from a module's parent module upon
module creation.
2. Unlike bindings created through syntax, invalidations from imports
to `const` is permitted.
Otherwise the mechanism behaves as an ordinary binding, including
obeying world age semantics, and being Revise-able, etc. In particular,
if you Revise the `@strict` setting in a top-level module it will
automatically (through binding invalidation) be updated in all
submodules. It was important to me that doing this would not leave
the settings inconsistent.
# Implemented strict mode flags
Two strict mode flags are implemented in this PR, but they should
largely be considered straw-man implementations to show how to
access the flags set from within either the runtime or lowering.
Everything works end-to-end, but we may want to do some extra work
refining the precise semantics of these flags once we've merged
the core mechanism. The reason for choosing these flags is simply
that they were easy to implement. Several of the other proposed
flags would require additional analysis in lowering, which should
go in their own PRs. Implemented flags are as follows:
```
* `typeimports`
This flag turns the 1.12 warning for implicit import of types into an error.
Note that the implicit import default may be removed in a future Julia syntax
iteration, in which case this flag will become a no-op for such versions.
```jldoctest
julia> @Base.Experimental.strict :typeimports
julia> String(x) = 1
ERROR: `@strict :typeimports` disallows extending types without explicit import in TypeImports: function Base.String must be explicitly imported to be extended
```
* `:nointliteraliterators`
Disallows (at the lowering stages) literal integers as iterators in `for` loops.
This protects against expressions like `for i in 10` which are commonly intended
to be `for i in 1:10`.
```jldoctest
julia> for i in 10
println(i)
end
10
julia> @Base.Experimental.strict :nointliteraliterators
julia> for i in 10
println(i)
end
ERROR: syntax: `@strict :nointliteraliterators` disallows integer literal iterators here around none:1
Stacktrace:
[1] top-level scope
@ none:1
```strict mode
1 parent ade1dc4 commit 73576bb
File tree
17 files changed
+246
-38
lines changed- base
- docs
- src
- test
17 files changed
+246
-38
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
6 | | - | |
| 5 | + | |
| 6 | + | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| |||
148 | 148 | | |
149 | 149 | | |
150 | 150 | | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
151 | 157 | | |
152 | 158 | | |
153 | 159 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
726 | 726 | | |
727 | 727 | | |
728 | 728 | | |
729 | | - | |
| 729 | + | |
730 | 730 | | |
731 | 731 | | |
732 | 732 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2782 | 2782 | | |
2783 | 2783 | | |
2784 | 2784 | | |
2785 | | - | |
| 2785 | + | |
2786 | 2786 | | |
2787 | 2787 | | |
2788 | 2788 | | |
| |||
2817 | 2817 | | |
2818 | 2818 | | |
2819 | 2819 | | |
2820 | | - | |
| 2820 | + | |
2821 | 2821 | | |
2822 | 2822 | | |
2823 | | - | |
2824 | | - | |
| 2823 | + | |
| 2824 | + | |
| 2825 | + | |
| 2826 | + | |
| 2827 | + | |
2825 | 2828 | | |
2826 | 2829 | | |
2827 | 2830 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
841 | 841 | | |
842 | 842 | | |
843 | 843 | | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
| 872 | + | |
| 873 | + | |
| 874 | + | |
| 875 | + | |
| 876 | + | |
| 877 | + | |
| 878 | + | |
| 879 | + | |
| 880 | + | |
| 881 | + | |
| 882 | + | |
| 883 | + | |
| 884 | + | |
| 885 | + | |
| 886 | + | |
| 887 | + | |
| 888 | + | |
| 889 | + | |
| 890 | + | |
| 891 | + | |
| 892 | + | |
| 893 | + | |
| 894 | + | |
| 895 | + | |
| 896 | + | |
| 897 | + | |
| 898 | + | |
| 899 | + | |
| 900 | + | |
| 901 | + | |
| 902 | + | |
| 903 | + | |
| 904 | + | |
| 905 | + | |
| 906 | + | |
| 907 | + | |
| 908 | + | |
| 909 | + | |
| 910 | + | |
| 911 | + | |
| 912 | + | |
| 913 | + | |
| 914 | + | |
| 915 | + | |
| 916 | + | |
| 917 | + | |
| 918 | + | |
| 919 | + | |
| 920 | + | |
| 921 | + | |
| 922 | + | |
| 923 | + | |
| 924 | + | |
| 925 | + | |
| 926 | + | |
| 927 | + | |
| 928 | + | |
| 929 | + | |
| 930 | + | |
| 931 | + | |
| 932 | + | |
| 933 | + | |
| 934 | + | |
| 935 | + | |
| 936 | + | |
| 937 | + | |
| 938 | + | |
| 939 | + | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
844 | 951 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
113 | 113 | | |
114 | 114 | | |
115 | 115 | | |
116 | | - | |
| 116 | + | |
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
262 | 262 | | |
263 | 263 | | |
264 | 264 | | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
265 | 270 | | |
266 | 271 | | |
267 | 272 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| 42 | + | |
42 | 43 | | |
43 | 44 | | |
44 | 45 | | |
| |||
151 | 152 | | |
152 | 153 | | |
153 | 154 | | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
154 | 162 | | |
155 | 163 | | |
156 | 164 | | |
157 | 165 | | |
158 | 166 | | |
| 167 | + | |
159 | 168 | | |
160 | 169 | | |
161 | 170 | | |
| |||
181 | 190 | | |
182 | 191 | | |
183 | 192 | | |
| 193 | + | |
184 | 194 | | |
185 | 195 | | |
186 | 196 | | |
| |||
204 | 214 | | |
205 | 215 | | |
206 | 216 | | |
| 217 | + | |
| 218 | + | |
207 | 219 | | |
208 | 220 | | |
209 | 221 | | |
210 | 222 | | |
211 | 223 | | |
212 | 224 | | |
213 | 225 | | |
| 226 | + | |
214 | 227 | | |
215 | 228 | | |
216 | 229 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
489 | 489 | | |
490 | 490 | | |
491 | 491 | | |
492 | | - | |
493 | | - | |
494 | | - | |
495 | | - | |
496 | | - | |
497 | | - | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
498 | 499 | | |
499 | 500 | | |
500 | 501 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1537 | 1537 | | |
1538 | 1538 | | |
1539 | 1539 | | |
1540 | | - | |
| 1540 | + | |
1541 | 1541 | | |
1542 | 1542 | | |
1543 | 1543 | | |
1544 | 1544 | | |
1545 | | - | |
1546 | | - | |
| 1545 | + | |
| 1546 | + | |
| 1547 | + | |
| 1548 | + | |
| 1549 | + | |
| 1550 | + | |
| 1551 | + | |
1547 | 1552 | | |
1548 | 1553 | | |
1549 | 1554 | | |
| |||
1569 | 1574 | | |
1570 | 1575 | | |
1571 | 1576 | | |
1572 | | - | |
| 1577 | + | |
1573 | 1578 | | |
1574 | | - | |
| 1579 | + | |
1575 | 1580 | | |
1576 | 1581 | | |
1577 | 1582 | | |
1578 | 1583 | | |
1579 | | - | |
| 1584 | + | |
1580 | 1585 | | |
1581 | 1586 | | |
1582 | 1587 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4454 | 4454 | | |
4455 | 4455 | | |
4456 | 4456 | | |
4457 | | - | |
| 4457 | + | |
4458 | 4458 | | |
4459 | 4459 | | |
4460 | 4460 | | |
| |||
0 commit comments