Skip to content

Associated const references using UFCS can bypass check_static_recursion #24949

Open
@quantheory

Description

@quantheory

Currently, check_static_recursion is run after resolve, but before typeck. This made sense before, because after resolve it was possible to determine which constants referenced one another, and running before typeck ensured that we don't fall into an infinite loop during type checking. However, when referencing an associated constant with some UFCS forms, we can't resolve the constant until typeck, meaning that check_static_recursion can't always tell when a recursive definition is present. On top of that, this check is not currently working right even for inherent impls, where it should be possible to discover a problem ahead of typeck running.

A set of test cases:

// Copyright 2015 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.

#![feature(associated_consts)]

trait Foo {
    const BAR: u32;
}

// Check for recursion involving references to trait-associated const.

const TRAIT_REF_BAR: u32 = <GlobalTraitRef>::BAR; //~ ERROR E0265

struct GlobalTraitRef;

impl Foo for GlobalTraitRef {
    const BAR: u32 = TRAIT_REF_BAR; //~ ERROR E0265
}

// Check for recursion involving references to impl-associated const.

const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; //~ ERROR E0265

struct GlobalImplRef;

impl GlobalImplRef {
    const BAR: u32 = IMPL_REF_BAR; //~ ERROR E0265
}

// Check for recursion involving references to trait-associated const default.

trait FooDefault {
    const BAR: u32 = DEFAULT_REF_BAR; //~ ERROR E0265
}

const DEFAULT_REF_BAR: u32 = <GlobalDefaultRef>::BAR; //~ ERROR E0265

struct GlobalDefaultRef;

impl FooDefault for GlobalDefaultRef {}

fn main() {}

The above cases should all fail in the recursion check, but they all compile successfully! If any of the constants are actually used in a context that requires the compiler to evaluate them, there will be an ICE (specifically, the compiler enters an infinite loop that ends up blowing the stack).

Update
playground link: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=76e9edaf2827806e18a557f37bc930ea

This issue has been assigned to @Daniel-Worrall via this comment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-associated-itemsArea: Associated items (types, constants & functions)A-type-systemArea: Type systemC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions