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

Generic arg disambiguation #66104

Merged
merged 6 commits into from
Nov 20, 2019
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
38 changes: 38 additions & 0 deletions src/librustc/hir/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,34 @@ impl DefKind {
_ => "a",
}
}

pub fn matches_ns(&self, ns: Namespace) -> bool {
match self {
DefKind::Mod
| DefKind::Struct
| DefKind::Union
| DefKind::Enum
| DefKind::Variant
| DefKind::Trait
| DefKind::OpaqueTy
| DefKind::TyAlias
| DefKind::ForeignTy
| DefKind::TraitAlias
| DefKind::AssocTy
| DefKind::AssocOpaqueTy
| DefKind::TyParam => ns == Namespace::TypeNS,

DefKind::Fn
| DefKind::Const
| DefKind::ConstParam
| DefKind::Static
| DefKind::Ctor(..)
| DefKind::Method
| DefKind::AssocConst => ns == Namespace::ValueNS,

DefKind::Macro(..) => ns == Namespace::MacroNS,
}
}
}

#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, HashStable)]
Expand Down Expand Up @@ -427,4 +455,14 @@ impl<Id> Res<Id> {
_ => None,
}
}

pub fn matches_ns(&self, ns: Namespace) -> bool {
match self {
Res::Def(kind, ..) => kind.matches_ns(ns),
Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => ns == Namespace::TypeNS,
Res::SelfCtor(..) | Res::Local(..) => ns == Namespace::ValueNS,
Res::NonMacroAttr(..) => ns == Namespace::MacroNS,
Res::Err => true,
}
}
}
61 changes: 56 additions & 5 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1155,13 +1155,64 @@ impl<'a> LoweringContext<'a> {
}
}

fn lower_generic_arg(&mut self,
arg: &ast::GenericArg,
itctx: ImplTraitContext<'_>)
-> hir::GenericArg {
fn lower_generic_arg(
&mut self,
arg: &ast::GenericArg,
itctx: ImplTraitContext<'_>
) -> hir::GenericArg {
match arg {
ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(&lt)),
ast::GenericArg::Type(ty) => GenericArg::Type(self.lower_ty_direct(&ty, itctx)),
ast::GenericArg::Type(ty) => {
// We parse const arguments as path types as we cannot distiguish them durring
// parsing. We try to resolve that ambiguity by attempting resolution in both the
// type and value namespaces. If we resolved the path in the value namespace, we
// transform it into a generic const argument.
if let TyKind::Path(ref qself, ref path) = ty.kind {
if let Some(partial_res) = self.resolver.get_partial_res(ty.id) {
let res = partial_res.base_res();
if !res.matches_ns(Namespace::TypeNS) {
debug!(
"lower_generic_arg: Lowering type argument as const argument: {:?}",
ty,
);

// Construct a AnonConst where the expr is the "ty"'s path.

let parent_def_index =
self.current_hir_id_owner.last().unwrap().0;
let node_id = self.resolver.next_node_id();

// Add a definition for the in-band const def.
self.resolver.definitions().create_def_with_parent(
parent_def_index,
node_id,
DefPathData::AnonConst,
ExpnId::root(),
ty.span,
);

let path_expr = Expr {
id: ty.id,
kind: ExprKind::Path(qself.clone(), path.clone()),
span: ty.span,
attrs: ThinVec::new(),
};

let ct = self.with_new_scopes(|this| {
hir::AnonConst {
hir_id: this.lower_node_id(node_id),
body: this.lower_const_body(&path_expr),
}
});
return GenericArg::Const(ConstArg {
value: ct,
span: ty.span,
});
}
}
}
GenericArg::Type(self.lower_ty_direct(&ty, itctx))
}
ast::GenericArg::Const(ct) => {
GenericArg::Const(ConstArg {
value: self.lower_anon_const(&ct),
Expand Down
46 changes: 46 additions & 0 deletions src/librustc_resolve/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,52 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
self.visit_where_predicate(p);
}
}

fn visit_generic_arg(&mut self, arg: &'tcx GenericArg) {
debug!("visit_generic_arg({:?})", arg);
match arg {
GenericArg::Type(ref ty) => {
// We parse const arguments as path types as we cannot distiguish them durring
// parsing. We try to resolve that ambiguity by attempting resolution the type
// namespace first, and if that fails we try again in the value namespace. If
// resolution in the value namespace succeeds, we have an generic const argument on
// our hands.
if let TyKind::Path(ref qself, ref path) = ty.kind {
// We cannot disambiguate multi-segment paths right now as that requires type
// checking.
if path.segments.len() == 1 && path.segments[0].args.is_none() {
let mut check_ns = |ns| self.resolve_ident_in_lexical_scope(
path.segments[0].ident, ns, None, path.span
).is_some();

if !check_ns(TypeNS) && check_ns(ValueNS) {
// This must be equivalent to `visit_anon_const`, but we cannot call it
// directly due to visitor lifetimes so we have to copy-paste some code.
self.with_constant_rib(|this| {
this.smart_resolve_path(
ty.id,
qself.as_ref(),
path,
PathSource::Expr(None)
);

if let Some(ref qself) = *qself {
this.visit_ty(&qself.ty);
}
this.visit_path(path, ty.id);
});

return;
}
}
}

self.visit_ty(ty);
}
GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
GenericArg::Const(ct) => self.visit_anon_const(ct),
}
}
}

impl<'a, 'b> LateResolutionVisitor<'a, '_> {
Expand Down
8 changes: 8 additions & 0 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1726,6 +1726,14 @@ impl<'a> Resolver<'a> {
}
}

if ns == TypeNS {
if let Some(prim_ty) = self.primitive_type_table.primitive_types.get(&ident.name) {
let binding = (Res::PrimTy(*prim_ty), ty::Visibility::Public,
DUMMY_SP, ExpnId::root()).to_name_binding(self.arenas);
return Some(LexicalScopeBinding::Item(binding));
}
}

None
}

Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/const-generics/const-generic-array-wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
#![feature(const_generics)]
//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash

struct Foo<T, const N: usize>([T; {N}]);
struct Foo<T, const N: usize>([T; N]);

impl<T, const N: usize> Foo<T, {N}> {
impl<T, const N: usize> Foo<T, N> {
yodaldevoid marked this conversation as resolved.
Show resolved Hide resolved
fn foo(&self) -> usize {
{N}
N
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/const-generics/fn-const-param-call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ fn function() -> u32 {

struct Wrapper<const F: fn() -> u32>;

impl<const F: fn() -> u32> Wrapper<{F}> {
impl<const F: fn() -> u32> Wrapper<F> {
fn call() -> u32 {
F()
}
}

fn main() {
assert_eq!(Wrapper::<{function}>::call(), 17);
assert_eq!(Wrapper::<function>::call(), 17);
petrochenkov marked this conversation as resolved.
Show resolved Hide resolved
}
10 changes: 5 additions & 5 deletions src/test/ui/const-generics/fn-const-param-infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ fn generic_arg<T>(val: T) -> bool { true }
fn generic<T>(val: usize) -> bool { val != 1 }

fn main() {
let _: Option<Checked<{not_one}>> = None;
let _: Checked<{not_one}> = Checked::<{not_one}>;
let _: Checked<{not_one}> = Checked::<{not_two}>; //~ mismatched types
let _: Option<Checked<not_one>> = None;
let _: Checked<not_one> = Checked::<not_one>;
let _: Checked<not_one> = Checked::<not_two>; //~ mismatched types

let _ = Checked::<{generic_arg}>;
let _ = Checked::<generic_arg>;
let _ = Checked::<{generic_arg::<usize>}>;
let _ = Checked::<{generic_arg::<u32>}>; //~ mismatched types

let _ = Checked::<{generic}>; //~ type annotations needed
let _ = Checked::<generic>; //~ type annotations needed
let _ = Checked::<{generic::<u16>}>;
let _: Checked<{generic::<u16>}> = Checked::<{generic::<u16>}>;
let _: Checked<{generic::<u32>}> = Checked::<{generic::<u16>}>; //~ mismatched types
Expand Down
12 changes: 6 additions & 6 deletions src/test/ui/const-generics/fn-const-param-infer.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ LL | #![feature(const_generics, const_compare_raw_pointers)]
= note: `#[warn(incomplete_features)]` on by default

error[E0308]: mismatched types
--> $DIR/fn-const-param-infer.rs:16:33
--> $DIR/fn-const-param-infer.rs:16:31
|
LL | let _: Checked<{not_one}> = Checked::<{not_two}>;
| ^^^^^^^^^^^^^^^^^^^^ expected `not_one`, found `not_two`
LL | let _: Checked<not_one> = Checked::<not_two>;
| ^^^^^^^^^^^^^^^^^^ expected `not_one`, found `not_two`
yodaldevoid marked this conversation as resolved.
Show resolved Hide resolved
|
= note: expected type `Checked<not_one>`
found type `Checked<not_two>`
Expand All @@ -25,10 +25,10 @@ LL | let _ = Checked::<{generic_arg::<u32>}>;
found type `fn(u32) -> bool {generic_arg::<u32>}`

error[E0282]: type annotations needed
--> $DIR/fn-const-param-infer.rs:22:24
--> $DIR/fn-const-param-infer.rs:22:23
|
LL | let _ = Checked::<{generic}>;
| ^^^^^^^ cannot infer type for `T`
LL | let _ = Checked::<generic>;
| ^^^^^^^ cannot infer type for `T`

error[E0308]: mismatched types
--> $DIR/fn-const-param-infer.rs:25:40
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/const-generics/impl-const-generic-struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

struct S<const X: u32>;

impl<const X: u32> S<{X}> {
impl<const X: u32> S<X> {
fn x() -> u32 {
X
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/const-generics/raw-ptr-const-param-deref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const A: u32 = 3;

struct Const<const P: *const u32>;

impl<const P: *const u32> Const<{P}> {
impl<const P: *const u32> Const<P> {
fn get() -> u32 {
unsafe {
*P
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::fmt;

struct Array<T, const N: usize>([T; N]);

impl<T: fmt::Debug, const N: usize> fmt::Debug for Array<T, {N}> {
impl<T: fmt::Debug, const N: usize> fmt::Debug for Array<T, N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(self.0.iter()).finish()
}
Expand Down
3 changes: 2 additions & 1 deletion src/test/ui/privacy/privacy-ns1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ pub mod foo2 {
fn test_glob2() {
use foo2::*;

let _x: Box<Bar>; //~ ERROR expected type, found function `Bar`
let _x: Box<Bar>; //~ ERROR wrong number of const arguments: expected 0, found 1
//~^ ERROR wrong number of type arguments: expected 1, found 0
}

// neither public
Expand Down
44 changes: 17 additions & 27 deletions src/test/ui/privacy/privacy-ns1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,8 @@ LL | use foo2::Bar;
LL | use foo3::Bar;
|

error[E0573]: expected type, found function `Bar`
--> $DIR/privacy-ns1.rs:35:17
|
LL | pub struct Baz;
| --------------- similarly named struct `Baz` defined here
...
LL | let _x: Box<Bar>;
| ^^^
|
help: a struct with a similar name exists
|
LL | let _x: Box<Baz>;
| ^^^
help: possible better candidates are found in other modules, you can import them into scope
|
LL | use foo1::Bar;
|
LL | use foo2::Bar;
|
LL | use foo3::Bar;
|

error[E0425]: cannot find function, tuple struct or tuple variant `Bar` in this scope
--> $DIR/privacy-ns1.rs:50:5
--> $DIR/privacy-ns1.rs:51:5
|
LL | pub struct Baz;
| --------------- similarly named unit struct `Baz` defined here
Expand All @@ -65,7 +43,7 @@ LL | use foo3::Bar;
|

error[E0412]: cannot find type `Bar` in this scope
--> $DIR/privacy-ns1.rs:51:17
--> $DIR/privacy-ns1.rs:52:17
|
LL | pub struct Baz;
| --------------- similarly named struct `Baz` defined here
Expand All @@ -86,7 +64,19 @@ LL | use foo2::Bar;
LL | use foo3::Bar;
|

error: aborting due to 4 previous errors
error[E0107]: wrong number of const arguments: expected 0, found 1
Copy link
Member

Choose a reason for hiding this comment

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

This is interesting fallout. I imagine we should be able to special-case this sort of error to get back the old ones, though I'm not sure how much work that would be.

Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I remember, the fix may be non-trivial and error-prone, so it's better to make an issue and fix it in a separate PR.

--> $DIR/privacy-ns1.rs:35:17
|
LL | let _x: Box<Bar>;
| ^^^ unexpected const argument

error[E0107]: wrong number of type arguments: expected 1, found 0
--> $DIR/privacy-ns1.rs:35:13
|
LL | let _x: Box<Bar>;
| ^^^^^^^^ expected 1 type argument

error: aborting due to 5 previous errors

Some errors have detailed explanations: E0412, E0423, E0425, E0573.
For more information about an error, try `rustc --explain E0412`.
Some errors have detailed explanations: E0107, E0412, E0423, E0425.
For more information about an error, try `rustc --explain E0107`.
6 changes: 4 additions & 2 deletions src/test/ui/privacy/privacy-ns2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,16 @@ pub mod foo2 {
fn test_single2() {
use foo2::Bar;

let _x : Box<Bar>; //~ ERROR expected type, found function `Bar`
let _x : Box<Bar>; //~ ERROR wrong number of const arguments: expected 0, found 1
//~^ ERROR wrong number of type arguments: expected 1, found 0
let _x : Bar(); //~ ERROR expected type, found function `Bar`
}

fn test_list2() {
use foo2::{Bar,Baz};

let _x: Box<Bar>; //~ ERROR expected type, found function `Bar`
let _x: Box<Bar>; //~ ERROR wrong number of const arguments: expected 0, found 1
//~^ ERROR wrong number of type arguments: expected 1, found 0
}

// neither public
Expand Down
Loading