Skip to content

Associated const references using UFCS can bypass check_static_recursion #24949

Open

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.

Activity

  1. Gankra commented on Jun 2, 2015

    @Gankra
    Contributor

    A simpler example of this:

    trait Foo {
        const FOO: Self;
    }
    
    impl Foo for u8 {
        const FOO: u8 = u8::FOO;
    }
    

    compiles.

  2. steveklabnik commented on Jan 3, 2017

    @steveklabnik
    Contributor

    Triage: @gankro 's example still compiles.

  3. Mark-Simulacrum commented on May 18, 2017

    @Mark-Simulacrum
    Member

    E-needstest.

  4. added
    E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.
    on May 18, 2017
  5. varkor commented on Feb 25, 2019

    @varkor
    Contributor
    trait Foo {
        const FOO: Self;
    }
    
    impl Foo for u8 {
        const FOO: u8 = u8::FOO;
    }

    fails to compile, but adding:

    fn main() {}

    makes it compile without an error.

  6. Daniel-Worrall commented on Apr 27, 2020

    @Daniel-Worrall
    Contributor

    @rustbot claim

  7. self-assigned this
    on Apr 27, 2020
  8. added a commit that references this issue on Apr 29, 2020
  9. added a commit that references this issue on Apr 29, 2020
  10. varkor commented on Apr 29, 2020

    @varkor
    Contributor

    This isn't fixed. The example in this comment still compiles.

  11. reopened this on Apr 29, 2020
  12. removed
    E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.
    on Apr 29, 2020
  13. added
    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.
    on Apr 5, 2023
  14. added and removed on Dec 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to 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