Auto merge of #63094 - Centril:rollup-lm7peuh, r=Centril
Rollup of 6 pull requests Successful merges: - #62809 (rustc: Update wasm32 support for LLVM 9) - #63055 (Various cleanups to save analysis) - #63076 (Miri: fix determining size of an "extra function" allocation) - #63077 (cleanup: Remove some language features related to built-in macros) - #63086 (Ignore test cases that are not supported by vxWorks) - #63092 (Update `impl Trait` gate issues) Failed merges: r? @ghost
This commit is contained in:
commit
8b94e9e918
38 changed files with 237 additions and 482 deletions
|
@ -913,9 +913,12 @@ pub fn compile_unit_metadata(
|
|||
}
|
||||
|
||||
debug!("compile_unit_metadata: {:?}", name_in_debuginfo);
|
||||
let rustc_producer = format!(
|
||||
"rustc version {}",
|
||||
option_env!("CFG_VERSION").expect("CFG_VERSION"),
|
||||
);
|
||||
// FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice.
|
||||
let producer = format!("clang LLVM (rustc version {})",
|
||||
(option_env!("CFG_VERSION")).expect("CFG_VERSION"));
|
||||
let producer = format!("clang LLVM ({})", rustc_producer);
|
||||
|
||||
let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
|
||||
let name_in_debuginfo = SmallCStr::new(&name_in_debuginfo);
|
||||
|
@ -980,6 +983,21 @@ pub fn compile_unit_metadata(
|
|||
gcov_metadata);
|
||||
}
|
||||
|
||||
// Insert `llvm.ident` metadata on the wasm32 targets since that will
|
||||
// get hooked up to the "producer" sections `processed-by` information.
|
||||
if tcx.sess.opts.target_triple.triple().starts_with("wasm32") {
|
||||
let name_metadata = llvm::LLVMMDStringInContext(
|
||||
debug_context.llcontext,
|
||||
rustc_producer.as_ptr() as *const _,
|
||||
rustc_producer.as_bytes().len() as c_uint,
|
||||
);
|
||||
llvm::LLVMAddNamedMetadataOperand(
|
||||
debug_context.llmod,
|
||||
const_cstr!("llvm.ident").as_ptr(),
|
||||
llvm::LLVMMDNodeInContext(debug_context.llcontext, &name_metadata, 1),
|
||||
);
|
||||
}
|
||||
|
||||
return unit_metadata;
|
||||
};
|
||||
|
||||
|
|
|
@ -678,14 +678,6 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
|
|||
sess.fatal(&format!("failed to run dsymutil: {}", e))
|
||||
}
|
||||
}
|
||||
|
||||
if sess.opts.target_triple.triple() == "wasm32-unknown-unknown" {
|
||||
super::wasm::add_producer_section(
|
||||
&out_filename,
|
||||
&sess.edition().to_string(),
|
||||
option_env!("CFG_VERSION").unwrap_or("unknown"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a boolean indicating whether the specified crate should be ignored
|
||||
|
|
|
@ -901,7 +901,45 @@ pub struct WasmLd<'a> {
|
|||
}
|
||||
|
||||
impl<'a> WasmLd<'a> {
|
||||
fn new(cmd: Command, sess: &'a Session, info: &'a LinkerInfo) -> WasmLd<'a> {
|
||||
fn new(mut cmd: Command, sess: &'a Session, info: &'a LinkerInfo) -> WasmLd<'a> {
|
||||
// If the atomics feature is enabled for wasm then we need a whole bunch
|
||||
// of flags:
|
||||
//
|
||||
// * `--shared-memory` - the link won't even succeed without this, flags
|
||||
// the one linear memory as `shared`
|
||||
//
|
||||
// * `--max-memory=1G` - when specifying a shared memory this must also
|
||||
// be specified. We conservatively choose 1GB but users should be able
|
||||
// to override this with `-C link-arg`.
|
||||
//
|
||||
// * `--import-memory` - it doesn't make much sense for memory to be
|
||||
// exported in a threaded module because typically you're
|
||||
// sharing memory and instantiating the module multiple times. As a
|
||||
// result if it were exported then we'd just have no sharing.
|
||||
//
|
||||
// * `--passive-segments` - all memory segments should be passive to
|
||||
// prevent each module instantiation from reinitializing memory.
|
||||
//
|
||||
// * `--export=__wasm_init_memory` - when using `--passive-segments` the
|
||||
// linker will synthesize this function, and so we need to make sure
|
||||
// that our usage of `--export` below won't accidentally cause this
|
||||
// function to get deleted.
|
||||
//
|
||||
// * `--export=*tls*` - when `#[thread_local]` symbols are used these
|
||||
// symbols are how the TLS segments are initialized and configured.
|
||||
let atomics = sess.opts.cg.target_feature.contains("+atomics") ||
|
||||
sess.target.target.options.features.contains("+atomics");
|
||||
if atomics {
|
||||
cmd.arg("--shared-memory");
|
||||
cmd.arg("--max-memory=1073741824");
|
||||
cmd.arg("--import-memory");
|
||||
cmd.arg("--passive-segments");
|
||||
cmd.arg("--export=__wasm_init_memory");
|
||||
cmd.arg("--export=__wasm_init_tls");
|
||||
cmd.arg("--export=__tls_size");
|
||||
cmd.arg("--export=__tls_align");
|
||||
cmd.arg("--export=__tls_base");
|
||||
}
|
||||
WasmLd { cmd, sess, info }
|
||||
}
|
||||
}
|
||||
|
@ -1004,6 +1042,13 @@ impl<'a> Linker for WasmLd<'a> {
|
|||
for sym in self.info.exports[&crate_type].iter() {
|
||||
self.cmd.arg("--export").arg(&sym);
|
||||
}
|
||||
|
||||
// LLD will hide these otherwise-internal symbols since our `--export`
|
||||
// list above is a whitelist of what to export. Various bits and pieces
|
||||
// of tooling use this, so be sure these symbols make their way out of
|
||||
// the linker as well.
|
||||
self.cmd.arg("--export=__heap_base");
|
||||
self.cmd.arg("--export=__data_end");
|
||||
}
|
||||
|
||||
fn subsystem(&mut self, _subsystem: &str) {
|
||||
|
|
|
@ -6,4 +6,3 @@ pub mod command;
|
|||
pub mod symbol_export;
|
||||
pub mod archive;
|
||||
pub mod rpath;
|
||||
pub mod wasm;
|
||||
|
|
|
@ -1,191 +0,0 @@
|
|||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::str;
|
||||
|
||||
use rustc_serialize::leb128;
|
||||
|
||||
// https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec
|
||||
const WASM_CUSTOM_SECTION_ID: u8 = 0;
|
||||
|
||||
/// Adds or augment the existing `producers` section to encode information about
|
||||
/// the Rust compiler used to produce the wasm file.
|
||||
pub fn add_producer_section(
|
||||
path: &Path,
|
||||
rust_version: &str,
|
||||
rustc_version: &str,
|
||||
) {
|
||||
struct Field<'a> {
|
||||
name: &'a str,
|
||||
values: Vec<FieldValue<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct FieldValue<'a> {
|
||||
name: &'a str,
|
||||
version: &'a str,
|
||||
}
|
||||
|
||||
let wasm = fs::read(path).expect("failed to read wasm output");
|
||||
let mut ret = WasmEncoder::new();
|
||||
ret.data.extend(&wasm[..8]);
|
||||
|
||||
// skip the 8 byte wasm/version header
|
||||
let rustc_value = FieldValue {
|
||||
name: "rustc",
|
||||
version: rustc_version,
|
||||
};
|
||||
let rust_value = FieldValue {
|
||||
name: "Rust",
|
||||
version: rust_version,
|
||||
};
|
||||
let mut fields = Vec::new();
|
||||
let mut wrote_rustc = false;
|
||||
let mut wrote_rust = false;
|
||||
|
||||
// Move all sections from the original wasm file to our output, skipping
|
||||
// everything except the producers section
|
||||
for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) {
|
||||
if id != WASM_CUSTOM_SECTION_ID {
|
||||
ret.byte(id);
|
||||
ret.bytes(raw);
|
||||
continue
|
||||
}
|
||||
let mut decoder = WasmDecoder::new(raw);
|
||||
if decoder.str() != "producers" {
|
||||
ret.byte(id);
|
||||
ret.bytes(raw);
|
||||
continue
|
||||
}
|
||||
|
||||
// Read off the producers section into our fields outside the loop,
|
||||
// we'll re-encode the producers section when we're done (to handle an
|
||||
// entirely missing producers section as well).
|
||||
info!("rewriting existing producers section");
|
||||
|
||||
for _ in 0..decoder.u32() {
|
||||
let name = decoder.str();
|
||||
let mut values = Vec::new();
|
||||
for _ in 0..decoder.u32() {
|
||||
let name = decoder.str();
|
||||
let version = decoder.str();
|
||||
values.push(FieldValue { name, version });
|
||||
}
|
||||
|
||||
if name == "language" {
|
||||
values.push(rust_value);
|
||||
wrote_rust = true;
|
||||
} else if name == "processed-by" {
|
||||
values.push(rustc_value);
|
||||
wrote_rustc = true;
|
||||
}
|
||||
fields.push(Field { name, values });
|
||||
}
|
||||
}
|
||||
|
||||
if !wrote_rust {
|
||||
fields.push(Field {
|
||||
name: "language",
|
||||
values: vec![rust_value],
|
||||
});
|
||||
}
|
||||
if !wrote_rustc {
|
||||
fields.push(Field {
|
||||
name: "processed-by",
|
||||
values: vec![rustc_value],
|
||||
});
|
||||
}
|
||||
|
||||
// Append the producers section to the end of the wasm file.
|
||||
let mut section = WasmEncoder::new();
|
||||
section.str("producers");
|
||||
section.u32(fields.len() as u32);
|
||||
for field in fields {
|
||||
section.str(field.name);
|
||||
section.u32(field.values.len() as u32);
|
||||
for value in field.values {
|
||||
section.str(value.name);
|
||||
section.str(value.version);
|
||||
}
|
||||
}
|
||||
ret.byte(WASM_CUSTOM_SECTION_ID);
|
||||
ret.bytes(§ion.data);
|
||||
|
||||
fs::write(path, &ret.data).expect("failed to write wasm output");
|
||||
}
|
||||
|
||||
struct WasmSections<'a>(WasmDecoder<'a>);
|
||||
|
||||
impl<'a> Iterator for WasmSections<'a> {
|
||||
type Item = (u8, &'a [u8]);
|
||||
|
||||
fn next(&mut self) -> Option<(u8, &'a [u8])> {
|
||||
if self.0.data.is_empty() {
|
||||
return None
|
||||
}
|
||||
|
||||
// see https://webassembly.github.io/spec/core/binary/modules.html#sections
|
||||
let id = self.0.byte();
|
||||
let section_len = self.0.u32();
|
||||
info!("new section {} / {} bytes", id, section_len);
|
||||
let section = self.0.skip(section_len as usize);
|
||||
Some((id, section))
|
||||
}
|
||||
}
|
||||
|
||||
struct WasmDecoder<'a> {
|
||||
data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> WasmDecoder<'a> {
|
||||
fn new(data: &'a [u8]) -> WasmDecoder<'a> {
|
||||
WasmDecoder { data }
|
||||
}
|
||||
|
||||
fn byte(&mut self) -> u8 {
|
||||
self.skip(1)[0]
|
||||
}
|
||||
|
||||
fn u32(&mut self) -> u32 {
|
||||
let (n, l1) = leb128::read_u32_leb128(self.data);
|
||||
self.data = &self.data[l1..];
|
||||
return n
|
||||
}
|
||||
|
||||
fn skip(&mut self, amt: usize) -> &'a [u8] {
|
||||
let (data, rest) = self.data.split_at(amt);
|
||||
self.data = rest;
|
||||
data
|
||||
}
|
||||
|
||||
fn str(&mut self) -> &'a str {
|
||||
let len = self.u32();
|
||||
str::from_utf8(self.skip(len as usize)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
struct WasmEncoder {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl WasmEncoder {
|
||||
fn new() -> WasmEncoder {
|
||||
WasmEncoder { data: Vec::new() }
|
||||
}
|
||||
|
||||
fn u32(&mut self, val: u32) {
|
||||
leb128::write_u32_leb128(&mut self.data, val);
|
||||
}
|
||||
|
||||
fn byte(&mut self, val: u8) {
|
||||
self.data.push(val);
|
||||
}
|
||||
|
||||
fn bytes(&mut self, val: &[u8]) {
|
||||
self.u32(val.len() as u32);
|
||||
self.data.extend_from_slice(val);
|
||||
}
|
||||
|
||||
fn str(&mut self, val: &str) {
|
||||
self.bytes(val.as_bytes())
|
||||
}
|
||||
}
|
|
@ -54,6 +54,16 @@ pub trait AllocMap<K: Hash + Eq, V> {
|
|||
k: K,
|
||||
vacant: impl FnOnce() -> Result<V, E>
|
||||
) -> Result<&mut V, E>;
|
||||
|
||||
/// Read-only lookup.
|
||||
fn get(&self, k: K) -> Option<&V> {
|
||||
self.get_or(k, || Err(())).ok()
|
||||
}
|
||||
|
||||
/// Mutable lookup.
|
||||
fn get_mut(&mut self, k: K) -> Option<&mut V> {
|
||||
self.get_mut_or(k, || Err(())).ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods of this trait signifies a point where CTFE evaluation would fail
|
||||
|
|
|
@ -535,14 +535,26 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
|
|||
id: AllocId,
|
||||
liveness: AllocCheck,
|
||||
) -> InterpResult<'static, (Size, Align)> {
|
||||
// # Regular allocations
|
||||
// Don't use `self.get` here as that will
|
||||
// a) cause cycles in case `id` refers to a static
|
||||
// b) duplicate a static's allocation in miri
|
||||
match self.alloc_map.get_or(id, || Err(())) {
|
||||
Ok((_, alloc)) => Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align)),
|
||||
Err(()) => {
|
||||
// Not a local allocation, check the global `tcx.alloc_map`.
|
||||
if let Some((_, alloc)) = self.alloc_map.get(id) {
|
||||
return Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align));
|
||||
}
|
||||
|
||||
// # Function pointers
|
||||
// (both global from `alloc_map` and local from `extra_fn_ptr_map`)
|
||||
if let Ok(_) = self.get_fn_alloc(id) {
|
||||
return if let AllocCheck::Dereferencable = liveness {
|
||||
// The caller requested no function pointers.
|
||||
err!(DerefFunctionPointer)
|
||||
} else {
|
||||
Ok((Size::ZERO, Align::from_bytes(1).unwrap()))
|
||||
};
|
||||
}
|
||||
|
||||
// # Statics
|
||||
// Can't do this in the match argument, we may get cycle errors since the lock would
|
||||
// be held throughout the match.
|
||||
let alloc = self.tcx.alloc_map.lock().get(id);
|
||||
|
@ -557,14 +569,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
|
|||
// Need to duplicate the logic here, because the global allocations have
|
||||
// different associated types than the interpreter-local ones.
|
||||
Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align)),
|
||||
Some(GlobalAlloc::Function(_)) => {
|
||||
if let AllocCheck::Dereferencable = liveness {
|
||||
// The caller requested no function pointers.
|
||||
err!(DerefFunctionPointer)
|
||||
} else {
|
||||
Ok((Size::ZERO, Align::from_bytes(1).unwrap()))
|
||||
}
|
||||
},
|
||||
Some(GlobalAlloc::Function(_)) =>
|
||||
bug!("We already checked function pointers above"),
|
||||
// The rest must be dead.
|
||||
None => if let AllocCheck::MaybeDead = liveness {
|
||||
// Deallocated pointers are allowed, we should be able to find
|
||||
|
@ -577,8 +583,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
|
|||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fn_alloc(&self, id: AllocId) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
|
||||
trace!("reading fn ptr: {}", id);
|
||||
|
|
|
@ -23,7 +23,7 @@ use rustc_data_structures::fx::FxHashSet;
|
|||
use std::path::Path;
|
||||
use std::env;
|
||||
|
||||
use syntax::ast::{self, Attribute, NodeId, PatKind, CRATE_NODE_ID};
|
||||
use syntax::ast::{self, Attribute, NodeId, PatKind};
|
||||
use syntax::parse::token;
|
||||
use syntax::visit::{self, Visitor};
|
||||
use syntax::print::pprust::{
|
||||
|
@ -75,15 +75,13 @@ macro_rules! access_from_vis {
|
|||
};
|
||||
}
|
||||
|
||||
pub struct DumpVisitor<'l, 'tcx, 'll> {
|
||||
save_ctxt: SaveContext<'l, 'tcx>,
|
||||
pub struct DumpVisitor<'l, 'tcx> {
|
||||
pub save_ctxt: SaveContext<'l, 'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
dumper: &'ll mut Dumper,
|
||||
dumper: Dumper,
|
||||
|
||||
span: SpanUtils<'l>,
|
||||
|
||||
cur_scope: NodeId,
|
||||
|
||||
// Set of macro definition (callee) spans, and the set
|
||||
// of macro use (callsite) spans. We store these to ensure
|
||||
// we only write one macro def per unique macro definition, and
|
||||
|
@ -92,36 +90,29 @@ pub struct DumpVisitor<'l, 'tcx, 'll> {
|
|||
// macro_calls: FxHashSet<Span>,
|
||||
}
|
||||
|
||||
impl<'l, 'tcx, 'll> DumpVisitor<'l, 'tcx, 'll> {
|
||||
impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
|
||||
pub fn new(
|
||||
save_ctxt: SaveContext<'l, 'tcx>,
|
||||
dumper: &'ll mut Dumper,
|
||||
) -> DumpVisitor<'l, 'tcx, 'll> {
|
||||
) -> DumpVisitor<'l, 'tcx> {
|
||||
let span_utils = SpanUtils::new(&save_ctxt.tcx.sess);
|
||||
let dumper = Dumper::new(save_ctxt.config.clone());
|
||||
DumpVisitor {
|
||||
tcx: save_ctxt.tcx,
|
||||
save_ctxt,
|
||||
dumper,
|
||||
span: span_utils,
|
||||
cur_scope: CRATE_NODE_ID,
|
||||
// mac_defs: FxHashSet::default(),
|
||||
// macro_calls: FxHashSet::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn nest_scope<F>(&mut self, scope_id: NodeId, f: F)
|
||||
where
|
||||
F: FnOnce(&mut DumpVisitor<'l, 'tcx, 'll>),
|
||||
{
|
||||
let parent_scope = self.cur_scope;
|
||||
self.cur_scope = scope_id;
|
||||
f(self);
|
||||
self.cur_scope = parent_scope;
|
||||
pub fn analysis(&self) -> &rls_data::Analysis {
|
||||
self.dumper.analysis()
|
||||
}
|
||||
|
||||
fn nest_tables<F>(&mut self, item_id: NodeId, f: F)
|
||||
where
|
||||
F: FnOnce(&mut DumpVisitor<'l, 'tcx, 'll>),
|
||||
F: FnOnce(&mut Self),
|
||||
{
|
||||
let item_def_id = self.tcx.hir().local_def_id_from_node_id(item_id);
|
||||
if self.tcx.has_typeck_tables(item_def_id) {
|
||||
|
@ -320,7 +311,7 @@ impl<'l, 'tcx, 'll> DumpVisitor<'l, 'tcx, 'll> {
|
|||
|
||||
// walk the fn body
|
||||
if let Some(body) = body {
|
||||
self.nest_tables(id, |v| v.nest_scope(id, |v| v.visit_block(body)));
|
||||
self.nest_tables(id, |v| v.visit_block(body));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -405,7 +396,7 @@ impl<'l, 'tcx, 'll> DumpVisitor<'l, 'tcx, 'll> {
|
|||
self.visit_ty(&ret_ty);
|
||||
}
|
||||
|
||||
self.nest_tables(item.id, |v| v.nest_scope(item.id, |v| v.visit_block(&body)));
|
||||
self.nest_tables(item.id, |v| v.visit_block(&body));
|
||||
}
|
||||
|
||||
fn process_static_or_const_item(
|
||||
|
@ -1311,7 +1302,7 @@ impl<'l, 'tcx, 'll> DumpVisitor<'l, 'tcx, 'll> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'l, 'tcx, 'll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll> {
|
||||
impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> {
|
||||
fn visit_mod(&mut self, m: &'l ast::Mod, span: Span, attrs: &[ast::Attribute], id: NodeId) {
|
||||
// Since we handle explicit modules ourselves in visit_item, this should
|
||||
// only get called for the root module of a crate.
|
||||
|
@ -1349,7 +1340,7 @@ impl<'l, 'tcx, 'll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll> {
|
|||
attributes: lower_attributes(attrs.to_owned(), &self.save_ctxt),
|
||||
},
|
||||
);
|
||||
self.nest_scope(id, |v| visit::walk_mod(v, m));
|
||||
visit::walk_mod(self, m);
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, item: &'l ast::Item) {
|
||||
|
@ -1404,7 +1395,7 @@ impl<'l, 'tcx, 'll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll> {
|
|||
}
|
||||
Mod(ref m) => {
|
||||
self.process_mod(item);
|
||||
self.nest_scope(item.id, |v| visit::walk_mod(v, m));
|
||||
visit::walk_mod(self, m);
|
||||
}
|
||||
Ty(ref ty, ref ty_params) => {
|
||||
let qualname = format!("::{}",
|
||||
|
@ -1570,7 +1561,7 @@ impl<'l, 'tcx, 'll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll> {
|
|||
// walk the body
|
||||
self.nest_tables(ex.id, |v| {
|
||||
v.process_formals(&decl.inputs, &id);
|
||||
v.nest_scope(ex.id, |v| v.visit_expr(body))
|
||||
v.visit_expr(body)
|
||||
});
|
||||
}
|
||||
ast::ExprKind::ForLoop(ref pattern, ref subexpression, ref block, _) => {
|
||||
|
|
|
@ -22,8 +22,8 @@ impl Dumper {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_output(self, f: impl FnOnce(&Analysis)) {
|
||||
f(&self.result)
|
||||
pub fn analysis(&self) -> &Analysis {
|
||||
&self.result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,12 +35,11 @@ use syntax::visit::{self, Visitor};
|
|||
use syntax::print::pprust::{arg_to_string, ty_to_string};
|
||||
use syntax_pos::*;
|
||||
|
||||
use dumper::Dumper;
|
||||
use dump_visitor::DumpVisitor;
|
||||
use span_utils::SpanUtils;
|
||||
|
||||
use rls_data::{Def, DefKind, ExternalCrateData, GlobalCrateId, MacroRef, Ref, RefKind, Relation,
|
||||
RelationKind, SpanData, Impl, ImplKind};
|
||||
RelationKind, SpanData, Impl, ImplKind, Analysis};
|
||||
use rls_data::config::Config;
|
||||
|
||||
use log::{debug, error, info};
|
||||
|
@ -997,12 +996,10 @@ impl<'l> Visitor<'l> for PathCollector<'l> {
|
|||
|
||||
/// Defines what to do with the results of saving the analysis.
|
||||
pub trait SaveHandler {
|
||||
fn save<'l, 'tcx>(
|
||||
fn save(
|
||||
&mut self,
|
||||
save_ctxt: SaveContext<'l, 'tcx>,
|
||||
krate: &ast::Crate,
|
||||
cratename: &str,
|
||||
input: &'l Input,
|
||||
save_ctxt: &SaveContext<'_, '_>,
|
||||
analysis: &Analysis,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1062,28 +1059,17 @@ impl<'a> DumpHandler<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> SaveHandler for DumpHandler<'a> {
|
||||
fn save<'l, 'tcx>(
|
||||
impl SaveHandler for DumpHandler<'_> {
|
||||
fn save(
|
||||
&mut self,
|
||||
save_ctxt: SaveContext<'l, 'tcx>,
|
||||
krate: &ast::Crate,
|
||||
cratename: &str,
|
||||
input: &'l Input,
|
||||
save_ctxt: &SaveContext<'_, '_>,
|
||||
analysis: &Analysis,
|
||||
) {
|
||||
let sess = &save_ctxt.tcx.sess;
|
||||
let (output, file_name) = self.output_file(&save_ctxt);
|
||||
let mut dumper = Dumper::new(save_ctxt.config.clone());
|
||||
let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
|
||||
|
||||
visitor.dump_crate_info(cratename, krate);
|
||||
visitor.dump_compilation_options(input, cratename);
|
||||
visit::walk_crate(&mut visitor, krate);
|
||||
|
||||
dumper.to_output(|analysis| {
|
||||
if let Err(e) = serde_json::to_writer(output, analysis) {
|
||||
if let Err(e) = serde_json::to_writer(output, &analysis) {
|
||||
error!("Can't serialize save-analysis: {:?}", e);
|
||||
}
|
||||
});
|
||||
|
||||
if sess.opts.debugging_opts.emit_artifact_notifications {
|
||||
sess.parse_sess.span_diagnostic
|
||||
|
@ -1097,27 +1083,13 @@ pub struct CallbackHandler<'b> {
|
|||
pub callback: &'b mut dyn FnMut(&rls_data::Analysis),
|
||||
}
|
||||
|
||||
impl<'b> SaveHandler for CallbackHandler<'b> {
|
||||
fn save<'l, 'tcx>(
|
||||
impl SaveHandler for CallbackHandler<'_> {
|
||||
fn save(
|
||||
&mut self,
|
||||
save_ctxt: SaveContext<'l, 'tcx>,
|
||||
krate: &ast::Crate,
|
||||
cratename: &str,
|
||||
input: &'l Input,
|
||||
_: &SaveContext<'_, '_>,
|
||||
analysis: &Analysis,
|
||||
) {
|
||||
// We're using the Dumper here because it has the format of the
|
||||
// save-analysis results that we will pass to the callback. IOW, we are
|
||||
// using the Dumper to collect the save-analysis results, but not
|
||||
// actually to dump them to a file. This is all a bit convoluted and
|
||||
// there is certainly a simpler design here trying to get out (FIXME).
|
||||
let mut dumper = Dumper::new(save_ctxt.config.clone());
|
||||
let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
|
||||
|
||||
visitor.dump_crate_info(cratename, krate);
|
||||
visitor.dump_compilation_options(input, cratename);
|
||||
visit::walk_crate(&mut visitor, krate);
|
||||
|
||||
dumper.to_output(|a| (self.callback)(a))
|
||||
(self.callback)(analysis)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1148,7 +1120,13 @@ pub fn process_crate<'l, 'tcx, H: SaveHandler>(
|
|||
impl_counter: Cell::new(0),
|
||||
};
|
||||
|
||||
handler.save(save_ctxt, krate, cratename, input)
|
||||
let mut visitor = DumpVisitor::new(save_ctxt);
|
||||
|
||||
visitor.dump_crate_info(cratename, krate);
|
||||
visitor.dump_compilation_options(input, cratename);
|
||||
visit::walk_crate(&mut visitor, krate);
|
||||
|
||||
handler.save(&visitor.save_ctxt, &visitor.analysis())
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -132,6 +132,14 @@ pub fn options() -> TargetOptions {
|
|||
// non-relative calls and such later on).
|
||||
relocation_model: "static".to_string(),
|
||||
|
||||
// When the atomics feature is activated then these two keys matter,
|
||||
// otherwise they're basically ignored by the standard library. In this
|
||||
// mode, however, the `#[thread_local]` attribute works (i.e.
|
||||
// `has_elf_tls`) and we need to get it to work by specifying
|
||||
// `local-exec` as that's all that's implemented in LLVM today for wasm.
|
||||
has_elf_tls: true,
|
||||
tls_model: "local-exec".to_string(),
|
||||
|
||||
.. Default::default()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,11 +75,6 @@ panic_immediate_abort = ["core/panic_immediate_abort"]
|
|||
# requires rebuilding the standard library to use it.
|
||||
wasm_syscall = []
|
||||
|
||||
# An off-by-default features to enable libstd to assume that wasm-bindgen is in
|
||||
# the environment for hooking up some thread-related information like the
|
||||
# current thread id and accessing/getting the current thread's TCB
|
||||
wasm-bindgen-threads = []
|
||||
|
||||
# Enable std_detect default features for stdarch/crates/std_detect:
|
||||
# https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/Cargo.toml
|
||||
std_detect_file_io = []
|
||||
|
|
|
@ -47,6 +47,8 @@ pub mod stdio;
|
|||
pub mod thread;
|
||||
#[path = "../wasm/thread_local.rs"]
|
||||
pub mod thread_local;
|
||||
#[path = "../wasm/fast_thread_local.rs"]
|
||||
pub mod fast_thread_local;
|
||||
pub mod time;
|
||||
pub mod ext;
|
||||
|
||||
|
|
9
src/libstd/sys/wasm/fast_thread_local.rs
Normal file
9
src/libstd/sys/wasm/fast_thread_local.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
#![unstable(feature = "thread_local_internals", issue = "0")]
|
||||
|
||||
pub unsafe fn register_dtor(_t: *mut u8, _dtor: unsafe extern fn(*mut u8)) {
|
||||
// FIXME: right now there is no concept of "thread exit", but this is likely
|
||||
// going to show up at some point in the form of an exported symbol that the
|
||||
// wasm runtime is oging to be expected to call. For now we basically just
|
||||
// ignore the arguments, but if such a function starts to exist it will
|
||||
// likely look like the OSX implementation in `unix/fast_thread_local.rs`
|
||||
}
|
|
@ -37,6 +37,8 @@ pub mod stack_overflow;
|
|||
pub mod thread;
|
||||
pub mod time;
|
||||
pub mod stdio;
|
||||
pub mod thread_local;
|
||||
pub mod fast_thread_local;
|
||||
|
||||
pub use crate::sys_common::os_str_bytes as os_str;
|
||||
|
||||
|
@ -48,13 +50,10 @@ cfg_if::cfg_if! {
|
|||
pub mod mutex;
|
||||
#[path = "rwlock_atomics.rs"]
|
||||
pub mod rwlock;
|
||||
#[path = "thread_local_atomics.rs"]
|
||||
pub mod thread_local;
|
||||
} else {
|
||||
pub mod condvar;
|
||||
pub mod mutex;
|
||||
pub mod rwlock;
|
||||
pub mod thread_local;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,48 +59,40 @@ pub mod guard {
|
|||
pub unsafe fn init() -> Option<Guard> { None }
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(target_feature = "atomics", feature = "wasm-bindgen-threads"))] {
|
||||
#[link(wasm_import_module = "__wbindgen_thread_xform__")]
|
||||
extern {
|
||||
fn __wbindgen_current_id() -> u32;
|
||||
fn __wbindgen_tcb_get() -> u32;
|
||||
fn __wbindgen_tcb_set(ptr: u32);
|
||||
}
|
||||
pub fn my_id() -> u32 {
|
||||
unsafe { __wbindgen_current_id() }
|
||||
}
|
||||
// This is only used by atomics primitives when the `atomics` feature is
|
||||
// enabled. In that mode we currently just use our own thread-local to store our
|
||||
// current thread's ID, and then we lazily initialize it to something allocated
|
||||
// from a global counter.
|
||||
#[cfg(target_feature = "atomics")]
|
||||
pub fn my_id() -> u32 {
|
||||
use crate::sync::atomic::{AtomicU32, Ordering::SeqCst};
|
||||
|
||||
// These are currently only ever used in `thread_local_atomics.rs`, if
|
||||
// you'd like to use them be sure to update that and make sure everyone
|
||||
// agrees what's what.
|
||||
pub fn tcb_get() -> *mut u8 {
|
||||
use crate::mem;
|
||||
assert_eq!(mem::size_of::<*mut u8>(), mem::size_of::<u32>());
|
||||
unsafe { __wbindgen_tcb_get() as *mut u8 }
|
||||
}
|
||||
static NEXT_ID: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
pub fn tcb_set(ptr: *mut u8) {
|
||||
unsafe { __wbindgen_tcb_set(ptr as u32); }
|
||||
}
|
||||
#[thread_local]
|
||||
static mut MY_ID: u32 = 0;
|
||||
|
||||
// FIXME: still need something for hooking exiting a thread to free
|
||||
// data...
|
||||
|
||||
} else if #[cfg(target_feature = "atomics")] {
|
||||
pub fn my_id() -> u32 {
|
||||
panic!("thread ids not implemented on wasm with atomics yet")
|
||||
unsafe {
|
||||
// If our thread ID isn't set yet then we need to allocate one. Do so
|
||||
// with with a simple "atomically add to a global counter" strategy.
|
||||
// This strategy doesn't handled what happens when the counter
|
||||
// overflows, however, so just abort everything once the counter
|
||||
// overflows and eventually we could have some sort of recycling scheme
|
||||
// (or maybe this is all totally irrelevant by that point!). In any case
|
||||
// though we're using a CAS loop instead of a `fetch_add` to ensure that
|
||||
// the global counter never overflows.
|
||||
if MY_ID == 0 {
|
||||
let mut cur = NEXT_ID.load(SeqCst);
|
||||
MY_ID = loop {
|
||||
let next = cur.checked_add(1).unwrap_or_else(|| {
|
||||
crate::arch::wasm32::unreachable()
|
||||
});
|
||||
match NEXT_ID.compare_exchange(cur, next, SeqCst, SeqCst) {
|
||||
Ok(_) => break next,
|
||||
Err(i) => cur = i,
|
||||
}
|
||||
|
||||
pub fn tcb_get() -> *mut u8 {
|
||||
panic!("thread local data not implemented on wasm with atomics yet")
|
||||
};
|
||||
}
|
||||
|
||||
pub fn tcb_set(_ptr: *mut u8) {
|
||||
panic!("thread local data not implemented on wasm with atomics yet")
|
||||
}
|
||||
} else {
|
||||
// stubbed out because no functions actually access these intrinsics
|
||||
// unless atomics are enabled
|
||||
MY_ID
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +1,26 @@
|
|||
use crate::boxed::Box;
|
||||
use crate::ptr;
|
||||
|
||||
pub type Key = usize;
|
||||
|
||||
struct Allocated {
|
||||
value: *mut u8,
|
||||
dtor: Option<unsafe extern fn(*mut u8)>,
|
||||
#[inline]
|
||||
pub unsafe fn create(_dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
|
||||
panic!("should not be used on the wasm target");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
|
||||
Box::into_raw(Box::new(Allocated {
|
||||
value: ptr::null_mut(),
|
||||
dtor,
|
||||
})) as usize
|
||||
pub unsafe fn set(_key: Key, _value: *mut u8) {
|
||||
panic!("should not be used on the wasm target");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn set(key: Key, value: *mut u8) {
|
||||
(*(key as *mut Allocated)).value = value;
|
||||
pub unsafe fn get(_key: Key) -> *mut u8 {
|
||||
panic!("should not be used on the wasm target");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn get(key: Key) -> *mut u8 {
|
||||
(*(key as *mut Allocated)).value
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn destroy(key: Key) {
|
||||
let key = Box::from_raw(key as *mut Allocated);
|
||||
if let Some(f) = key.dtor {
|
||||
f(key.value);
|
||||
}
|
||||
pub unsafe fn destroy(_key: Key) {
|
||||
panic!("should not be used on the wasm target");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn requires_synchronized_create() -> bool {
|
||||
false
|
||||
panic!("should not be used on the wasm target");
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
use crate::sys::thread;
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||
|
||||
const MAX_KEYS: usize = 128;
|
||||
static NEXT_KEY: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
struct ThreadControlBlock {
|
||||
keys: [*mut u8; MAX_KEYS],
|
||||
}
|
||||
|
||||
impl ThreadControlBlock {
|
||||
fn new() -> ThreadControlBlock {
|
||||
ThreadControlBlock {
|
||||
keys: [core::ptr::null_mut(); MAX_KEYS],
|
||||
}
|
||||
}
|
||||
|
||||
fn get() -> *mut ThreadControlBlock {
|
||||
let ptr = thread::tcb_get();
|
||||
if !ptr.is_null() {
|
||||
return ptr as *mut ThreadControlBlock
|
||||
}
|
||||
let tcb = Box::into_raw(Box::new(ThreadControlBlock::new()));
|
||||
thread::tcb_set(tcb as *mut u8);
|
||||
tcb
|
||||
}
|
||||
}
|
||||
|
||||
pub type Key = usize;
|
||||
|
||||
pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
|
||||
drop(dtor); // FIXME: need to figure out how to hook thread exit to run this
|
||||
let key = NEXT_KEY.fetch_add(1, SeqCst);
|
||||
if key >= MAX_KEYS {
|
||||
NEXT_KEY.store(MAX_KEYS, SeqCst);
|
||||
panic!("cannot allocate space for more TLS keys");
|
||||
}
|
||||
// offset by 1 so we never hand out 0. This is currently required by
|
||||
// `sys_common/thread_local.rs` where it can't cope with keys of value 0
|
||||
// because it messes up the atomic management.
|
||||
return key + 1
|
||||
}
|
||||
|
||||
pub unsafe fn set(key: Key, value: *mut u8) {
|
||||
(*ThreadControlBlock::get()).keys[key - 1] = value;
|
||||
}
|
||||
|
||||
pub unsafe fn get(key: Key) -> *mut u8 {
|
||||
(*ThreadControlBlock::get()).keys[key - 1]
|
||||
}
|
||||
|
||||
pub unsafe fn destroy(_key: Key) {
|
||||
// FIXME: should implement this somehow, this isn't typically called but it
|
||||
// can be called if two threads race to initialize a TLS slot and one ends
|
||||
// up not being needed.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn requires_synchronized_create() -> bool {
|
||||
false
|
||||
}
|
|
@ -243,9 +243,6 @@ declare_features! (
|
|||
// Allows using `#![needs_allocator]`, an implementation detail of `#[global_allocator]`.
|
||||
(active, allocator_internals, "1.20.0", None, None),
|
||||
|
||||
// Allows using the `format_args_nl` macro.
|
||||
(active, format_args_nl, "1.29.0", Some(0), None),
|
||||
|
||||
// no-tracking-issue-end
|
||||
|
||||
// Added for testing E0705; perma-unstable.
|
||||
|
@ -286,12 +283,6 @@ declare_features! (
|
|||
// feature-group-start: actual feature gates
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// Allows using `asm!` macro with which inline assembly can be embedded.
|
||||
(active, asm, "1.0.0", Some(29722), None),
|
||||
|
||||
// Allows using the `concat_idents!` macro with which identifiers can be concatenated.
|
||||
(active, concat_idents, "1.0.0", Some(29599), None),
|
||||
|
||||
// Allows using the `#[link_args]` attribute.
|
||||
(active, link_args, "1.0.0", Some(29596), None),
|
||||
|
||||
|
@ -307,12 +298,6 @@ declare_features! (
|
|||
// Allows using `#[thread_local]` on `static` items.
|
||||
(active, thread_local, "1.0.0", Some(29594), None),
|
||||
|
||||
// Allows using the `log_syntax!` macro.
|
||||
(active, log_syntax, "1.0.0", Some(29598), None),
|
||||
|
||||
// Allows using the `trace_macros!` macro.
|
||||
(active, trace_macros, "1.0.0", Some(29598), None),
|
||||
|
||||
// Allows the use of SIMD types in functions declared in `extern` blocks.
|
||||
(active, simd_ffi, "1.0.0", Some(27731), None),
|
||||
|
||||
|
@ -402,9 +387,6 @@ declare_features! (
|
|||
// Allows `extern "x86-interrupt" fn()`.
|
||||
(active, abi_x86_interrupt, "1.17.0", Some(40180), None),
|
||||
|
||||
// Allows module-level inline assembly by way of `global_asm!()`.
|
||||
(active, global_asm, "1.18.0", Some(35119), None),
|
||||
|
||||
// Allows overlapping impls of marker traits.
|
||||
(active, overlapping_marker_traits, "1.18.0", Some(29864), None),
|
||||
|
||||
|
@ -472,7 +454,7 @@ declare_features! (
|
|||
(active, doc_alias, "1.27.0", Some(50146), None),
|
||||
|
||||
// Allows defining `existential type`s.
|
||||
(active, existential_type, "1.28.0", Some(34511), None),
|
||||
(active, existential_type, "1.28.0", Some(63063), None),
|
||||
|
||||
// Allows inconsistent bounds in where clauses.
|
||||
(active, trivial_bounds, "1.28.0", Some(48214), None),
|
||||
|
@ -525,7 +507,7 @@ declare_features! (
|
|||
(active, bind_by_move_pattern_guards, "1.30.0", Some(15287), None),
|
||||
|
||||
// Allows `impl Trait` in bindings (`let`, `const`, `static`).
|
||||
(active, impl_trait_in_bindings, "1.30.0", Some(34511), None),
|
||||
(active, impl_trait_in_bindings, "1.30.0", Some(63065), None),
|
||||
|
||||
// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.
|
||||
(active, lint_reasons, "1.31.0", Some(54503), None),
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f6446fa8e9629ffb1861303f17930c3aa83ef660
|
||||
Subproject commit 9b64ca5b7e1e3583978f9ac8af6d93b220a13d90
|
|
@ -8,6 +8,7 @@
|
|||
// ignore-cloudabi no processes
|
||||
// ignore-emscripten no processes
|
||||
// ignore-sgx no processes
|
||||
// ignore-vxworks no 'cat' and 'sleep'
|
||||
|
||||
// N.B., these tests kill child processes. Valgrind sees these children as leaking
|
||||
// memory, which makes for some *confusing* logs. That's why these are here
|
||||
|
|
|
@ -4,7 +4,7 @@ error[E0658]: existential types are unstable
|
|||
LL | existential type Item: Bug;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/34511
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/63063
|
||||
= help: add `#![feature(existential_type)]` to the crate attributes to enable
|
||||
|
||||
error[E0277]: the trait bound `(): Bug` is not satisfied
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// gate-test-asm
|
||||
// ignore-emscripten
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error[E0658]: use of unstable library feature 'asm': inline assembly is not stable enough for use and is subject to change
|
||||
--> $DIR/feature-gate-asm2.rs:6:26
|
||||
--> $DIR/feature-gate-asm2.rs:5:26
|
||||
|
|
||||
LL | println!("{:?}", asm!(""));
|
||||
| ^^^
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// gate-test-concat_idents
|
||||
|
||||
fn main() {
|
||||
concat_idents!(a, b); //~ ERROR `concat_idents` is not stable enough
|
||||
//~| ERROR cannot find value `ab` in this scope
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error[E0658]: use of unstable library feature 'concat_idents': `concat_idents` is not stable enough for use and is subject to change
|
||||
--> $DIR/feature-gate-concat_idents2.rs:4:5
|
||||
--> $DIR/feature-gate-concat_idents2.rs:2:5
|
||||
|
|
||||
LL | concat_idents!(a, b);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
@ -8,7 +8,7 @@ LL | concat_idents!(a, b);
|
|||
= help: add `#![feature(concat_idents)]` to the crate attributes to enable
|
||||
|
||||
error[E0425]: cannot find value `ab` in this scope
|
||||
--> $DIR/feature-gate-concat_idents2.rs:4:5
|
||||
--> $DIR/feature-gate-concat_idents2.rs:2:5
|
||||
|
|
||||
LL | concat_idents!(a, b);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// gate-test-concat_idents
|
||||
|
||||
const XY_1: i32 = 10;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error[E0658]: use of unstable library feature 'concat_idents': `concat_idents` is not stable enough for use and is subject to change
|
||||
--> $DIR/feature-gate-concat_idents3.rs:7:20
|
||||
--> $DIR/feature-gate-concat_idents3.rs:5:20
|
||||
|
|
||||
LL | assert_eq!(10, concat_idents!(X, Y_1));
|
||||
| ^^^^^^^^^^^^^
|
||||
|
@ -8,7 +8,7 @@ LL | assert_eq!(10, concat_idents!(X, Y_1));
|
|||
= help: add `#![feature(concat_idents)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: use of unstable library feature 'concat_idents': `concat_idents` is not stable enough for use and is subject to change
|
||||
--> $DIR/feature-gate-concat_idents3.rs:8:20
|
||||
--> $DIR/feature-gate-concat_idents3.rs:6:20
|
||||
|
|
||||
LL | assert_eq!(20, concat_idents!(X, Y_2));
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
|
@ -4,7 +4,7 @@ error[E0658]: existential types are unstable
|
|||
LL | existential type Foo: std::fmt::Debug;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/34511
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/63063
|
||||
= help: add `#![feature(existential_type)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: existential types are unstable
|
||||
|
@ -13,7 +13,7 @@ error[E0658]: existential types are unstable
|
|||
LL | existential type Baa: std::fmt::Debug;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/34511
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/63063
|
||||
= help: add `#![feature(existential_type)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// gate-test-log_syntax
|
||||
|
||||
fn main() {
|
||||
println!("{:?}", log_syntax!()); //~ ERROR `log_syntax!` is not stable
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error[E0658]: use of unstable library feature 'log_syntax': `log_syntax!` is not stable enough for use and is subject to change
|
||||
--> $DIR/feature-gate-log_syntax2.rs:4:22
|
||||
--> $DIR/feature-gate-log_syntax2.rs:2:22
|
||||
|
|
||||
LL | println!("{:?}", log_syntax!());
|
||||
| ^^^^^^^^^^
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
// ignore-cloudabi no subprocesses support
|
||||
// ignore-emscripten no threads support
|
||||
// ignore-vxworks no 'sh'
|
||||
|
||||
use std::process;
|
||||
use std::thread;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// ignore-cloudabi no processes
|
||||
// ignore-emscripten no processes
|
||||
// ignore-sgx no processes
|
||||
// ignore-vxworks no 'ps'
|
||||
|
||||
#![feature(rustc_private)]
|
||||
|
||||
|
|
|
@ -32,5 +32,6 @@ pub fn main() {
|
|||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "vxworks",
|
||||
target_os = "solaris"))]
|
||||
pub fn main() { }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue