rustc: Add a `#[wasm_custom_section]` attribute
This commit is an implementation of adding custom sections to wasm artifacts in rustc. The intention here is to expose the ability of the wasm binary format to contain custom sections with arbitrary user-defined data. Currently neither our version of LLVM nor LLD supports this so the implementation is currently custom to rustc itself. The implementation here is to attach a `#[wasm_custom_section = "foo"]` attribute to any `const` which has a type like `[u8; N]`. Other types of constants aren't supported yet but may be added one day! This should hopefully be enough to get off the ground with *some* custom section support. The current semantics are that any constant tagged with `#[wasm_custom_section]` section will be *appended* to the corresponding section in the final output wasm artifact (and this affects dependencies linked in as well, not just the final crate). This means that whatever is interpreting the contents must be able to interpret binary-concatenated sections (or each constant needs to be in its own custom section). To test this change the existing `run-make` test suite was moved to a `run-make-fulldeps` folder and a new `run-make` test suite was added which applies to all targets by default. This test suite currently only has one test which only runs for the wasm target (using a node.js script to use `WebAssembly` in JS to parse the wasm output).
This commit is contained in:
parent
5092c6b01a
commit
7df6f4161c
|
@ -313,6 +313,7 @@ impl<'a> Builder<'a> {
|
|||
test::RunPassFullDepsPretty, test::RunFailFullDepsPretty,
|
||||
test::Crate, test::CrateLibrustc, test::CrateRustdoc, test::Linkcheck,
|
||||
test::Cargotest, test::Cargo, test::Rls, test::ErrorIndex, test::Distcheck,
|
||||
test::RunMakeFullDeps,
|
||||
test::Nomicon, test::Reference, test::RustdocBook, test::RustByExample,
|
||||
test::TheBook, test::UnstableBook,
|
||||
test::Rustfmt, test::Miri, test::Clippy, test::RustdocJS, test::RustdocTheme,
|
||||
|
|
|
@ -915,7 +915,7 @@ impl Step for Assemble {
|
|||
}
|
||||
}
|
||||
|
||||
let lld_install = if build.config.lld_enabled && target_compiler.stage > 0 {
|
||||
let lld_install = if build.config.lld_enabled {
|
||||
Some(builder.ensure(native::Lld {
|
||||
target: target_compiler.host,
|
||||
}))
|
||||
|
|
|
@ -759,12 +759,18 @@ test!(RunFailFullDepsPretty {
|
|||
host: true
|
||||
});
|
||||
|
||||
host_test!(RunMake {
|
||||
default_test!(RunMake {
|
||||
path: "src/test/run-make",
|
||||
mode: "run-make",
|
||||
suite: "run-make"
|
||||
});
|
||||
|
||||
host_test!(RunMakeFullDeps {
|
||||
path: "src/test/run-make-fulldeps",
|
||||
mode: "run-make",
|
||||
suite: "run-make-fulldeps"
|
||||
});
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
struct Compiletest {
|
||||
compiler: Compiler,
|
||||
|
@ -827,8 +833,7 @@ impl Step for Compiletest {
|
|||
// FIXME: Does pretty need librustc compiled? Note that there are
|
||||
// fulldeps test suites with mode = pretty as well.
|
||||
mode == "pretty" ||
|
||||
mode == "rustdoc" ||
|
||||
mode == "run-make" {
|
||||
mode == "rustdoc" {
|
||||
builder.ensure(compile::Rustc { compiler, target });
|
||||
}
|
||||
|
||||
|
@ -849,7 +854,7 @@ impl Step for Compiletest {
|
|||
cmd.arg("--rustc-path").arg(builder.rustc(compiler));
|
||||
|
||||
// Avoid depending on rustdoc when we don't need it.
|
||||
if mode == "rustdoc" || mode == "run-make" {
|
||||
if mode == "rustdoc" || (mode == "run-make" && suite.ends_with("fulldeps")) {
|
||||
cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler.host));
|
||||
}
|
||||
|
||||
|
@ -931,7 +936,7 @@ impl Step for Compiletest {
|
|||
|
||||
// Only pass correct values for these flags for the `run-make` suite as it
|
||||
// requires that a C++ compiler was configured which isn't always the case.
|
||||
if suite == "run-make" {
|
||||
if suite == "run-make-fulldeps" {
|
||||
let llvm_components = output(Command::new(&llvm_config).arg("--components"));
|
||||
let llvm_cxxflags = output(Command::new(&llvm_config).arg("--cxxflags"));
|
||||
cmd.arg("--cc").arg(build.cc(target))
|
||||
|
@ -944,12 +949,12 @@ impl Step for Compiletest {
|
|||
}
|
||||
}
|
||||
}
|
||||
if suite == "run-make" && !build.config.llvm_enabled {
|
||||
if suite == "run-make-fulldeps" && !build.config.llvm_enabled {
|
||||
println!("Ignoring run-make test suite as they generally don't work without LLVM");
|
||||
return;
|
||||
}
|
||||
|
||||
if suite != "run-make" {
|
||||
if suite != "run-make-fulldeps" {
|
||||
cmd.arg("--cc").arg("")
|
||||
.arg("--cxx").arg("")
|
||||
.arg("--cflags").arg("")
|
||||
|
|
|
@ -650,6 +650,8 @@ define_dep_nodes!( <'tcx>
|
|||
|
||||
[] GetSymbolExportLevel(DefId),
|
||||
|
||||
[] WasmCustomSections(CrateNum),
|
||||
|
||||
[input] Features,
|
||||
|
||||
[] ProgramClausesFor(DefId),
|
||||
|
|
|
@ -25,6 +25,7 @@ enum Target {
|
|||
Struct,
|
||||
Union,
|
||||
Enum,
|
||||
Const,
|
||||
Other,
|
||||
}
|
||||
|
||||
|
@ -35,6 +36,7 @@ impl Target {
|
|||
hir::ItemStruct(..) => Target::Struct,
|
||||
hir::ItemUnion(..) => Target::Union,
|
||||
hir::ItemEnum(..) => Target::Enum,
|
||||
hir::ItemConst(..) => Target::Const,
|
||||
_ => Target::Other,
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +62,17 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
|
|||
if name == "inline" {
|
||||
self.check_inline(attr, item, target)
|
||||
}
|
||||
|
||||
if name == "wasm_custom_section" {
|
||||
if target != Target::Const {
|
||||
self.tcx.sess.span_err(attr.span, "only allowed on consts");
|
||||
}
|
||||
|
||||
if attr.value_str().is_none() {
|
||||
self.tcx.sess.span_err(attr.span, "must be of the form \
|
||||
#[wasm_custom_section = \"foo\"]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -318,6 +318,11 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt,
|
|||
return true;
|
||||
}
|
||||
|
||||
// These constants are special for wasm
|
||||
if attr::contains_name(attrs, "wasm_custom_section") {
|
||||
return true;
|
||||
}
|
||||
|
||||
tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow
|
||||
}
|
||||
|
||||
|
|
|
@ -678,6 +678,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::instance_def_size_estimate<'tcx>
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> QueryDescription<'tcx> for queries::wasm_custom_sections<'tcx> {
|
||||
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
|
||||
format!("custom wasm sections for a crate")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> QueryDescription<'tcx> for queries::generics_of<'tcx> {
|
||||
#[inline]
|
||||
fn cache_on_disk(def_id: Self::Key) -> bool {
|
||||
|
|
|
@ -424,6 +424,8 @@ define_maps! { <'tcx>
|
|||
[] fn features_query: features_node(CrateNum) -> Lrc<feature_gate::Features>,
|
||||
|
||||
[] fn program_clauses_for: ProgramClausesFor(DefId) -> Lrc<Vec<Clause<'tcx>>>,
|
||||
|
||||
[] fn wasm_custom_sections: WasmCustomSections(CrateNum) -> Lrc<Vec<DefId>>,
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -940,6 +940,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
|
|||
DepKind::Features => { force!(features_query, LOCAL_CRATE); }
|
||||
|
||||
DepKind::ProgramClausesFor => { force!(program_clauses_for, def_id!()); }
|
||||
DepKind::WasmCustomSections => { force!(wasm_custom_sections, krate!()); }
|
||||
}
|
||||
|
||||
true
|
||||
|
|
|
@ -271,6 +271,8 @@ provide! { <'tcx> tcx, def_id, other, cdata,
|
|||
|
||||
Arc::new(cdata.exported_symbols())
|
||||
}
|
||||
|
||||
wasm_custom_sections => { Lrc::new(cdata.wasm_custom_sections()) }
|
||||
}
|
||||
|
||||
pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
|
||||
|
|
|
@ -1067,6 +1067,16 @@ impl<'a, 'tcx> CrateMetadata {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn wasm_custom_sections(&self) -> Vec<DefId> {
|
||||
let sections = self.root
|
||||
.wasm_custom_sections
|
||||
.decode(self)
|
||||
.map(|def_index| self.local_def_id(def_index))
|
||||
.collect::<Vec<_>>();
|
||||
info!("loaded wasm sections {:?}", sections);
|
||||
return sections
|
||||
}
|
||||
|
||||
pub fn get_macro(&self, id: DefIndex) -> (InternedString, MacroDef) {
|
||||
let entry = self.entry(id);
|
||||
match entry.kind {
|
||||
|
|
|
@ -435,6 +435,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
&exported_symbols);
|
||||
let exported_symbols_bytes = self.position() - i;
|
||||
|
||||
// encode wasm custom sections
|
||||
let wasm_custom_sections = self.tcx.wasm_custom_sections(LOCAL_CRATE);
|
||||
let wasm_custom_sections = self.tracked(
|
||||
IsolatedEncoder::encode_wasm_custom_sections,
|
||||
&wasm_custom_sections);
|
||||
|
||||
// Encode and index the items.
|
||||
i = self.position();
|
||||
let items = self.encode_info_for_items();
|
||||
|
@ -478,6 +484,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
def_path_table,
|
||||
impls,
|
||||
exported_symbols,
|
||||
wasm_custom_sections,
|
||||
index,
|
||||
});
|
||||
|
||||
|
@ -1444,6 +1451,11 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
|
|||
.cloned())
|
||||
}
|
||||
|
||||
fn encode_wasm_custom_sections(&mut self, statics: &[DefId]) -> LazySeq<DefIndex> {
|
||||
info!("encoding custom wasm section constants {:?}", statics);
|
||||
self.lazy_seq(statics.iter().map(|id| id.index))
|
||||
}
|
||||
|
||||
fn encode_dylib_dependency_formats(&mut self, _: ()) -> LazySeq<Option<LinkagePreference>> {
|
||||
match self.tcx.sess.dependency_formats.borrow().get(&config::CrateTypeDylib) {
|
||||
Some(arr) => {
|
||||
|
|
|
@ -204,6 +204,7 @@ pub struct CrateRoot {
|
|||
pub def_path_table: Lazy<hir::map::definitions::DefPathTable>,
|
||||
pub impls: LazySeq<TraitImpls>,
|
||||
pub exported_symbols: LazySeq<(ExportedSymbol, SymbolExportLevel)>,
|
||||
pub wasm_custom_sections: LazySeq<DefIndex>,
|
||||
|
||||
pub index: LazySeq<index::Index>,
|
||||
}
|
||||
|
|
|
@ -11,9 +11,11 @@
|
|||
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
use rustc::hir::TransFnAttrFlags;
|
||||
use rustc::hir::{self, TransFnAttrFlags};
|
||||
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc::hir::itemlikevisit::ItemLikeVisitor;
|
||||
use rustc::session::config::Sanitizer;
|
||||
use rustc::ty::TyCtxt;
|
||||
use rustc::ty::maps::Providers;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
||||
|
@ -161,4 +163,32 @@ pub fn provide(providers: &mut Providers) {
|
|||
.collect())
|
||||
}
|
||||
};
|
||||
|
||||
providers.wasm_custom_sections = |tcx, cnum| {
|
||||
assert_eq!(cnum, LOCAL_CRATE);
|
||||
let mut finder = WasmSectionFinder { tcx, list: Vec::new() };
|
||||
tcx.hir.krate().visit_all_item_likes(&mut finder);
|
||||
Lrc::new(finder.list)
|
||||
};
|
||||
}
|
||||
|
||||
struct WasmSectionFinder<'a, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
list: Vec<DefId>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for WasmSectionFinder<'a, 'tcx> {
|
||||
fn visit_item(&mut self, i: &'tcx hir::Item) {
|
||||
match i.node {
|
||||
hir::ItemConst(..) => {}
|
||||
_ => return,
|
||||
}
|
||||
if i.attrs.iter().any(|i| i.check_name("wasm_custom_section")) {
|
||||
self.list.push(self.tcx.hir.local_def_id(i.id));
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {}
|
||||
|
||||
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use back::wasm;
|
||||
use cc::windows_registry;
|
||||
use super::archive::{ArchiveBuilder, ArchiveConfig};
|
||||
use super::bytecode::RLIB_BYTECODE_EXTENSION;
|
||||
|
@ -810,6 +811,11 @@ fn link_natively(sess: &Session,
|
|||
Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
if sess.opts.target_triple == "wasm32-unknown-unknown" {
|
||||
wasm::add_custom_sections(&out_filename,
|
||||
&trans.crate_info.wasm_custom_sections);
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_linker(sess: &Session, cmd: &mut Command, tmpdir: &Path)
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// 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.
|
||||
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use serialize::leb128;
|
||||
|
||||
pub fn add_custom_sections(path: &Path, sections: &BTreeMap<String, Vec<u8>>) {
|
||||
let mut wasm = fs::read(path).expect("failed to read wasm output");
|
||||
|
||||
// see https://webassembly.github.io/spec/core/binary/modules.html#custom-section
|
||||
for (section, bytes) in sections {
|
||||
// write the `id` identifier, 0 for a custom section
|
||||
let len = wasm.len();
|
||||
leb128::write_u32_leb128(&mut wasm, len, 0);
|
||||
|
||||
// figure out how long our name descriptor will be
|
||||
let mut name = Vec::new();
|
||||
leb128::write_u32_leb128(&mut name, 0, section.len() as u32);
|
||||
name.extend_from_slice(section.as_bytes());
|
||||
|
||||
// write the length of the payload
|
||||
let len = wasm.len();
|
||||
let total_len = bytes.len() + name.len();
|
||||
leb128::write_u32_leb128(&mut wasm, len, total_len as u32);
|
||||
|
||||
// write out the name section
|
||||
wasm.extend(name);
|
||||
|
||||
// and now the payload itself
|
||||
wasm.extend_from_slice(bytes);
|
||||
}
|
||||
|
||||
fs::write(path, &wasm).expect("failed to write wasm output");
|
||||
}
|
|
@ -74,6 +74,7 @@ use rustc::util::nodemap::{FxHashMap, FxHashSet, DefIdSet};
|
|||
use CrateInfo;
|
||||
|
||||
use std::any::Any;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::CString;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
@ -1070,8 +1071,24 @@ impl CrateInfo {
|
|||
used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic),
|
||||
used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic),
|
||||
used_crate_source: FxHashMap(),
|
||||
wasm_custom_sections: BTreeMap::new(),
|
||||
};
|
||||
|
||||
let load_wasm_sections = tcx.sess.crate_types.borrow()
|
||||
.iter()
|
||||
.any(|c| *c != config::CrateTypeRlib) &&
|
||||
tcx.sess.opts.target_triple == "wasm32-unknown-unknown";
|
||||
|
||||
if load_wasm_sections {
|
||||
info!("attempting to load all wasm sections");
|
||||
for &id in tcx.wasm_custom_sections(LOCAL_CRATE).iter() {
|
||||
let (name, contents) = fetch_wasm_section(tcx, id);
|
||||
info.wasm_custom_sections.entry(name)
|
||||
.or_insert(Vec::new())
|
||||
.extend(contents);
|
||||
}
|
||||
}
|
||||
|
||||
for &cnum in tcx.crates().iter() {
|
||||
info.native_libraries.insert(cnum, tcx.native_libraries(cnum));
|
||||
info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string());
|
||||
|
@ -1091,6 +1108,14 @@ impl CrateInfo {
|
|||
if tcx.is_no_builtins(cnum) {
|
||||
info.is_no_builtins.insert(cnum);
|
||||
}
|
||||
if load_wasm_sections {
|
||||
for &id in tcx.wasm_custom_sections(cnum).iter() {
|
||||
let (name, contents) = fetch_wasm_section(tcx, id);
|
||||
info.wasm_custom_sections.entry(name)
|
||||
.or_insert(Vec::new())
|
||||
.extend(contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1270,3 +1295,44 @@ mod temp_stable_hash_impls {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_wasm_section(tcx: TyCtxt, id: DefId) -> (String, Vec<u8>) {
|
||||
use rustc::mir::interpret::{GlobalId, Value, PrimVal};
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
|
||||
info!("loading wasm section {:?}", id);
|
||||
|
||||
let section = tcx.get_attrs(id)
|
||||
.iter()
|
||||
.find(|a| a.check_name("wasm_custom_section"))
|
||||
.expect("missing #[wasm_custom_section] attribute")
|
||||
.value_str()
|
||||
.expect("malformed #[wasm_custom_section] attribute");
|
||||
|
||||
let instance = ty::Instance::mono(tcx, id);
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None
|
||||
};
|
||||
let param_env = ty::ParamEnv::reveal_all();
|
||||
let val = tcx.const_eval(param_env.and(cid)).unwrap();
|
||||
|
||||
let val = match val.val {
|
||||
ConstVal::Value(val) => val,
|
||||
ConstVal::Unevaluated(..) => bug!("should be evaluated"),
|
||||
};
|
||||
let val = match val {
|
||||
Value::ByRef(ptr, _align) => ptr.into_inner_primval(),
|
||||
ref v => bug!("should be ByRef, was {:?}", v),
|
||||
};
|
||||
let mem = match val {
|
||||
PrimVal::Ptr(mem) => mem,
|
||||
ref v => bug!("should be Ptr, was {:?}", v),
|
||||
};
|
||||
assert_eq!(mem.offset, 0);
|
||||
let alloc = tcx
|
||||
.interpret_interner
|
||||
.get_alloc(mem.alloc_id)
|
||||
.expect("miri allocation never successfully created");
|
||||
(section.to_string(), alloc.bytes.clone())
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@ pub use llvm_util::target_features;
|
|||
use std::any::Any;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc;
|
||||
use std::collections::BTreeMap;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
||||
use rustc::dep_graph::DepGraph;
|
||||
|
@ -98,6 +99,7 @@ mod back {
|
|||
pub mod symbol_export;
|
||||
pub mod write;
|
||||
mod rpath;
|
||||
mod wasm;
|
||||
}
|
||||
|
||||
mod abi;
|
||||
|
@ -400,6 +402,7 @@ struct CrateInfo {
|
|||
used_crate_source: FxHashMap<CrateNum, Lrc<CrateSource>>,
|
||||
used_crates_static: Vec<(CrateNum, LibSource)>,
|
||||
used_crates_dynamic: Vec<(CrateNum, LibSource)>,
|
||||
wasm_custom_sections: BTreeMap<String, Vec<u8>>,
|
||||
}
|
||||
|
||||
__build_diagnostic_array! { librustc_trans, DIAGNOSTICS }
|
||||
|
|
|
@ -1182,9 +1182,15 @@ pub fn check_item_type<'a,'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Item
|
|||
let _indenter = indenter();
|
||||
match it.node {
|
||||
// Consts can play a role in type-checking, so they are included here.
|
||||
hir::ItemStatic(..) |
|
||||
hir::ItemStatic(..) => {
|
||||
tcx.typeck_tables_of(tcx.hir.local_def_id(it.id));
|
||||
}
|
||||
hir::ItemConst(..) => {
|
||||
tcx.typeck_tables_of(tcx.hir.local_def_id(it.id));
|
||||
if it.attrs.iter().any(|a| a.check_name("wasm_custom_section")) {
|
||||
let def_id = tcx.hir.local_def_id(it.id);
|
||||
check_const_is_u8_array(tcx, def_id, it.span);
|
||||
}
|
||||
}
|
||||
hir::ItemEnum(ref enum_definition, _) => {
|
||||
check_enum(tcx,
|
||||
|
@ -1256,6 +1262,21 @@ pub fn check_item_type<'a,'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Item
|
|||
}
|
||||
}
|
||||
|
||||
fn check_const_is_u8_array<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
def_id: DefId,
|
||||
span: Span) {
|
||||
match tcx.type_of(def_id).sty {
|
||||
ty::TyArray(t, _) => {
|
||||
match t.sty {
|
||||
ty::TyUint(ast::UintTy::U8) => return,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
tcx.sess.span_err(span, "must be an array of bytes like `[u8; N]`");
|
||||
}
|
||||
|
||||
fn check_on_unimplemented<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
trait_def_id: DefId,
|
||||
item: &hir::Item) {
|
||||
|
|
|
@ -451,6 +451,9 @@ declare_features! (
|
|||
|
||||
// `use path as _;` and `extern crate c as _;`
|
||||
(active, underscore_imports, "1.26.0", Some(48216), None),
|
||||
|
||||
// The #[wasm_custom_section] attribute
|
||||
(active, wasm_custom_section, "1.26.0", None, None),
|
||||
);
|
||||
|
||||
declare_features! (
|
||||
|
@ -1004,6 +1007,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
|||
"never will be stable",
|
||||
cfg_fn!(rustc_attrs))),
|
||||
|
||||
("wasm_custom_section", Whitelisted, Gated(Stability::Unstable,
|
||||
"wasm_custom_section",
|
||||
"attribute is currently unstable",
|
||||
cfg_fn!(wasm_custom_section))),
|
||||
|
||||
// Crate level attributes
|
||||
("crate_name", CrateLevel, Ungated),
|
||||
("crate_type", CrateLevel, Ungated),
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue