Приведение ссылки на функцию с неверным указателем? - PullRequest
9 голосов
/ 16 октября 2019

Я отслеживаю ошибку в стороннем коде и сузил ее до чего-то вроде строчек.

use libc::c_void;

pub unsafe fn foo() {}

fn main() {
    let ptr = &foo as *const _ as *const c_void;
    println!("{:x}", ptr as usize);
}

Запустил на стабильном 1.38.0, это печатает указатель функции, но бета (1.39.0-бета.6) и ночной возврат '1'. ( Детская площадка )

Что означает вывод _ и почему изменилось поведение?

Я предполагаю, что правильный способ разыграть это будет просто foo as *const c_void, но это не мой код.

Ответы [ 2 ]

3 голосов
/ 17 октября 2019

Этот ответ основан на ответах на отчет об ошибке, мотивированный этим вопросом .

Каждая функция в Rust имеет свой отдельный тип элемента функции , которыйотличается от типа элемента функции любой другой функции. По этой причине экземпляру типа функционального элемента вообще не нужно хранить какую-либо информацию - на какую функцию он указывает, ясно из его типа. Таким образом, переменная x в

let x = foo;

является переменной размера 0.

Типы элементов функций неявно приводят к типам указателей на функции , где это необходимо. Переменная

let x: fn() = foo;

является общим указателем на любую функцию с сигнатурой fn(), и, следовательно, должна хранить указатель на функцию, на которую она фактически указывает, поэтому размер x - это размер указателя.

Если вы берете адрес функции, &foo, вы фактически берете адрес временного значения нулевого размера. До эта фиксация в rust репо , временные шкалы нулевого размера использовались для создания выделения в стеке, и &foo возвращал адрес этого выделения. После этой фиксации типы нулевого размера больше не создают выделений и вместо этого используют магический адрес 1. Это объясняет разницу между различными версиями Rust.

2 голосов
/ 16 октября 2019

Что означает вывод _ и почему изменилось поведение?

Каждый раз, когда вы выполняете приведение необработанного указателя, вы можете изменить только один фрагмент информации (ссылкаили необработанный указатель; изменчивость; тип). Поэтому, если вы выполните это приведение:

let ptr = &foo as *const _

, поскольку вы изменили ссылку на необработанный указатель, тип, выведенный для _ , должен быть неизменным и поэтомутип foo, который является неописуемым типом для функции foo.

Вместо этого вы можете напрямую привести к указателю на функцию, который выражается в синтаксисе Rust:

let ptr = foo as *const fn() as *const c_void;

Что касается того, почему он изменился, сказать сложно. Это может быть ошибка в ночной сборке. Стоит сообщить об этом - даже если это не ошибка, вы, вероятно, получите хорошее объяснение от команды компиляторов о том, что на самом деле происходит!

...