-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy path0000-rust-ver-attr.html
More file actions
627 lines (525 loc) · 29.7 KB
/
0000-rust-ver-attr.html
File metadata and controls
627 lines (525 loc) · 29.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="generator" content="rustdoc">
<title>RFC: Rust Version Attribute</title>
</head>
<body class="rustdoc">
<!--[if lte IE 8]>
<div class="warning">
This old browser is unsupported and will most likely display funky
things.
</div>
<![endif]-->
<h1 class="title">RFC: Rust Version Attribute</h1>
<nav id="TOC"><ul>
<li><a href="#summary">1 Summary</a><ul>
<li><a href="#context">1.1 Context</a><ul></ul></li></ul></li>
<li><a href="#motivation">2 Motivation</a><ul>
<li><a href="#clearer-errors-and-advice">2.1 Clearer Errors and Advice</a><ul></ul></li>
<li><a href="#package-ecosystem-support">2.2 Package Ecosystem Support</a><ul></ul></li>
<li><a href="#transitioning-to-2.0-(future-concern)">2.3 Transitioning To 2.0 (Future Concern)</a><ul></ul></li>
<li><a href="#why-<em>now</em>?">2.4 Why <em>Now</em>?</a><ul></ul></li></ul></li>
<li><a href="#detailed-design">3 Detailed design</a><ul>
<li><a href="#limiting-features-by-version">3.1 Limiting Features by Version</a><ul></ul></li>
<li><a href="#halting-parsing-early">3.2 Halting Parsing Early</a><ul></ul></li>
<li><a href="#aside:-improving-cfg">3.3 Aside: Improving <code>cfg</code></a><ul></ul></li></ul></li>
<li><a href="#drawbacks">4 Drawbacks</a><ul></ul></li>
<li><a href="#alternatives">5 Alternatives</a><ul></ul></li>
<li><a href="#unresolved-questions">6 Unresolved questions</a><ul></ul></li></ul></nav><style type="text/css">
/**
* Copyright 2013 The Rust Project Developers. See the COPYRIGHT
* file at the top-level directory of this distribution and at
* http://rust-lang.org/COPYRIGHT.
* With elements taken from Bootstrap v3.0.2 (MIT licensed).
*
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
* option. This file may not be copied, modified, or distributed
* except according to those terms.
*/
*:not(body) {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
/* General structure */
body {
margin: 0 auto;
padding: 0 15px;
font-family: "Source Serif Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 18px;
color: #333;
line-height: 1.428571429;
}
@media (min-width: 768px) {
body {
max-width: 750px;
}
}
h1, h2, h3, h4, h5, h6, nav, #versioninfo {
font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
h1, h2, h3, h4, h5, h6 {
color: black;
font-weight: 400;
line-height: 1.1;
}
h1, h2, h3 {
margin-top: 20px;
margin-bottom: 15px;
}
h1 {
margin-bottom: 20px;
}
h4, h5, h6 {
margin-top: 12px;
margin-bottom: 10px;
padding: 5px 10px;
}
h5, h6 {
text-decoration: underline;
}
h1 {
font-size: 28px;
font-weight: 500;
padding: .1em .4em;
border-bottom: 2px solid #ddd;
}
h1.title {
line-height: 1.5em;
}
h2 {
font-size: 26px;
padding: .2em .5em;
border-bottom: 1px solid #ddd;
}
h3 {
font-size: 24px;
padding: .2em .7em;
border-bottom: 1px solid #DDE8FC;
}
h4 {
font-size: 22px;
}
h5 {
font-size: 20px;
}
h6 {
font-size: 18px;
}
@media (min-width: 992px) {
h1 {
font-size: 36px;
}
h2 {
font-size: 30px;
}
h3 {
font-size: 26px;
}
}
nav {
column-count: 2;
-moz-column-count: 2;
-webkit-column-count: 2;
font-size: 15px;
margin: 0 0 1em 0;
}
p {
margin: 0 0 1em 0;
}
strong {
font-weight: bold;
}
em {
font-style: italic;
}
footer {
border-top: 1px solid #ddd;
font-size: 14.3px;
font-style: italic;
padding-top: 5px;
margin-top: 3em;
margin-bottom: 1em;
}
/* Links layout */
a {
text-decoration: none;
color: #428BCA;
background: transparent;
}
a:hover, a:focus {
color: #2A6496;
text-decoration: underline;
}
a:focus {
outline: thin dotted #333;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
a:hover, a:active {
outline: 0;
}
h1 a:link, h1 a:visited, h2 a:link, h2 a:visited,
h3 a:link, h3 a:visited, h4 a:link, h4 a:visited,
h5 a:link, h5 a:visited {color: black;}
h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover,
h5 a:hover {text-decoration: none;}
/* Code */
pre, code {
font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", monospace;
}
pre {
border-left: 2px solid #eee;
white-space: pre-wrap;
padding: 14px;
padding-right: 0;
margin: 20px 0;
font-size: 13px;
word-break: break-all;
word-wrap: break-word;
}
code {
padding: 0 2px;
color: #8D1A38;
white-space: pre-wrap;
}
pre code {
padding: 0;
font-size: inherit;
color: inherit;
}
a > code {
color: #428BCA;
}
/* Code highlighting */
pre.rust .kw { color: #8959A8; }
pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; }
pre.rust .number, pre.rust .string { color: #718C00; }
pre.rust .self, pre.rust .boolval, pre.rust .prelude-val,
pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; }
pre.rust .comment { color: #8E908C; }
pre.rust .doccomment { color: #4D4D4C; }
pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; }
pre.rust .lifetime { color: #B76514; }
/* The rest */
#versioninfo {
text-align: center;
margin: 0.5em;
font-size: 1.1em;
}
@media (min-width: 992px) {
#versioninfo {
font-size: 0.8em;
position: fixed;
bottom: 0px;
right: 0px;
}
.white-sticker {
background-color: #fff;
margin: 2px;
padding: 0 2px;
border-radius: .2em;
}
}
#versioninfo a.hash {
color: gray;
font-size: 80%;
}
blockquote {
color: #000;
margin: 20px 0;
padding: 15px 20px;
background-color: #f2f7f9;
border-top: .1em solid #e5eef2;
border-bottom: .1em solid #e5eef2;
}
blockquote p {
font-size: 17px;
font-weight: 300;
line-height: 1.4;
}
blockquote p:last-child {
margin-bottom: 0;
}
ul, ol {
padding-left: 25px;
}
ul ul, ol ul, ul ol, ol ol {
margin-bottom: 0;
}
dl {
margin-bottom: 20px;
}
dd {
margin-left: 0;
}
nav ul {
list-style-type: none;
margin: 0;
padding-left: 0px;
}
/* Only display one level of hierarchy in the TOC */
nav ul ul {
display: none;
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
}
hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eeeeee;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
table tr.odd {
background: #eee;
}
table td,
table th {
border: 1px solid #ddd;
padding: 5px;
}
/* Code snippets */
.rusttest { display: none; }
pre.rust { position: relative; }
.test-arrow {
display: inline-block;
position: absolute;
top: 0;
right: 10px;
font-size: 150%;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
.unstable-feature {
border: 2px solid red;
padding: 5px;
}
@media (min-width: 1170px) {
pre {
font-size: 15px;
}
}
@media print {
* {
text-shadow: none !important;
color: #000 !important;
background: transparent !important;
box-shadow: none !important;
}
a, a:visited {
text-decoration: underline;
}
p a[href]:after {
content: " (" attr(href) ")";
}
footer a[href]:after {
content: "";
}
a[href^="javascript:"]:after, a[href^="#"]:after {
content: "";
}
pre, blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
@page {
margin: 2cm .5cm;
}
h1:not(.title), h2, h3 {
border-bottom: 0px none;
}
p, h2, h3 {
orphans: 3;
widows: 3;
}
h2, h3 {
page-break-after: avoid;
}
table {
border-collapse: collapse !important;
}
table td, table th {
background-color: #fff !important;
}
}
#keyword-table-marker + table thead { display: none; }
#keyword-table-marker + table td { border: none; }
#keyword-table-marker + table {
margin-left: 2em;
margin-bottom: 1em;
}
</style>
<ul>
<li>Start Date: 2014-11-04</li>
<li>RFC PR: (leave this empty)</li>
<li>Rust Issue: (leave this empty)</li>
<li>Latest Version: <a href="https://danielkeep.github.io/0000-rust-ver-attr.html">https://danielkeep.github.io/0000-rust-ver-attr.html</a></li>
</ul>
<h1 id="summary" class='section-header'><a
href="#summary">1 Summary</a></h1>
<p>This RFC proposes adding a <code>#![rust_ver="..."]</code> attribute and <code>--rust-ver</code> compiler flag.</p>
<h2 id="context" class='section-header'><a
href="#context">1.1 Context</a></h2>
<ul>
<li><a href="https://github.com/rust-lang/rust/issues/3392">Issue #3392: language version markers</a> (Open).</li>
<li><a href="https://github.com/rust-lang/rust/issues/3795">Issue #3795: Need a mechanism to write rust-version-specific code</a> (Open).</li>
</ul>
<h1 id="motivation" class='section-header'><a
href="#motivation">2 Motivation</a></h1>
<p>There are several major motivations for this addition:</p>
<ul>
<li>Giving users clearer, more accurate information when attempting to compile code that is unsupported by their current compiler.</li>
<li>Giving the package ecosystem a formal way of dealing with language version dependencies.</li>
<li>Smoothing the transition to a future, backward-incompatible release of the language.</li>
</ul>
<h2 id="clearer-errors-and-advice" class='section-header'><a
href="#clearer-errors-and-advice">2.1 Clearer Errors and Advice</a></h2>
<p>Consider what would happen if a user of the current version of <code>rustc</code> were to try and compile a source file from Rust 1.8 with its amazing support for numbers with units:</p>
<pre><code class="language-shell">> type unit_test.rs
fn main() {
let x = 42.0_m;
println!("{}", x);
}
> rustc unit_test.rs
unit_test.rs:2:18: 2:19 error: expected `;`, found `m`
unit_test.rs:2 let x = 42.0_m;
^
</code></pre>
<p>It's not clear whether this is unsupported syntax or a typo that made its way into the code (unlikely, but possible), and it's not clear what the solution is. Now, consider what the compiler could tell the user if the code itself contained version information:</p>
<pre><code class="language-shell">> type unit_test_ver.rs
#![rust_ver="1.8"]
fn main() {
let x = 42.0_m;
println!("{}", x);
}
> rustc unit_test_ver.rs
unit_test.rs:1:1: 1:19 error: unsupported version of the Rust language required.
unit_test.rs:1 #![rust_ver="1.8"]
^~~~~~~~~~~~~~~~~~
unit_test.rs:1:1: 1:19 note: this compiler supports Rust 0.14.0, version 1.8 required.
</code></pre>
<p>This gives the user a clear indication that the problem is not the code itself, but the version of the compiler, <em>and</em> it tells them what version they need to acquire. The compiler does not need to guess, or be aware of future changes to the language.</p>
<p>We have seen this issue on IRC in cases where potential users are confronted with example code that does not work because their OS ships an old version of the compiler. For a new user, it's not clear where the problem lies; perhaps they've failed to correctly configure the compiler itself? Is it an issue with their packaged version specifically? If it's out of date, <em>how</em> out of date is it? Does the code only work on nightly builds, not stable (assuming they even know about the release channels)?</p>
<h2 id="package-ecosystem-support" class='section-header'><a
href="#package-ecosystem-support">2.2 Package Ecosystem Support</a></h2>
<p>Although not a direct concern for the compiler, having a formal notion of what language version is required for code will allow for some useful behaviour on the part of the surrounding package ecosystem.</p>
<p>First, it means that if a user attempts to compile a package that requires a newer version of Rust than their current compiler supports, they can be told this directly, rather than through syntax errors, or errors about missing functions. This also means it will be much easier to distinguish between "I forgot to use a trait" and "the required method really doesn't exist yet". Not only that, they can be told precisely what version they need to update to.</p>
<p>In addition, it means that the package manager can develop the ability to automatically constrain its selection of packages based on language version. For various reasons, users can find themselves "trapped" on a given version of the compiler. Consider a scenario where a developer is already using <code>libsplang</code> version 2.7 as part of a larger project that uses Rust 1.5. They do a <code>cargo update</code> and see the following:</p>
<pre><code class="language-text">warning: found libsplang v2.8.0, but did not update: requires Rust 1.6.
</code></pre>
<p>This removes a potential source of apprehension when updating dependencies. This may be particularly valuable to the aforementioned "trapped" developers for whom "just update <code>rustc</code>" is not a viable solution if they discover a dependency has stopped building. It also means they do not need to go hunting through the package history to find the most up-to-date version that supports their compiler.</p>
<h2 id="transitioning-to-2.0-(future-concern)" class='section-header'><a
href="#transitioning-to-2.0-(future-concern)">2.3 Transitioning To 2.0 (Future Concern)</a></h2>
<p>Consider the following piece of Python code:</p>
<pre><code class="language-python">print "Hello, World!"
</code></pre>
<p>This code works in Python 2.x, but produces the following output in 3.x:</p>
<pre><code class="language-text"> File "<stdin>", line 1
print "Hello, World!"
^
SyntaxError: invalid syntax
</code></pre>
<p>Is it the string literal that's the problem, the <code>!</code> in the string, or the <code>print</code> itself? (In fact, it is because <code>print</code> is no longer a statement in Python 3.x, it is a regular function.) The error does not make it in any way clear that the code is fundamentally incompatible with their interpreter.</p>
<p>Rust, at present, does somewhat better than this. For example, with a recent <code>rustc</code>:</p>
<pre><code class="language-shell">> type tilde_test.rs
fn main() {
let x = ~42;
println!("{}", x);
}
> rustc tilde_test.rs
tilde_test.rs:2:13: 2:14 error: obsolete syntax: `~` notation for owned pointer
allocation
tilde_test.rs:2 let x = ~42;
^
note: use the `box` operator instead of `~`
error: aborting due to previous error
</code></pre>
<p>However, how long will this error continue to exist? This also means the compiler has to keep <em>parsing</em> obsolete, invalid syntax. Consider a hypothetical version 2.0 of rust; with enough changes, it may simply be infeasible to support errors on every construct that has changed or been removed. Worse, it may not be possible to syntactically distinguish between 1.x and 2.x code that has changed meaning.</p>
<p>A <code>rust_ver</code> attribute provides a simple way to provide more accurate information to the user.</p>
<p>This could also work with <code>cfg</code> attributes (assuming they are processed <em>prior</em> to parsing modules; see "Aside: Improving <code>cfg</code>" below) to allow libraries to phase in support for new features without breaking backward compatibility.</p>
<p>Finally, this design also leaves open the door for having multiple versions of <code>rustc</code> installed side-by-side, or possibly even a <code>rustc</code> that supports multiple language versions. In the former case, a launcher program (much like <code>py</code>/<code>pyw</code> on Windows) could use this information to decide which version of the <code>rustc</code> compiler to invoke for a given crate. In the latter, a single compiler could embed the parsing and analysis logic for multiple versions of the language, making a transition even easier.</p>
<h2 id="why-<em>now</em>?" class='section-header'><a
href="#why-<em>now</em>?">2.4 Why <em>Now</em>?</a></h2>
<p>One important question is: why not simply make these changes later, when Rust 2.0 is definitely going to happen? The problem here is not with the language itself, but rather with the <em>ecosystem</em>. The "Python Package Index" has, amongst its package metadata, what version or versions of the language a package supports. However, this metadata is <em>not required</em> and (to the author's knowledge) was only added <em>after</em> Python 3.0 became a concern. As a result, it is entirely possible to accidentally install a package for the wrong version of the language.</p>
<p>In addition, the 2.7/3.0 break caused problems for anyone attempting to <em>run</em> Python code. In cases where scripts began with <code>#!/usr/bin/env python</code>, installing version 3.0 of Python could potentially break all existing Python scripts. As a result, more (though not all!) developers started to use <code>#!/usr/bin/env python2</code> or <code>#!/usr/bin/env python3</code> as appropriate. However, this <em>does not work on Windows</em>, which has no support for hashbangs. Instead, the developers had to write and ship a custom launcher (<code>py</code>/<code>pyw</code>) which handled this for Windows users.</p>
<p>This is less of an issue now, but is still a potential for user confusion if a script is <em>not</em> annotated correctly. It is also still an issue for other programs still coded to directly execute the default <code>python</code> on a system. Finally, the solution on Windows (run with <code>py</code> instead of <code>python</code>) does not apply to other platforms.</p>
<p>Making these changes <em>now</em> ensures that if Rust 2.0 starts development, the ecosystem has the necessary metadata and conventions to ease the transition.</p>
<h1 id="detailed-design" class='section-header'><a
href="#detailed-design">3 Detailed design</a></h1>
<p>The first change would be to formally give the compiler a concept of what version of the language it compiles. This would not necessarily (but preferably would) be the same as the version of the <em>compiler</em>. This version would be interpreted as a semantic version: a <code>rustc</code> that supports version 1.y of the Rust language would assume it can compile code for version 1.x (where x < y), but not code for version 1.z (where z > y) or 2.0 or higher.</p>
<p>A user should be able to ask <code>rustc</code> what version (or versions) it supports. To facilitate this, this RFC proposes an additional line be output with the <code>-v verbose</code> switch that specifies the minimum versions supported by the compiler. This would be a comma-delimited list of semantic version numbers. To give a hypothetical example of a <code>rustc</code> compiler that supports multiple language versions:</p>
<pre><code class="language-shell">> rustc -v verbose
rustc 2.3.56 (fe28bad1g 2016-10-31 02:27:15 +0000)
rust-version: 1.14.3, 2.3.56
binary: rustc
host: i686-w64-mingw32
release: 2.3.56
</code></pre>
<p>Secondly, this RFC proposes a method for specifying the minimum Rust language version required to compile a piece of code. This would be achieved using a <code>rust_ver</code> attribute (name open for bike-shedding), which can be applied in any position where attributes are valid (although, see below about "Halting Parsing Early"). The specific syntax is:</p>
<pre><code class="language-bnf">rust_ver_item : 'rust_ver' '=' string_lit
</code></pre>
<p>It the compiler encounters a version attribute indicating a version which is <em>not</em> supported, then the compiler should <em>immediately</em> abort, informing the user of the required version, as well as what version or versions it supports.</p>
<p>If the version attribute specifies a <em>compatible</em> version, then the compiler should do nothing.</p>
<p>The justification for allowing the attribute in <em>all</em> positions is that it allows language version-specific additions to be localised upon introduction. It also means that <em>example code</em> (such as might appear in the guides, tutorials, snippets, <em>etc.</em>) can accurately record its version requirements without having to always resort to a complete crate. Ensuring that documentation which contains code examples specify the version of <em>at least</em> one representative piece of code (such that it would be caught my simple copy and paste) could be encouraged as a social norm.</p>
<p>If the compiler <em>does not</em> encounter any language version attributes, it should assume the code is compatible. The reasoning for this choice is that if a new user comes to the language and tries to compile a "Hello, World!" program, or some other short, simple program, then having to also specify a language version (without knowing what version to specify) simply imposes an additional point of friction: another excuse to say "this is too much trouble".</p>
<p>On the other hand, this is a bad default for the package ecosystem; it would allow old code to break with no indication as to why. For this reason, this RFC also proposes a compiler switch used to specify the "default language version" of the code being passed to it.</p>
<pre><code class="language-shell"> --rust-ver VERSION Specify the version of the Rust language that the
code being compiled requires.
</code></pre>
<p>If the compiler is given a default language version that it does not support, it should <em>immediately</em> abort, informing the user of the required version, as well as what version or versions it supports.</p>
<p>The intention is that Cargo (or other packaging tools) would be modified to strongly discourage, <em>preferably reject</em>, packages which do not contain an explicit language version in their metadata.</p>
<p>To prevent needlessly annoying developers when working on an in-development package, this could potentially be enforced <em>only</em> upon uploading a package to the central repository. In other cases, a warning about the missing information, as well as an indication as to what the fallback version is, would be useful. For example:</p>
<pre><code class="language-shell">> cargo build
Compiling libsplang v0.4.7
warning: expected a value of type `string` for `package.language-version`
warning: defaulting to current rustc version: `1.14.3`
</code></pre>
<h2 id="limiting-features-by-version" class='section-header'><a
href="#limiting-features-by-version">3.1 Limiting Features by Version</a></h2>
<p>One potentially <em>very</em> useful feature would be to allow the compiler to <em>restrict</em> what features are available, based on the stated language version.</p>
<p>Currently, new features can be introduced behind a feature gate. If that feature is used <em>without</em> the gate, a compile error is issued, indicating the syntax in question, and what the feature gate is called.</p>
<p>This mechanism could be expanded to inform users about features they are using which require a newer version of the language than the one they have specified. This would take the form of an error, telling the user what minimum language version they need for that feature. In effect, new features would transition from a feature gate to a language version gate upon stabilisation.</p>
<p>It is unclear how much effort implementing these errors <em>comprehensively</em> would be. As such, this RFC merely <em>recommends</em> that such errors be introduced where feasible.</p>
<h2 id="halting-parsing-early" class='section-header'><a
href="#halting-parsing-early">3.2 Halting Parsing Early</a></h2>
<p>One issue with the <code>rust_ver</code> attribute is that the compiler will attempt to parse the entire source <em>before</em> examining attributes. As such, the RFC proposes the following two-stage implementation:</p>
<ul>
<li><p>Initially, <code>rust_ver</code> will <em>only</em> be valid when applied to a module as an inner attribute. When <code>rust_ver</code> is encountered, the compiler should assert the specified version is compatible <em>immediately</em>. This might be done by making the parser itself aware of what language version it should be parsing, with an "ignore <code>rust_ver</code>" setting to assist with external users (such as code formatting or completion).</p></li>
<li><p>The above should be expanded to attributes in all contexts. This may be more difficult, especially when macros are considered. Since this would permit <em>strictly</em> more code to be valid, it would be a backwards-compatible change and could be introduced as convenient.</p></li>
</ul>
<p>This would allow the compiler to report problems with incompatible source code <em>prior</em> to encountering a syntax error.</p>
<h2 id="aside:-improving-cfg" class='section-header'><a
href="#aside:-improving-cfg">3.3 Aside: Improving <code>cfg</code></a></h2>
<p>The "process <code>rust_ver</code> early" behaviour described above could potentially be generalised to allow attributes to flag themselves as needing "early evaluation". In the case of <code>cfg</code>, it could be changed so that instead of discarded an already-parsed item, it instead causes the parser to <em>ignore</em> the body of an item.</p>
<p>The simplest and most useful such behaviour would be for the parser to <em>not</em> fetch and parse the body of a <code>mod</code> item when the body is kept in an external file. This would allow code to be segmented based on language version support.</p>
<p>However, this is not needed for this RFC and should be specified independently, if at all.</p>
<h1 id="drawbacks" class='section-header'><a
href="#drawbacks">4 Drawbacks</a></h1>
<p>This represents an additional "hoop" to jump through when contributing a new package to the ecosystem. It also represents (for Cargo), additional work in the form of almost assured requests from users for them to "do the right thing" in selecting packages with supported language versions.</p>
<p>Additionally, in the absence of a general "early attribute processing" system, this requires the parser to actively enforce the semantics of the <code>rust_ver</code> attribute. Having such processing hard-wired into the parser is not ideal, from a "separation of concerns" perspective.</p>
<p>If the "language version gate" idea is adopted, this likely means additional internal complexity in the compiler to define, check, and assert these gates, for every new feature introduced.</p>
<h1 id="alternatives" class='section-header'><a
href="#alternatives">5 Alternatives</a></h1>
<p>One alternative is to simply do nothing. This will likely cause, at worst, minor discomfort to users, until a backward-incompatible version of the language comes into existence. It should be noted that these changes would be <em>difficult to make at a later date</em>, since in order to be effective, all existing packages would have to be updated. That said, provided there is sufficient time between implementation and the existence of a backward-incompatible version of Rust, any negative effects are likely to be minimal.</p>
<p>The package ecosystem concerns could also <em>potentially</em> be ameliorated prior to a backward-incompatible transition by tagging all existing packages with external metadata and requiring all new or updated packages to specify a version number.</p>
<p>A potential alternative to the simple <code>rust_ver</code> syntax would be to allow for more specific version specifications. For example, <code>"1.3 .. 1.3.78, 1.4 .."</code> might be used to work around problems in specific versions. However, this is really more appropriate for the <code>cfg</code> attribute.</p>
<h1 id="unresolved-questions" class='section-header'><a
href="#unresolved-questions">6 Unresolved questions</a></h1>
<p>What should the exact name of the attribute be?</p>
<script type="text/javascript">
window.playgroundUrl = "";
</script>
</body>
</html>