Skip to content

Commit 56d724e

Browse files
committed
Improve feature handling
This vendors some of rustc_feature 1.49 and generates a list of features by parsing the vendored code. This ensures that our list of unstable rust features can be more easily kept in sync with rustc. I wrote https://github.com/powerboat9/feature-scan before I wrote this patch, but that program has a lot of unnecessary dependencies. This patch only uses flex and bison to parse the included files, instead of trying to compile them as part of a rust project that will only work with a 5 year old version of rustc. gcc/rust/ChangeLog: * Make-lang.in: Compile feature-related source files, including a flex/bison based parser for the vendored rust files. * checks/errors/rust-feature-gate.cc: Move to... * checks/errors/feature/rust-feature-gate.cc: ...here. (FeatureGate::gate): Handle removal of Feature::create. (FeatureGate::visit): Refer to AUTO_TRAITS as OPTIN_BUILTIN_TRAITS. * checks/errors/rust-feature-gate.h: Move to... * checks/errors/feature/rust-feature-gate.h: ...here. * checks/errors/rust-feature.cc: Move to... * checks/errors/feature/rust-feature.cc: ...here. (Feature::create): Remove. (Feature::feature_list): New static member variable. (Feature::name_hash_map): Use "rust-feature-defs.h" to define. (Feature::lookup): New member function definition. * checks/errors/rust-feature.h: Move to... * checks/errors/feature/rust-feature.h: ...here. (Feature::State): Add comments. (Feature::Name): Use "rust-feature-defs.h" to define. (Feature::as_string): Make const. (Feature::name): Likewise. (Feature::state): Likewise. (Feature::issue): Likewise. (Feature::description): Remove. (Feature::create): Remove. (Feature::lookup): New member function declarations. (Feature::Feature): Adjust arguments. (Feature::m_rustc_since): Rename to... (Feature::m_rust_since): ...here. (Feature::m_description): Remove. (Feature::m_reason): New member variable. (Feature::feature_list): New static member variable. * checks/errors/feature/rust-feature-gen.l: New file. * checks/errors/feature/rust-feature-gen.y: New file. * checks/errors/feature/vendor/LICENSE-APACHE: New file. * checks/errors/feature/vendor/LICENSE-MIT: New file. * checks/errors/feature/vendor/README: New file. * checks/errors/feature/vendor/accepted.rs: New file. * checks/errors/feature/vendor/active.rs: New file. * checks/errors/feature/vendor/removed.rs: New file. Signed-off-by: Owen Avery <powerboat9.gamer@gmail.com>
1 parent e4a7b46 commit 56d724e

15 files changed

+1694
-191
lines changed

gcc/rust/Make-lang.in

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ check_rust_parallelize = 10
284284
# such as Bison output files which are not version-controlled, but should be included in any release
285285
# tarballs. This target will be executed during a bootstrap if ‘--enable-generated-files-in-srcdir’
286286
# was specified as a configure option.
287-
rust.srcextra:
287+
rust.srcextra: rust/feature-include/rust-feature-defs.h
288+
cp -p rust/feature-include/rust-feature-defs.h $(srcdir)/rust/checks/errors/feature/
288289

289290
rust.all.cross: gccrs$(exeext)
290291

@@ -443,9 +444,11 @@ RUST_INCLUDES = -I $(srcdir)/rust \
443444
-I $(srcdir)/rust/checks/errors \
444445
-I $(srcdir)/rust/checks/errors/privacy \
445446
-I $(srcdir)/rust/checks/errors/borrowck \
447+
-I $(srcdir)/rust/checks/errors/feature \
446448
-I $(srcdir)/rust/util \
447449
-I $(srcdir)/rust/metadata \
448-
-I $(srcdir)/../libgrust
450+
-I $(srcdir)/../libgrust \
451+
-I rust/feature-include
449452

450453
# add files that require cross-folder includes - currently rust-lang.o, rust-lex.o
451454
CFLAGS-rust/rust-lang.o += $(RUST_INCLUDES)
@@ -515,6 +518,54 @@ rust/%.o: rust/checks/errors/%.cc
515518
$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
516519
$(POSTCOMPILE)
517520

521+
# build rust/checks/errors/feature files
522+
# includes some flex/bison usage
523+
# and some code generation
524+
525+
# heavily based on the definition of COMPILE
526+
COMPILE_BUILD.base = $(CC_FOR_BUILD) -c $(CFLAGS_FOR_BUILD) -o $@
527+
ifeq ($(CXXDEPMODE),depmode=gcc3)
528+
COMPILE_BUILD = $(COMPILE_BUILD.base) -MT $@ -MMD -MP -MF $(@D)/$(DEPDIR)/$(*F).TPo
529+
else
530+
COMPILE_BUILD = source='$<' object='$@' libtool=no \
531+
DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) $(COMPILE_BUILD.base)
532+
endif
533+
534+
FEATURE_GEN = rust/checks/errors/feature/rust-feature-gen
535+
536+
build/rust/rust-feature-gen-parse.c: $(FEATURE_GEN).y
537+
mkdir -p build/rust
538+
mkdir -p rust/feature-include
539+
$(BISON) -o $@ \
540+
--defines=build/rust/rust-feature-gen-parse.h \
541+
$<
542+
543+
build/rust/rust-feature-gen-scan.c: $(FEATURE_GEN).l
544+
mkdir -p build/rust
545+
$(FLEX) -o $@ -Ca \
546+
--header-file=build/rust/rust-feature-gen-scan.h \
547+
$<
548+
549+
build/rust/rust-feature-gen-%.o: build/rust/rust-feature-gen-%.c build/rust/rust-feature-gen-parse.c build/rust/rust-feature-gen-scan.c
550+
mkdir -p build/rust/.deps
551+
$(COMPILE_BUILD) -I build/rust $<
552+
$(POSTCOMPILE)
553+
554+
build/rust/feature-gen$(build_exeext): build/rust/rust-feature-gen-scan.o build/rust/rust-feature-gen-parse.o
555+
$(LINKER_FOR_BUILD) $(BUILD_LINKERFLAGS) $(BUILD_LDFLAGS) -o $@ $^
556+
557+
RUST_FEATURE_FILES = $(patsubst %,rust/checks/errors/feature/vendor/%.rs,accepted active removed)
558+
559+
rust/feature-include/rust-feature-defs.h: build/rust/feature-gen$(build_exeext) $(RUST_FEATURE_FILES)
560+
cat $(patsubst %,$(srcdir)/%,$(RUST_FEATURE_FILES)) | ./$< > $@
561+
562+
rust/feature-include/%.h: rust/checks/errors/feature/%.h
563+
cp -p $< $@
564+
565+
rust/%.o: rust/checks/errors/feature/%.cc rust/feature-include/rust-feature-defs.h
566+
$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
567+
$(POSTCOMPILE)
568+
518569
# build privacy pass files in rust folder
519570
rust/%.o: rust/checks/errors/privacy/%.cc
520571
$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<

gcc/rust/checks/errors/rust-feature-gate.cc renamed to gcc/rust/checks/errors/feature/rust-feature-gate.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ FeatureGate::gate (Feature::Name name, location_t loc,
8282
{
8383
if (!valid_features.count (name))
8484
{
85-
auto feature = Feature::create (name);
85+
auto &feature = Feature::lookup (name);
8686
if (auto issue = feature.issue ())
8787
{
8888
auto issue_number = issue.value ();
@@ -184,7 +184,7 @@ void
184184
FeatureGate::visit (AST::Trait &trait)
185185
{
186186
if (trait.is_auto ())
187-
gate (Feature::Name::AUTO_TRAITS, trait.get_locus (),
187+
gate (Feature::Name::OPTIN_BUILTIN_TRAITS, trait.get_locus (),
188188
"auto traits are experimental and possibly buggy");
189189
AST::DefaultASTVisitor::visit (trait);
190190
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/* Copyright (C) 2025 Free Software Foundation, Inc.
2+
3+
This file is part of GCC.
4+
5+
GCC is free software; you can redistribute it and/or modify it under
6+
the terms of the GNU General Public License as published by the Free
7+
Software Foundation; either version 3, or (at your option) any later
8+
version.
9+
10+
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11+
WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with GCC; see the file COPYING3. If not see
17+
<http://www.gnu.org/licenses/>. */
18+
19+
%{
20+
21+
#include "rust-feature-gen-parse.h"
22+
23+
static int p_count = 0;
24+
25+
%}
26+
27+
%x INSIDE COMMENT
28+
29+
%%
30+
31+
declare_features! BEGIN (INSIDE);
32+
.|\n /* ignore */
33+
34+
<INSIDE>\( p_count++; return '(';
35+
<INSIDE>\) if (!--p_count) { BEGIN (0); } return ')';
36+
<INSIDE>, return ',';
37+
<INSIDE>:: return SCOPE;
38+
<INSIDE>Some return K_SOME;
39+
<INSIDE>None return K_NONE;
40+
<INSIDE>active return K_ACTIVE;
41+
<INSIDE>accepted return K_ACCEPTED;
42+
<INSIDE>removed return K_REMOVED;
43+
<INSIDE>stable_removed return K_STABLE_REMOVED;
44+
<INSIDE>Edition return K_E_START;
45+
<INSIDE>Edition2018 return K_E_2018;
46+
47+
<INSIDE>[A-Za-z_][A-Za-z0-9_]* yylval.str = strdup (yytext); return IDENT;
48+
<INSIDE>[1-9][0-9]* yylval.str = strdup (yytext); return NUM;
49+
<INSIDE>\"[^"]+\" yylval.str = strdup (yytext); return STR;
50+
<INSIDE>"/""/" BEGIN (COMMENT);
51+
<INSIDE>[ \n] /* ignore */
52+
<INSIDE>. { fprintf (stderr, "unrecognized character %u\n", (unsigned int) yytext[0]); exit (1); }
53+
54+
<COMMENT>. /* skip */
55+
<COMMENT>\n BEGIN (INSIDE);
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/* Copyright (C) 2025 Free Software Foundation, Inc.
2+
3+
This file is part of GCC.
4+
5+
GCC is free software; you can redistribute it and/or modify it under
6+
the terms of the GNU General Public License as published by the Free
7+
Software Foundation; either version 3, or (at your option) any later
8+
version.
9+
10+
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11+
WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with GCC; see the file COPYING3. If not see
17+
<http://www.gnu.org/licenses/>. */
18+
19+
%{
20+
21+
#include <stdlib.h>
22+
#include <stdio.h>
23+
#include <ctype.h>
24+
25+
int yylex (void);
26+
void yyerror (char const *);
27+
28+
#include "rust-feature-gen-scan.h"
29+
30+
// expands to three %s parameters
31+
#define UNWRAP_OPT_STR(prefix, s) (s ? prefix "_SOME (" : prefix "_NONE"), (s ? s : ""), (s ? ")" : "")
32+
33+
%}
34+
35+
%union
36+
{
37+
char *str;
38+
};
39+
40+
%token <str> IDENT STR NUM
41+
%token SCOPE
42+
%token K_SOME K_NONE
43+
%token K_ACTIVE K_ACCEPTED K_REMOVED K_STABLE_REMOVED
44+
%token K_E_START K_E_2018
45+
46+
%nterm <str> issue
47+
%nterm <str> edition
48+
%nterm <str> reason
49+
50+
%%
51+
52+
multi_database: multi_database database
53+
| database
54+
;
55+
56+
database: '(' entry_list ')';
57+
58+
entry_list: entry_list entry ','
59+
| entry ','
60+
;
61+
62+
entry: '(' K_ACTIVE ',' IDENT ',' STR ',' issue ',' edition ')' {
63+
char *ident_upper = strdup ($4);
64+
for (size_t i = 0; ident_upper[i]; i++)
65+
ident_upper[i] = toupper (ident_upper[i]);
66+
printf ("FEATURE_ACTIVE (\"%s\", %s, %s, %s%s%s, EDITION_%s)\n", $4, ident_upper, $6, UNWRAP_OPT_STR ("ISSUE", $8), $10 ? $10 : "NONE");
67+
free ($4);
68+
free (ident_upper);
69+
free ($6);
70+
free ($8);
71+
}
72+
| '(' K_ACCEPTED ',' IDENT ',' STR ',' issue ',' K_NONE ')' {
73+
char *ident_upper = strdup ($4);
74+
for (size_t i = 0; ident_upper[i]; i++)
75+
ident_upper[i] = toupper (ident_upper[i]);
76+
printf ("FEATURE_ACCEPTED (\"%s\", %s, %s, %s%s%s)\n", $4, ident_upper, $6, UNWRAP_OPT_STR ("ISSUE", $8));
77+
free ($4);
78+
free (ident_upper);
79+
free ($6);
80+
free ($8);
81+
}
82+
| '(' K_REMOVED ',' IDENT ',' STR ',' issue ',' K_NONE ',' reason ')' {
83+
char *ident_upper;
84+
// HACK: convert no_debug to F_NO_DEBUG instead
85+
// since NO_DEBUG is used as an unrelated macro
86+
if (!strcmp ($4, "no_debug"))
87+
{
88+
ident_upper = strdup ("F_NO_DEBUG");
89+
}
90+
else
91+
{
92+
ident_upper = strdup ($4);
93+
for (size_t i = 0; ident_upper[i]; i++)
94+
ident_upper[i] = toupper (ident_upper[i]);
95+
}
96+
printf ("FEATURE_REMOVED (\"%s\", %s, %s, %s%s%s, %s%s%s)\n", $4, ident_upper, $6, UNWRAP_OPT_STR ("ISSUE", $8), UNWRAP_OPT_STR ("REASON", $12));
97+
free ($4);
98+
free (ident_upper);
99+
free ($6);
100+
free ($8);
101+
free ($12);
102+
}
103+
| '(' K_STABLE_REMOVED ',' IDENT ',' STR ',' issue ',' K_NONE ')' {
104+
char *ident_upper = strdup ($4);
105+
for (size_t i = 0; ident_upper[i]; i++)
106+
ident_upper[i] = toupper (ident_upper[i]);
107+
printf ("FEATURE_STABLE_REMOVED (\"%s\", %s, %s, %s%s%s)\n", $4, ident_upper, $6, UNWRAP_OPT_STR ("ISSUE", $8));
108+
free ($4);
109+
free (ident_upper);
110+
free ($6);
111+
free ($8);
112+
}
113+
;
114+
115+
issue: K_SOME '(' NUM ')' { $$ = $3; }
116+
| K_NONE { $$ = NULL; }
117+
;
118+
119+
/* TODO: expand this as needed */
120+
edition: K_NONE { $$ = NULL; }
121+
| K_SOME '(' K_E_START SCOPE K_E_2018 ')' { $$ = "2018"; }
122+
;
123+
124+
reason: K_SOME '(' STR ')' { $$ = $3; }
125+
| K_NONE { $$ = NULL; }
126+
;
127+
128+
%%
129+
130+
void yyerror (const char *msg)
131+
{
132+
fprintf (stderr, "%s\n", msg);
133+
}
134+
135+
int yywrap (void)
136+
{
137+
return 1;
138+
}
139+
140+
int main (void)
141+
{
142+
return yyparse ();
143+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright (C) 2020-2025 Free Software Foundation, Inc.
2+
3+
// This file is part of GCC.
4+
5+
// GCC is free software; you can redistribute it and/or modify it under
6+
// the terms of the GNU General Public License as published by the Free
7+
// Software Foundation; either version 3, or (at your option) any later
8+
// version.
9+
10+
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11+
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
// for more details.
14+
15+
// You should have received a copy of the GNU General Public License
16+
// along with GCC; see the file COPYING3. If not see
17+
// <http://www.gnu.org/licenses/>.
18+
19+
#include "rust-feature.h"
20+
21+
namespace Rust {
22+
23+
Feature Feature::feature_list[] = {
24+
#define ISSUE_SOME(n) n
25+
#define ISSUE_NONE tl::nullopt
26+
#define EDITION_2018 Edition::E2018
27+
#define EDITION_NONE tl::nullopt
28+
#define REASON_SOME(r) r
29+
#define REASON_NONE tl::nullopt
30+
31+
#define FEATURE_BASE(state, name_str, name, rust_since, issue, ...) \
32+
Feature (Feature::Name::name, Feature::State::state, name_str, rust_since, \
33+
issue, __VA_ARGS__),
34+
35+
#define FEATURE_ACTIVE(a, b, c, d, edition) \
36+
FEATURE_BASE (ACTIVE, a, b, c, d, edition, tl::nullopt)
37+
38+
#define FEATURE_ACCEPTED(a, b, c, d) \
39+
FEATURE_BASE (ACCEPTED, a, b, c, d, tl::nullopt, tl::nullopt)
40+
41+
#define FEATURE_REMOVED(a, b, c, d, reason) \
42+
FEATURE_BASE (REMOVED, a, b, c, d, tl::nullopt, reason)
43+
44+
#define FEATURE_STABLE_REMOVED(a, b, c, d) \
45+
FEATURE_BASE (ACCEPTED, a, b, c, d, tl::nullopt, tl::nullopt)
46+
47+
#include "rust-feature-defs.h"
48+
49+
#undef ISSUE_SOME
50+
#undef ISSUE_NONE
51+
#undef EDITION_2018
52+
#undef EDITION_NONE
53+
#undef REASON_SOME
54+
#undef REASON_NONE
55+
56+
#undef FEATURE_BASE
57+
#undef FEATURE_ACTIVE
58+
#undef FEATURE_ACCEPTED
59+
#undef FEATURE_REMOVED
60+
#undef FEATURE_STABLE_REMOVED
61+
};
62+
63+
const std::map<std::string, Feature::Name> Feature::name_hash_map = {
64+
#define FEATURE(s, name, ...) {s, Feature::Name::name},
65+
#define FEATURE_ACTIVE(...) FEATURE (__VA_ARGS__)
66+
#define FEATURE_ACCEPTED(...) FEATURE (__VA_ARGS__)
67+
#define FEATURE_REMOVED(...) FEATURE (__VA_ARGS__)
68+
#define FEATURE_STABLE_REMOVED(...) FEATURE (__VA_ARGS__)
69+
#include "rust-feature-defs.h"
70+
#undef FEATURE
71+
#undef FEATURE_ACTIVE
72+
#undef FEATURE_ACCEPTED
73+
#undef FEATURE_REMOVED
74+
#undef FEATURE_STABLE_REMOVED
75+
};
76+
77+
tl::optional<Feature::Name>
78+
Feature::as_name (const std::string &name)
79+
{
80+
if (Feature::name_hash_map.count (name))
81+
return Feature::name_hash_map.at (name);
82+
83+
return tl::nullopt;
84+
}
85+
86+
tl::optional<std::reference_wrapper<const Feature>>
87+
Feature::lookup (const std::string &name)
88+
{
89+
return as_name (name).map (
90+
[] (Name n) { return std::ref (Feature::lookup (n)); });
91+
}
92+
93+
const Feature &
94+
Feature::lookup (Feature::Name name)
95+
{
96+
return feature_list[static_cast<size_t> (name)];
97+
}
98+
99+
} // namespace Rust

0 commit comments

Comments
 (0)