Rust: Problema de seguimiento para "Macros 1.1" (RFC # 1681)

Creado en 22 ago. 2016  ·  268Comentarios  ·  Fuente: rust-lang/rust

Problema de seguimiento para rust-lang / rfcs # 1681.

cc @alexcrichton

Estabilización TODO

Pruebas de tornasol:

caracteristicas:

  • El nombre de la caja, actualmente es proc_macro
  • Tipo de caja, actualmente proc-macro
  • El atributo #[proc_macro_derive(Foo)]
  • Cargando proc-macro cajas con -L y #[macro_use] para cargarlas
  • el sombreado es un error
  • sin higiene
  • pasar un flujo de tokens para toda la estructura y recibirlo todo de vuelta
  • Atributo de manifiesto de carga, actualmente proc-macro = true

Errores conocidos:

  • [] - La derivación en pánico puede tener un intervalo incorrecto - # 36935
  • [] - transmisiones de tokens con mod foo fail - # 36691
  • [] - los documentos no se publican por proc_macro - # 38749
  • [x] - los atributos personalizados para varios modos son difíciles - https://github.com/rust-lang/rust/issues/35900#issuecomment -252499766
  • [x] - falla la prueba de carga para las bibliotecas de macros proc - # 37480
  • [x] - el pedido sigue siendo importante - https://github.com/rust-lang/rust/issues/35900#issuecomment -252430957 (corregido por https://github.com/rust-lang/rust/pull/37067)
  • [x] - No se pueden documentar las cajas rustc-macro - https://github.com/rust-lang/rust/issues/36820 (corregido en # 36847)
  • [x] - La carga se reconstruye con demasiada frecuencia - https://github.com/rust-lang/rust/issues/36625 (corregido en https://github.com/rust-lang/rust/pull/36776)
  • [x] - Los atributos generados por el compilador dificultan la vida de los autores derivados personalizados - https://github.com/rust-lang/rust/issues/35900#issuecomment -245978831

  • [x] - Crea una caja de rustc_macro

    • [x] - Tener librustc_macro enlace a libsyntax . Depende de librustc_macro en librustc_driver
    • [x] - Etiqueta rustc_macro como inestable con nuestro encabezado estándar .
    • [x] - Solo etiquete rustc_macro con #![crate_type = "rlib"] , no genere un dylib.
    • [x] - Implementa la API de rustc_macro usando libsyntax 's TokenStream internamente
    • [x] - etiqueta rustc_macro con un elemento TokenStream lang para que el compilador lo sepa.
  • [x] - Agregar atributo rustc_macro_derive

    • [x] - validar que tiene la forma exacta foo(bar) , sin otros argumentos / formatos

    • [x]: verifica que solo se aplique a funciones

    • [x]: verifique que solo se aplique a funciones en el módulo raíz

    • [x] - Verifique la firma con el elemento TokenStream lang agregado arriba

    • [x]: codifica todos los símbolos de función con rustc_macro_derive en metadatos junto con el modo de derivación para el que se utilizan.

  • [x] - Agrega un tipo de caja rustc-macro para otras cajas

    • [x] - conéctelo para producir un dylib

    • [x] - asegúrese de que el dylib obtenga metadatos

    • [x] - asegúrese de que las cajas de rustc-macro no se puedan vincular como dylibs

    • [x] - asegúrese de que no haya elementos _alcanzables_ distintos de los etiquetados con #[rustc_macro_derive]

    • [x] - Agregue cfg(rustc_macro) como directiva cfg inestable, configúrelo para el tipo de caja rustc-macro

    • [x] - asegúrese de que las cajas rustc-macro vinculen dinámicamente a libsytnax

  • [x] - Complete #[macro_use] soporte para rustc-macro cajas

    • [x] - extienda el cargador de la biblioteca para encontrar rustc-macro cajas por separado de dylib / rlib rastreadas hoy al cargar cajas

    • [x] - Analiza metadatos para cajas de rustc-macro para obtener información sobre las combinaciones de modo símbolo / derivación

    • [x] - dlopen el dylib

    • [x]: genera un error si cualquier modo de derivación sombreara a cualquier otro.

  • [x] - Agregar integración de carga

    • [x] - reconoce rustc-macro similar a plugin = true

    • [x] - pasa --crate-type=rustc-macro cuando depende de ello

    • [x] - conecta la misma lógica de host / objetivo para las cajas rustc-macro que está presente para las cajas de complementos (por ejemplo, compila siempre las cajas rustc-macro para los hosts)

  • [x] - Pruebas

    • [x] - prueba de humo cargando un rasgo rustc-macro, ficticio #[derive]

    • [x] - los conflictos de nombres son un error

    • [x] - compilar para la arquitectura incorrecta es un error

    • [x] - no se puede emitir el tipo de caja rustc-macro y cualquier otra cosa (por ejemplo, rustc-macro + dylib) es un error

    • [x] - la información del intervalo no es terrible

    • [x]: eliminar atributos de una estructura, así como campos

    • [x] - agregando impls junto a una estructura

    • [x]: la compilación cruzada busca el tipo de caja de host rustc-macro

    • [x] - no cargue dylibs vainilla como tipos de cajas rustc-macro

    • [x] - no se pueden tener exportaciones públicas más allá de las funciones de derivación macro en una caja rustc-macro

    • [x]: las macros derivadas deben tener la firma requerida

    • [x] - carga dos cajas de macro en una compilación

B-RFC-implemented B-unstable T-lang final-comment-period

Comentario más útil

Ok, voy a investigar esto hoy y ver qué tan lejos llego.

Todos 268 comentarios

Actualicé la descripción del problema con una lista de verificación de lo que se debe hacer. Es probable que no sea exhaustivo, pero debería llevarnos el 90% del camino, con suerte

Ok, voy a investigar esto hoy y ver qué tan lejos llego.

De # 35957: deberíamos quitar el nombre de la caja librustc_macro un poco más. En particular, esta está destinada a ser una caja de larga duración que tendrá elementos esenciales para todos los autores de macro, por lo que limitarse a rustc_macro (que en mi opinión, al menos) es casi la idea 1.1 parece malo. Anteriormente quería libmacro para esto, pero como macro es una palabra reservada (y podríamos quererla como palabra clave en el futuro) eso es imposible. @cgswords y yo hemos estado usando libproc_macro, y creo que no es un mal nombre, aunque no estoy 100% satisfecho con él.

@nrc : Bien, mis pensamientos inmediatos sobre los nombres:

  • extern crate macros; - breve y dulce, pero se puede leer como que contiene macros, en lugar del código de soporte para escribirlas
  • extern crate macro_runtime; - exactamente lo que dice en la lata
  • extern crate metarust; - escribe Rust, sobre Rust, para operar en Rust
  • extern crate bikeshed; - ¡con macros de procedimiento, puede tener el color de óxido que desee!
  • extern crate macrame; - suena como "macro make [r]"; posiblemente sea mejor dejarlo para una futura API "agradable" sobre la biblioteca en bruto.

@nrc Parece que un aspecto importante de esta pregunta es el nombre de nuestros diversos estilos de macro en general - en particular, si vamos con libproc_macro , estamos haciendo un fuerte compromiso con la "macro de procedimiento" terminología. No tengo una opinión fuerte aquí, pero no estoy seguro de si hemos explorado abiertamente el espacio de la terminología.

Para ser claros, ¿estás pensando que macro_rules serían simplemente "macros", es decir, lo que queremos decir por defecto con macros, mientras que tienes que calificar "macros de procedimiento"? Me parece un plan bastante razonable. Y en ese mundo, diría que libproc_macro es mejor que libmacros .

Mi pensamiento aquí es que todas las macros son "macros", y cuando necesitamos hacer una distinción basada en la implementación (el uso debe ser exactamente el mismo para todos los tipos) usamos "macros de procedimiento" vs "macros por ejemplo". Me gustaría eliminar la "extensión de sintaxis" y el "complemento del compilador" por completo (y algún día reutilizar este último para complementos reales).

Pero, sí, quiero firmemente apoyarme en la terminología de "macro de procedimiento".

@nrc ¡ Tiene sentido para mí! Si bien "macros por ejemplo" es un poco difícil de manejar, también es un término muy evocador / intuitivo. Mi única preocupación acerca de la "macro de procedimiento" es que el "procedimiento" no es una cosa en Rust. Me pregunto si hay alguna forma de hacer la conexión más directa. La "macro de funciones" no es del todo correcta, pero ¿tal vez te dé una idea de lo que quiero decir?

Sí, no es del todo perfecto, pero dado que es un término muy conocido / utilizado fuera de Rust, creo que es mejor que acuñar nuestro propio término. La "macro programática" es posible, pero yo prefiero "procedimental".

El término de Perl para el equivalente más cercano que tiene de "macros de procedimiento" es "filtros de origen", que (especialmente con el cambio de AST a tokens) es una descripción bastante adecuada.

¿Quizás 'transformadores de sintaxis' o 'macros programáticas' funcionarían bien como nombres? Sin embargo, no tengo ningún problema con las macros de procedimiento.

"Procesal" ya es lo que la gente llama a esto, y creo que se entiende claramente lo que significa, especialmente en contraste con "macros por ejemplo". No me preocuparía por intentar encontrar un nombre diferente.

Me gusta el término "macro de procedimiento" para uso regular (o tal vez "macro personalizada"). Particularmente me gusta usar la palabra _macro_ para que quede claro que pueden (eventualmente ...) usarse de la misma manera que las "macros normales". Por esta razón, no me gustan los "filtros de origen" (también espero que un filtro simplemente elimine datos, no los transforme, aunque sé que el término se usa para ambos).

Estoy bien con libproc_macro o libmacros . Prefiero este último solo porque no me encanta tener _ en los nombres de las cajas cuando se puede evitar fácilmente. =)

Una pregunta: ¿alguna vez esperamos tener "rutinas de soporte" que puedan usarse desde macros no procedimentales? No conozco tales planes, pero si los tuviéramos y los quisiéramos en la misma caja, libmacros sería un nombre mejor. =)

(Estoy pensando un poco en, por ejemplo, el comentario de

@nikomatsakis : Una pregunta relacionada, pero sutilmente diferente, es un caso de uso que https://github.com/rust-lang/rfcs/pull/1561#discussion_r60459479 : ¿queremos que las funciones de implementación de macros de procedimiento puedan llamar otras funciones de implementación de macros de procedimiento?

Puedo ver fácilmente el deseo de permitir que una derivación personalizada llame a otra, y eso esencialmente haría que las propias definiciones de macros de procedimiento pudieran usarse como tales "rutinas de soporte"

Pero sí, creo que el ejemplo gensym @dherman es bastante convincente. Por supuesto, si la respuesta a mi pregunta anterior es "sí", gensym es tanto una macro (que podría ser usada por macros por ejemplo) como una función de soporte (que podría ser usada por macros de procedimiento).

Tengo un PR de carga https://github.com/rust-lang/cargo/pull/3064 que debería marcar todas las casillas de "integración de carga" en la lista de verificación.

Dejé un comentario sobre el RP de carga, pero creo que queremos un tipo diferente de _dependencia_, no solo un tipo diferente de _paquete_. En primer lugar, creo que esto es mejor estética y ergonómicamente, pero esa es solo mi opinión. Pero también tengo dos razones concretas.

  • En un futuro con departamentos públicos y privados, es importante saber que es importante saber que los departamentos públicos de los departamentos procesales y los departamentos públicos regulares no tienen por qué coincidir. P.ej
  • En un futuro con macros de procedimiento en línea / cuasi-comillas, se puede usar cualquier biblioteca en tiempo de compilación.
  • > ¿Querremos que las funciones de implementación de macros de procedimiento puedan llamar a las funciones de implementación de otras macros de procedimiento?

Como esto muestra, la misma caja podría ser un depósito regular de otra caja de macros de procedimiento, o una macrodep de otra caja.

¿Serde funciona?

https://github.com/serde-rs/serde/releases/tag/v0.8.6

(excepto para los atributos de contenedor en algunos casos # 36211)

¡Genial, gracias por las actualizaciones @dtolnay!

¿Hay documentos en estas macros? Supongo que el único ejemplo de cómo usarlos es en serde, ¿verdad?

Ha aterrizado bien la carga. Está bien, pero sería bueno volver a visitar https://github.com/rust-lang/rust/issues/35900#issuecomment -243976887 en algún momento antes de la estabilización. [Por lo que vale, quise mencionar esto en el RFC original, pero lo olvidé].

Creo que "macros por ejemplo" y "macros de procedimiento" podrían catalogarse mejor como "macros declarativas" y "macros imperativas" respectivamente. Esto proporciona un paralelismo informativo con la categorización declarativa / imperativa más conocida de los lenguajes de programación. Como imperativo se trata como un superconjunto o sinónimo de procedimental, debería estar lo suficientemente cerca para que la gente acostumbrada a la terminología de "macro procedimental" dé el salto. También debe evitar cualquier confusión con los conceptos de procedimiento / función / método en el propio rust.
Este esquema de nomenclatura nos da una caja y un módulo macro_imp en paralelo macro_rules . macro_rules eventualmente podría convertirse en un módulo de una caja macro_dec más general.

@nrc , cuando te refieres a "complementos reales", ¿estás incluyendo cosas como metacollect y clippy , cosas como rustw , rustfmt y Rust Language Server o alguna otra categoría de programa?

Por lo que vale, me disgusta levemente el nombre "por ejemplo" (ya que los patrones $foo no son "ejemplos" en mi mente). Declarativo vs imperativo me suena mejor.

Jugando con esto, he notado un problema. Parece que las derivadas proporcionadas por Rust a veces agregan atributos que el compilador sabe que debe ignorar. Este contexto se pierde cuando pasa por una derivación personalizada. Esperaría que la función de identificación dada como una derivación personalizada no sea operativa, pero causará errores alrededor del atributo #[structural_match] que se agrega.


Guión de reproducción

(En una caja llamada demo_plugin )

#![feature(rustc_macro, rustc_macro_lib)]

extern crate rustc_macro;

use rustc_macro::TokenStream;

#[rustc_macro_derive(Foo)]
pub fn derive_foo(input: TokenStream) -> TokenStream {
    input
}

(en otra caja)

#![feature(rustc_macro)]

#[macro_use] extern crate demo_plugin;

#[derive(PartialEq, Eq, Foo)]
struct Bar {
    a: i32,
    b: i32,
}

Eliminar #[derive(Eq)] hace que todo funcione bien.

@sgrif ah sí, gracias por recordármelo!

Entonces, hay algunas cosas que suceden aquí:

  • Con #[derive(PartialEq, Eq)] , el compilador está agregando silenciosamente #[structural_match] porque entiende estáticamente que la derivación PartialEq sí cumple ese contrato.
  • Creo que surge un atributo similar con #[derive(Copy, Clone)] y se agrega #[rustc_copy_clone_marker] .
  • Actualmente esto funciona porque el intervalo de estos atributos dice "permite inestabilidad interna". Sin embargo, perdemos la información del intervalo al analizar y analizar.

Entonces, algunas soluciones que podríamos hacer:

  • De manera convencional, la derivación personalizada es lo primero, por ejemplo, #[derive(Foo, Eq, PartialEq)]
  • Confíe en la derivación personalizada para omitir estos atributos
  • No emita atributos personalizados si detectamos derivaciones personalizadas
  • Utilice un mecanismo completamente diferente en el compilador para comunicar lo que dicen estos atributos, pero no a través del AST en sí.

Yo estaría a favor de no emitir estos atributos o de utilizar un mecanismo diferente. Sin embargo, esto es realmente complicado, porque #[derive(Copy, Foo, Clone)] también necesita funcionar (donde se ejecuta un código personalizado en el medio que podría cambiar las definiciones).

Mi curso de acción preferido sería simplemente no emitir estos atributos si detectamos derivaciones personalizadas. Incluso eso, sin embargo, puede no ser trivial. Por ahora una convención de "personalizado primero y estándar último" debería ser suficiente, pero creo que deberíamos arreglar esto antes de estabilizar.

Descargo de responsabilidad: solo tengo una vista externa del compilador, por lo que hay muchas cosas que no sé. ^^

Pero por lo que entiendo, el enfoque actual de la derivación personalizada de macros 1.1 es más como una solución temporal. La solución temporal podría traducirse en "bastante tiempo". Pero a largo plazo (= macros 2.0) ya no haremos el viaje de ida y vuelta de análisis de cadenas, lo que actualmente conduce a la pérdida de información de intervalo. Me pregunto si estos atributos ocultos en el AST fueron algo tan malo en un mundo de macros 2.0. Quizás alguien con más conocimiento interno sobre el compilador pueda decirlo. Si estos atributos ocultos realmente tienen sentido en ese mundo futuro, yo diría que optar por la solución al poner convencionalmente derivaciones personalizadas al principio. De todos modos, todo el asunto ya es una solución alternativa, ¿no?

@ colin-kiegel Creo que tiene razón en el sentido de que lo que estamos haciendo en este momento _no_ está preparado para el futuro. Digamos que tiene, por ejemplo:

#[derive(Eq, Foo, PartialEq)]

Hoy agregaríamos la implementación Eq , luego ejecutaríamos el código personalizado para Foo , y luego agregaríamos una implementación de PartialEq . La estructura podría _cambiar_ entre Eq y PartialEq , por lo que el #[structural_match] agregado por Eq puede no ser correcto después de ejecutar Foo .

En ese sentido, estoy de acuerdo en que, en cualquier caso, no son necesariamente a prueba de futuro.

Mi sensación es que las derivaciones personalizadas que se basan en cambios estructurales generalmente no se componen muy bien, independientemente de esos atributos ocultos. ¿Un derivado personalizado v2.0 podrá cambiar la estructura del artículo, o se limitará de alguna manera solo a la decoración?

Sí, creo que la capacidad siempre estará ahí (inherente a la interfaz TokenStream -> TokenStream) aunque sospecho que cualquier implementación razonable de #[derive] conservaría la estructura de la estructura original.

Supongo que no podríamos requerir que el flujo de tokens de salida no contenga la estructura en sí, para asegurarnos de que la estructura no cambie. ¿La estructura de entrada TokenStream sería un prefijo inmutable? El gran problema sería asegurarse de ignorar los atributos no reconocidos que utilizan los complementos después de que se completa la compilación. Quizás cada # [derive ()] pueda tener un prefijo (digamos # [derive (Foo)] tiene el prefijo Foo_) con el cual los atributos que entienden deben comenzar, y después de procesar cada derivado personalizado, quitamos esos atributos.

@mystor sí, el problema con ese enfoque son los atributos no reconocidos, por lo que tenemos la estructura completa como entrada. Eso es generalmente más flexible que confiar en un prefijo / sufijo / registro / etc. en particular.

Si un derivado personalizado v2.0 pudiera marcar atributos personalizados como _usados_, podría limitarse al acceso de _solo lectura_ al resto del flujo de tokens de elementos. De esta manera, IMO podría garantizar una mejor componibilidad de los derivados personalizados. Si una macro v2.0 necesita cambiar la estructura de un elemento, tendría que usar otra API, pero no una derivación personalizada. De esta forma, el problema con #[structural_mach] y el orden de las derivadas (personalizadas) solo estaría presente en las macros 1.1. ¿Eso tendría sentido?

Otro problema. Si una estructura tiene dos derivaciones personalizadas diferentes y la segunda entra en pánico, el intervalo de error apuntará a la primera, no a la que entró en pánico.


Guión de reproducción

En una caja llamada demo_plugin

#![feature(rustc_macro, rustc_macro_lib)]

extern crate rustc_macro;

use rustc_macro::TokenStream;

#[rustc_macro_derive(Foo)]
pub fn derive_foo(input: TokenStream) -> TokenStream {
    input
}

#[rustc_macro_derive(Bar)]
pub fn derive_bar(input: TokenStream) -> TokenStream {
    panic!("lolnope");
}

En otra caja

#![feature(rustc_macro)]

#[macro_use] extern crate demo_plugin;

#[derive(Foo, Bar)]
struct Baz {
    a: i32,
    b: i32,
}

El error resaltará Foo aunque Bar entró en pánico.

¡Gracias por el informe @sgrif! He actualizado la descripción de este problema y espero poder realizar un seguimiento de todos los problemas pendientes relacionados con las macros 1.1 allí también.

Hmm, la interacción de la derivación personalizada con #[structural_eq] es algo en lo que no había pensado antes. ¡Me molesta bastante!

Me parece que tener una forma de "agregar" texto a una secuencia de tokens podría ser una mejor interfaz al final del día ... preservaría la información del intervalo y evitaría este problema, ¿no?

Una ventaja de la interfaz más general es que permite que los paquetes tengan atributos en los campos, que se pueden eliminar cuando se ejecuta la macro. Este es el único caso de uso real que conozco para permitir que las macros de derivación personalizadas alteren el elemento original.

Creo que la cuestión de la interfaz se reduce a si queremos que la derivación personalizada sea 'solo otra macro', en cuyo caso parece importante tener la misma interfaz que otras macros de procedimiento (donde queremos modificar el elemento original). O si debería ser algo propio con una interfaz especial, en cuyo caso la interfaz más restrictiva (añadir) tiene sentido.

Notaré que las extensiones de sintaxis tenían durante mucho tiempo una distinción entre modificadores y decoradores, y creo que todos los involucrados realmente odiaban esa distinción. Por lo tanto, soy un poco reacio a seguir el camino de que la derivación personalizada sea un poco especial (una alternativa que se ha discutido es algún tipo de derivación personalizada muy especial, posiblemente incluso un formato declarativo).

@nrc bueno, todavía puede ser tokenstream -> tokenstream, donde simplemente no exponemos un método new que comienza desde cero, ¿verdad?

Estoy de acuerdo en que debe ser posible tener atributos personalizados en los campos para que una derivación personalizada realmente tenga sentido. Pero creo que puede haber muchas formas de hacer esto con el estilo "añadir"; Definitivamente preferiría el estilo de adición, porque prefiero un mundo donde las macros de derivación no cambian el elemento y son componibles, además de que resuelve el problema #[structural_eq] . Esta es la cantidad justa de libertad en mi opinión.

Si a la gente no le gustó ese destino, me gustaría preguntar por qué, porque obviamente había razones suficientes para hacer esta distinción antes, ¿no es así?

(Supongo que lo que sugerí es solo un truco temporal, en realidad no aborda el problema de componibilidad a largo plazo).

Mis diversas bibliotecas tienen actualmente macros que se parecen a foo!(Bar, parameters...) , que generan una estructura Bar partir de los parámetros.

Durante una discusión sobre IRC, tuvimos la idea de escribir #[derive(Foo)] #[params] struct Bar; lugar, y reemplazar foo! con #[derive(Foo)] que generaría el cuerpo de la estructura.

Obviamente, no es un argumento sólido, pero realmente me gustó la idea, ya que es más claro para el usuario que se está construyendo una estructura.

Me pregunto si podríamos reelaborar el #[structural_match] para colocarlo en el impl generado.

(Realmente no resuelve el problema)

Vale la pena señalar que tanto Serde como Diesel hacen mucho uso de atributos personalizados en los campos, por lo que existe una necesidad definida de derivación personalizada para permitir el reemplazo.

Entonces, en realidad, tal vez no esté pensando con claridad sobre el problema #[structural_match] . Después de todo, ¿qué puede hacer una derivación personalizada?

  • Si la derivación personalizada inserta el atributo #[structural_match] falsamente, debería fallar la comprobación de estabilidad. Si no, ¡eso parece un error en sí mismo! (Sin embargo, dada la forma compleja y extraña en que funciona este código, eso no me sorprendería).
  • Si la derivación personalizada lo elimina, no se produce ningún daño.

Perdón por escribir toneladas de pequeños comentarios y pensar en voz alta, pero hay otra preocupación. Aunque es posible que una derivación personalizada no pueda "falsificar" una anotación #[structural_match] (porque terminaría sin el "intervalo mágico"), probablemente acabaría arruinando el intervalo de cualquier anotación existente, a menos que el las derivadas se aplican en el orden correcto, lo cual es lamentable. Básicamente una instancia de la no componibilidad de la que ha estado hablando @ colin-kiegel, pero sin ningún intento de modificar la estructura en vuelo.

(En otras palabras, dado que confiamos en el intervalo para juzgar si se pueden usar elementos estables, la pérdida de información del intervalo puede causar algunos problemas complicados).

EDITAR: OK, leyendo de nuevo, veo que acabo de rederivar lo que @sgrif ya informó. Lo siento de nuevo. ;)

También es un poco asqueroso, porque significa que estamos exponiendo detalles de implementación inestables a código estable. El código idealmente estable nunca sabría que existe la anotación #[structural_match] .

@nikomatsakis bueno, de una forma u otra, necesitamos hacer cumplir diferentes restricciones dependiendo de si la macro está destinada a ser una derivación personalizada o de algún otro tipo. Eso significa algún tratamiento separado (y semántica diferente), cualquiera que sea la firma de la función.

@ colin-kiegel

Definitivamente prefiero el estilo de adición, porque prefiero un mundo en el que las macros de derivación no cambien el elemento y sean componibles, además de que resuelve el problema # [estructural_eq]. Esta es la cantidad justa de libertad en mi opinión.

Creo que las macros mutantes se pueden componer, aunque, por supuesto, los términos de componibilidad son diferentes. Es evidente que debe haber un conjunto de condiciones previas y posteriores al funcionamiento de las derivaciones, ya sea impuestas por el compilador o por convención. La no mutación parece ser un extremo en el espectro de invariantes que podríamos elegir aquí, y tenga en cuenta que ya estamos discutiendo formas en las que se puede suavizar (por ejemplo, marcar los atributos utilizados). Creo que, en general, preferiríamos las condiciones más simples y que el compilador las hiciera cumplir. Sin embargo, esto queda subsumido en cierta medida por la cuestión de cómo deben tratarse las derivaciones especiales.

Si a la gente no le gustó ese destino, me gustaría preguntar por qué, porque obviamente había razones suficientes para hacer esta distinción antes, ¿no es así?

No creo que hubiera una motivación fuerte en ese momento. Creo que fue fácil de implementar y "me pareció una buena idea". No ha sido del agrado ya que porque hace la implementación más compleja, agrega una distinción para los autores de macros que generalmente es irrelevante, y hace que las macros sean menos flexibles al tener que elegir si modificar o decorar.

Me gustaría mucho considerar el diseño a largo plazo de la derivación personalizada y asegurarme de que vamos en la dirección correcta. Me parece que las limitaciones de la solución 1.1 y el deseo de hacer todo lo posible lo antes posible están enturbiando las aguas aquí y estamos perdiendo de vista la visión más amplia.

Estoy de acuerdo con @jimmycuadra en que parece que admitir atributos personalizados de una forma u otra es un requisito estricto. @nikomatsakis también tiene razón en que el tratamiento actual de #[derive(PartialEq, Eq)] es insatisfactorio y no deberíamos estabilizarlo. Finalmente, @mystor tiene un muy buen punto de que los modos de derivación personalizados ni siquiera deberían conocer este atributo mágico. Estamos obligados a querer agregar más en el futuro y no quiero que las macros 1.1 nos impidan hacer eso.

También haciendo eco del sentimiento de @nrc sobre el diseño a largo plazo de la #[derive] . Si admitimos atributos arbitrarios, creo que #[derive] es bastante especial cuando una derivación personalizada no define un nuevo atributo, sino que simplemente agrega uno existente.

En este momento, la implementación tiene una expansión estricta de izquierda a derecha de los modos #[derive] . Todos los modos de derivación internos se expanden en un bucle y cada vez que se activa un modo de derivación personalizado, reserializamos, expandimos y luego volvemos a la etapa 1. Esto a su vez significa que el compilador puede ejecutar el atributo #[derive] _múltiples veces para uno tipo definición_. Eso conduce a un montón de vellosidad.

Una propuesta que podría tener es modificar el orden de expansión de #[derive] :

  • Primero, todos los atributos de derivación personalizados de las macros 1.1 se expanden uno por uno. Es decir, si tiene #[derive(Clone, Foo)] , primero derivaríamos Foo donde la estructura tenía una anotación #[derive(Clone)] . Si ese #[derive] persistiera, obtendríamos el rasgo incorporado personalizado Clone .
  • En segundo lugar, todos los atributos derivados desconocidos por el compilador se expanden a atributos #[derive_Bar] . Este es solo un truco de compatibilidad con versiones anteriores que deberíamos eliminar en algún momento, y es la forma en que los atributos se expandieron.
  • Finalmente, el compilador expande todos los atributos #[derive] conocidos e integrados

Esto tiene el efecto sorprendente de que no se está expandiendo de izquierda a derecha, pero recuerde nuevamente que esto es solo #[derive] . Esto le da al compilador el máximo conocimiento sobre la definición de la estructura y cuando se expanden los rasgos incorporados sabemos que la estructura del tipo nunca cambiará.

¿Como suena eso? Creo que resuelve todas las limitaciones aquí.


@nikomatsakis No estoy seguro de que la estrategia de colocar el atributo en impl funcione porque otros modos de derivación personalizados podrían, en teoría, cambiar el diseño de la estructura, incluso los tipos de campos. Creo que esto violaría las suposiciones del compilador cuando se expandió por primera vez.

¿Alguna vez se ha declarado oficialmente que el orden en el que se procesan los derivados es de izquierda a derecha, a través de la referencia de Rust o algo así? De manera más general, ¿importa alguna vez el orden de los atributos? Parece que es incidental que se implementó de esa manera, y los autores de macros no deberían haber dependido de un orden de izquierda a derecha. La propuesta de Alex de procesar derivaciones personalizadas primero para que nunca vean atributos mágicos agregados por el compilador tiene mucho sentido.

Solo me gustaría agregar que no me gusta la idea de que las derivaciones personalizadas puedan cambiar el diseño de la estructura. Me gustaría poder usar esto para algo que sea sensible a la seguridad. Como ejemplo, considere la implementación #[derive(Trace)] utilizada por rust-gc .

#[derive(Trace)]
struct Foo {
    a: Gc<i32>,
}

Ampliando a:

struct Foo {
    a: Gc<i32>,
}

unsafe impl Trace { // NOTE: Strawman impl
    unsafe fn trace(&self) { Trace::trace(&self.a) }
}

Sin embargo, si permitimos cambiar los campos en la estructura, podemos definir una derivación personalizada Evil :

#[derive(Evil)]
struct Foo {
    a: Gc<i32>,
}

Ampliando a:

struct Foo {
    a: Gc<i32>,
    b: Gc<i32>,
}

Que, si los combinamos:

#[derive(Trace, Evil)]
struct Foo {
    a: Gc<i32>,
}

Ampliando a:

struct Foo {
    a: Gc<i32>,
    b: Gc<i32>,
}

unsafe impl Trace {
    unsafe fn trace(&self) { Trace::trace(&self.a) }
}

Que es una implementación poco sólida de Trace . Cuando se usa con rust-gc , esto permite que b sea ​​una referencia pendiente, lo cual es terriblemente inseguro e inseguro. Esto significa que Trace ya no es algo seguro para #[derive] en un tipo, lo cual es muy desafortunado.

Personalmente, creo que cualquier #[derive] se comporte bien no modificará el diseño / composición de una estructura, y si lo hace, entonces no tendrá suerte. La capacidad de un derivado personalizado para eliminar atributos es fundamental, y renunciar a eso no es un comienzo. Además, otras implementaciones que implican algún tipo de inclusión en listas blancas o cualquier otra cosa difieren mucho de la interfaz simple que tenemos hoy.

Dicho de otra manera, no creo que la "pureza" de que #[derive] nunca modifiquen la estructura valga la pena.

Me pregunto si habrá una forma de eliminar atributos sin permitirle agregar o eliminar campos (por ejemplo, verificar que los campos sean los mismos en la estructura analizada nuevamente que la estructura original y cometer errores si no lo son) t, pero sin quejarse si se cambian otras cosas).

Tengo un mal presentimiento sobre permitir que Derive cambie la estructura. El ejemplo de @mystor es lo que tenía en mente cuando hablaba de componibilidad ... en un nivel alto (puede que no sea el término correcto).

Creo que la gente aprovechará esto, si está disponible. Y esto obligará a los consumidores a razonar sobre los detalles de las implementaciones de derivaciones personalizadas y su orden de ejecución.

Preferiría que pudiera decir 'oye, no sé qué hace esta derivación, pero entiendo la otra' sin interdependencia. De lo contrario, será un dolor en el dedo del pie y creo que sucederá.

¿Es una macro de procedimiento que hace algo malicioso realmente diferente de cualquier caja que use para hacer algo malicioso? Cualquier caja puede tener un código inseguro que hace algo que no debería. Parece que este caso simplemente se relaciona con la forma en que determina la confiabilidad de cualquier código que no haya escrito usted mismo, por ejemplo, reputación de la comunidad, inspeccionando la fuente usted mismo, etc.

No creo que las cajas vayan a intentar hacer algo malicioso, más bien espero que sean "inteligentes" y hagan trucos ingeniosos para hacer su implementación más eficiente o porque pueden, y para que rompa otras implementaciones de derivaciones personalizadas. No me sorprendería mucho si algunas derivadas personalizadas comenzaran a agregar campos a estructuras que solo se usan en sus implementaciones porque pueden, y luego rompen algo como Trace.

@mystor Eso suena relevante en teoría, pero recuerde que en realidad necesita proporcionar todos los campos de una estructura en Rust, por lo que es mucho menos probable que funcione silenciosamente así, que en C ++, por ejemplo.

@alexcrichton

Una propuesta que podría tener es modificar el orden de expansión de #[derive]

Me gusta esta idea. Quizás lo que hay que decir en términos de documentación es simplemente que el orden de expansión es "indefinido". Sucede que expandimos PartialEq / Eq últimamente estos días, pero no hay una razón estricta para hacerlo.

En cuanto a las derivaciones que modifican la definición de la estructura, estoy de acuerdo en que suena algo sutil, pero no me molesta demasiado. También creo que los campos de "inserción automática" podrían ser bastante útiles, pero preferiría que dichos modificadores sean atributos distintos en lugar de incluirlos en la lista de derivaciones, principalmente porque no queremos que las personas confíen en el pedido. de expansión en este momento.

PD. Consideraría seriamente usar un RNG (determinista) sembrado por el hash de la caja o algo así para reordenar la expansión de las derivaciones personalizadas del usuario, de modo que la gente no pueda confiar en el orden de expansión. Siempre quise hacer esto en algún contexto para evitar dependencias implícitas, pero nunca tuve la oportunidad. ;)

EDITAR: he cambiado de opinión y no veo ninguna razón para no permitir más la mutación de la estructura, pero aquí está mi comentario original para el contexto

Entonces, según tengo entendido, estos son los argumentos para permitir que el #[derive] mute la estructura:

  • Podría ser útil en algunos casos (no he visto ningún ejemplo, pero creo que existen)
  • Queremos poder eliminar atributos una vez que se hayan utilizado
  • Dar más poder a los autores derivados personalizados

Si bien los argumentos para agregar limitaciones a las implementaciones personalizadas #[derive] (como exigir que el nombre de la estructura y los campos / nombres de los campos en la estructura permanezcan iguales) son:

  • Permite que el código generado por un #[derive] dependa de la estructura del tipo del que se deriva para su solidez (por ejemplo, #[derive(Trace)] de rust-gc, que _debe_ ver el tipo de respaldo real, o no es sólido, potencialmente de una manera sutil de uso después de la liberación)
  • Reduce la probabilidad de dependencias implícitas en macro expansiones, ya que hay menos información transmitida entre ellas a través de la estructura

En mi opinión, la capacidad de escribir implementaciones de derivación personalizadas de sonido que generan unsafe trait impls o código que depende de un código inseguro es extremadamente importante, y creo que hay formas de lograr la mayoría de las habilidades en la primera sección ( con la excepción de la capacidad de agregar campos de estructura) de forma segura. Si no tenemos algún tipo de restricción, no creo que las cajas como rust-gc sean posibles de implementar de manera segura. Tengo dos ideas:

Idea 1

Antes de ejecutar una pasada de derivación personalizada, lea el nombre de la estructura y los nombres de cada uno de los campos. Cuando se complete el pase y se vuelva a analizar la estructura, verifique si el nombre de la estructura es el mismo y si los nombres de cada uno de los campos (y el recuento de los campos) son los mismos. si no es así, genere un error y finalice la compilación.

Esto aseguraría que las propiedades estructurales básicas de las que esperamos que dependan los complementos de derivación personalizados no se rompan, y significa que tenemos más complementos de derivación personalizados y sólidos. Esto también tiene la ventaja de ser retrocompatible con el enfoque actual, por lo que si decidimos que nos gusta más en el futuro, podemos simplemente cambiar y romper el código de nadie. También maneja el caso de atributos no utilizados, como hoy.

Idea 2

Dé a cada complemento derivado personalizado la misma entrada TokenStream (el texto original escrito en el programa). Cuando se vuelve a analizar el resultado, registre qué atributos todavía están presentes en la estructura de salida. Si un atributo está presente en cada flujo de tokens de salida, entonces quejese sobre un atributo no utilizado.

Esto significa que es imposible tener dependencias de ordenamiento (ya que conceptualmente, cada complemento derivado personalizado funciona con el mismo objeto original), y también hace que sea imposible estropear la estructura del complemento. Me gusta esta idea, ya que garantiza que cada derivado personalizado actúe de una manera mayormente sana, solo generando nuevos elementos basados ​​en la estructura existente. Esto probablemente también sería fácil de transformar en cualquier solución en la que pudiéramos transformar la solución actual.

TL; DR

En resumen, me gustaría entender lo que la ventaja particular es de permitir estructuras mutantes, y por qué pesa más que los problemas de seguridad de hacer segura #[derive(Trace)] y siempre correcto- #[derive(Serialize)] etc posible. Estoy seguro de que si terminamos siguiendo la ruta de las estructuras mutantes, habrá una buena razón, pero me entristecerá mucho cambiar el nombre de mi derivación personalizada de Trace a #[derive(unsafe_Trace)] .

Encuentro que la solución de @alexcrichton es una buena compensación. Definitivamente esperaría que cualquier cambio que realicen algunas derivaciones personalizadas, se apliquen las predeterminadas.

Aunque @mystor tiene un buen punto de que puede llevar a sorpresas desagradables, tener la posibilidad de cambiar struct parece obligatorio. Por otro lado, las cajas que combinan los casos de uso de macros de procedimiento, código inseguro _y_ preocupaciones de seguridad parecen bastante poco comunes.

Fuera del tema: ¿esta implementación proporcionará una forma para que la macro informe errores con elegancia?

Me gusta la idea de @nikomatsakis para aleatorizar el orden de expansión de las

@mystor, una tercera opción sería realizar estas comprobaciones de seguridad solo una vez después de aplicar todas las derivadas. Eso no sería 100% correcto (dos derivadas podrían agregar y eliminar el mismo campo), pero en términos de una contramedida heurística, debería ser suficiente para evitar cualquier intento de cambiar la definición de estructura en una derivada personalizada en primer lugar.

Realmente no veo la preocupación por la modificación de estructuras. Un campo no se puede agregar de forma invisible, habrá que cuidar algo durante la inicialización. Si está _realmente_ preocupado por que eso suceda, puede escribir su derivado para generar código que no se compila si no ve la estructura completa con bastante facilidad.

@sgrif probablemente sea cierto en la mayoría de los casos, pero no tanto si también deriva y usa el rasgo predeterminado, o algo equivalente.

@sgrif PD: Es cierto que la mayoría de los autores de rust probablemente entiendan lo que está sucediendo en su propio código y, por lo tanto, es posible que no se sorprendan por las alteraciones de la estructura si eligen usar tal macro a propósito.

El caso de uso general para aplicar macros en estructuras ciertamente es una combinación de alteraciones de estructuras + decoraciones. Pero espero que la proporción general sea "muchas decoraciones" con "solo algunas modificaciones". Es bueno tener una separación clara aquí, porque eso mejora la legibilidad y la flexibilidad.

  • flexibilidad: supongamos que desea aplicar dos derivaciones personalizadas que hacen ambas cosas, es decir, alteran y decoran. No importa cómo los pida, es posible que no obtenga el resultado que desea. Sin embargo, si la alteración ocurre a través de un mecanismo diferente y todas las decoraciones se aplican después, tiene la flexibilidad de combinar múltiples alteraciones y decoraciones de una manera más controlable.
  • legibilidad: si lee el código de otra persona y hay 10 derivaciones aplicadas a una estructura y una de ellas altera la estructura, necesitará más tiempo para averiguarlo.

Entonces, aunque encuentro convincente esta parte del argumento de

Permite que el código generado por un # [derivar] personalizado dependa de la estructura del tipo del que se deriva para su solidez (por ejemplo, # [derivar (Trazar)] de rust-gc que debe ver el tipo de respaldo real, o no es sólido, potencialmente de una manera sutil de uso después de la liberación)

Creo que tratar de hacer cumplir esta derivación puede ser la forma incorrecta de hacer las cosas. En concreto, es probable que queremos la capacidad de modificar las definiciones de estructura en el caso general (sí, no va a ser transparente, pero ¿y qué). Lo que significa que la idea de tener un "sonido" Trace que pueda estar seguro de que la estructura no cambia después, podría necesitar ser resuelta de alguna otra manera. Considere si los decoradores se aplican de abajo hacia arriba:

#[mangle] // <-- custom decorator that does bad things to struct definition
#[derive(Trace)]
struct Foo {
    x: T, y: U
}

Una idea podría ser que la Trace impl puede escribirse de tal manera que _it_ no se compile si cambia la definición de struct . Por ejemplo:

unsafe impl Trace for Foo {
    fn trace(&self) {
        let &Foo { ref x, ref y } = self;
        <T as Trace>::trace(x);
        <U as Trace>::trace(y);
    }
}

Sin embargo, tenga en cuenta que #[mangle] también puede arruinar su impl si es realmente diabólico. =) Hay mucho que podemos hacer aquí.

Como observador de estas conversaciones, me complacería tener la regla formal o informal de que #[derive] solo puede agregar bloques impl e introducir una anotación de hermanos ( #[mangle(Foo, Bar)] bueno para mí 😸) que se dedica a _cambiar_ la estructura de un tipo. #[mangle] podría tener un orden de evaluación definido.

Probablemente deba haber un orden de evaluación definido entre las anotaciones si aún no lo hay.

Creo que mis opiniones sobre esto se han relajado. @nikomatsakis hace un buen punto de que incluso si saldríamos con la nuestra de poder hacer suposiciones sobre los diseños de estructuras en el código de todos modos. El truco let Foo{ ... } parece que funcionará para asegurarse de que el diseño sea correcto en casos cuerdos. Sin embargo, probablemente necesitemos documentarlo en algún lugar para que todos no tengan que descubrirlo de forma independiente.

@nikomatsakis

  • mh ... podría existir la regla adicional, que _decoradores personalizados deben aplicarse antes de derivar_, además de que _derives no pueden alterar el artículo_. Esto todavía permitiría endurecer / purificar la mecánica derivada en general.

Pero también me alegra ver que hay otra forma de fortalecer las derivaciones personalizadas _individualmente_ frente a cambios posteriores. :-)

Con respecto a los casos que necesitan modificar el interior del elemento al que se aplica el atributo, me encontré con el hilo "Comentarios previos a la implementación para Qt con Rust" en u.r-l.o , e hice esta publicación:

https://users.rust-lang.org/t/pre-implementation-feedback-for-qt-with-rust/7300/19

Una faceta notable es que aquí, sugiero que se aplique #[derive] (o similar) a un _trait_, en lugar de a una estructura, y el elemento agregado dentro sería un método const trait .

Similar a los problemas de carga que mencioné anteriormente, no estoy seguro

#[macro_use]
extern crate double;

importar macros de procedimiento desde una caja llamada double . Dado que estamos usando código de tiempo de ejecución (como en Ruest real) en tiempo de compilación, debería haber una declaración de importación de incremento de fase análoga a (require (for-syntax ...)) Racket. [El documento de Racket es https://docs.racket-lang.org/reference/require.html#% 28form ._% 28% 28lib._racket% 2Fprivate% 2Fbase..rkt% 29._for-meta% 29% 29, desafortunadamente, no puedo encontrar la manera de vincular la sección correcta.]

La publicación del blog http://blog.ezyang.com/2016/07/what-template-haskell-gets-wrong-and-racket-gets-right/ señala los errores de fase cometidos en Template Haskell y puede ser de interés- - No quiero cometer los mismos errores en Rust.

@ Ericson2314 La diferencia en Rust es que double ya está compilado _ para una fase específica_.
Es decir, la caja solo exporta la interfaz de macro / modificador, como si estuvieran definidos con, por ejemplo, macro_rules .
Sería interesante poder crear cajas que exporten tanto las macros de procedimiento como los elementos de Rust subyacentes (que forman la macro de procedimiento), pero hasta ahora no parece que se proponga de ninguna manera.

Puede tener sentido permitir que la caja que se está construyendo elija mucho sobre qué y cómo exportar, pero simplemente tomar un sistema al por mayor de un LISP con un modelo de compilación diferente parece contraproducente.

Sí, @eddyb , soy escéptico de esta metodología de "crear sabe cómo se utilizará en el futuro". En todo caso, las fases son más importantes para nosotros que Racket debido a nuestro modelo de compilación (ni siquiera estoy seguro de si Racket puede realizar una compilación cruzada), así que no entiendo tu último argumento.

Nominado para la discusión del equipo lang sobre: ​​un plan de estabilización.

En el lado de serde, aquí está la breve lista de problemas restantes antes de que podamos dejar de admitir el complemento del compilador existente y recomendar oficialmente Macros 1.1 para todos los usuarios nocturnos: https://github.com/serde-rs/serde/issues/545. Lo único que necesitamos de Rust es que se repare el # 36211. En todo lo demás estamos progresando rápidamente.

Tengo un PR abierto que implementa nuestro rustc_macro sin usar syntex, por lo que podemos dejar de preocuparnos por el tiempo de compilación https://github.com/serde-rs/serde/pull/548.

EDITAR: no importa, # 36211 solo afectó a la antigua implementación de syntex

Intentaré terminar el puerto de Diesel el viernes para poder confirmar que esto hace todo lo que necesitamos en ese sentido.

@dtolnay dado el serde-rs / serde # 548, ¿quedan bloqueadores para serde?

@sgrif increíble, ¡gracias! Una vez que haya hecho eso (o antes), ¿podría comentar aquí con los bloqueadores restantes que haya encontrado?

Sí lo haré.

El martes 27 de septiembre de 2016 a las 7:29 p.m. Alex Crichton [email protected]
escribió:

@dtolnay https://github.com/dtolnay dado serde-rs / serde # 548
https://github.com/serde-rs/serde/pull/548 , ¿quedan algunos
bloqueadores para serde?

@sgrif https://github.com/sgrif increíble, ¡gracias! Una vez que hayas hecho eso
(o antes) ¿podría comentar aquí con los bloqueadores restantes que haya
encontrado?

-
Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/rust-lang/rust/issues/35900#issuecomment -250028743,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/ABdWK7MnA1rpn-WGji8gwAT2JCIqB4LFks5quaa-gaJpZM4JqEAX
.

@alexcrichton

dado serde-rs / serde # 548, ¿quedan bloqueadores para serde?

No, si @ oli-obk o @erickt revisan ese PR hoy o mañana, entonces puedo sacarlo todo al día siguiente y migrar algunos usuarios prominentes como Rusoto.

@dtolnay Estoy haciendo un uso intensivo de serde_macros en Ruma y me gustaría ayudarlo a probar serde_derive también, si necesita más ojos.

De hecho, también uso diesel_codegen, por lo que Ruma es un buen campo de pruebas para la versión Macros 1.1 de estas dos bibliotecas.

Lancé Serde 0.8.10 anunciando que serde_macros está en desuso a favor de Macros 1.1.

Agreguemos una casilla de verificación para "trabajos de documentación de carga": https://github.com/rust-lang/cargo/issues/3132.

@dtolnay hecho!

No estoy seguro si se trata de errores serde_derive o errores de rustc / rustdoc, pero estoy notando dos problemas en los documentos generados:

  • Aparece un literal "///" al comienzo de las cadenas de documentos generadas de tipos que utilizan derivaciones personalizadas.
  • Deserialize y Serialize no aparecen en la sección "Implementaciones de rasgos" para los tipos que usan derivaciones personalizadas.

Aparece un literal "///" al comienzo de las cadenas de documentos generadas de tipos que utilizan derivaciones personalizadas.

Este fue un error de syn . Lancé 0.8.2 con una solución, así que cargo update para recogerlo.

No sé sobre el segundo, se lo dejo a @alexcrichton.

@jimmycuadra

  • Deserialize y Serialize no aparecen en la sección "Implementaciones de rasgos" para los tipos que usan derivaciones personalizadas.

¡Eso suena bastante sospechoso! Sin embargo, parece que esto es un error en rustdoc, pero quizás no uno nuevo. Por ejemplo, rustdoc no muestra esto con la implementación Copy :

#[derive(Clone)]           
pub struct Point {         
    x: i32,                
    y: i32,                
}                          

const _FOO: () = {         
    impl Copy for Point {} 
    ()                     
};                         

Serde-derive usa ese patrón para generar impls. @dtolnay, ¿ serde_macros tenía la misma forma de generación? ¿Sería fácil alejarse por ahora para resolver el problema de la documentación?

@jimmycuadra quiere abrir un error separado para rastrear el problema de rustdoc sobre eso?

@dtolnay, ¿ serde_macros tenía la misma forma de generación?

Si.

¿Sería fácil alejarse por ahora para resolver el problema de la documentación?

No. Hasta donde yo sé, esta es la única solución que cumple con estas dos restricciones:

  • Debe admitir serde no en la raíz de la caja, es decir, no ::serde . Esto es común cuando las personas colocan serde impls detrás de una marca de función en un módulo separado.
  • Debe poder usar tipos privados del módulo circundante.

Consulte https://github.com/serde-rs/serde/issues/159 para obtener más detalles.

@dtolnay ah ok, entonces para aclarar, @jimmycuadra esto no es una regresión de antes, ¿verdad?

Actualización: No del todo terminado con el puerto, pero casi está. No hay más problemas que los que ya he informado o limitaciones menores que sabíamos que encontraríamos. Probablemente sea seguro marcar la casilla "diesel funciona", tendremos un lanzamiento mañana en Macros 1.1 si no sucede esta noche.

Para aquellos que siguen, he abierto # 36945 para renombrar rustc_macro a proc_macro prácticamente en todas partes, lo que parece ser el consenso sobre el nombre de esta caja.

Parece que las implementaciones de rasgos que faltan en rustdoc (que ahora se están rastreando en otro número) no son específicas de Macros 1.1. Es seguro ignorarlo.

Intenté portar mi biblioteca mockers desde el complemento del compilador a macros 1.1 y obtuve un "error: los atributos de derivación personalizados solo se pueden aplicar a elementos de estructura / enumeración". Con el complemento del compilador es posible (aunque algo extraño) usar "derivar" en rasgos. ¿No tengo suerte aquí?

@kriomant interesante! Creo que en realidad puede ser un error en la derivación personalizada hoy, ya que no estoy seguro de que alguna vez haya tenido la intención de permitir que #[derive] aplique a un rasgo ...

Creo que por ahora, para ser conservadores, es probable que todavía no _estabilicemos_ los rasgos derivados, pero quizás podríamos agregarle una característica inestable. Pensamientos @ rust-lang / lang?

@alexcrichton ¿Cuál es la diferencia entre rasgos y estructuras de un aspecto de custom_derive?

@KalitaAlexey none, es una limitación artificial coincidir con la implementación real derive

@alexcrichton ¿Podríamos extender un apoyo a los rasgos?

Como mencioné anteriormente , es probable que sea un error que la derivación personalizada alguna vez se haya permitido en los rasgos, y esto no se especificó en el RFC, por lo que sería necesario discutir más antes de extender. En cualquier caso, es poco probable que se estabilice el soporte para derivar en rasgo en la primera pasada, pero podríamos considerar una puerta de función separada.

Personalmente, no quisiera agregar derivar en un rasgo, ya que parece mucho más en la línea del territorio de atributos personalizados en lugar de derivarse en sí mismo. (por ejemplo, va en contra del espíritu de #[derive] como se creó originalmente)

Preferiría que la derivación personalizada siga exactamente las mismas reglas que la derivación regular. _Podríamos_ querer cambiar eso más tarde, pero creo que debería ser RFC (también soy bastante frío con la idea, tbh, pero podría cambiar de opinión con un caso de uso convincente y un manejo decente de varios casos extremos ).

@alexcrichton

Creo que por ahora, para ser conservadores, es probable que no estabilicemos los rasgos derivados por el momento, pero tal vez podríamos agregarle una característica inestable. Pensamientos @ rust-lang / lang?

👍 de mí.

Ok, la característica inestable es buena, pero significa que mi biblioteca no funcionará estable todavía (sin generación de código). Y la sintaxis de macro!(…) no está cubierta por "macros 1.1" también, ¿estoy en lo correcto?

Estoy revisando el proc macro RFC y el plan era que las cajas de macro deberían declararse #[cfg(macro)] . No lo requerimos para macros 1.1, pero sí requerimos un tipo de caja especial. Los dos mecanismos son algo ortogonales: el primero describe la fase de compilación, el segundo el tipo de caja. Pero también se superponen un poco: lo último implica lo primero. El primero también escala para declarar macros proc y funciones no macro en las mismas cajas, mientras que el segundo no lo hace.

No estoy seguro de si necesitamos cambiar algo aquí, probablemente podríamos piratear la propuesta de macros proc para que sea compatible con versiones anteriores (omite deliberadamente la descripción del mecanismo de carga de macros y, por lo tanto, los tipos de cajas). Pero algo para reflexionar durante el período de estabilización.

@kriomant correcto, sí

@nrc ahora mismo está definido como cfg(rustc_macro) (aunque pronto cambiará a proc_macro ). No lo necesitamos, no, pero pensé que iba a ser necesario para el concepto de vincular a una caja en tiempo de compilación y también en tiempo de ejecución. Es decir, compilaríamos proc-macro crate dos veces: una vez con el tipo de caja proc-macro y una vez con el tipo de caja rlib , y esta última no se vincularía a libsyntax ni nada parecido ese.

Por ahora, aunque parece bien no _requitarlo_, aunque supongo que eso significaría que en una fecha posterior tiene que optar por el soporte en tiempo de ejecución. (compilando la caja dos veces)

@alexcrichton ¿Podría #[proc_macro_derive] implicarlo? De la misma manera #[test] implica #[cfg(test)] .
(No significa que tengamos que agregarlo ahora, solo que el peor de los casos si agregamos cfg son las advertencias de artículos no utilizados).

@eddyb ¿Qué hay de extern crate proc_macro; ? Siento que esos también se romperían.

@mystor Es solo una caja normal con un montón de tipos e implicaciones.

Sin embargo, ¿no se vincula también a libsyntax , lo que significa que si una caja usa una caja proc_macro y quiere realizar una compilación cruzada, también tendrá que cruzar la sintaxis de compilación?

@eddyb#[proc_macro_derive] puede ser ignorado automáticamente, pero el problema es que _también_ necesitamos ignorar todo lo que esas funciones alcanzan de manera transitiva (como extern crate proc_macro ). Aunque es "solo una caja", tiene graves implicaciones en el tiempo de ejecución (vinculación dinámica, no disponible para objetivos de compilación cruzada, etc.).

Tenga en cuenta que las pruebas tienen #[cfg(test)] , por lo que me parece razonable que todavía demos #[cfg(proc_macro)] básicamente para el mismo propósito.

@alexcrichton _Idealmente_, la caja no tendría más que lo que se exporta y no requeriría un enlace dinámico ni nada por el estilo, solo libstd . Sin embargo, pude ver que causaba problemas en los casos #![no_std] .

Parece que tendríamos que hacer la doble compilación desde el principio si queremos captar todo: desordenado : .

EDITAR : Espera, ¿en qué estoy pensando? Requiere un tipo de caja personalizada, la doble compilación se aplica a cajas _regulares_ que _también_ exportan macros de procedimiento / atributos / deriva / etc. Entonces no es relevante por ahora.
Pero al menos podríamos introducir #[cfg(proc_macro)] que siempre está configurado para el nuevo tipo de caja.

🔔 ¡ Esta función está entrando en su período de comentarios final con la intención de estabilizarse al final de este ciclo de lanzamiento! 🔔

La razón fundamental para considerar la estabilización ahora es:

  • La superficie API de esta función es, por diseño, _extremamente_ conservadora. Al mismo tiempo, es compatible con el plan de facto para macros de procedimiento.
  • La función se está generalizando rápidamente a través de Serde y pronto de Diesel, aunque, en particular, el uso generalizado es principalmente como un _cliente_ de derivación personalizada.
  • Entrar en FCP ahora nos da tres meses antes de que la función se envíe realmente de forma estable, lo que debería ser suficiente tiempo para detectar cualquier problema restante.

Tenga en cuenta que el nombre está cambiando a proc_macro , según el consenso anterior sobre este hilo. Podemos continuar eliminando este y otros puntos delicados durante el resto de este ciclo de lanzamiento.

Compilar la caja dos veces suena muy burdo: el alcance no funcionará correctamente. Realmente algo como extern! crate needed_for_my_inline_proc_macros; es una solución mucho mejor.

@aturon qué, ¿la gente no comenzó a usar esto hace días?

@ Ericson2314 Ha sido un poco más largo que eso, pero como explica el comentario de FCP, el _mínimo_ tiempo antes del envío al canal estable es de tres meses a partir de ahora, lo que creemos que es más que suficiente para esta interfaz extremadamente estrecha.

Tenga en cuenta que el FCP en sí es un asunto de 6 semanas que no necesariamente significa que lo pondríamos en el camino hacia la estabilización. Pero al comenzar el proceso al menos ahora, creamos la oportunidad de enviar esta función en 3 meses, suponiendo que no se descubran problemas antes de esa fecha.

Ah ok. Recordé la parte de los 3 meses, pero olvidé la ubicación del FCP dentro de esas 3 bocas, para que no fueran 3 meses desde el 22 de agosto. No importa el momento en el que entonces.

Creo que los problemas de fases que mencioné tienen un impacto incluso en esta pequeña parte de la historia de las macros proc, por lo que me gustaría que se aborden.

@alexcrichton los rustc_copy_clone_marker y otros rustc_attrs todavía nos están mordiendo. Consulte https://github.com/serde-rs/serde/issues/577.

#[derive(Copy, Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct MyStruct {
    value: i64,
}

La solución es cambiar el orden, pero pensé que comprobaría si esto se puede arreglar.

#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone)]
pub struct MyStruct {
    value: i64,
}

Terminé de migrar https://github.com/sfackler/rust-postgres-derive a macros 1.1 Fue relativamente sencillo, pero el manejo de atributos que son leídos por múltiples derivadas es un gran dolor. Serde se encontró con lo mismo, pero requiere un montón de lógica extraña para expandir ambas derivadas al mismo tiempo: https://github.com/sfackler/rust-postgres-derive/blob/master/postgres-derive-internals/ src / lib.rs # L26

No debería bloquear la estabilización, pero creo que es un problema ergonómico relativamente grande en el lado de la autoría que probablemente deberíamos abordar pronto.

Lancé quote 0.3.0 con soporte para elegantes repeticiones de estilo macro_rules! (gracias @nrc por el empujón). Si está desarrollando macros proc sin cuasi / syntex probablemente desee esto. Ejemplo de uso de postgres-derive:

pub fn enum_body(name: &str, variants: &[Variant]) -> Tokens {
    let num_variants = variants.len();
    let variant_names = variants.iter().map(|v| &v.name);

    quote! {
        if type_.name() != #name {
            return false;
        }

        match *type_.kind() {
            ::postgres::types::Kind::Enum(ref variants) => {
                if variants.len() != #num_variants {
                    return false;
                }

                variants.iter().all(|v| {
                    match &**v {
                        #(                           // \
                            #variant_names => true,  //  |----- new feature
                        )*                           // /
                        _ => false,
                    }
                })
            }
            _ => false,
        }
    }
}

Tengo curiosidad, ¿por qué # y no $ ?

EDITAR : Siempre pensé que la sintaxis de citas sería ${...} pero tal vez estoy demasiado apegado a los literales con plantilla de ES6, incluso si han pasado varios años. \{...} también funciona, aunque sería útil en otros lugares.
Sin corchetes parece difícil de detectar, pero no debería preocuparme.

Tengo curiosidad, ¿por qué # y no $ ?

Porque es una macro macro_rules y no puedo hacer lo que quiera: smile : $v se empareja como un solo token y no hay forma de pasar de $v a usar la variable v . En contraste, #v son dos tokens, por lo que puedo combinarlos por separado y hacer cosas con el ident.

macro_rules! demo {
    ($tt:tt) => {};
}

fn main() {
    demo!($v);
}

¡Espere, todo eso se hace en macro_rules ?! Las macros olvidadas 1.1 se derivaron solo por un momento. Dicho esto, $x ser un token es un defecto de diseño, en mi opinión. cc @jseyfried

@dtolnay

@alexcrichton, el rustc_copy_clone_marker y otros rustc_attrs todavía nos están picando. Ver serde-rs / serde # 577.

¡Ay, eso parece malo! Actualizaré la lista en la parte superior. ¿Pensamientos de @nrc o @jseyfried sobre cómo podríamos abordar esto? No estoy muy familiarizado con el orden de expansión de todo, pero tal vez cuando se expande un atributo #[derive] , podría intentar absorber todos los futuros y hacerlo primero.

@sfackler

el manejo de atributos que son leídos por múltiples derivadas es un gran dolor

No estoy seguro de haber seguido el ejemplo al que vinculó, ¿podría explicarlo?

@alexcrichton Tomemos por ejemplo

#[derive(ToSql, FromSql)]
enum Foo {
    #[postgres(name = "bar")]
    Bar
}

El atributo #[postgres] se usa para ajustar cómo se generan las implementaciones ToSql y FromSql . Debe eliminarse antes de la salida final, ya que de lo contrario es un atributo desconocido, pero las implementaciones ToSql y FromSql se ejecutan por separado. Si lo hace de forma ingenua, simplemente generando la implementación y luego eliminando los atributos, la segunda derivación no tendrá las personalizaciones.

serde-derive y postgres-derive solucionan esto ahora mismo al tener la implementación para ambas implicaciones derivadas hacia la misma función que genera ambas a la vez. Tenemos que volver a adjuntar el #[derive] para el que se está invocando actualmente desde que el compilador lo elimina, y luego enviarlo para expandirlo: https://github.com/sfackler/rust-postgres-derive/blob /master/postgres-derive/src/lib.rs#L18

@sfackler Creo que también puede hacerlo eliminando los atributos adicionales solo cuando no quede ningún derivado del mismo implementador. Su camino podría ser mejor _ encogerse de hombros_.

@sfackler ah ok, tiene mucho sentido. ¡Gracias!

Sin embargo, me pregunto si esa lógica puede ser frágil. Por ejemplo, si está expandiendo ToSql ¿cómo se detecta la expansión futura de FromSql ? ¿Podría estar detrás de un #[cfg] como @dtolnay mencionado anteriormente, tal vez? ¿Entonces puede que no sea posible detectarlo en todos los casos?

@alexcrichton Sí, es frágil, por lo que una solución real parece importante.

¿No se procesarían #[cfg] y #[cfg_attr] antes de las derivaciones?

Dado que la API de macros 1.1 funciona en cadenas, solo puedo imaginar tres soluciones:

  1. déjelo como está y espere macros 2.0
  2. permitir atributos no utilizados en la derivación personalizada
  3. Extienda la API de macros 1.1 al permitir que una derivación personalizada envíe nombres de atributos a una lista blanca específica para el elemento actual

Cada opción tiene sus pros y sus contras.
estafa 1: soluciones quebradizas mientras tanto
con 2: algunos atributos no utilizados no serán detectados
con 3: más superficie api para una solución provisional

He considerado envolver atributos en #[used(...)] para mantenerlos y ponerlos en la lista blanca al mismo tiempo, pero es bastante tonto y demasiado estable.

@alexcrichton

cuando se está expandiendo un atributo #[derive] , podría intentar absorber todos los futuros

Me gusta esta idea. Dado que #[cfg] sy #[cfg_attr] s se procesan antes que #[derive] s, #[cfg] -guarded #[derive] s no son un problema para este enfoque (o para la solución análoga de @sfackler al problema de eliminación de atributos).

Este enfoque haría que la solución de @sfackler fuera un poco más simple, ya que otras sugerencia de @eddyb simplificaría las cosas).

Una posibilidad para el problema de los atributos de control es agregar devoluciones de llamada posteriores a la expansión, algo similar a las de syntex: https://github.com/sfackler/rust-postgres-derive/blob/master/postgres-derive-codegen/src/lib. rs # L23 -L50

Sus derivaciones implícitas pueden ser independientes y no eliminar los atributos de control, y podría registrar un pase que se ejecute después de que se complete toda la expansión para limpiar.

Diesel también está solucionando esto manualmente. https://github.com/diesel-rs/diesel/blob/master/diesel_codegen/src/lib.rs#L101 -L112

@dtolnay, su preocupación anterior sobre múltiples atributos #[derive] debería solucionarse en breve gracias a @jseyfried

Jugué con esto usando los paquetes syn y quote , y tuve una experiencia realmente positiva. Esta será una gran característica cuando se estabilice.

El problema con los atributos está afectando actualmente a mi impl, siendo posterior a la lógica de derivación de serde s.

Ignorar los atributos no utilizados en el código derivado, o permitir que las expansiones tardías que se ejecutan después de las derivadas regulares los eliminen, me parecen soluciones razonables.

Sería ideal si las macros no tuvieran la opción de mutar el flujo de tokens original, parece una característica peligrosa que podría terminar siendo utilizada en la naturaleza entre ahora y las macros 2.0 y, por lo tanto, difícil de eliminar más adelante.

Entonces, faltaba un punto y coma en una ruta de tipo dentro de una función genérica y recibí el siguiente mensaje de error de rustc. Me pregunto si se podría proporcionar más información, como qué macro de derivación causó el error lex, qué token lo causó, etc.

error: custom derive attribute panicked
  --> src/simple.rs:69:1
   |
69 | struct C(u64);
   | ^^^^^^^^^^^^^^
   |
   = help: message: Failure parsing derived impl: LexError { _inner: () }

Hice un caso de prueba para LexError, parece que sucedió con el 13 de octubre por la noche (rustup no tenía actualizaciones) https://github.com/keeperofdakeys/proc_macro_derive_test.

@ rust-lang / lang Creo que debemos discutir la cuestión de si las derivaciones pueden modificar su elemento anotado. Aunque es una solución simple para permitirlo, parece ir en contra de las expectativas del usuario y parece bastante impopular. Sigo prefiriendo la solución actual: la simplicidad es agradable y ninguna de las alternativas propuestas me parece tremendamente mejor.

Necesitaremos alguna solución para controlar los atributos si el artículo se puede modificar. No creo que me sienta particularmente convencido de mantener la capacidad de derivar para modificar el elemento, siempre que (en el futuro) las macros de atributos no derivados puedan hacerlo.

Alguien debería definir "modificar". ¿Estamos hablando de modificar los miembros de la estructura o simplemente estamos hablando de eliminar atributos para evitar que el compilador se queje de atributos desconocidos?

Si estamos hablando de modificar los miembros de la estructura, en mi opinión, la mejor solución probablemente sea permitir solo una macro de modificación de estructura en cada estructura que se expanda antes que todas las demás. Eso tendría un comportamiento bien definido y (en mi opinión) esperado.

Si solo estamos hablando de eliminar atributos, probablemente debería haber una forma de integrar atributos en el mecanismo de macros en lugar de dejarlo a discreción de la macro.

Mi intuición sobre cómo se comporta Derive es que agrega impls. Para el usuario final, las derivaciones deberían parecer agregar implicaciones y no hacer nada más. En particular, no deben modificar la estructura de una manera que sea importante para otros derivadores y, por lo tanto, deben ser independientes del orden. ¿Estamos de acuerdo en que así es como debe comportarse una derivada, o la gente cree que las derivadas también deben poder realizar transformaciones en el tipo adjunto?

Si estamos de acuerdo en eso, la pregunta es: ¿queremos que la interfaz que exponemos haga cumplir esto, o queremos dejarlo para que los autores deriven para hacer cumplir esto? Aquí, parece haber dos problemas compensatorios:

  • Hacer cumplir el contrato conductual de derivar ciertamente me parece más la solución de Rust.
  • Hacer que serde y deisel trabajen con esa restricción presenta muchos desafíos.

Y, por supuesto, cómo otros atributos pueden modificar la estructura parece no estar relacionado con cómo derivar, específicamente, debería comportarse.

Vale la pena señalar que la capacidad de eliminar el elemento anotado permite que el sistema actual cumpla una gran cantidad de funciones que de otro modo no haría.

@sgrif ¿Podrías explicar cómo deisel está usando derive? Siento que entiendo cómo serde usa su capacidad para modificar la estructura (para eliminar atributos que informan a la macro que omita o cambie el nombre de los campos en el rasgo de serialización), pero tal vez deisel está usando esto para hacer otra cosa. Cuando dice "eliminar el elemento anotado", parece que está realizando una transformación bastante radical en el elemento.

@withoutboats El 90% es más o menos lo que cabría esperar. No tocamos los elementos proporcionados por el usuario. Sin embargo, eliminamos elementos anotados para piratear macros de bang en el sistema. https://github.com/diesel-rs/diesel/blob/master/diesel/src/macros/macros_from_codegen.rs#L12 -L18. Más allá de eso, la única vez que tocamos algo en el flujo del token de entrada es para eliminar las anotaciones. https://github.com/diesel-rs/diesel/blob/master/diesel_codegen/src/lib.rs#L109 -L120

@sgrif También he abusado de la

Sin embargo, puedo entender el argumento para mantener derivar como una simple macro.

Mi perspectiva es que el objetivo de Macros 1.1 es ser lo más flexible posible para cubrir tantas necesidades como sea posible, con una carga de mantenimiento baja para que pueda estabilizarse rápidamente y actuar como un recurso provisional hasta las macros 2.0. En mi opinión, el diseño actual encaja muy bien en ese papel.

Si estuviéramos hablando de algo destinado a ocupar permanentemente este papel, me opondría mucho más.

Tal vez me equivoque, pero mi lectura del RFC es que se pretende que sea la base del comportamiento de derivar de forma permanente. Es decir, se agregarán más métodos a TokenStream en el futuro, pero las macros de derivación utilizarán esta API, que actualmente les permite realizar mutaciones arbitrarias en el elemento anotado (y esta capacidad es necesaria para el caso de uso de deisel ).

Me siento bastante negativo sobre permitir que los derivados hagan esto de forma permanente. Si aceptamos que este es un sistema que va a quedar obsoleto, junto con las macros macro_rules , en algún momento en el futuro, y bajo las macros 2.0, se preferirá una API de derivación diferente que es más restringida, estoy más cómodo con él.

Parece que la intención es apoyar a los decoradores como transformadores que pueden hacer cualquier cosa.
Y derivar expuesto como un simple decorador sin entrada en el atributo.

@withoutboats : serde admite una serie de atributos para modificar la forma en que se generan las impls, por lo que definitivamente necesitamos la capacidad de eliminarlas o ignorarlas después de haberlas procesado. Si pudiera ayudar, de alguna manera podríamos proporcionar al compilador una lista de atributos que deberían ser eliminados, en lugar de que nosotros quisiéramos hacerlo nosotros mismos.

@eddyb Estoy a favor de que los decorados puedan hacer cualquier cosa, pero no de que los derivados sean decoradores desenfrenados (a largo plazo).

@erickt Correcto. Creo que a largo plazo, la solución ideal sería que esos atributos se registren como atributos personalizados no operativos, en lugar de que el derivador sea responsable de eliminarlos. Pero eso no es factible a corto plazo.

Creo que a largo plazo, la solución ideal sería que esos atributos se registren como atributos personalizados no operativos, en lugar de que el derivador sea responsable de eliminarlos. Pero eso no es factible a corto plazo.

No estoy familiarizado con los componentes internos del compilador que hacen que esto sea inviable a corto plazo, pero ¿sería posible que el complemento de derivación personalizada presentara una lista de atributos personalizados que pretende eliminar y luego rechazar cualquier otra transformación en los atributos en el artículo original?

También noté que Diesel no sigue el enfoque de Serde de tener todos sus atributos personalizados bajo un nombre (por ejemplo, #[serde(rename = "name")] en lugar de #[table_name = "name"] Diesel). ¿Simplificaría la implementación si solo un nombre de atributo personalizado? se registró en lugar de una lista?

Una posibilidad para el problema de los atributos de control es agregar devoluciones de llamada posteriores a la expansión, algo similar a las de syntex: https://github.com/sfackler/rust-postgres-derive/blob/master/postgres-derive-codegen/src/lib. rs # L23 -L50

Sus derivaciones implícitas pueden ser independientes y no eliminar los atributos de control, y podría registrar un pase que se ejecute después de que se complete toda la expansión para limpiar.

Implementé devoluciones de llamada posteriores a la expansión para Macros 1.1 en post-expansion . Sus derivaciones implícitas pueden ser independientes y no eliminar los atributos de control, y puede registrar un pase que se ejecuta después de que se haya realizado toda la expansión para eliminar los atributos.

Hoy discutimos el problema de modificación / pedido en la reunión del equipo de lang. Había un deseo de cumplir con las expectativas de los usuarios y de simplicidad (en términos de diseño, no tener demasiados trucos en capas en la parte superior y no estar demasiado sujeto a errores difíciles de descifrar / depurar). Se señaló que, aunque la modificación de los datos objetivo es sorprendente, no es insegura. También se consideró que _podríamos_ tender hacia la pureza por sí misma, más que por razones bien motivadas.

Al final, decidimos que las derivadas que no modifican la fuente son probablemente mejores y deberíamos cambiar a ese modelo. Eso podría significar prolongar el período de FCP. No pensamos que debería haber un mecanismo especial para tratar los atributos de eliminación. Más bien, que la forma en que el compilador maneja los atributos debería permitir que los utilizados por una macro permanezcan en el programa. RFC 1755 debería tener esto en cuenta.

Esto retrasará la estabilización de algunos usuarios derivados personalizados. Sin embargo, creemos que la mayoría de los usos de derivados (especialmente aquellos que mantienen a los usuarios fuera de una cadena de herramientas estable) no usan atributos, por lo que, por ejemplo, la mayoría de los usuarios de Serde podrán pasar a estable pronto. Aquellos que necesitan atributos, tomarán algunos ciclos más _pero eso no afectará el caso común_.

cc @alexcrichton , @dtolnay , @sgrif , @erickt - ¿pensamientos?

Los atributos probablemente se usan más comúnmente con Serde y Diesel de lo que sugiere. Solo por mi propia experiencia, estoy bastante seguro de que todos mis programas que usan Serde usan atributos. Con Diesel definitivamente uso atributos, y creo que es necesario para decirle a diesel_codegen qué tabla de base de datos se asigna a la estructura.

Dicho esto, no dejar que la derivación personalizada mute la estructura me parece la elección correcta. Simplemente simplifica todo, evitando muchos casos extremos extraños. Es más importante hacerlo bien que hacerlo rápidamente, por lo que si la función tiene que permanecer inestable un poco más, también parece estar bien.

Se señaló que, aunque la modificación de los datos objetivo es sorprendente, no es insegura.

No es inseguro, a menos que esté derivando un rasgo inseguro.

En mi opinión, uno de los casos de uso de las derivadas personalizadas es implementar de manera segura rasgos marcados como inseguros, como por ejemplo un rasgo que debe describir exactamente el diseño de los miembros de la estructura en la que se implementa.

Mi caja asn1 también requiere atributos para cualquier uso que no sea trivial, por lo que tendría que esperar hasta que aparezcan los atributos personalizados.

¿Sería una buena idea dividir los atributos personalizados rfc en dos?

  1. Un rfc para proporcionar la notación y el espacio de nombres para los atributos personalizados en general (permitiendo atributos no operativos en estable).
  2. Un rfc sobre cómo definir y la semántica en torno a la ejecución de macros de atributos personalizados.

Las macros de atributos personalizados parecen algo que requerirá mucho si se desarrolla. Por lo tanto, dividir el rfc en dos puede proporcionar atributos estables para paquetes de derivación personalizados antes.

Esto también significa que los atributos están separados por nombres y son intencionales (es decir, se requiere una caja externa para usar atributos para una caja). Puedo prever que esto es bueno para evitar que dos macros de derivación personalizadas usen el mismo nombre de atributo. Una buena expectativa aquí sería usar el nombre de la caja en la que se encuentra el rasgo para los atributos.

¿Hay alguna razón por la que esta implementación no incluya las macros habituales name! ? Simple TokenStream in, TokenStream out para macros habituales resultaría extremadamente útil en plagas donde los tiempos de compilación para gramáticas complejas superan los 30 segundos.

@dragostis Para citar el resumen de rfc:

Extraiga una pequeña porción del sistema de macros de procedimiento actual en el compilador, lo suficiente para obtener funciones básicas como el funcionamiento de derivaciones personalizadas, para tener una API finalmente estable. Asegúrese de que estas características no supongan una carga de mantenimiento para el compilador, pero tampoco intente proporcionar suficientes características para el "sistema de macros perfecto" al mismo tiempo. En general, esto debe considerarse un paso incremental hacia una "macros 2.0" oficial.

O en términos más prácticos, se necesitará mucho diseño y pruebas para obtener un sistema macro de procedimientos que funcione bien, al igual que el sistema de cierre que ahora hemos tenido que construir. Las cajas como serde y diesel tienen serios problemas de usabilidad sin una función de derivación personalizada adecuada, así que hagamos una medida provisional para solucionarlo ahora; eso también ayuda a las herramientas y la experiencia con una posible macrosistema procedimental. Las cajas syn y quote son buenos ejemplos de esto.

@keeperofdakeys Sí, lo tengo. No estoy solicitando una implementación de macros 2.0, solo me pregunto si hay alguna carga involucrada para agregar otro atributo, digamos proc_macro que es una implementación mínima que simplemente refleja el diseño derivado solo para macros habituales. El ejemplo en pest simplemente derivaría un analizador para un struct solo que no lo derivaría del struct sí mismo sino de una gramática simple definida entre {} . ¡Sin embargo, espero no desenterrar discusiones muertas!

@nrc

También se consideró que podríamos estar tendiendo a la pureza por sí misma, más que por razones bien motivadas.

Una nota sobre esto: en muchos casos en los que hemos tenido que tomar decisiones difíciles, como la parametricidad y la especialización, asegurar de forma estática la noción relevante de "pureza" sería difícil / imposible. Pero en este caso, en realidad es muy sencillo garantizarlo mediante la construcción, y eliminar la importancia del orden para las derivadas parece una simplificación bastante fuerte para los usuarios.

En lo que respecta a la estabilidad, podríamos considerar agregar un mecanismo inestable para ignorar atributos en este momento, que sería estable en la práctica (en el sentido de que la API no sería propensa a romperse) incluso si aún requiere su uso nocturno. Incluso podríamos considerar estabilizar dicho canal lateral, con planes para desaprobarlo en favor de un mecanismo más general una vez que esté disponible. O podríamos considerar la sugerencia de @keeperofdakeys y avanzar rápidamente en la parte de la solución de atributos generales que abordaría la preocupación inmediata.

Desde mi perspectiva, es importante que ninguna de estas preocupaciones impida que las macros 1.1 sean ampliamente utilizables para Serde y al menos "de facto" estables (es decir, no se rompan).

@sgrif

Si estuviéramos hablando de algo destinado a ocupar permanentemente este papel, me opondría mucho más.

Quería hacerme eco de

Mi impresión del uso de atributos en la derivación personalizada fue similar a la impresión de @jimmycuadra , que es que se usan con bastante frecuencia. Creo (pero corrígeme si me equivoco) que los atributos personalizados fueron cruciales para varios casos de uso de diesel.

En ese sentido, no estoy 100% seguro de cuál sería la mejor manera de avanzar con respecto a esos atributos. Sería una lástima estabilizar las macros 1.1 pero dejar a un gran número de usuarios encendidas todas las noches (incluso si es "estable de facto" todas las noches) porque eso frustra un poco el propósito de la estabilización rápida de las macros 1.1 en primer lugar. En otras palabras, si no estamos llevando a la mayoría de los usuarios de macros 1.1 _en realidad_ a Rust estable, creo que la estabilización de macros 1.1 puede esperar.

Sin embargo, un punto que me ha estado molestando es cómo creemos que se verá el atributo personalizado a largo plazo y lo racionalizamos con el sistema actual de macros 1.1. En el RFC actual para atributos personalizados, las cajas tienen que declarar espacios de nombres de atributos en la lista blanca en la parte superior de una caja. Esto significa que usar un atributo personalizado en la derivación personalizada es radicalmente diferente a usar un atributo personalizado en otro lugar. Esa desconexión me preocupa en términos de una perspectiva ergonómica y de "mínima sorpresa", aunque también lo veo como una función forzada para ajustar el RFC (por ejemplo, si me vinculo a la caja serde tal vez eso pueda incluir automáticamente los atributos de serde en la lista blanca ).

En general, personalmente estaría bien avanzando con la estabilización completa del sistema de macros 1.1 tal como está hoy. O en otras palabras, estabilice el hecho de que las funciones de expansión derivadas personalizadas reciben una estructura y luego deben devolver la estructura también si quieren que se conserve.

Tiendo a cambiar de rustc-serialize a serde _porque_ la generación de código de serde admite atributos de control.

Podríamos extender macros 1.1 para admitir argumentos para el atributo derivar en sí. Eso parece algo bueno en general, y podría abusarse de él para evitar la falta de atributos de control a corto plazo.

Lo mismo ocurre con @jimmycuadra y @sfackler , los atributos son más integrales para Serde de lo que el comentario de

Todavía no tengo una opinión sobre el movimiento correcto aquí, pero sé que si nos estabilizamos sin atributos, consideraría seriamente analizar los comentarios del documento para extraer los atributos. Me imagino que muchas personas no querrían lidiar con un script de compilación solo para poder escribir:

#[serde(skip_serializing)]

.. en vez de:

/// <!-- serde(skip_serializing) -->

@tomaka

En mi opinión, uno de los casos de uso de las derivadas personalizadas es implementar de manera segura rasgos marcados como inseguros, como por ejemplo un rasgo que debe describir exactamente el diseño de los miembros de la estructura en la que se implementa.

¿Qué opinas de mi comentario aquí al respecto? Encontré que era un patrón satisfactorio.

Quiero aclarar una cosa:

¿Eliminar atributos personalizados es lo más importante que hace la gente con la derivación personalizada?

Sé que hay algunos trucos para hacer foo! expansión en el contexto de un tipo (por ejemplo, @sgrif mencionó un caso de uso de este tipo en diesel, creo que @tomaka también mencionó uno): ¿qué tan centrales son esos casos de uso? ?

La razón por la que pregunto es la siguiente: veo los beneficios de que el mecanismo de derivación solo devuelva una lista de implicaciones adicionales. En particular, significa que los intervalos para la declaración de tipo en sí serían correctos. Agregar una API rápida y sucia al contexto que le permite proporcionar una lista de nombres de atributos para incluir en la lista blanca en el contexto de ese tipo parece una solución bastante fácil para los atributos, y siempre podríamos desaprobarla.

Sin embargo, si queremos habilitar más casos de uso (y, francamente, esos casos de uso son un poco más ... sorprendentes, cuando se piensa en lo que uno espera de derivar), entonces esto no funcionará. En ese caso, probablemente estaría bien con estabilizador como es y la planificación para despreciar la API "de bytes sin formato" en el futuro a favor de una mejor manera para derivar de escritura (después de todo, realmente no esperar que la gente sea usando bytes sin procesar de todos modos, ¿verdad? ¿Pero más bien transmisiones de tokens?) Quizás podríamos darle a la API un nombre un poco más torpe =)

@nikomatsakis La principal necesidad no es eliminar, sino ignorar los atributos. No soy consciente de los inconvenientes graves de ignorarlos. Proporcionar una lista blanca de la función de derivación a través de un parámetro adicional al atributo principal, por ejemplo, debería ser suficiente para todas las necesidades prácticas que son verdaderas derivaciones y no hacks de macro de procedimiento.

Sí, estoy dividido entre dos enfoques sobre esto, los cuales tienen claras desventajas:

  1. El enfoque de "derivaciones simples": ajusta la API para que solo produzca elementos adicionales, lo que hace aún inestable que los usuarios usen atributos personalizados como parte de la derivación y cierran el agujero por el que están saltando las cajas de diésel y otras para obtener macros de procedimiento arbitrarias.
  2. El enfoque de "obsolescencia programada": estabilizar la API tal como está, con pequeños ajustes posiblemente como Niko mencionó sobre la denominación, con la intención de desaprobarla algún día. Esto requerirá que todos los autores de derivaciones personalizadas algún día reescriban su código y permitan la posibilidad de un comportamiento sorprendente en el período intermedio.

EDITAR: pero la lista blanca de @eddyb también suena prometedora.

Quiero decir que @nrc no está proponiendo algo muy diferente (aunque IIRC está hablando de la lista blanca en la caja del usuario), y se vuelve tonto hablar de "marcar atributos como usados" cuando todo lo que tienes es un flujo de tokens.

Las macros de procedimiento arbitrarias para las que he abusado del sistema de macros 1.1 en rust-cpp serían totalmente posibles con un enfoque alternativo que simplemente brinda la capacidad de agregar impls e ignorar atributos.

Creo que tener la capacidad de ignorar atributos es esencial, pero aparte de eso, estaría bien si no pudiera modificar la estructura más allá de ese punto.

No estoy seguro de qué formas de hacks, que @sgrif está utilizando en deisel que no serían _possible_ que hacer en un mundo donde no podemos modificar la estructura en sí, y en su lugar sólo se pueden añadir elementos adicionales.

Permitir que las derivadas personalizadas tomen argumentos, como sugiere @sfackler , podría ser una forma de que todos obtengan la funcionalidad que necesitan y, al mismo tiempo, permitir que se difiera cualquier decisión sobre atributos personalizados. No será tan bonito ni tan legible, pero hará el trabajo. p.ej:

#[derive(Debug, Clone, Serialize(field("bar", rename = "baz")))]
pub struct Foo {
  pub bar: String,
}

Este formulario podría quedar obsoleto en favor de los atributos personalizados, una vez que se tome una decisión sobre ellos.

Tanto serde como mi caja requieren atributos de campo / variante. Tratar de emular esto con argumentos derivados no tiene mucho sentido, necesitamos atributos.

Cualquiera que sea la decisión que tomemos para estabilizar esto, sería bueno si el usuario de macros de derivación personalizadas no necesitara modificar su código cuando / si cambiamos a una API de derivación de macros 2.0 (obviamente, los autores de macros de derivación personalizadas lo harán). Parece que la decisión más sensata es darle al compilador una lista de atributos para eliminar de su derivación y, fundamentalmente, esos solo se eliminan después de que se hayan ejecutado todas las macros de derivación. Serde, diesel y mi caja tienen el problema de requerir el mismo atributo en múltiples macros de derivación.

Con el comportamiento de eliminación, no necesitaría la caja posterior a la expansión que @dtolnay hizo para agregar otra macro de

FWIW, la única razón para eliminar esos atributos es mantener el HIR más delgado; en lo que respecta a todo lo demás, solo necesita marcarlos como usados.

¿Eliminar atributos personalizados es lo principal que hace la gente con la derivación personalizada?

¡Sé que hay algunos trucos para hacer foo! expansión en el contexto de un tipo (por ejemplo, @sgrif mencionó un caso de uso de este tipo en diesel, creo que @tomaka mencionó uno también): ¿qué tan centrales son esos casos de uso?

Estoy totalmente de acuerdo con el hecho de que las derivaciones personalizadas no pueden modificar la estructura.

A menudo me quejo por la falta de complementos en Rust estable, así que cuando vi derivaciones personalizadas, las usé como una oportunidad para tener complementos. Pero obviamente no voy a argumentar que las derivaciones personalizadas deban admitir algo para lo que no fueron diseñadas.

Parece que hay una regresión en la última noche. Obteniendo el error

Queryable es un modo de derivación

al compilar nuestros ejemplos.

#[derive(Queryable)]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub body: String,
    pub published: bool,
}

@sgrif Eso fue causado por # 37198, que cambió las derivaciones personalizadas para usar el mismo espacio de nombres que otras macros (de modo que bang!() macros, #[attribute] macros y #[derive(custom)] macros compartan el mismo espacio de nombres).

En ese ejemplo, #[macro_use] extern crate diesel; importa una macro de explosión llamada Queryable y #[macro_use] extern crate diesel_codegen; importa una macro de derivación personalizada también llamada Queryable , que sobrescribe silenciosamente la macro de explosión (aparte - #[macro_use] sobrescribir silenciosamente no es ideal, pero no será un problema una vez que podamos importar macros desde cajas externas con use lugar de #[macro_use] , ¡pronto!).

Creo que el error se debe a una invocación de macro bang Queryable!(); en la expansión de la derivación personalizada, que se resuelve en la derivación personalizada de diesel_codegen lugar de la macro bang de diesel .

@jseyfried ¿Cuál es la razón para usar un solo espacio de nombres para los tres "tipos" de macros? No me parece que tenga sentido tratar los atributos y las macros bang como si ocuparan el mismo espacio de nombres, más de lo que tendría sentido tratar las funciones y los tipos como si ocupasen el mismo espacio de nombres (derivar macros aún menos).

@withoutboats Creo que esa es la analogía incorrecta, prefiero pensar que diferentes tipos de macros comparten un espacio de nombres de la misma manera que lo hacen los diferentes tipos de valores o tipos (por ejemplo, funciones y constantes). Hay un costo de complejidad en tener espacios de nombres y creo que cuantos menos tengamos, mejor, así que la pregunta debería ser ¿por qué necesitamos diferentes espacios de nombres? Claramente, para la compatibilidad con versiones anteriores, necesitamos que las macros estén en un espacio de nombres diferente al de las funciones y los tipos.

El problema real es que no puede usar use para importar o renombrar macros de forma selectiva. Por lo tanto, un usuario de cajas no tiene la capacidad de sortear los conflictos de nombres de macros. Al mismo tiempo, no esperaría que la simple importación de una caja de ProcMacro chocara con los nombres de macro locales; pensé que las macros de derivación comenzaban con derive_ ?

Hay un costo de complejidad en tener espacios de nombres y creo que cuantos menos tengamos, mejor, así que la pregunta debería ser ¿por qué necesitamos diferentes espacios de nombres?

Creo que los elementos deben compartir un espacio de nombres solo si pueden ser ambiguos entre sí. consts y fns están en el mismo espacio de nombres porque ambos se usan como idents en un contexto de expresión. Los tipos y rasgos están en el mismo espacio de nombres debido a la sintaxis de los objetos de rasgos.

Estos tres tipos de macros se invocan de distintas formas. Para mí no tiene sentido que deban compartir un espacio de nombres, porque nunca puede haber ambigüedad en la invocación.

Si bien hay un costo de complejidad de implementación, no creo que haya un costo significativo en la complejidad de _uso_ al espaciarlos por separado. Cuando pienso en el costo de complejidad de una característica del lenguaje, generalmente pienso en la complejidad de uso.


¿Diría que _todos_ los elementos deberían estar en el mismo espacio de nombres si no fuera un cambio importante? Tratando de aclarar tu proceso de pensamiento.

Reflexionando un poco más, estaría bien si dijera que solo debería haber 1 espacio de nombres en el que estén todos los elementos; Todo el patrón de "constructores reales" es un poco confuso en mi opinión, pero esa no es la decisión que tomó Rust para los elementos que no son macro, por lo que me parece que sería más consistente y esperado que los elementos macro ocupen diferentes espacios de nombres.

El problema real es que no puede usar el uso para importar o cambiar el nombre de macros de forma selectiva.

Podrás hacerlo con macros 2.0. El modelo aquí es básicamente un truco temporal.

Pensé que las macros de derivación comenzaban con derive_?

Ese era el antiguo sistema de derivación personalizado, no creo que las macros 1.1 hagan esto.

Si bien existe un costo de complejidad de implementación, no creo que haya un costo significativo en la complejidad de uso

Existe un costo para alguna definición de uso: hace la vida más difícil para las herramientas, en particular (aunque se podría argumentar que no mucho). También creo que no es tanto la complejidad de la implementación lo que importa (aunque los espacios de nombres definitivamente complican el compilador, estoy de acuerdo en que esto no es tan importante) como la complejidad del lenguaje: los usuarios tienen que razonar sobre estas cosas para usar Rust y tiene una repercusión efecto a cosas como la higiene y la sombra, complicando aún más las cosas.

¿Diría que todos los elementos deberían estar en el mismo espacio de nombres si no fuera un cambio importante? Tratando de aclarar tu proceso de pensamiento.

No iría tan lejos; creo que hay ventajas en que los valores y los tipos estén separados, pero ciertamente sería mucho más agradable trabajar con un lenguaje con un solo espacio de nombres de muchas maneras.

pero esa no es la decisión que tomó Rust para los elementos que no son macro

Contrapunto: los módulos y los campos están en el espacio de nombres de valor, aunque los lugares en los que se pueden usar son (creo) distintos de los lugares en los que se pueden usar otros valores.

@caballero

El problema real es que no puede usar use para importar o renombrar macros de forma selectiva

Pronto podremos use macros de extern crate s detrás de una puerta de función (~ 1 semana), cf https://github.com/rust-lang/rfcs/pull/1561 y # 35896. Podríamos decidir estabilizar las macros use ing de las cajas de derivaciones personalizadas junto con las propias derivaciones personalizadas.

Pensé que las macros de derivación comenzaban con derive_

Esto fue cierto para los derivados personalizados de estilo antiguo. Con las derivaciones personalizadas de las macros 1.1, #[derive(Serialize)] (por ejemplo) espera que Serialize resuelva en una macro de derivación personalizada.

@sin barcos

¿Cuál es la razón para utilizar un solo espacio de nombres para los tres "tipos" de macros?

  • Prioridad: las macros de bang y las macros de atributos han compartido un espacio de nombres durante mucho tiempo, por lo que hasta que eso cambie, creo que las derivaciones personalizadas también deberían compartir ese espacio de nombres.
  • Compatibilidad con versiones anteriores: si comenzamos con un solo espacio de nombres de macro, podemos dividirlo de manera compatible con versiones anteriores. Si comenzamos con múltiples espacios de nombres de macro, nos quedamos con ellos para siempre.
  • Simplicidad: si cada "tipo" de macro tuviera su propio espacio de nombres, tendríamos cinco espacios de nombres en total. Por lo tanto, después de https://github.com/rust-lang/rfcs/pull/1561 aterriza, use foo::bar; podría importar _cinco elementos separados_ llamados bar (un valor, un tipo, una macro bang , etc.), y no habría una forma sencilla de, por ejemplo, reexportar la macro bang pero no la macro de derivación personalizada o simplemente importar la macro de derivación personalizada como baz .

Compatibilidad con versiones anteriores: si comenzamos con un solo espacio de nombres de macro, podemos dividirlo de manera compatible con versiones anteriores. Si comenzamos con múltiples espacios de nombres de macro, nos quedamos con ellos para siempre.

Esto es convincente, especialmente porque se supone que 1.1 es una solución provisional. : +1:

¿Este código de ruptura para cualquiera que tuviera una macro macro_rules llamada, por ejemplo, PartialEq! ?

No, PartialEq no está definido en el espacio de nombres de macros hoy en día, ya que no es una derivación personalizada.
#[derive(Foo)] primero comprueba si Foo es un "derivado incorporado" ( PartialEq , Copy , etc.) antes de buscar un derivado personalizado Foo en el espacio de nombres de la macro. Las incorporaciones están codificadas en la definición de derive .

Dicho esto, podríamos descifrar el código como lo describió si finalmente decidimos hacer de PartialEq una derivación personalizada en el preludio en lugar de una incorporada, por lo que podría ser una buena idea hacer una prueba futura de eso ahora.

Propuesta para el problema de los atributos (asumiendo que trabajamos en un modelo donde las macros derivadas no pueden modificar los elementos en los que están declarados, solo decorar):

  • Entre bastidores, las macros de derivación personalizadas implementan un rasgo, actualmente MultiItemModifier . El plan es que las macros 2.0 continúen implementando un rasgo y este rasgo se use para la extensibilidad del mecanismo. La función anotada que es la macro implementa este rasgo. Aunque no usamos el registrador de complementos, esto es esencialmente un desasosiego.
  • deberíamos dividir un rasgo CustomDerive específicamente para la derivación personalizada de macros 1.1. En el caso común, los autores de macros nunca lo ven. Pero tienen la opción de implementar el rasgo directamente, en lugar de usar un atributo en una función (espero que volvamos a usar el atributo en el impl, tal vez necesitemos discutir este mecanismo de registro).
  • agregamos una función declare_attributes a CustomDerive que devuelve Vec<String> . Tiene un impl predeterminado que devuelve un vec vacío.
  • Si los autores de macros implementan este método, entonces cualquier atributo nombrado exactamente como una de las cadenas devueltas se considera que pertenece a la macro. Cualquiera de estos atributos nunca se busca como una macro y no activa el atributo personalizado lint. Los atributos se dejan en el código mediante la expansión de derivación, pero se marcan como usados. Cualquier atributo de este tipo que no se vea afectado por una expansión de derivación aún activaría el atributo lint no utilizado (pero no el atributo personalizado lint).
  • En el futuro, podríamos introducir atributos de ámbito que las macros, incluidas las derivadas personalizadas, pueden usar, estos serían _ además_ de declare_attributes y este mecanismo no quedaría obsoleto.
  • Alternativa: las cadenas devueltas por declare_attributes se verifican como un prefijo de ruta para los atributos, por ejemplo, si declare_attributes devolvió vec!["foo::bar"] , entonces #[foo::bar::baz] y #[foo::bar::qux] Se permitiría

Pensamientos cc @alexcrichton @jseyfried @dtolnay @sgrif @erickt @ rust-lang / lang

@nrc ¿ #[cfg(..)] , ya sea por accidente o intencionalmente? ¿Y eso podría cambiarse?

@nrc, desafortunadamente, no puedo ver cómo se vería la implementación dadas esas restricciones, por lo que no estoy completamente seguro de si funcionaría o no.

Dicho esto, soy un poco escéptico sobre si los rasgos y los objetos de rasgo funcionarían, porque ¿dónde están las instancias del rasgo que se está creando?

@nrc ¿Por qué esto tiene que ser imperativo y no puede ser simplemente una lista en el atributo que designa la función de expansión derivada? Por ejemplo, #[proc_macro_derive(Serialize, uses_attrs(serde_foo, serde_bar))] o algo así.

@ colin-kiegel Me imagino que solo podría aplicarse en el alcance de la aplicación de derivación, en cuyo caso deshabilitar otros atributos probablemente sea el comportamiento esperado, aunque creo que podríamos aplicar cfgs antes de la expansión de macro (esto ha cambiado, pero no puedo recordar el antes vs el después).

@alexcrichton

Dicho esto, soy un poco escéptico sobre si los rasgos y los objetos de rasgo funcionarían, porque ¿dónde están las instancias del rasgo que se está creando?

Hmm, buen punto, supongo que tendremos que abordar esto para macros 2.0

@eddyb Esta es una buena idea y probablemente más fácil de hacer a corto plazo. Mi razón para preferir el enfoque imperativo es que es un mecanismo de extensibilidad general y uno que queremos mantener, mientras que agregar cosas al atributo no se escala demasiado bien y puede que no sea algo con lo que queramos estar atrapados para siempre. . Por otro lado, ciertamente es más fácil de hacer ahora y no parece nada malo tenerlo rondando, así que creo que podría ser una mejor apuesta.

Ponerse al día, he estado fuera del país y luego enfermo. Me gustaría volver a visitar https://github.com/rust-lang/rust/pull/37198 , ya que no creo que tenga sentido que todo tipo de macros ocupe el mismo espacio de nombres. Por lo menos esperaría que las macros de derivación personalizadas estén en ese espacio de nombres como derive_Foo , o similar. Hay casos de uso para varios tipos de macros que usan el mismo nombre incluso en la biblioteca estándar (por ejemplo, #[cfg] y cfg! )

También encuentro un poco incómodo el espacio de nombres compartido. Desde la perspectiva del usuario final, podría parecer como si hubiera algún tipo de intercambiabilidad, ambigüedad o algo interesante, que no es así. Creo que las limitaciones 'aleatorias' como esta podrían dificultar un poco la comprensión de Rust como idioma ...

Sin embargo, creo que probablemente no sea el fin del mundo. Parece que aún se podrían introducir diferentes espacios de nombres en el futuro sin romper demasiado.

@sgrif @ colin-kiegel Describí mi justificación para un solo espacio de nombres macro en https://github.com/rust-lang/rust/issues/35900#issuecomment -256247659.

No creo que tenga sentido que todo tipo de macros ocupen el mismo espacio de nombres

Para ser claros, las macros bang y las macros de atributos siempre han ocupado el mismo espacio de nombres; # 37198 acaba de mover derivaciones personalizadas a este espacio de nombres.

Hay casos de uso para varios tipos de macros que usan el mismo nombre incluso en la biblioteca estándar (por ejemplo, #[cfg] y cfg! )

Alternativamente, cfg podría verse como una sola macro que se puede usar mediante una invocación de bang o una invocación de atributo (aunque hoy en día no es posible que los usuarios definan una macro que se pueda invocar mediante un bang o un atributo, podríamos decidir permitirlo en macros 2.0).

Desde la perspectiva del usuario final, podría parecer como si hubiera algún tipo de intercambiabilidad

Creo que esto podría mejorarse con mensajes de error claros cuando dos macros entran en conflicto (trabajaré para mejorar los mensajes de hoy).

Parece que aún se podrían introducir diferentes espacios de nombres en el futuro sin romper demasiado

Creo que dividir el espacio de nombres de macros es totalmente compatible con versiones anteriores (corrígeme si me equivoco). Esta es mi principal motivación para mantener un solo espacio de nombres de macro hoy: queremos que las macros 1.1 sean lo más compatibles con el futuro.

Finalmente, creo que los conflictos de macro de bang / atributo vs macro de derivación personalizada serán raros en la práctica, ya que las macros de bang / atributo suelen ser minúsculas y las derivadas personalizadas suelen ser mayúsculas. En otras palabras, las derivaciones personalizadas ya tienen su propio espacio de nombres cuando se siguen esas convenciones de nombres.

Parece que la rotura (https://github.com/diesel-rs/diesel/issues/485) se debe al soporte, por ejemplo, Queryable! { struct S; } y #[derive(Queryable)] struct S; . Una vez que las derivaciones personalizadas sean estables, no será necesario admitir Queryable! { struct S; } por lo que esto ya no será un problema, ¿verdad?

Mientras tanto, creo que podríamos actualizar diesel para que

  • Queryable! todavía se admite sin #[macro_use] extern crate diesel_codegen; , y
  • #[derive(Queryable)] , pero no Queryable! , es compatible con #[macro_use] extern crate diesel_codegen; .

Siéntase libre de enviarme un ping en IRC para discutir: estaría feliz de escribir un PR.

@nrc @alexcrichton

Dicho esto, soy un poco escéptico sobre si los rasgos y los objetos de rasgo funcionarían, porque ¿dónde están las instancias del rasgo que se está creando?

Estoy de acuerdo con @eddyb en que si todo lo que queremos es manejar atributos, en aras de la conveniencia y la conveniencia, deberíamos simplemente extender los atributos.

Pero si _did_ queríamos un sistema de extensión más flexible, entonces había imaginado que usaríamos rasgos, pero esos rasgos se definirían como una colección de métodos que no se refieren a Self :

trait CustomDerive {
     fn foo(); // note: no self
     fn bar(); // again, no self
}

Luego lo implementaría en un tipo ficticio como struct my_annotation_type; (que comparte el mismo nombre que el atributo, ¿supongo?), Y usaríamos la resolución de rasgos para extraer las funciones relevantes como <my_annotation as CustomDerive>::foo (probablemente al escribir los metadatos, supongo). El punto es que nunca haríamos (o necesitaríamos) una instancia de my_annotation , es solo un mecanismo de agrupación para agrupar un montón de funciones relacionadas.

Ciertamente, no es lo más elegante que existe, pero no estoy seguro de una mejor manera. Creo que la falta de elegancia es precisamente la razón por la que queríamos comenzar con funciones atribuidas. =)

Con respecto a los espacios de nombres, @sgrif hace un buen caso con los ejemplos #[cfg] y cfg! . Ciertamente puedo imaginarme a uno que quiera #[derive(SomeTrait)] y también tener una macro como SomeTrait! { ... } que ... hace algo. =) Pero @jseyfried también hace un buen caso con la compatibilidad con versiones anteriores, siempre que no estemos alcanzando limitaciones _ya_.

Tiendo a preferir menos espacios de nombres de forma predeterminada, principalmente debido al dolor que creo que nos trae ahora tener una división de espacio de nombres de valor / tipo. Dicho esto, creo que la mayoría de los puntos débiles conocidos no se aplican aquí:

  • la división tipo / valor es un problema en la resolución de nombres porque use foo::bar podría o no importar un módulo llamado bar y, por lo tanto, podría (o no) ser relevante para una resolución de nombres como bar::baz ;
  • la división tipo / valor es una especie de molestia para los genéricos sobre las constantes, aunque esa dificultad también se debe a la división sintáctica entre tipos y valores.

Pero en gran medida, desde que hemos cruzado el puente, no estoy seguro de que tener una "derivación personalizada" en vivo en su propio espacio de nombres suponga un desafío en particular, ¿verdad?

Podríamos agregar el prefijo derive_ a las macros generadas, para hacerlas pseudo-espacio de nombres. Si quisiéramos hacer ese cambio, ahora sería el momento.

@keeperofdakeys ¿No sería esto equivalente a que los autores de derivaciones personalizadas derive_* ? Independientemente, creo que los nombres de derive_* son demasiado feos para los usuarios finales.

@nikomatsakis

Pero en gran medida, desde que hemos cruzado el puente, no estoy seguro de que tener una "derivación personalizada" en vivo en su propio espacio de nombres suponga un desafío en particular, ¿verdad?

El algoritmo de resolución de importación puede manejar arbitrariamente muchos espacios de nombres, pero realiza una cantidad de trabajo potencialmente no trivial para cada espacio de nombres. En particular, para cada importación I y cada espacio S nombres no utilizado I falla en S (cf https: // github. com / rust-lang / rfcs / pull / 1560 # issuecomment-209119266). Esta prueba a menudo requiere un DFS del gráfico de importación global (en el que los vértices son módulos y los bordes son importaciones globales) para buscar importaciones indeterminadas relevantes.

Sin embargo, este trabajo adicional por espacio de nombres podría no marcar la diferencia en la práctica y puede evitarse si es necesario (consulte https://github.com/rust-lang/rfcs/pull/1560#issuecomment-209119266) al costo de restricción menor que solo se aplicaría a los espacios de nombres de macros.

Fusioné los espacios de nombres en # 37198 para mantener nuestras opciones abiertas en su mayoría y porque no pensé que sería limitante en la práctica. Si la gente lo quiere hoy y @ rust-lang / lang está de acuerdo con tener múltiples espacios de nombres macro para siempre, no tengo objeciones.

@nikomatsakis

Creo que sí, tu estrategia funcionaría, aunque de forma inestable. Mi sensación personal es que la rareza no influye (por ejemplo, lo que tenemos hoy está bien para mí), pero debería ser implementable de cualquier manera.

@alexcrichton, como creo que escribí antes, estoy feliz de esperar hasta que necesitemos más poder (si es que alguna vez), y luego podemos hacer algo como el rasgo que describí, o tal vez algo mejor. Por ahora, creo que sería suficiente extender los atributos aplicados a fn .

Sentí curiosidad y comencé una implementación básica de esta idea (usando un atributo en la función proc_macro para indicar los nombres de los atributos que se marcarán como usados ​​en el elemento).

Eliminé la etiqueta FCP porque parece claro que todavía no hemos alcanzado el punto en el que estamos listos para estabilizarnos. Voy a hacer un esfuerzo para resumir el estado de la conversación y, en particular, para resaltar los errores donde aún necesitamos decisiones firmes y / o contribuciones de código:

Pregunta 1: ¿Deberían las macros derivadas personalizadas poder mutar el elemento sobre el que están caminando?

  • Nadie piensa que debería hacer eso _en la práctica_ por supuesto
  • Es otro modo, que inicialmente fue rechazado en el espíritu de YAGNI.
  • Las derivadas que cambian el conjunto de nombres de campo, etc. no se componen bien, haciendo visible el orden de la aplicación;
    el peligro de eso se mitiga con dos cosas:
  • Por otro lado, si decimos que la derivación personalizada solo necesita devolver las nuevas impls

    • la información del intervalo es mejor

    • las derivaciones personalizadas son más sencillas de escribir

  • _Pero_ muchas derivaciones personalizadas utilizan atributos personalizados para guiar su expansión, y estos generarán advertencias / errores

    • La técnica actual es sacarlos del AST

  • _Además: _ queremos sacar estas cosas al mundo lo antes posible, no queremos hacer experimentos prolongados o cambios drásticos

Propuestas:

  • Mantenga todo como está, tal vez desaproveche más tarde

    • Conveniente, algo desafortunado

  • Amplíe #[proc_macro] con una lista blanca de atributos, dígale a rustc que los considere "usados" e ignórelos

Pregunta 2: ¿Las derivaciones personalizadas deberían compartir el mismo espacio de nombres que otras macros?

Argumento a favor:

Argumento en contra:

Propuestas:

  • Dividir en espacio de nombres "derivado personalizado"
  • Mantener el status quo

¿Otras cosas?

¿Hay otras preguntas abiertas?

Posible solución al problema de la mutación:

En lugar de que las derivaciones personalizadas tengan el tipo TokenStream -> TokenStream , tendrían el tipo
Item -> TokenStream , donde Item es un tipo opaco que comenzaría con solo dos métodos:

  • item.tokens() , que devuelve el TokenStream que se pasaría hoy, y
  • item.use_attrs(name) , que marcaría todos los atributos con el nombre dado como se usa.

El TokenStream devuelto solo incluiría los impl s derivados.
Eventualmente podríamos agregar a la API de Item con funciones de conveniencia (por ejemplo, iterando sobre los campos / variantes del elemento) o una API de derivación de nivel superior como la de syntax_ext::deriving::generic .

Me complacería implementar el permiso de atributos de la lista blanca en #[proc_macro_derive] (es decir, la segunda propuesta para la pregunta 1 en https://github.com/rust-lang/rust/issues/35900#issuecomment-258315395) o mi propuesta Item -> TokenStream .

Creo que sería un error estabilizar las derivaciones personalizadas que pueden mutar el elemento subyacente. La información de intervalo que sacrificamos al permitir la mutación ya está causando problemas; por ejemplo, tenemos que elegir entre arreglar # 37563 y arreglar # 36218.

Realmente no veo el atractivo de una lista blanca imperativa, ¿alguien puede pensar en un caso de uso?

No estoy seguro, ¿es intencional / deseado que este código compile:

#![feature(proc_macro)]

#[proc_macro_derive(Whatever)]
struct Foo {}

@eddyb No estoy seguro de que exista un atractivo inherente a las listas blancas imperativas; creo que el beneficio de
Item -> TokenStream es que es más fácil extender la API de Item que agregar más tipos de atributos declarativos. Dicho esto, pensando un poco más en esto, es posible que no haya muchos otros casos de uso que requieran un Item sobre un TokenStream , entonces TokenStream -> TokenStream con una lista blanca declarativa podría ser mejor.

¿Las macros 2.0 reemplazarán a esta api? Si es así, la extensibilidad no es una preocupación real, y la api Item -> TokenStream no parece muy atractiva.

@keeperofdakeys Según el RFC:

En general, esto debe considerarse un paso incremental hacia una "macros 2.0" oficial.

La intención de las macros 1.1 es estar lo más cerca posible de las macros 2.0 en espíritu e implementación, solo que sin estabilizar grandes cantidades de características. En ese sentido, es la intención que, dado un macros 1.1 estable, podamos superponer funciones de manera compatible con versiones anteriores para llegar a macros 2.0.

https://github.com/rust-lang/rfcs/pull/1681#issuecomment -233449053 y https://github.com/rust-lang/rfcs/pull/1681#issuecomment -233708395 y https: // github. com / rust-lang / rfcs / pull / 1681 # issuecomment -239558657 parecen indicar que sólo se

@eddyb

Realmente no veo el atractivo de una lista blanca imperativa, ¿alguien puede pensar en un caso de uso?

Bueno, podría imaginar un caso en el que los nombres de los atributos se generen dinámicamente de alguna manera (¿según el nombre del tipo o campos, tal vez?) Y, por lo tanto, no se pueden declarar con anticipación. Sin embargo, parece algo artificial y no es una práctica particularmente buena.

Encuentro atractiva la alternativa de Item podría enriquecerse con el tiempo. es decir, podríamos admitir recorrer el conjunto de campos de una manera muy estructurada, brindar acceso a más datos (como resoluciones de nombres) que podamos tener a nuestra disposición, etc.

Por supuesto, algo de esto también vendrá (_ eventualmente_) de alguna forma de API AST estándar.

Una nota sobre el tiempo: Realmente, realmente quiero ver que Macros 1.1 se estabilice en el próximo ciclo. Las cosas en las que estamos bloqueados se sienten, en última instancia, bastante menores.

Con ese espíritu, mi opinión sobre los temas que describí:

  1. Estoy contento con una extensión declarativa #[proc_macro] _o_ cambiando el tipo a Item . También creo que cualquiera que elijamos, potencialmente podríamos adoptar al otro en el futuro si nos parece una buena idea. Quiero ir con lo que se implemente primero. =)
  2. Con respecto a los múltiples espacios de nombres, creo que deberíamos mantenerlos en el mismo espacio de nombres por ahora. Para mí, la combinación de compatibilidad con versiones anteriores (es decir, mantener abiertas nuestras opciones) con el hecho de que las mayúsculas ya distingue las macros de "derivación personalizada" de otras cosas en la práctica es convincente. Distinguir #[cfg] vs cfg! parece algo que podríamos manejar de otras maneras, o si queremos, podemos introducir los espacios de nombres divididos _entonces_. No parece que tener un espacio de nombres unificado esté dañando el caso de uso de derivación personalizada en particular, que es lo único de lo que estamos hablando de estabilizar de todos modos. ¿Correcto?

@nikomatsakis su resumen suena preciso, ¡gracias por escribirlo! Me entristece que no obtengamos macros 1.1 en Rust 1.14, pero entiendo que se trata de cuestiones controvertidas.

Mi sentimiento personal sigue siendo estabilizar todo como está donde la derivación personalizada debe eliminar los atributos y solo hay un espacio de nombres.

No sigo la API Item -> TokenStream , ¿el flujo de token devuelto aún abarca el elemento original o solo las implicaciones agregadas? ¿Eso significa que debería tomar &mut Item ?

@ est31 tu comentario suena como un error, ¿puedes abrir una edición separada para eso?

Estoy bastante de acuerdo con el comentario de @nikomatsakis . Creo que es importante que los derivados no tengan rienda suelta para mutar el elemento al que están adjuntos, pero todas las implementaciones propuestas me parecen bien.

No parece que tener un espacio de nombres unificado esté dañando el caso de uso de derivación personalizada en particular, que es lo único de lo que estamos hablando de estabilizar de todos modos. ¿Correcto?

El problema surgió porque tiene diesel roto, que actualmente tiene macros llamadas, por ejemplo, Queryable! que puede ajustar una estructura para obtener Queryable .

Como alguien que no usa ni mantiene diesel en este momento (es decir, como alguien que no se ve afectado por lo que estoy a punto de proponer: sweat_smile :), creo que lo mejor es mantener 1 espacio de nombres por ahora y que diesel cambie el nombre de esas macros a derive_Queryable! o algo así. Serán obsoletos cuando esto sea estable de todos modos, ¿verdad?

Creé PR https://github.com/rust-lang/rust/pull/37614 para la función sugerida. Utiliza un atributo separado #[proc_macro_attributes(..)] , y ya no necesita devolver el artículo en el TokenStream devuelto.

Presenté # 37637 para averiguar cómo deben tratar las macros proc $crate .

Solo para aclarar, este caso de uso se considera correcto o un mal uso del sistema:

Genero nuevas estructuras basadas en la estructura y los atributos existentes aquí y me gusta bastante el diseño, ya que me permite consolidar las cosas en una estructura.

Sin embargo, si el sistema pudiera cambiar más adelante para no permitir este tipo de implementación, podría detenerme ahora en lugar de poner más esfuerzo en él (la implementación actual es solo una rápida prueba de concepto).

@Neikos

El principal cambio incompatible hacia atrás será que ya no puede modificar el elemento (no lo devuelve al TokenStream). Yo diría que no se pretende derivar otra cosa que no sea un impl, pero no hay nada que le impida hacer esto. Solo deberá tener cuidado con los conflictos de nombres.

El otro cambio principal es poder proporcionar una lista de nombres de atributos que no deberían desencadenar errores de atributos personalizados en el artículo.

RE: El conflicto de espacio de nombres en Diesel. No estoy seguro de si desaprobaré esas macros o no una vez que esta característica sea estable, dependerá de si todavía hay un deseo por parte de algunos de cosas gratuitas de extensión del compilador. Es dudoso. Sin embargo, sigue siendo útil para probar internamente, pero cambiarle el nombre está bien para eso.

Creo que es lamentable perder la capacidad de modificar el flujo de entrada. Ser capaz de eliminar el elemento que se está anotando nos permite tener macros bang con este sistema también. Entiendo las preocupaciones, pero es una pena perder esa capacidad sobre ellas.

@TheNeikos @sgrif mi punto de vista es que cualquier cosa que modifique seriamente la entrada no es una derivación y, por lo tanto, no debe abordarse aquí. Creo que es algo peligroso (falta de higiene, etc.) utilizar derivaciones personalizadas para macros proc de propósito general, por lo que no estoy dispuesto a fomentarlo.

No poder modificar el elemento significa que se mantiene la información del intervalo del elemento, lo que hace que los mensajes de error en el elemento sean mucho más claros (también significa que los mensajes de error en la salida derivada ya no apuntan al intervalo del elemento, sino al intervalo del atributo derivado). No veo una buena razón para permitir que las personas abusen de esta función si realmente solo queremos macros de procedimiento adecuadas.

@TheNeikos No creo que vayamos a prohibir la generación de nuevas estructuras a través de una derivación siempre que no motives la estructura para la que estás derivando.

En términos de lo que creo que es idiomático; Creo que está bien generar nuevos tipos, pero es mucho mejor si esos tipos son tipos asociados para un rasgo que estás derivando. Por ejemplo, imagina que pudiéramos derivar IntoIterator para un tipo, eso implicaría crear una estructura Iterator . Conceptualmente, debería derivar un comportamiento para este tipo, pero ese comportamiento podría no ser "un rasgo implícito".

@sgrif

Creo que es lamentable perder la capacidad de modificar el flujo de entrada. Ser capaz de eliminar el elemento que se está anotando nos permite tener macros bang con este sistema también. Entiendo las preocupaciones, pero es una pena perder esa capacidad sobre ellas.

Hmm, definitivamente soy comprensivo, aunque obviamente (como señaló @nrc ) esto no es para lo que se diseñó el sistema, y ​​tenderá a exponer las diversas asperezas. Probablemente esto no importe en sus casos de uso. Supongo que una pregunta clave es con qué rapidez podemos avanzar en una especie de "bang macros 1.1".

El RP se ha fusionado, por lo que debería poder ver los siguientes cambios en la próxima noche. La función proc_macro_derive ya no debería devolver el elemento (al hacerlo, se generará un error acerca de la definición de un tipo dos veces), y ahora puede proporcionar una lista de nombres de atributos para incluir en la lista blanca como esta #[proc_macro_derive(Derive, attributes(Foo, Bar)] .

cc @dtolnay , @sgrif ^

que causará roturas pronto desafortunadamente

Sí, presenté https://github.com/serde-rs/serde/issues/614 para rastrear nuestro final.

Creo que he arreglado la rotura en Diesel en https://github.com/diesel-rs/diesel/pull/493 , lo sabré con seguridad una vez que los nightlies se vuelvan a construir.

Entonces, si estoy leyendo este hilo correctamente, dado que una macro de proceso solo puede emitir elementos adicionales adjuntos al elemento inicial, ¿tampoco puede agregar más anotaciones al elemento inicial? (Veo una mención de permitir "anotaciones de hermanos", pero nada más al respecto).

Tengo una macro #[derive(newtype)] proc en mi caja (pequeña, no publicada) que se expande a un conjunto diferente de otros #[derive()] s según la estructura que está anotando. Por ejemplo, #[derive(newtype)] struct Foo(u64) expande a #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] struct Foo(u64); mientras que #[derive(newtype)] struct Foo(::semver::VersionReq) expande a #[derive(Clone, Debug, PartialEq)] struct Foo(::semver::VersionReq); . Entonces, los miembros de la estructura no son modificados por esta macro, pero se le agregan otras derivadas (estas tampoco modifican la estructura).

Hay unas pocas docenas de estructuras de este tipo y cada una tiene diez o más derivadas nuevas después de expandir esta macro. Me gusta que si me doy cuenta de que quiero que todos los nuevos tipos u64 implementen un rasgo más, puedo cambiar el conjunto de derivaciones en un lugar del código de macro newtype lugar de en cada estructura.

Solía ​​tener una macro macro_rules newtype! para esto, pero cambié a una macro proc porque:

  • La presencia o ausencia de comentarios de documentos, la presencia o ausencia de pub , etc. se manejan de forma gratuita sin necesidad de un número combinatorio de brazos de coincidencia macro.
  • Incluso si escribí los brazos combinatorios de macro coincidencia, fue difícil encontrar un orden en el que no entraran en conflicto entre sí.

Desafortunadamente no, ya no puede hacer esto como lo estaba haciendo.

Con respecto a la compatibilidad futura de que esta característica sea estable: dado que no se requiere que la función del complemento sea "pura", será un cambio importante si el orden de los objetos dados a la función procesada cambia en el futuro, o si rustc implementa multihilo compilando?

@ est31 Si tenemos tiempo, deberíamos intentar llevar a cabo el aislamiento de IPC que se ha mencionado.

Constantemente veo un ICE en Diesel después de los cambios más recientes.

../src/librustc_metadata/decoder.rs:490: entrada: id no encontrado: DefIndex (1) en la caja "diesel_codegen" con el número 28

@sgrif ese sería el problema # 37788 que será arreglado por # 37793 (esperemos que termine en la noche de mañana ...).

@ est31 Es demasiado tarde a esta hora para fusionarlo antes de que comience la compilación nocturna.

https://github.com/rust-lang/rust/issues/37839 es un problema con el uso de una caja de bibliotecas que a su vez usa una caja proc_macro. AFAICT ninguna de las pruebas se ve afectada por esto porque compilan solo el módulo de macro proc, o un módulo de macro proc y un módulo bin que hace referencia directamente a la macro proc.

Editar: ¡Ahora arreglado!

@Arnavion El problema que vio con # 37839 está solucionado por una complicación regular, pero permanece roto cuando se usa --target , como se informa en # 37958. Proporcioné un caso de prueba mínimo usando --target que aún se rompe.

@rfcbot fcp fusionar

Ahora que se implementó la función de atributo de la lista blanca, creo que deberíamos estabilizar esto. Serde, Diesel y otros usuarios: ahora es su cambio de objeción si el diseño actual no le funciona. =)

cc @sgrif @erickt

El miembro del equipo @nikomatsakis ha propuesto fusionar esto. El siguiente paso lo revisan el resto de equipos etiquetados:

  • [x] @alexcrichton
  • [x] @aturon
  • [x] @brson
  • [x] @eddyb
  • [x] @japaric
  • [x] @michaelwoerister
  • [x] @nikomatsakis
  • [x] @nrc
  • [x] @pnkfelix
  • [x] @vadimcn
  • [x] @withoutboats
  • [x] @wycats

No hay preocupaciones actualmente enumeradas.

Una vez que estos revisores lleguen a un consenso, entrará en su período de comentarios final. Si detecta un problema importante que no se ha planteado en ningún momento de este proceso, ¡hable!

Consulte este documento para obtener información sobre los comandos que pueden darme los miembros del equipo etiquetados.

Me encantaría ver exploradas las macros de bang en un futuro próximo. Aunque sin objeciones.

@rfcbot revisado

Actualmente, si un TokenStream no puede analizar, se devuelve un LexError vacío . ¿Sería posible devolver un mensaje de error mejor aquí, como qué token no se pudo analizar? Aunque los usuarios de su biblioteca probablemente no quieran ver este tipo de mensajes de error.

Sí, eso sería conveniente para los autores de macros. Tuve que recurrir a escribir la transmisión en un archivo y verla en el patio de recreo para encontrar errores.

Creo que los usuarios también se beneficiarán, aunque solo sea para presentar mejores informes de errores contra la macro.

En https://github.com/rust-lang/rust/pull/38140 , estamos obligando a que las declaraciones de derivación personalizadas sean públicas. La teoría es que algún día podríamos querer derivaciones privadas y luego probablemente querremos hacer esa distinción en función de la visibilidad de la función definitoria. En cualquier caso, esta es la opción conservadora. Pensé que lo dejaría macerar durante uno o dos días para que la gente lo vea antes de fusionarse, ya que estamos estabilizando esta función.

# 37480 está cerrado, lo que debería reflejarse en la lista de verificación.

Fijo

@pnkfelix Me tomé la libertad de marcar su casilla por usted, ya que está en PTO y creo que está totalmente de acuerdo. ¡Háganos saber si ese no es el caso!

: bell: Esto ahora está entrando en su período de comentarios final , según la revisión anterior . :campana:

psst @nikomatsakis , no pude agregar la etiqueta final-comment-period , hágalo.

¿La estabilización inminente supone que los errores conocidos restantes en la lista de verificación en la parte superior se corregirán primero? ¿O los que no bloquean la estabilización?

Hay dos ahora mismo:

  • # 36935 tiene un comentario que dice que está resuelto.
  • # 36691 no me parece un bloqueo, podemos permitir que estos se expandan a mod foo; algún día en el futuro si queremos sin romper nada de lo que creo.

¡Oye! Esta es una auditoría de documentación RFC 1636 . Esta es la primera característica importante que se estabiliza desde que se aceptó RFC 1636, y deberíamos hacer un buen trabajo para seguirla en este caso.

El RFC dice:

Antes de estabilizar una función, las funciones ahora se documentarán de la siguiente manera:

  • Características del idioma:

    • debe estar documentado en la Referencia de óxido.

    • debe estar documentado en el lenguaje de programación Rust.

    • puede documentarse en Rust por ejemplo.

  • Tanto las características del idioma como los cambios de biblioteca estándar deben incluir:

    • una sola línea para el registro de cambios

    • un resumen más extenso del anuncio de lanzamiento de formato largo.

¿Cuál es el proceso correcto para esto? ¿Deberíamos agregar elementos de la lista de verificación al comentario principal de este problema o crear un nuevo problema para la documentación de seguimiento? Me parece que deberíamos tener documentación que cumpla estos requisitos en árbol para la versión 1.15.

cc @ rust-lang / docs

¡Gracias @withoutboats ! Es el primero importante, sí. Tenía esto en mi lista para mirar esta mañana, y he aquí, me adelantaste 😄

Siempre me imaginé que la decisión de estabilizar y la estabilización real son independientes. Es decir, @ rust-lang / lang puede decir "esto se puede hacer estable", pero el compromiso para eliminar la puerta también asegura que la documentación exista. En un mundo donde existe el libro inestable , esto llevaría los documentos de allí a los documentos estables; pero hasta entonces, las cosas son un poco incómodas.

Dado que acabamos de tener un lanzamiento, mi plan era hacer algo como esto:

  1. Espere a que esto salga de FCP
  2. Consigue algunos documentos. (Estaba planeando escribirlos en este caso)
  3. Realice la estabilización PR.

Posiblemente combinando dos y tres.

/ cc @ rust-lang / core, ya que se trata de un problema entre equipos.

@steveklabnik, eso me suena bien, y también estaría bien aterrizando doctores en cualquier momento (incluso antes de terminar FCP). El FCP aquí está una especie de pseudo-terminado de todos modos, ya que accidentalmente tardamos mucho en ingresar.

¡También sería bueno incorporarlos rápidamente para que podamos garantizar un backport seguro a la rama 1.15 beta!

Si soy el primero en llegar a esto, no puede ser tan malo, pero incluyémoslo en errores conocidos: el uso de una macro de tipo dentro de una estructura con una derivación personalizada provoca un ICE https://github.com/rust-lang/ óxido / problemas / 38706

https://github.com/rust-lang/rust/pull/38737 corrige el tipo de macros ICE: heart :

Acabo de crear # 38749 con respecto a la documentación de la caja proc_macro .

He leído varias veces que Macros 1.1 se estabilizará en 1.15, pero 1.15.0-beta.1 se envió hace dos semanas y al menos extern crate proc_macro; todavía está habilitado en él, así como en 4ecc85beb nocturno de 2016 -12-28. ¿El plan respaldará el cambio de estabilización?

@SimonSapin sí, ese era el plan, ¡pero tenemos que hacerlo realidad!

Todavía es el plan: p

Si el usuario escribe #[derive(Foo)] #[foo_def = "definition.json"] struct MyStruct; el controlador de macros no tiene forma de saber cuál es "el directorio actual" y, por lo tanto, no puede encontrar definition.json .

Esto es por diseño y, por lo tanto, no sería fácil de solucionar, y supongo que es demasiado tarde para solucionarlo de todos modos.

Puede ir Span -> FileMap -> nombre de archivo -> directorio. Todo lo que falta es acceder a la información a través de proc_macro .

También necesitaría decirle al compilador que agregue una dependencia a definition.json para que la compilación esté sucia si se modifica.

La macro proc puede usar env::var("CARGO_MANIFEST_DIR") para obtener el directorio que contiene Cargo.toml de la caja que contiene la invocación de la macro. Presumiblemente foo_def es relativo a eso. Consulte https://github.com/dtolnay/syn/issues/70#issuecomment -268895281.

@tomaka que se puede hacer mutando FileMap, por ejemplo, así es como lo hace la macro include_str! .

Presumiblemente foo_def es relativo a eso.

Creo que no es muy intuitivo tener que poner la ruta en relación con Cargo.toml.

eso se puede hacer mutando FileMap, por ejemplo, así es como include_str! macro lo hace.

Sí, sé que se puede hacer, simplemente no se puede hacer con la API de macros de procedimiento actual.

El parámetro es Item . Sería aceptable agregar un método para recuperar un intervalo de ese elemento, pero agregar un método a Item para pedirle al compilador que agregue una dependencia sería un truco IMO.

Puede ir a Span -> FileMap -> nombre de archivo -> directorio.

¿Están estas API (en particular FileMap ) en una ruta para estabilizarse?

No tienen por qué serlo, de hecho, no quisiera estabilizar ninguno de los componentes internos. En cambio, podemos estabilizar las API que extraen información sobre un Span (por ejemplo, línea, columna, nombre de archivo).

Recibí este error en una caja mía. ¿Que esta pasando?

`` error: Cannot use #! [Feature (proc_macro)] and #! [Feature (custom_attribute)] al mismo tiempo
`` ``

@alexreg Si está usando #[derive] , ahora es estable. No necesitas #![feature(proc_macro)] .

@alexreg
proc_macro_derive s (macros 1.1) ahora son estables; simplemente puede eliminar #![feature(proc_macro)] .

#[proc_macro_attribute] aterrizó recientemente detrás de la puerta de características #![feature(proc_macro)] ; estos son incompatibles con #![feature(custom_attribute)] . #![feature(custom_attribute)] quedará obsoleto una vez que llegue el reemplazo (https://github.com/rust-lang/rfcs/pull/1755).

@jseyfried Creo que deberíamos cambiar el problema de seguimiento en proc_macro ya que está cerrado y no contiene información relevante.

Gracias chicos. Eso tiene sentido.

@abonander Sí, #![feature(proc_macro)] definitivamente debería apuntar a # 38356 ahora, debería haberlo verificado al revisar # 38842. ¿Podrías enviar un PR?

¿Fue útil esta página
0 / 5 - 0 calificaciones