Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement RFC 2052 (Epochs) #48014

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/librustc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#![feature(macro_vis_matcher)]
#![feature(match_default_bindings)]
#![feature(never_type)]
#![feature(non_exhaustive)]
#![feature(nonzero)]
#![feature(quote)]
#![feature(refcell_replace_swap)]
Expand Down
45 changes: 43 additions & 2 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,31 @@ pub enum OutputType {
DepInfo,
}

/// The epoch of the compiler (RFC 2052)
#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq)]
#[non_exhaustive]
pub enum Epoch {
// epochs must be kept in order, newest to oldest

/// The 2015 epoch
Epoch2015,
/// The 2018 epoch
Epoch2018,

// when adding new epochs, be sure to update:
//
// - the list in the `parse_epoch` static
// - the match in the `parse_epoch` function
// - add a `rust_####()` function to the session
// - update the enum in Cargo's sources as well
//
// When -Zepoch becomes --epoch, there will
// also be a check for the epoch being nightly-only
// somewhere. That will need to be updated
// whenever we're stabilizing/introducing a new epoch
// as well as changing the default Cargo template.
}

impl_stable_hash_for!(enum self::OutputType {
Bitcode,
Assembly,
Expand Down Expand Up @@ -802,11 +827,13 @@ macro_rules! options {
Some("`string` or `string=string`");
pub const parse_lto: Option<&'static str> =
Some("one of `thin`, `fat`, or omitted");
pub const parse_epoch: Option<&'static str> =
Some("one of: `2015`, `2018`");
}

#[allow(dead_code)]
mod $mod_set {
use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer, Lto};
use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer, Lto, Epoch};
use rustc_back::{LinkerFlavor, PanicStrategy, RelroLevel};
use std::path::PathBuf;

Expand Down Expand Up @@ -1010,6 +1037,15 @@ macro_rules! options {
};
true
}

fn parse_epoch(slot: &mut Epoch, v: Option<&str>) -> bool {
match v {
Some("2015") => *slot = Epoch::Epoch2015,
Some("2018") => *slot = Epoch::Epoch2018,
_ => return false,
}
true
}
}
) }

Expand Down Expand Up @@ -1297,6 +1333,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
`everybody_loops` (all function bodies replaced with `loop {}`),
`hir` (the HIR), `hir,identified`, or
`hir,typed` (HIR with types for each node)."),
epoch: Epoch = (Epoch::Epoch2015, parse_epoch, [TRACKED],
"The epoch to build Rust with. Newer epochs may include features
that require breaking changes. The default epoch is 2015 (the first
epoch). Crates compiled with different epochs can be linked together."),
}

pub fn default_lib_output() -> CrateType {
Expand Down Expand Up @@ -2088,7 +2128,7 @@ mod dep_tracking {
use std::path::PathBuf;
use std::collections::hash_map::DefaultHasher;
use super::{Passes, CrateType, OptLevel, DebugInfoLevel, Lto,
OutputTypes, Externs, ErrorOutputType, Sanitizer};
OutputTypes, Externs, ErrorOutputType, Sanitizer, Epoch};
use syntax::feature_gate::UnstableFeatures;
use rustc_back::{PanicStrategy, RelroLevel};

Expand Down Expand Up @@ -2150,6 +2190,7 @@ mod dep_tracking {
impl_dep_tracking_hash_via_hash!(cstore::NativeLibraryKind);
impl_dep_tracking_hash_via_hash!(Sanitizer);
impl_dep_tracking_hash_via_hash!(Option<Sanitizer>);
impl_dep_tracking_hash_via_hash!(Epoch);

impl_dep_tracking_hash_for_sortable_vec_of!(String);
impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf);
Expand Down
7 changes: 6 additions & 1 deletion src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use lint;
use middle::allocator::AllocatorKind;
use middle::dependency_format;
use session::search_paths::PathKind;
use session::config::{BorrowckMode, DebugInfoLevel, OutputType};
use session::config::{BorrowckMode, DebugInfoLevel, OutputType, Epoch};
use ty::tls;
use util::nodemap::{FxHashMap, FxHashSet};
use util::common::{duration_to_secs_str, ErrorReported};
Expand Down Expand Up @@ -864,6 +864,11 @@ impl Session {
pub fn teach(&self, code: &DiagnosticId) -> bool {
self.opts.debugging_opts.teach && !self.parse_sess.span_diagnostic.code_emitted(code)
}

/// Are we allowed to use features from the Rust 2018 epoch?
pub fn rust_2018(&self) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should prepare for macro compatibility here, and make this some kind of test that takes a span. I'm not sure where the method should go -- maybe put it on the tcx for now? That seems like it would definitely have access to whatever data it needs to make the determination.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the tcx makes sense; the idea behind epoches was that most of the changes would be to stuff like parsing and all, the "language frontend". We don't have a tcx when parsing. I think we should keep this unresolved for now and when someone knowledgeable about macro stuff comes along they can look into this.

I think the Session already has a lot of the Span info that can be used here; at least "originates from macro from X crate" can be done from the Session, though the interaction of many macros may be confusing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the tcx makes sense

In the future, we plan to move so that the tcx is the first thing that is created, so that everything can be query driven. This is why I suggested tcx. However, session is also fine.

I think we should keep this unresolved for now and when someone knowledgeable about macro stuff comes along they can look into this.

Mm, I'm not sure. I think it'll be useful to require a span so that, for any change, we are at least thinking about how to deal with it.

--

Eh. Having thought about it some more, while I'd rather lay out a "forwards compatible" API with what we will likely eventually need, I don't care enough to block on it. And there's always an argument for doing the simplest possible thing to start.

self.opts.debugging_opts.epoch >= Epoch::Epoch2018
}
}

pub fn build_session(sopts: config::Options,
Expand Down
18 changes: 12 additions & 6 deletions src/librustc_typeck/check/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,13 +326,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if reached_raw_pointer
&& !self.tcx.sess.features.borrow().arbitrary_self_types {
// this case used to be allowed by the compiler,
// so we do a future-compat lint here
// so we do a future-compat lint here for the 2015 epoch
// (see https://github.com/rust-lang/rust/issues/46906)
self.tcx.lint_node(
lint::builtin::TYVAR_BEHIND_RAW_POINTER,
scope_expr_id,
span,
&format!("the type of this value must be known in this context"));
if self.tcx.sess.rust_2018() {
span_err!(self.tcx.sess, span, E0908,
"the type of this value must be known \
to call a method on a raw pointer on it");
} else {
self.tcx.lint_node(
lint::builtin::TYVAR_BEHIND_RAW_POINTER,
scope_expr_id,
span,
&format!("the type of this value must be known in this context"));
}
} else {
let t = self.structurally_resolved_type(span, final_ty);
assert_eq!(t, self.tcx.types.err);
Expand Down
49 changes: 49 additions & 0 deletions src/librustc_typeck/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4698,6 +4698,55 @@ element type `T`. Also note that the error is conservatively reported even when
the alignment of the zero-sized type is less than or equal to the data field's
alignment.
"##,


E0908: r##"
A method was called on a raw pointer whose inner type wasn't completely known.

For example, you may have done something like:

```compile_fail
# #![deny(warnings)]
let foo = &1;
let bar = foo as *const _;
if bar.is_null() {
// ...
}
```

Here, the type of `bar` isn't known; it could be a pointer to anything. Instead,
specify a type for the pointer (preferably something that makes sense for the
thing you're pointing to):

```
let foo = &1;
let bar = foo as *const i32;
if bar.is_null() {
// ...
}
```

Even though `is_null()` exists as a method on any raw pointer, Rust shows this
error because Rust allows for `self` to have arbitrary types (behind the
arbitrary_self_types feature flag).

This means that someone can specify such a function:

```ignore (cannot-doctest-feature-doesnt-exist-yet)
impl Foo {
fn is_null(self: *const Self) -> bool {
// do something else
}
}
```

and now when you call `.is_null()` on a raw pointer to `Foo`, there's ambiguity.

Given that we don't know what type the pointer is, and there's potential
ambiguity for some types, we disallow calling methods on raw pointers when
the type is unknown.
"##,

}

register_diagnostics! {
Expand Down
23 changes: 23 additions & 0 deletions src/test/compile-fail/epoch-raw-pointer-method-2015.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2012 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.

// ignore-tidy-linelength
// compile-flags: -Zepoch=2015 -Zunstable-options

// tests that epochs work with the tyvar warning-turned-error

#[deny(warnings)]
fn main() {
let x = 0;
let y = &x as *const _;
let _ = y.is_null();
//~^ error: the type of this value must be known in this context [tyvar_behind_raw_pointer]
//~^^ warning: this was previously accepted
}
22 changes: 22 additions & 0 deletions src/test/compile-fail/epoch-raw-pointer-method-2018.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2012 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.

// ignore-tidy-linelength
// compile-flags: -Zepoch=2018 -Zunstable-options

// tests that epochs work with the tyvar warning-turned-error

#[deny(warnings)]
fn main() {
let x = 0;
let y = &x as *const _;
let _ = y.is_null();
//~^ error: the type of this value must be known to call a method on a raw pointer on it [E0908]
}