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

function pointers as match patterns have optimization-dependent behavior #70861

Closed
pnkfelix opened this issue Apr 6, 2020 · 11 comments · Fixed by #124661
Closed

function pointers as match patterns have optimization-dependent behavior #70861

pnkfelix opened this issue Apr 6, 2020 · 11 comments · Fixed by #124661
Labels
A-patterns Relating to patterns and pattern matching C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@pnkfelix
Copy link
Member

pnkfelix commented Apr 6, 2020

I tried this code, supplied by @eddyb play:

#![feature(test)]
use std::hint::black_box;

fn foo() {}
const FOO: fn() = foo;

fn bar() {}
const BAR: fn() = bar;

#[inline(never)]
fn print(s: &str) {
    println!("{}", s);
}

fn main() {
    match black_box(FOO) {
        FOO => print("FOO"),
        BAR => print("BAR"),
        _ => unreachable!(),
    }
    
    match black_box(FOO) {
        BAR => print("BAR"),
        FOO => print("FOO"),
        _ => unreachable!(),
    }
    
    match black_box(BAR) {
        FOO => print("FOO"),
        BAR => print("BAR"),
        _ => unreachable!(),
    }
    
    match black_box(BAR) {
        BAR => print("BAR"),
        FOO => print("FOO"),
        _ => unreachable!(),
    }
}

I expected to see this happen: print out of

FOO
FOO
BAR
BAR

(and in debug builds, that is what happens).

Instead, this happened: in release builds, it prints:

FOO
BAR
FOO
BAR

Meta

This was on a playpen running 1.44.0-nightly, 2020-04-05.

This all arose from a conversation with @eddyb regarding structural match; you can read it on the zulip archive

cc #31434

@pnkfelix pnkfelix added the C-bug Category: This is a bug. label Apr 6, 2020
@pnkfelix
Copy link
Member Author

pnkfelix commented Apr 6, 2020

It is not clear to me what the best thing to do here is. @eddyb would like to disallow matching against function pointer constants entirely. I am thinking it might be better to issue a lint, since I know from experience some people simply are doing this, for better or for worse, and I suspect that its not a soundness issue, just a potential gotcha.

Also, for you archeaologists out there, here is a classic rrrs-authors thread on this subject.

Update: and yes, in case you were wondering, the scheme specifiers are still debating this matter nearly 30 years later: http://scheme-reports.org/mail/scheme-reports/msg03616.html

@eddyb
Copy link
Member

eddyb commented Apr 6, 2020

The scary thing to me is this same thing happening with const generics, because they use the same "structural match" requirement.
(but there's no "runtime value" there, so it's more nebulous in a different way I suppose)

@eddyb
Copy link
Member

eddyb commented Apr 6, 2020

Looks like this is where the whitelisting is missing:

_ => PatKind::Constant { value: cv },

@eddyb
Copy link
Member

eddyb commented Apr 7, 2020

Very disconcertingly, it doesn't look like we turn &T constants into reference patterns, which means we can't fully correctly check leaves behind them (and I'm scared to look at how anything works with them, are they lazily expanded?).

Fixing that should probably be a priority, I wonder if @oli-obk already has plans for this.

@oli-obk
Copy link
Contributor

oli-obk commented Apr 7, 2020

Not just that, I have an implementation of it! #70743

@eddyb
Copy link
Member

eddyb commented Apr 7, 2020

Ah, thanks, I vaguely remembered some discussion, I should've checked your PRs first, sorry!

@hanna-kruppe
Copy link
Contributor

I believe the non-deterministic comparison of function pointers is already tracked by #54685, though the question of what to do about it w.r.t. patterns seems novel and worth tracking independently.

@eddyb
Copy link
Member

eddyb commented Apr 7, 2020

Yesterday I wrote (#70861 (comment)):

The scary thing to me is this same thing happening with const generics, because they use the same "structural match" requirement.
(but there's no "runtime value" there, so it's more nebulous in a different way I suppose)

But today I filed #70889, due to which we might need to do a completely separate check for const generics, so we don't have to be as strict on pattern match-ing anyway.

@RalfJung
Copy link
Member

RalfJung commented Apr 8, 2020

I think this is basically a duplicate of #54685, caused by unnamed_addr.

though the question of what to do about it w.r.t. patterns seems novel and worth tracking independently.

match with a constant and == are just two different ways to write a comparison. This definitely should be mentioned in the other issue but I feel separate tracking is confusing.

@Alexendoo Alexendoo added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Apr 9, 2020
@pnkfelix
Copy link
Member Author

pnkfelix commented Apr 28, 2020

I think this is basically a duplicate of #54685, caused by unnamed_addr.

though the question of what to do about it w.r.t. patterns seems novel and worth tracking independently.

match with a constant and == are just two different ways to write a comparison. This definitely should be mentioned in the other issue but I feel separate tracking is confusing.

Separate tracking may indeed be confusing, but I just want to note that I do not consider match with a constant to be equivalent to using ==.

In particular, the point of rust-lang/rfcs#1445 is that the compiler has not historically treated such matches as being equivalent to == (namely in cases where PartialEq has not been implemented via #[derive(..)]), and we did not want to commit at that time to choosing between so-called "structural" vs "semantic" equality.

Having said that, certainly this is related to #54685, and it may indeed be a good idea to merge these two tickets. But I want to make sure we do it for the right reason.

@RalfJung
Copy link
Member

RalfJung commented Jan 9, 2024

This code causes forward-compat lints now, so once pointer_structural_match is a hard error this can be closed. (Cc #62411)

bors added a commit to rust-lang-ci/rust that referenced this issue May 3, 2024
…atterns, r=<try>

Turn remaining non-structural-const-in-pattern lints into hard errors

This completes the implementation of rust-lang#120362 by turning out remaining future-compat lints into hard errors: indirect_structural_match and pointer_structural_match.

They have been future-compat lints for a while (indirect_structural_match for many years, pointer_structural_match since Rust 1.75), and have shown up in dependency breakage reports since Rust 1.78 (released yesterday). I don't expect any code will still depend on them, but we will of course do a crater run.

A lot of cleanup is now possible in const_to_pat, but that is deferred to a later PR.

Fixes rust-lang#70861
@bors bors closed this as completed in 5fe5543 May 26, 2024
RalfJung pushed a commit to RalfJung/miri that referenced this issue May 27, 2024
…r=pnkfelix

Turn remaining non-structural-const-in-pattern lints into hard errors

This completes the implementation of rust-lang/rust#120362 by turning our remaining future-compat lints into hard errors: indirect_structural_match and pointer_structural_match.

They have been future-compat lints for a while (indirect_structural_match for many years, pointer_structural_match since Rust 1.75 (released Dec 28, 2023)), and have shown up in dependency breakage reports since Rust 1.78 (just released on May 2, 2024). I don't expect a lot of code will still depend on them, but we will of course do a crater run.

A lot of cleanup is now possible in const_to_pat, but that is deferred to a later PR.

Fixes rust-lang/rust#70861
bors added a commit to rust-lang/rust-analyzer that referenced this issue Jun 20, 2024
…r=pnkfelix

Turn remaining non-structural-const-in-pattern lints into hard errors

This completes the implementation of rust-lang/rust#120362 by turning our remaining future-compat lints into hard errors: indirect_structural_match and pointer_structural_match.

They have been future-compat lints for a while (indirect_structural_match for many years, pointer_structural_match since Rust 1.75 (released Dec 28, 2023)), and have shown up in dependency breakage reports since Rust 1.78 (just released on May 2, 2024). I don't expect a lot of code will still depend on them, but we will of course do a crater run.

A lot of cleanup is now possible in const_to_pat, but that is deferred to a later PR.

Fixes rust-lang/rust#70861
flip1995 pushed a commit to flip1995/rust-clippy that referenced this issue Jun 28, 2024
…r=pnkfelix

Turn remaining non-structural-const-in-pattern lints into hard errors

This completes the implementation of rust-lang/rust#120362 by turning our remaining future-compat lints into hard errors: indirect_structural_match and pointer_structural_match.

They have been future-compat lints for a while (indirect_structural_match for many years, pointer_structural_match since Rust 1.75 (released Dec 28, 2023)), and have shown up in dependency breakage reports since Rust 1.78 (just released on May 2, 2024). I don't expect a lot of code will still depend on them, but we will of course do a crater run.

A lot of cleanup is now possible in const_to_pat, but that is deferred to a later PR.

Fixes rust-lang/rust#70861
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-patterns Relating to patterns and pattern matching C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants