Rust: Rastreamento de problema para RFC 1566: macros procedurais

Criado em 14 dez. 2016  ·  184Comentários  ·  Fonte: rust-lang/rust

Status atual

Este problema foi resolvido em favor de problemas de rastreamento mais refinados

~ Descrição atualizada ~

Próximos passos:

  • [x] [Estabilizar use_extern_macros ] (https://github.com/rust-lang/rust/pull/50911)

    • esperando na cratera

  • [] Estabilize o recurso proc_macro

Possíveis obstáculos de estabilização

Descrição Original

RFC .

Este RFC propõe uma evolução do sistema macro procedural de Rust (também conhecido como sintaxe
extensões, também conhecidas como plug-ins do compilador). Este RFC especifica a sintaxe para a definição
de macros procedurais, uma visão de alto nível de sua implementação no compilador,
e descreve como eles interagem com o processo de compilação.

No nível mais alto, as macros são definidas pela implementação de funções marcadas com
um atributo #[macro] . As macros operam em uma lista de tokens fornecida pelo
compilador e retorna uma lista de tokens pelos quais o uso da macro é substituído. Nós
fornecer recursos de baixo nível para operar nesses tokens. Nível superior
recursos (por exemplo, para analisar tokens para um AST) devem existir como caixas de biblioteca.

Roteiro: https://github.com/rust-lang/rust/issues/38356#issuecomment -274377210.


Tarefas

  • [x] Implementar #[proc_macro_attribute] (PR # 38842).

    • [x] Fix # 39347 (PR # 39572).

    • [x] Fix # 39336 (PR # 44528).

  • [x] Implementar #[proc_macro] (PR # 40129).
  • [x] Identifique e colete os usos de proc_macro_derive s em InvocationCollector (PR # 39391).
  • [x] Suporta proc_macro_derive importações macro-expandidas.

    • Por exemplo:

#[derive(Trait, OtherTrait)] struct S; // Both these derives should resolve
macro_rules! m { () => {
    #[macro_use(Trait)] extern crate derives;
    use derives::OtherTrait; // this kind of import is gated behind `#![feature(proc_macro)]`
} }
m!();
  • [] Expanda os itens antes de expandir os aplicados proc_macro_derive s (PR # 48465).
  • [x] Implementar avisos para #[macro_use] importações não utilizadas (PR # 39060).
  • [x] Refatore o analisador para consumir árvores de token (PR # 39118).
  • [x] Limpe TokenStream na preparação para refatoração adicional (PR # 39173).
  • [x] Remova TokenTree::Sequence (PR # 39419).
  • [x] Use TokenStream s em vez de Vec<TokenTree> na variante de tokenstream::TokenTree Delimited (PR # 40202).
  • [x] Use Path s e TokenStream s em ast::Attribute s (PR # 40346).

    • [x] Suporta caminhos não triviais em macros de atributo / derivação (por exemplo, #[foo::bar] , #[derive(foo::Bar)] ).

  • [x] Incluir informações de higiene com todos os tokens, não apenas identificadores (PR # 40597).
  • [x] Implementar uma API mínima para proc_macro::TokenStream conforme descrito no RFC (PR # 40939).

    • [x] Inclui a fonte TokenStream s para fragmentos AST interpolados em Token::Interpolated tokens.

    • [x] Inclui um TokenStream quoter proc_macro::quote! atrás do portão de recursos proc_macro .

  • [x] Fornece uma maneira para proc_macro autores criarem expansões que usam itens em uma caixa predeterminada foo sem exigir que o usuário macro inclua extern crate foo; na raiz da caixa (PR # 40939).

    • [] Melhore a ergonomia.

  • [] Incluir a fonte TokenStream s para itens no AST.
  • [] Macros de verificação de estabilidade (proc-) (problema # 34079).
  • [x] Permitir que a macro proc inicialize um campo privado com um valor def_site (problema # 47311). (PR # 48082)
  • [x] Inconsistência entre o campo de acesso de estrutura com chaves vs estrutura de tupla na macro proc (problema # 47312). (PR # 48083)
  • [] Disponibilize std para macro raiz proc na fase 1 (problema # 47314).
  • [x] Melhorar o erro de sintaxe inválida dentro de proc_macro::quote! (problema # 47315).
  • [] Inconsistência entre Display e IntoIterator para um TokenStream contendo um módulo (problema # 47627).
  • [x] # [cfg_attr] faz com que .to_string () e TokenStream discordem (problema # 48644).
  • [x] Lista de desejos para libproc_macro (lista de verificação em # 47786).
A-macros A-macros-1.2 A-macros-2.0 A-plugin B-RFC-approved B-unstable C-tracking-issue T-lang T-libs finished-final-comment-period

Comentários muito úteis

Ok, este problema é enorme e chegou ao ponto que não acho muito útil manter aberto e rastrear APIs. Para esse fim, abri https://github.com/rust-lang/rust/pull/54728, que divide esse problema em vários problemas de rastreamento mais refinados:

Neste ponto, fecharei isso, mas se eu esqueci de separar qualquer outro problema de rastreamento, por favor me avise! Certamente posso abrir mais alguns acompanhamentos

Todos 184 comentários

cc @nrc @jseyfried

Adoraria que #[proc_macro_attribute] fosse implementado em breve. Já tenho um protótipo e teste de uso que fiz antes de perceber que ainda não há suporte para compilador: unamused::

Protótipo: https://github.com/abonander/anterofit/blob/proc_macro/macros/src/lib.rs
Exemplo / teste: https://github.com/abonander/anterofit/blob/proc_macro/examples/post_service_proc_macro.rs

Tarefas

(edição dtolnay: moveu a lista de verificação para o OP)

cc @nrc @petrochenkov @durka @Ralith

@jseyfried Encontrei um problema em que, se uma macro legada e um atributo com o mesmo nome forem importados para o mesmo escopo, tentar usar o atributo gera um erro de que as macros não podem ser usadas como atributos. Podemos fazer este trabalho de forma que ambos possam estar no mesmo escopo e possam ser usados ​​como pretendido?

@abonander Todas as macros (bang, attribute e derive) compartilham o mesmo namespace, portanto, não podemos usar duas macros diferentes com o mesmo nome no mesmo escopo. No entanto, poderíamos melhorar essa mensagem de erro - você poderia abrir um problema?

Desculpe, estou atrasado para a festa. Estou feliz com a orientação de expor tokens em vez de AST, mas tenho algumas preocupações sobre a API TokenStream proposta na RFC :

pub enum TokenKind {
    Sequence(Delimiter, TokenStream),

    // The content of the comment can be found from the span.
    Comment(CommentKind),

    // `text` is the string contents, not including delimiters. It would be nice
    // to avoid an allocation in the common case that the string is in the
    // source code. We might be able to use `&'codemap str` or something.
    // `raw_markers` is for the count of `#`s if the string is a raw string. If
    // the string is not raw, then it will be `None`.
    String { text: Symbol, raw_markers: Option<usize>, kind: StringKind },

    // char literal, span includes the `'` delimiters.
    Char(char),

    // These tokens are treated specially since they are used for macro
    // expansion or delimiting items.
    Exclamation,  // `!`
    Dollar,       // `$`
    // Not actually sure if we need this or if semicolons can be treated like
    // other punctuation.
    Semicolon,    // `;`
    Eof,          // Do we need this?

    // Word is defined by Unicode Standard Annex 31 -
    // [Unicode Identifier and Pattern Syntax](http://unicode.org/reports/tr31/)
    Word(Symbol),
    Punctuation(char),
}

pub enum StringKind {
    Regular,
    Byte,
}

Não está claro se esta API foi concebida como um plano completo que foi aceito quando o RFC foi mesclado ou apenas um exemplo a ser trabalhado posteriormente.

Geralmente, isso parece longe da sintaxe "normal" do Rust aceita pelo compilador fora das macros. Enquanto algumas macros desejarão analisar alguma linguagem específica de domínio ad-hoc, outras desejarão analisar a sintaxe "real do Rust" e entendê-la.

  1. (Menor) Não acho que Eof seja necessário. Um Iterator presumivelmente será usado para, bem, iterar sobre um TokenStream e Iterator::next já retorna None para sinalizar o fim da iteração.

  2. (Menor) Não acho que Exclamation , Dollar ou Semicolon sejam necessários. Combinar Punctuation('!') por exemplo, não é mais difícil.

  3. (Menor) Como outros mencionaram no RFC PR, podemos omitir comentários que não sejam comentários de documentos. (Qualquer caso de uso que deseja preservar o comentário provavelmente deseja preservar também os espaços em branco.)

  4. Até onde eu posso dizer, o que fazer com operadores de vários caracteres (que provavelmente devem ser um único token cada) ainda é uma questão em aberto. Uma possível solução é discutida nos comentários de RP, mas parece que não foi incluída no texto RFC.

  5. Literais de número estão faltando. As macros devem analisar [Punct('1'), Punct('_'), Punct('2'), Punct('3'), Punct('4'), Punct('.'), Punct('5'), Punct('e'), Punct('6')] sozinhas para avaliar um literal? Eles nem mesmo podem usar str::parse::<f32> para fazer isso, já que a sintaxe que ele aceita não é a mesma que a sintaxe literal do Rust (que pode ter _ no meio, por exemplo).

    Eu imagino que haja uma preocupação com a estabilidade aqui. Podemos introduzir novos tipos numéricos como u128 / i128 (e possivelmente no futuro f128 , u256 ,…) e seus literais, sem interromper as alterações para a API de tokens? Uma maneira de tornar isso possível:

    struct IntegerLiteral { negative: bool, decimal_digits: String, type_suffix: Option<String> }
    impl TryInto<u32> IntegerLiteral { type Err = OutOfRange; /* … */ }
    // Other impls for integer types supported in this compiler version
    
    // Something similarly for floats
    

    Ou talvez outra coisa. Mas não acho que "fingir que os números não existem" seja uma boa maneira de fazer isso.

  6. // A palavra é definida pelo Padrão Unicode Anexo 31 -

    Essa definição precisa ser mais precisa do que isso. UAX 31 especifica algumas variações diferentes da sintaxe do identificador e nenhuma delas é chamada de "palavra". Mas escolher a variação exata que queremos é o motivo pelo qual os identificadores não ASCII são bloqueados por recursos no momento.

    Em vez disso, acho que isso deve ser definido como "tudo o que o compilador atual aceita como um identificador ou palavra-chave" (que pode mudar de acordo com # 28979). Talvez com uma API pública pub fn is_identifier(&str) -> bool em libmacro.

  7. Strings Unicode e literais de string de byte compartilham uma única variante de token, o que eu acho errado, pois as representações de memória de seus valores têm tipos diferentes ( str vs [u8] ). Também não está claro se o componente text: Symbol pretende ser uma parte literal do código-fonte ou o valor após a resolução de escapes de barra invertida. Acho que definitivamente deveria ser o último. (Para comparação, Char(char) tem que ser o último, pois \u{A0} leva mais de um char para representar literalmente.)

outra maneira de escrever macros de alto nível seria usar lisp como macros, mas isso precisaria de uma representação de expressão s para todo o rust ast.

@SimonSapin ,

Como outros mencionaram no RFC PR, podemos querer omitir comentários que não sejam comentários de documentos. (Qualquer caso de uso que deseja preservar o comentário provavelmente deseja preservar também os espaços em branco.)

Por favor, não faça isso. Eu tenho um caso de uso em que desejo usar (embora não preservar - eles serão escritos em um produto de compilação separado) comentários na sintaxe.

Especificamente, desejo criar macros de tradução que carreguem traduções de uma string de um arquivo de origem separado e gostaria de gerar uma lista de strings a serem traduzidas como subproduto na compilação de depuração. E deve haver uma maneira de incluir comentários a serem emitidos nessa lista (rust-locale / rust-locale # 19). Portanto, faz sentido usar a sintaxe de comentário e a macro precisa vê-los.

Eu concordo com os outros pontos desse post.

@ jan-hudec
Mesmo se não tivéssemos um TokenKind::Comment , você ainda poderia usar comentários examinando o conteúdo dos intervalos entre os tokens consecutivos.

Não acho que não devamos ter TokenKind::Comment para encorajar macros procedurais a ignorar comentários, de forma que os usuários sejam livres para adicionar comentários a invocações de macro sem se preocupar em mudar a semântica.

@ jan-hudec Existe um motivo pelo qual os atributos não funcionam com sua solução?

@abonander , atributos absolutamente não fazem sentido. Strings traduzíveis atuam como literais, não como itens. Mas extraí-los durante a compilação seria apenas por conveniência - sempre pode ser feito como uma análise separada (e, na verdade, pode acabar sendo assim, porque eu preciso ver _todos_ eles na caixa e a compilação incremental quebraria isso).

Eu quero fazer uma macro procedural que é baseada na derivação de serde (e então chama as funções tokenstream serde diretamente), mas não há como dizer que eu quero consumir derivação serde como uma biblioteca em vez de uma macro procedural. Isso não é exclusivo para derivar macros, posso ver algo semelhante sendo desejado para macros procedurais 'normais' também.

Minha única solução agora parece ser a bifurcação do serde_derive.

O problema é esta mensagem de erro de rustc:

error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type

É fácil remover isso e fazer funcionar, mas também há alguma complexidade que não tenho certeza de como resolver - uma caixa de macro procedural poderia plausivelmente querer usar o proc-macro derivado de outra caixa de macro procedural, bem como chamando as funções para gerar a derivação para um usuário downstream. Qual seria a aparência disso? Existe algo parecido com isso no momento, onde uma caixa pode ser conectada de duas maneiras diferentes a pedido da caixa de consumo?

@aidanhs

uma macro crate procedural poderia plausivelmente querer usar a derivação proc-macro de outra macro crate procedural, bem como chamar as funções para gerar a derivação para um usuário downstream. Qual seria a aparência disso?

Você não pode acessar as funções (ou qualquer outra coisa além de macros procedurais) de uma caixa proc-macro . Se você quiser usar as funções TokenStream -> TokenStream e as macros procedimentais correspondentes, você precisará colocar as funções TokenStream -> TokenStream em uma caixa separada, não proc-macro , e também ter uma caixa proc-macro que apenas delega a essas funções.

Este RFC será implementado principalmente quando o # 40939 aterrissar.

Fornece uma maneira para proc_macro autores criarem expansões que usam itens em uma caixa predeterminada foo sem exigir que o usuário da macro inclua extern crate foo; na raiz da caixa

Suponha que eu queira apresentar uma única caixa, que contém itens não macro e uma macro procedural que se refere a esses itens. Quando o # 40939 chegar, esse padrão de três caixas será a maneira idiomática de atingir esse objetivo?

  1. Coloque todos os itens não macro em foo_runtime
  2. Implemente a macro procedural em foo_macros , referindo-se aos símbolos em foo_runtime conforme necessário
  3. Adicione uma caixa de "fachada" final foo que pub use s os itens de foo_runtime e foo_macros

    • Esta é a única caixa que o usuário importará diretamente

    • Isso funciona porque o sistema de higiene corrige as macros para apontar para a caixa certa

Eu pergunto porque meu caso de uso envolve a importação de dois engradados e seria ótimo para usabilidade se eu pudesse me safar com apenas um.

@lfairy , acho que um padrão de "duas caixas" será a forma idiomática:

  1. Coloque todos os itens não macro em foo
  2. Implemente a macro procedural em foo_macros , referindo-se aos símbolos em foo conforme necessário, por exemplo
#[proc_macro]
fn m(_: TokenStream) -> TokenStream {
    quote! {
        extern crate foo; // due to hygiene, this is never a conflict error
        foo::f();
        // --- or just --- (if/when we get the sugar)
        $universe::foo::f();
    }
}
  1. pub use itens de foo_macros em foo .

Isso funciona porque o sistema de higiene corrige as macros para apontar para a caixa certa

A reexportação de uma macro procedural em uma caixa diferente não afeta como os nomes da macro procedural são resolvidos.

@jseyfried : Você sabe se esse truque de reexportação também funciona com derivados personalizados? Porque essas caixas têm exatamente a mesma limitação de não poder exportar nenhum item.

@ colin-kiegel
As caixas de derivação personalizadas são apenas caixas de macro proc que possuem apenas #[proc_macro_derive] s.
Com #[feature(proc_macro)] , você pode reexportar derivados personalizados em caixas comuns, da mesma forma que pode reexportar outras macros proc.

@jseyfried Estou ciente da situação como ela se encontra no momento, fiz a pergunta porque não acho que seja ideal e espero ter uma discussão sobre isso. Na situação que você descreve, 'delegar' / reutilizar as macros procedurais de outra caixa torna-se uma questão de convencer o autor da macro (neste caso, serde) a dividir suas macros procedurais em duas caixas. Se eu posso chamar macros procedurais como funções normais, o autor da caixa upstream nem precisa saber que estou usando a caixa deles.

Dito isso, eu reconheço o risco de compatibilidade - o tokentree exato gerado por uma macro torna-se parte da interface estável, então se serde mudar como eles geram Derive em uma versão de patch e eu escrevi uma macro frágil, meu macro será quebrada para cada novo usuário de minha caixa (ao contrário de uma macro frágil no caso atual, onde no pior caso só funcionará para entradas específicas, mas de forma consistente).

@jseyfried

Isso puxa foo do esgotador de carga atual? Isso soa mal (ou seja, ele fará algo particularmente estúpido se houver 2 caixas chamadas foo vinculadas ao binário atual?).

@aidanhs Essa seria uma grande alteração / adição de idioma que justificaria seu próprio RFC.

@ arielb1

Isso puxa foo do esgotador de carga atual? Isso soa mal

Sim - infelizmente, nomes de extern crate citados não são higiênicos, ou seja, a resolução depende de quais nomes de caixas estão no escopo onde a macro procedural é usada. Podemos atenuar isso usando o truque de reexportação (ou seja, reexportar foo_macros em foo para que saibamos que foo estará no escopo), mas isso não protege contra erros de ambigüidade quando há duas caixas chamadas foo .

Acho que a melhor solução aqui é adicionar dependências de fase 1 (isto é, host wrt alvo vs alvo) às caixas Cargo.toml para proc-macro por meio de um argumento de linha de comando --target-extern . Isso nos permitiria listar explicitamente os extern crate nomes no escopo dentro de quote! .

@jseyfried

A ideia é que uma caixa proc-macro teria uma dependência em seus metadados "alvo", certo?

@ arielb1 Sim, exatamente.

Este RFC será implementado principalmente quando o # 40939 aterrissar.

@jseyfried Tipo , pronto para ser estabilizado quando esse PR chegar? Do contrário, o que permaneceria bloqueando a estabilização? Só não quero que este seja mais um recurso em que parece que chegamos a 95% do caminho em direção à implementação e as pessoas ficam animadas, e então as coisas se deterioram de forma anticlimática.

Tipo, pronto para ser estabilizado quando esse PR pousar?

Não, queremos obter alguma experiência com a API antes de estabilizar e, talvez, higienização à prova de futuro para extern crate nomes (ou seja, resolver esse problema que @ arielb1 apontou).

Provavelmente, desejaremos fazer alterações significativas nesta API; @eddyb propôs / considerou generalizar OpKind para todas as árvores de tokens. Além disso, podemos mudar a forma como lidamos com comentários de documentos, literais de ponto flutuante, etc. No geral, a API neste PR ainda não está madura o suficiente para considerar a estabilização.

@bstrie infelizmente, o RFC para acelerar a estabilização da macro proc (com uma API limitada onde, por exemplo, os fluxos de token são acessíveis apenas por meio de sua representação de string), como a estabilização de macro derivada falhou: https://github.com/rust-lang/rfcs/ puxar / 1913

@ est31 Adiado, mais

A API baseada em String interage mal com macros declarativas 2.0 e já está limitando hoje, mesmo sem macros 2.0 e apenas com #[derive] s. Queremos evitar a proliferação da API baseada em String tanto quanto possível para evitar problemas conforme as pessoas migram para macros 2.0.

Eu abri um problema para #[proc_macro_attribute] aparentemente não foi expandido nos métodos de características (talvez itens de características em geral?)

Uma vez que este é agora o problema de rastreamento para a caixa proc_macro e suas novas APIs, pensei em escrever algumas ideias também. Publiquei uma caixa chamada proc-macro2 que se destina a ser exatamente igual à caixa proc_macro na árvore, exceto que fornece a capacidade de compilar no Rust estável. Ele também tem a capacidade de usar um recurso para compilar no Rust noturno para obter o benefício de melhores informações de amplitude. Essa biblioteca se destina a se tornar a base para outras bibliotecas como syn , e no desenvolvimento de syn encontramos algumas deficiências que podemos desejar abordar em proc_macro diretamente:

  • Não há construtor Literal para alguns tipos de literais. Isso é contornado por meio de stringificação seguida de análise, mas seria ótimo poder construí-los diretamente sem ter que passar pela API de string.

    • Strings brutas - r###" foo "###

    • Strings de bytes brutos - rb#" foo "#

    • Literais de byte - b'x'

    • Comentários do documento - atualmente são representados como o token Literal .

  • Não há como inspecionar Literal e extrair seu valor. No momento, estamos contando com a caixa literalext para to_string um literal e re-analisá-lo, mas esta informação em teoria já está armazenada em Literal e seria bom poder Acesse isso.
  • O mapeamento de tokens em alguns casos pode ser interpretado como um pouco estranho. Nomeadamente agora, os comentários do documento são mapeados para o tipo Literal .

Acredito que todas as outras preocupações que começam aqui já foram abordadas.

Eu encontrei uma falha ao testar com #![feature(proc_macro)] que afeta as derivações personalizadas que têm #[proc_macro_derive(foo, attributes(foo))] . Ou seja, uma derivação customizada que possui o nome de um atributo que é o mesmo que a derivação customizada. Uma dessas caixas é minha - derive-error-chain, que tem #[derive(error_chain)] #[error_chain(...)] struct ErrorKind { ... } . Outro é derivar-novo, que tem #[derive(new)] #[new] struct S; . Não sei se existem outros.

Para código como este, o compilador reclama no segundo atributo que "foo" is a derive mode . Isso é intencional ou pode ser consertado? Se for intencional, preciso me preparar para renomear meu derivado personalizado para ErrorChain ou algo assim.

@Arnavion
Isso foi intencional em geral - uma vez que proc_macro_attribute s deve ser expandido antes da derivação, se new fosse proc_macro_attribute então a expansão seria ambígua. Seria possível permitir especificamente que new fosse proc_macro_derive , mas não tenho certeza se vale a pena (também pode ser um risco de compatibilidade futura).

Isso foi intencional em geral - uma vez que proc_macro_attributes deve ser expandido antes da derivação, se new fosse proc_macro_attribute então a expansão seria ambígua.

Ok, vou renomear #[derive(error_chain)] para #[derive(ErrorChain)] .

Seria possível permitir especificamente que new fosse proc_macro_derive , mas não tenho certeza se vale a pena (também pode ser um risco de compatibilidade futura).

Claro, eu não estava pedindo new para um case especial. Foi apenas um exemplo de um dos dois proc_macro_derive s que conheço que foram quebrados por isso.

@Arnavion Desculpe, meu último comentário não foi o mais claro - eu não quis dizer um caso especial new especificamente, mas permitir #[derive(some_macro)] #[some_attr] struct S; quando some_attr resolver para proc_macro_derive . Quando some_attr resolve para proc_macro_attribute , isso precisaria ser um erro de ambigüidade; hoje, é um erro de ambigüidade se some_attr resolver para qualquer macro.

Sim, eu entendi.

( Espero que este seja o lugar certo para uma pergunta como esta. )

Qual é a situação disso?

  • [] Fornece uma maneira para proc_macro autores criarem expansões que usam itens em uma caixa predeterminada foo sem exigir que o usuário da macro inclua extern crate foo; na raiz da caixa (PR # 40939 )

O PR pousou, mas a caixa ainda não está marcada. @jseyfried mencionou algo aqui e parece que funciona. No entanto, não parece funcionar com use alguma:

let call_site_self = TokenTree {
    kind: TokenNode::Term(Term::intern("self")),
    span: Span::call_site(),
};
quote! {
    extern crate foo; // due to hygiene, this is never a conflict error

    // Neither of those works    
    use foo::f;
    use self::foo::f;
    use $call_site_self::foo:f;
}

Estou esquecendo de algo? Qual é a maneira idiomática de use símbolos de uma caixa externa importada na macro?

Você não pode usar use veja https://github.com/rust-lang/rfcs/issues/959. Mas, para uma macro, não é realmente uma desvantagem usar o caminho totalmente qualificado sempre. (Exceto por traços, eu acho)

@parched Obrigado por vincular este outro problema. Meu caso de uso foi o seguinte:

Em minha macro, quero permitir que o usuário escreva algo semelhante a um match-matcher. Especificamente, o usuário escreve um Term e isso pode ser uma variante de um enum ou um nome de variável simples que vincula o valor de correspondência. Para escrever algum pseudo código com a sintaxe macro_rules! :

macro_rules foo {
    ($matcher:ident) => {
        match something() {
            $matcher => {}
            _ => {}
        }
    }
}

Agora, quero que o usuário possa apenas especificar o nome da variante sem o nome de enum. Portanto, eu inseriria uma instrução use my_crate::AnEnum::*; no código gerado. Mas como isso não é possível (agora), preciso verificar por mim mesmo se $matcher é ou não uma variante do enum.

Espero que minha explicação seja compreensível. Eu só queria dar outro caso de uso para use em código gerado por macro.

@LukasKalbertodt Você poderia usar my_crate::AnEnum::$matcher => {} em match ?
Esqueça, eu sou o problema - acredito que precisaremos de https://github.com/rust-lang/rfcs/issues/959 para isso.

@jseyfried Não: $matcher pode ser um nome de variante (nesse caso, sua solução funcionaria) ou um nome de variável simples como em match x { simple_var_name => {} } . No último caso, não funcionaria AFAICT. (aliás, só queria mencionar outro caso de uso para mostrar que usar use é importante)

@jseyfried

Isso foi intencional em geral - uma vez que proc_macro_attributes deve ser expandido antes da derivação, se new fosse proc_macro_attribute então a expansão seria ambígua.

Ok, vou renomear #[derive(error_chain)] para #[derive(ErrorChain)] .

Parece que os atributos de derivados personalizados também entram em conflito com macros macro_rules , em vez de substituí-los como os derivados personalizados fazem com base na ordem de importação. Ou seja, este código compila:

#![feature(proc_macro)]
#[macro_use] extern crate error_chain; // macro_rules! error_chain
#[macro_use] extern crate derive_error_chain; // #[proc_macro_derive(error_chain, attributes(error_chain))]

#[derive(error_chain)] // No error. Resolves to custom derive
enum ErrorKind {
    /*#[error_chain]*/ // (1) As discussed above, can't use this any more since it conflicts with the name of the custom derive
    Foo,
}

Isso coincide com o comportamento do atual estável Rust, com a ressalva de que (1) funciona em estável. Eu até mesmo documentei explicitamente que os usuários que desejam usar #[macro_use] com a caixa error-chain precisarão importá-la antes de importar derive-error-chain .

Mas mesmo se eu renomear a derivação personalizada para ErrorChain para fazer (1) trabalhar com o recurso proc_macro (que já é uma alteração importante para o código estável):

#![feature(proc_macro)]
#[macro_use] extern crate error_chain; // macro_rules! error_chain
#[macro_use] extern crate derive_error_chain; // #[proc_macro_derive(ErrorChain, attributes(error_chain))]

#[derive(ErrorChain)] // Unique name, so no error
enum ErrorKind {
    #[error_chain] // (2)
    Foo,
}

ele ainda não compila - o atributo em (2) produz o erro: macro `error_chain` may not be used in attributes porque a macro macro_rules aparentemente entra em conflito com o atributo registrado pela derivação personalizada em vez de ser substituído como no primeiro caso.

Portanto, preciso renomear a derivação customizada e seu atributo. O atributo é usado muito mais (um em cada variante do enum) do que a derivação customizada (um em cada enum), então essa é uma alteração de quebra maior do que eu esperava. Eu entendo que esta é uma situação complicada de minha própria construção (reutilizando o nome da macro macro_rules para uma derivação customizada e seu atributo), mas este também é um código que foi compilado no estável desde que as derivações customizadas foram estabilizou, então eu não tinha nenhuma razão para pensar que seria um problema seis meses depois.

Pode ser feito de forma que os atributos de derivações customizadas substituam as macros macro_rules assim como as derivações customizadas substituem as macros macro_rules ? Na verdade, não vejo como pode haver qualquer ambigüidade entre eles, mas presumo que seja o mesmo motivo de quando uma macro macro_rules é importada após uma derivação personalizada do mesmo nome - que todas as macros são colocadas no mesmo namespace sem considerar que tipo de macro eles são.

Existe algum "lugar" menos formal para falar sobre macros proc? Gosta de um canal de IRC #rust-proc-macro ? Adoraria fazer pequenas perguntas sobre o recurso de vez em quando, mas parece errado enviar spam para este tópico: see_no_evil: E no canal #rust , a maioria das pessoas não trabalhou com proc-macros e especialmente a nova API proc_macro (já que é instável e tudo). Então: alguma ideia de onde discutir esse assunto?

@LukasKalbertodt #rust-internals , talvez, ou apenas inicie um novo tópico em / r / rust.

TokenStream::from_str entra em pânico quando usado fora de uma macro procedural (por exemplo, em um script de construção):

thread 'main' panicked at 'proc_macro::__internal::with_sess() called before set_parse_sess()!', /checkout/src/libproc_macro/lib.rs:758:8

Seria possível / desejável substituir esse pânico com a criação implícita de uma "sessão" fictícia? Ou talvez adicione uma API pública (com um caminho para a estabilização) para criar uma?

Alguém já olhou a literatura sobre macros de outros sistemas? Eu gostaria de ouvir a opinião das pessoas sobre isso. Vou falar sobre Scheme aqui, já que é com isso que estou mais familiarizado.

Estou trabalhando pessoalmente na implementação de syntax-rules para o Esquema R7RS em meu próprio projeto e descobri que syntax-case pode formar a base para apoiar sistemas macro higiênicos e anti-higiênicos ( defmacro e syntax-rules ). GNU Guile faz isso. syntax-case também tem suporte para defesas que podem executar validação de predicado adicional em listas de objetos de sintaxe (ou, algo entre as linhas de TokenStream em Scheme). Posso ver que Mark está sendo trabalhado e parece que foi inspirado em Bindings as Sets of Scopes .

Além disso, devemos também discutir se a computação arbitrária em tempo de compilação deve ser suportada? O Racket, na verdade, leva uma abordagem de "fase" inteira para as coisas, ao que parece, com begin-for-syntax permitindo definições e cálculos (?) No nível de tempo de compilação durante a expansão da macro. .

O controle sobre a higiene é muito possível com (datum->syntax <thing-to-copy-scope-from> <thing-to-apply-scope-to>) em Scheme, permitindo que você escape do escopo de uma macro e, em vez disso, assuma o escopo de um objeto fora do escopo imediato.

Veja este exemplo de The Scheme Programming Language, 3ª ed. por R. Kent Dybvig (Chez Scheme, agora na Cisco Systems): http://www.scheme.com/tspl3/syntax.html . O exemplo mostra (include "filename.scm") como uma macro syntax-case e permitindo que o intérprete use uma macro para configurar o tempo de execução para ler de um arquivo e continuar a avaliação. A questão mais profunda aqui é se queremos um sistema macro-macro para permitir que tais coisas aconteçam no tempo de expansão da macro e acionar cálculos em tempo de compilação, como acionar uma importação de arquivo (embora isso pareça ocorrer na funcionalidade do compilador direto, então talvez não queiramos fazer isso).

Quais devem ser os limites das macros? Eu imagino que o Rust, querendo reduzir seu tempo de compilação, quer restringir a avaliação em tempo de compilação (e especialmente evitar loops infinitos). Racket adotou a abordagem de "torre de preparadores e expansores" com fases, conforme referenciado em Lisp in Small Pieces. Queremos permitir coisas como permitir o acesso a uma API de tempo de compilação para executar E / S de arquivo e computação recursiva limitada? Devemos permitir que coisas como macros procedurais sejam capazes de transformar as especificações da planilha CSV em instruções switch?

Adoraria ouvir sobre outros sistemas! Ouvi dizer que Template Haskell tem uma abordagem interessante com tipos bem definidos para representar seu AST, e preguiça em Haskell pode substituir muitos usos de macros para estruturas de controle.

Desculpe se estou saindo da linha.

Quais devem ser os limites das macros?

Para macros procedurais , discutidas nesta edição, nenhuma . Uma macro procedural é uma extensão do compilador. Pode ser necessário um pouco de código C ++, executá-lo por meio do clang e adicionar o objeto resultante à compilação. Pode ser necessário algum SQL, consultar o banco de dados para encontrar o tipo de resultado correspondente e gerar o conjunto de resultados apropriado. Esses são casos de uso reais que as pessoas desejam fazer!

Observe que Rust tem outro sistema macro. Sua atualização foi aprovada como RFC 1584 e a implementação é monitorada por https://github.com/rust-lang/rust/issues/39412.

@VermillionAzure ,

O macro_rules , e sua atualização de acordo com RFC 1584 , é semelhante a syntax-rules . Se você tiver sugestões para aprimorá-los, https://github.com/rust-lang/rust/issues/39412 é provavelmente o melhor lugar para discutir isso.

As proc-macros, sobre as quais se trata desse problema, são como a forma geral de define-syntax . E esta RFC ( 1566 ) muito intencionalmente não define nada como syntax-case . Apenas uma interface para chamar uma função para transformar o fluxo de token.

A interface é definida de tal forma que algo como syntax-case pode ser implementado em uma caixa separada (biblioteca) e a intenção é fazer isso dessa forma. Se você quiser, fique à vontade para brincar. Qualquer protótipo e relatório sobre o quão fácil ou difícil de usar a API é certamente serão bem-vindos.

Acho que a ideia era definir macros como funções, como em lisp, mas ter uma macro, que retorna a macro, que macro_rules! define.

Então, o seguinte seria equivalente:

    macro_rules! foo {/*define macro here*/}
#[proc_macro]
pub fn foo(tokens: TokenStream) -> TokenStream {
    macro_case! tokens {/*define macro here*/} //takes `tokens` as first argument, returns a `TokenStream`
}

É assim que syntax-rules e syntax-case parecem funcionar no esquema.

@VermillionAzure
É isso que você gostaria?

@ porky11 Não, não parece. Eu só queria ver se macros Scheme seria uma ideia relevante para adicionar à discussão - é óbvio que, uma vez que macros procedurais se destinam a ser muito mais poderosas do que o sistema de macros syntax-case em Scheme, isso é trivial para implementar todos os macrossistemas em termos do poder arbitrário fornecido aqui.

@ jan-hudec É sensato permitir qualquer cálculo arbitrário como uma extensão do compilador sem qualquer tipo de garantia de segurança? Estou absolutamente chocado com a ideia de que macros procedurais serão tão poderosas aqui, mas algum usuário potencial do Rust consideraria isso uma desvantagem de usar pacotes? Não sou um especialista em segurança de forma alguma, mas as vulnerabilidades em qualquer biblioteca usada nas extensões do compilador poderiam ser facilmente usadas para transformar o compilador Rust em um vetor de ataque de forma maliciosa? Além disso, se ocorrer algum erro em bibliotecas usadas em macros procedurais (por exemplo, segfault acionado por código de biblioteca C incorreto), isso significa que o segfault iria surgir e fazer o compilador falhar sem a devida mensagem de erro?

Haveria uma maneira de encapsular erros que ocorrem em macros procedurais de uma forma que não afetaria nenhuma outra parte do compilador?

Outra ideia: quando as macros procedurais são executadas? Se as macros procedurais podem interagir com o código que tem efeitos colaterais que podem ser relevantes (por exemplo, comunicação com um servidor externo stateful, mutação de um banco de dados SQL externo, obtenção de uma chave de segurança para registrar em um sistema externo), então isso não significa que o a ordem em que as macros procedurais são acionadas pelo processo de compilação é importante?

Os pacotes @VermillionAzure Cargo já podem ter scripts de construção que executam código arbitrário em tempo de compilação, então macros procedurais não tornam as coisas piores nesse aspecto: você já precisa confiar em suas dependências. (Isso é um pouco mais fácil porque crates.io é imutável / somente anexa, e as dependências não são atualizadas automaticamente se você tiver um arquivo Cargo.lock : você só precisa confiar em versões específicas.) E mesmo se os scripts de compilação não existir, suas dependências ainda podem, por natureza, executar código arbitrário em tempo de execução. O tempo de compilação é muito pior?

Essa discussão me faz pensar em um problema relacionado, mas diferente.

Suponha que uma caixa defina duas macros proc: foo!() grava um arquivo temporário e bar!() lê esse mesmo arquivo. Um consumidor desta caixa invoca foo!() e bar!() no mesmo módulo. Então, se a compilação é bem-sucedida ou não, dependerá de qual foo!() ou bar!() é expandido primeiro. Essa ordem é definida pela implementação e, se um número suficiente de pessoas escrever um código como esse, pode se tornar o padrão de fato.

Não tenho certeza de quanto isso é um problema. Só estou preocupado em saber se isso levará a uma repetição das travessuras de ordenação do campo de struct.

@SimonSapin

Embora eu concorde com sua posição, devo salientar que há uma diferença significativa entre a execução em tempo de compilação e a execução em tempo de execução:

Ele fornece um modelo de ameaça diferente, uma vez que as coisas tendem a ser compiladas uma vez e, em seguida, implantadas em várias máquinas. (por exemplo, explorar a desatenção do mantenedor mais uma deficiência de sandbox em um cluster de construção de distro do Linux.)

@lfairy Sim, este é o problema exato que a Racket teve mais de uma década atrás em 2002. Matthew Flatt, o maior contribuidor da Racket, criou um artigo chamado "Macros composíveis e compiláveis: você quer quando?. R. Kent Dybvig, que trabalhou no Chez Scheme, também escreveu um artigo sobre as fases de avaliação para biblioteca / módulos em "Implicit Phasing in R6RS Libraries" .

@SimonSapin O tempo de compilação pode ser potencialmente muito pior. Se o seu compilador travar aleatoriamente ou executar um comportamento malicioso acionado pelo compilador, eu aposto que alguém acabaria escrevendo um enorme post no Reddit que seria intitulado "Módulos do Rust são inerentemente inseguros" ou algo parecido.

@VermillionAzure , não li os artigos com muito cuidado, mas não acho que sejam relevantes para a discussão, uma vez que os problemas enfrentados pelo Rust são muito diferentes dos problemas enfrentados pelo Scheme.

No Scheme, uma biblioteca pode fornecer funções e macros, de modo que o compilador deve classificar corretamente quais funções precisa em tempo de compilação e quais precisa em tempo de execução. No entanto, em Rust, uma caixa fornece macros procedurais ou funções de tempo de execução, portanto, essa divisão é (por enquanto) óbvia.

(Observe que uma caixa que fornece funções de tempo de execução também pode fornecer macros baseadas em regras (higiênicas), mas esses são mecanismos separados no Rust)

O problema de que @lfairy está falando é de ordenação da execução das funções do expansor. No Rust, a compilação pode ser paralela para arquivos separados e pode ser incremental, portanto, a ordem de execução dos expansores é indefinida. Mas algum dos artigos realmente aborda isso? Eu não vi isso.

@ jan-hudec Sim, acho que você está certo. Mas a ordem de avaliação certamente importará se os efeitos colaterais forem permitidos em tempo de compilação, a menos que você possa garantir que um determinado módulo não produz efeitos colaterais. Um módulo pode ser digitado?

Eu acho que uma macro procedural “provavelmente não deveria” ter efeitos colaterais porque alguns detalhes (veja abaixo) não são confiáveis, mas provavelmente não teremos um mecanismo de sistema de tipo na linguagem para forçá-los a serem puramente funcional.

Esses detalhes incluem a ordem de execução e simultaneidade em comparação com outras macros proc e se uma macro proc é reexecutada em uma compilação incremental. Para o último, podemos querer adicionar algo para declarar dependências semelhantes a rerun-if-changed em scripts de construção. E, como o script de construção, essas declarações podem estar incompletas ou com erros. Não podemos prevenir todos os bugs estaticamente.

Acho que devemos evitar garantir qualquer coisa sobre os efeitos colaterais (ou seja, as macros proc não podem contar com o funcionamento dos efeitos colaterais, nem podem contar com o acionamento novamente para outra coisa senão uma mudança de código no módulo em que são aplicadas ( portanto, nenhum estado global).

Posteriormente, podemos relaxar esse requisito com uma maneira de especificar reexecutar se alterado e outras coisas.

(Eu tenho um script de construção geral / proposta de segurança de macro proc que vai ajudar um pouco aqui, mas eu realmente não o escrevi ainda)

As macros proc IMO / derivadas personalizadas devem ser colocadas em um ambiente de área restrita sem qualquer E / S ou outra conexão externa e ser avaliadas pelo miri, talvez com um JIT cranelift.

@ est31 é uma boa ideia, mas coisas como infer_schema diesel! já
existe, o bindgen precisa ler arquivos e executar programas, até mesmo incluir! e
env! usar I / O.

Em 9 de novembro de 2017 06:19, "est31" [email protected] escreveu:

IMO proc macros / custom derivado deve ser colocado em um ambiente de sandbox
sem qualquer I / O ou outra conexão com o exterior e ser avaliado por
miri, talvez com um cranelift JIT.

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/rust-lang/rust/issues/38356#issuecomment-343124957 ,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/AAC3n5VOPdKBsu81Sp3tp2XlIQ05L865ks5s0t_PgaJpZM4LMWlc
.

Parece que # 40939 e # 44528 já foram mesclados ... @jseyfried, você poderia atualizar a lista de verificação?

@ mark-im atualizado.

As macros procedurais podem gerar macros procedurais?

@VermillionAzure Eu não tentei, mas não vejo uma razão pela qual eles não puderam. Claro, como macros proc gerando código de "tempo de execução", eles precisam estar em caixas separadas daquelas onde são usadas.

Em sincronia, encontramos uma limitação de proc_macro :: TokenNode hoje - delimitadores de bloco { ... } estão associados a apenas um Span no TokenStream de entrada, portanto não há maneira de acionar um erro que aponta apenas para o fechamento } . Rustc não parece ter essa limitação.

mod m {
    type T =
}
error: expected type, found `}`
 --> src/main.rs:3:1
  |
3 | }
  | ^

As melhores coisas que podemos fazer em uma macro proc são apontar para o último token dentro do bloco, apontar para o bloco inteiro ou apontar para o próximo token após o bloco, nenhum dos quais é realmente o que você deseja para um erro como acima.

Uma solução geral seria ter Span::start e Span::end retornando um Span de 1 caractere em vez de LineColumn como fazem atualmente, então expor uma maneira de vá de Span para a primeira linha / coluna do intervalo.

sp.begin().line // before
sp.line() // after

sp.end().line // before
sp.end().line() // after

sp.end() // after, not possible before

Mencionando @abonander que adicionou essa API em # 43604.

faça com que Span :: start e Span :: end retornem um Span de 1 caractere ao invés de um LineColumn como fazem atualmente, então exponha uma maneira de ir de Span para a primeira linha / coluna de span.

Isso faria com que Span adotasse o comportamento especial de que as listas delimitadas precisam, mas seria errado para todos os outros spans. Geralmente não é correto. Considere obter um intervalo para uma expressão como foo(hi) juntando coisas. Agora você quer apontar para foo e pegar sp.begin() , mas sp.begin() aponta apenas para o primeiro caractere de foo .

Acho que as melhores soluções seriam adicionar dois spans a proc_macro::TokenNode::Group ou permitir a criação de spans arbitrários.

Span::begin / end tipos de retorno podem ter que mudar: https://github.com/rust-lang/rust/pull/43604#issuecomment -327643229

Estou tentando fazer com que Span::def_site() resolva as coisas em relação ao que está no escopo de minha macro procedural. Estou entendendo mal como isso deveria funcionar?

Quase o mesmo código funciona se eu usar Span::call_site() e tiver MySend definido no main.rs, que é o que eu esperava. Não fui capaz de fazê-lo funcionar com def_site() .

#![feature(proc_macro)]

extern crate proc_macro;

use std::marker::Send as MySend;
use proc_macro::{TokenStream, TokenTree, TokenNode, Term, Delimiter, Span};

#[proc_macro]
pub fn impl_mysend_for(tokens: TokenStream) -> TokenStream {
    let span = Span::def_site();
    let ident = tokens.into_iter().next().unwrap();
    vec![
        TokenTree { span, kind: TokenNode::Term(Term::intern("unsafe")) },
        TokenTree { span, kind: TokenNode::Term(Term::intern("impl")) },
        TokenTree { span, kind: TokenNode::Term(Term::intern("MySend")) },
        TokenTree { span, kind: TokenNode::Term(Term::intern("for")) },
        ident,
        TokenTree { span, kind: TokenNode::Group(Delimiter::Brace, TokenStream::empty()) }
    ].into_iter().collect()
}
#![feature(proc_macro)]

extern crate mac;

struct S;
mac::impl_mysend_for!(S);

fn main() {}
error[E0405]: cannot find trait `MySend` in this scope
 --> src/main.rs:6:1
  |
6 | mac::impl_mysend_for!(S);
  | ^^^^^^^^^^^^^^^^^^^^^^^^^ did you mean `Send`?

Rastreando isso no lado do Syn: https://github.com/dtolnay/syn/issues/290.

@dtolnay
O problema aqui é que MySend é importado na fase 0 (ou seja, para a arquitetura do host durante a compilação cruzada), portanto, não está disponível na fase 1 (ou seja, ao compilar para a arquitetura de destino).

A solução aqui é permitir que as caixas proc-macro tenham dependências de fase 1 (arquitetura de destino) para que possamos importar itens da fase 1 para o escopo.

Hoje, uma solução alternativa é retornar:

quote! { // n.b. non-interpolated tokens from `quote!` have `Span::def_site()`
    mod dummy {
        extern crate std;
        use self::std::marker::Send as MySend;
        unsafe impl MySend for $ident {} // this line is equivalent to what you have above
    }
} 

Você também pode construir isso manualmente, estou usando apenas quote! por conveniência.

Devido à higiene, dummy / std / MySend nunca colidirá com qualquer outra coisa no escopo, então, por exemplo, é seguro usar esta macro mais de uma vez no mesmo módulo, é seguro para ident ser "MySend", etc.

Esse problema, bem como a necessidade e a solução para mod dummy , são descritos com mais detalhes em https://github.com/rust-lang/rust/issues/45934#issuecomment -344497531.

Infelizmente, até que as dependências da fase 1 sejam implementadas, isso não será ergonômico.

Obrigado @jseyfried! Isso funciona. Algumas perguntas de acompanhamento:

  • No código do meu comentário anterior, se eu alterar impl_mysend_for para gerar um impl para Send vez de MySend então tudo compila. Para que Send isso está resolvendo e por que não atinge a distinção entre fase 0 e fase 1? Isso está funcionando intencionalmente ou por acidente?

  • O que mais está no escopo que pode ser usado por meus def_site() tokens, como Send ?

  • Se MySend precisa vir de uma biblioteca (como imagine que estamos derivando serde::Serialize ), então o usuário final ainda precisa de serde em seu Cargo.toml, mesmo que não preciso que eles escrevam extern crate serde . Seria possível fazer extern crate com uma resolução def_site() ident contra o Cargo.toml da macro procedural, e extern crate com uma resolução call_site() ident contra o downstream Cargo.toml?

Para as caixas externas, suponho que as caixas precisariam ser disponibilizadas explicitamente para a fase 1 pela macro proc.

#[proc_macro_derive(Serialize, attributes(serde), crates(serde))]

@dtolnay

No código do meu comentário anterior, se eu alterar impl_mysend_for para gerar um impl para Send em vez de MySend, então tudo será compilado. Para que Send está resolvido e por que não atinge a distinção de fase 0 vs fase 1? Isso está funcionando intencionalmente ou por acidente?

Boa pergunta. No momento, o prelúdio está no escopo do local de definição (a menos que a caixa proc-macro seja #![no_implicit_prelude] ) e isso é um acidente (em algum sentido) devido à distinção entre fase 0 e fase 1, conforme você aponta .

No entanto, para ergonomia, acho que a fase 1 deve conter implicitamente std na raiz proc-macro (para que você possa sempre quote!(use std::...); ) e o prelúdio por conveniência / ergonomia e uma vez que estes já estão implícitos na fase 0. Haverá um PR para adicionar std na raiz na fase 1 em breve.

O que mais está no escopo que pode ser usado por meus tokens def_site (), como Enviar?

Além do prelúdio e (em breve) std conforme discutido acima, as únicas outras coisas no escopo na fase 1 são as próprias proc-macros (não as funções proc-macro, que são a fase 0).

Por exemplo,

#[proc_macro]
fn f(input: TokenStream) -> TokenStream { ... }

#[proc_macro]
fn g(_input: TokenStream) -> TokenStream {
    quote! {
        f!(); ::f!(); // These both resolve to the above proc macro
        f(); // This doesn't resolve since the function is in phase 0
    }
}

Seria possível fazer extern crate com um ident def_site () resolver contra o Cargo.toml da macro procedural, e extern crate com um call_site () ident resolver contra o Cargo.toml downstream?

Sim, exceto que acredito que uma caixa externa com Span::def_site() deve resolver contra as dependências da fase 1 (destino) da macro procedural Cargo.toml - as dependências da fase 0 de hoje estão vinculadas a bibliotecas compiladas para a plataforma host . Como as dependências da fase 1 ainda não existem, o nome da caixa externa é resolvido de forma anti-higiênica, o que é irritante, como você apontou.

Assim que tivermos as dependências da fase 1, não precisaremos citar extern crate s em cada expansão para começar, portanto, isso será menos problemático. No entanto, ainda devemos consertá-lo - o plano atual é tentar resolver primeiro as dependências de destino da caixa proc-macro e depois voltar para a resolução anti-higiênica com um ciclo de aviso de baixa prioridade para evitar a rotatividade.

Para as caixas externas, suponho que as caixas precisariam ser disponibilizadas explicitamente para a fase 1 pela macro proc.
#[proc_macro_derive(Serialize, attributes(serde), crates(serde))]

Interessante, poderíamos implementá-lo desta forma.

Eu estava pensando, em vez disso, que a caixa da fase 1 seria declarada na raiz da caixa proc-macro (por exemplo, #[phase(1)] extern crate foo; ) para que estivesse automaticamente disponível em todas as macros proc (por exemplo, quote!(use foo::bar); ). Como extern crate está saindo de qualquer maneira, poderíamos evitar declarar as caixas da fase 1 por completo - todas as dependências de destino do Cargo.toml estariam automaticamente no escopo na raiz da caixa proc-macro na fase 1.

Em meu código, descobri que pareço estar usando spans para dois propósitos: para controlar a resolução de nomes e para controlar mensagens de erro. Esses dois estão inseparavelmente ligados ou seria possível fazer um intervalo que mistura o aspecto de resolução de nome de um intervalo com os locais de mensagem de erro de linha / coluna de um intervalo diferente? Espero que seja uma necessidade comum.

Para ser mais específico, tenho uma característica trazida ao escopo dentro do def_site de minha derivação customizada e desejo invocar métodos de característica nos campos da estrutura do usuário. Se o tipo de campo não implementar o traço correto, quero que a mensagem de erro sublinhe o campo de estrutura correspondente.

Posso gerar a chamada do método com um intervalo def_site que compila e executa, mas as mensagens de erro, infelizmente, sempre apontam para o atributo derivar, como vimos com Macros 1.1.

  |
4 | #[derive(HeapSize)]
  |          ^^^^^^^^

Ou posso gerar a chamada de método com a mesma extensão do ident ou tipo do campo de struct, que mostra os sublinhados corretos, mas não consegue resolver o traço no escopo em meu def_site.

  |
7 |     bad: std::thread::Thread,
  |     ^^^^^^^^^^^^^^^^^^^^^^^^

Como posso resolver corretamente e mostrar os erros da maneira que desejo?

@dtolnay Esse é um excelente ponto, obrigado.

Acho que a maneira adequada de corrigir https://github.com/rust-lang/rust/issues/46489 pode ser fazer com que os #[derive(…)] tokens gerados tenham intervalos de resolução de nomes no mesmo escopo como a definição de tipo e mensagens de erro na invocação da macro quote! {} que os criou.

Qual é a história da higiene atualmente? Eu tenho uma função como macro procedural que costumava funcionar (4 meses atrás), mas a partir de rustc 1.24.0-nightly (b65f0bedd 2018-01-01) ele reclama que o argumento não pode ser encontrado no escopo.

Desculpe, deveria ter pesquisado o rastreador de problemas primeiro, parece que acabei de acessar https://github.com/rust-lang/rust/issues/46489.

Eu registrei o nº 47311, que acredito que atualmente bloqueia uma implementação correta de derivar (Deserializar). As macros procedurais não podem construir uma estrutura com campos privados.

Arquivado outro, # 47312 em que o acesso a um campo de estrutura de tupla não nomeado como self.0 tem requisitos diferentes na extensão do token . que o acesso a um campo de estrutura nomeado como self.x .

47311 e # 47312 são fixos em # 48082 e # 48083, respectivamente.

Tenho os dois PRs acima aguardando revisão / comentário.

Esse PR foi abandonado, mas revivido e continua a ser trabalhado em # 48465. Atualmente esperando na cratera.

@petrochenkov @nrc

Olhando para syntax::ext::expand , parece que proc_macro_attribute s não estão sendo processados ​​em vários contextos (não exaustivo):

  • em blocos ( fold_block() é um noop)
  • em declarações / expressões (# 41475, # 43988)
  • dentro de blocos de extern {} (# 48747)

O RFC 1566 não lista tipos de nós AST específicos aos quais as macros de atributos podem ser aplicadas, sugerindo que devem ser aplicáveis ​​a quase tudo. Mas isso pode ficar um pouco ridículo, então devemos estabelecer claramente o que precisa ser processado, mas não é, e onde os atributos nunca devem ser permitidos, mas atualmente podem ser (# 43988)

@abonander, a intenção é que os atributos de macro proc possam ser usados ​​em qualquer lugar em que um atributo regular possa estar e em nenhum outro lugar, o que eu acho que deveria cobrir todos os

@nrc está lá em qualquer lugar que enumera esses locais porque a referência apenas diz que os atributos podem ser aplicados a qualquer item . No entanto, tenho quase certeza de que os atributos lint também podem ser aplicados a blocos e instruções.

@nrc está lá em qualquer lugar que enumera esses locais porque a referência apenas diz que os atributos podem ser aplicados a qualquer item. No entanto, tenho quase certeza de que os atributos lint também podem ser aplicados a blocos e instruções.

Não há um afaik - há um RFC aceito e um sinalizador de recurso instável para atributos em qualquer expressão, mas acho que só estabilizamos em declarações e blocos. A referência está sem dados.

Esse problema:

Macros de verificação de estabilidade (proc-) (problema # 34079).

agora está fechado WRT proc-macros. Meu PR simplesmente não adicionou a verificação de estabilidade para macros Macros 2.0, e é por isso que o problema ainda está aberto (embora provavelmente deva ser apenas um novo problema).

@rfcbot fcp merge

Eu gostaria de propor um subconjunto de história de macros 2.0 a ser estabilizado como macros 1.2 para a versão Rust 1.28. Rust 1.28 entra todas as noites em 10 de maio de 2018 (~ 2,5 semanas a partir desta redação) e se tornará estável em 2 de agosto de 2018. Acho que o FCP pode terminar antes do corte de 10 de maio para 1.27 entrar na versão beta, mas gostaria de manter desligue qualquer estabilização aqui até depois que o corte tenha acontecido e atrase para a liberação 1.28.

Isso foi discutido internamente recentemente junto com uma série de problemas registrados por @petrochenkov e que devem ser corrigidos agora (mas ainda não lançados todas as noites). Acho que seria útil recapitular aqui, então este é concretamente o subconjunto que seria estabilizado.

Porém, lembre-se de que este é um subconjunto . Funcionalidade ausente aqui não significa que nunca será estabilizada ou removida do compilador. Em vez disso, essa funcionalidade permanecerá instável após essa passagem proposta de estabilização para ser estabilizada em uma data posterior.

Macros e o sistema de módulo

Principalmente coberto por https://github.com/rust-lang/rust/issues/35896 e agora
tendo terminado seu FCP, a idéia principal é que você pode usar use instruções para
importar macros. Por exemplo, código como este:

use some_proc_macro_crate::bar;

#[bar]
fn baz() {}

ou

use some_proc_macro_crate::bar;
bar!();

ou mesmo

pub use some_proc_macro_crate::bar; // reexport an attribute or macro

Isso introduz um terceiro namespace em Rust (além do valor / tipo
namespaces), o namespace da macro. Atributos, macro_rules e procedural
todas as macros residem no namespace maro.

A diferença do sistema de módulo completo é que apenas um elemento caminhos terão permissão para invocarmacros .
Por exemplo #[foo::bar] ou ::bar::baz!() não serão permitidos. Esse
restrição pode ser levantada um dia, mas este é um bom caminho conservador para
começar com.

Onde a expansão pode acontecer?

Os atributos só podem ser aplicados a não módulositens .
"itens" aqui incluem itens como itens de característica, itens de impl e módulo estrangeiro
Itens. A expansão do módulo ainda não será estável devido à higiene e
ramificações de implementação. Resta especificar e estabilizar isso em um
data posterior.

As instruções e macros de atributo de expressão ainda não serão estáveis. Isto é
principalmente devido à necessidade real de higiene ao nível da expressão (como
oposto ao nível do item). Isso é deixado para se estabilizar em uma data posterior.

Finalmente, as macros de atributo devem ter argumentos dentrodelimitadores .
Por exemplo #[foo] , #[foo(bar)] e #[foo { bar baz ... @ | ^ hello }]
são invocações válidas. Invocações como #[foo = "baz"] , #[foo bar] ou
#[foo ... = ( baz )] não será inicialmente estável.

Qual é a aparência das macros procedurais?

Como a derivação customizada, eles são definidos em caixas do tipo proc-macro crate.
Atributos e macros procedurais são definidos da seguinte forma:

extern crate proc_macro;
use proc_macro::TokenStream;

/// Invoked as `foo!()`
///
/// When invoked as `foo!(a b ( c ))` then the `TokenStream`
/// here will be `a b ( c )`.
///
/// The invocation is replaced with the `TokenStream` returned
#[proc_macro]
pub fn foo(a: TokenStream) -> TokenStream {
    // ...
}

/// Invoked as `#[bar]`
///
/// The first argument, `attr`, is the token stream inside of the attribute
/// itself. The second argument, `item`, is the token stream corresponding to
/// the item the attribute is attached to.
///
/// An attribute of the form `#[bar ( a b [ c ] )]` will have the `attr`
/// argument look like `a b [ c ]`. Note the lack of delimiters passed to
/// `attr`! An API may later be added to learn what delimiter a macro was
/// invoked with.
///
/// The `item` here is a tokenified version of the original item.
///
/// The return value here will contain all non-expanded attributes as well for
/// this attribute to inspect. The return value replaces the original item.
#[proc_macro]
pub fn bar(attr: TokenStream, item: TokenStream) -> TokenStream {
    // ...
}

E quanto à higiene?

Acima, foi visto que atributos e macros personalizados só podem ser expandidos em
contextos de item , notavelmente apenas gerando novos nós AST que são itens. Esse
significa que só temos que nos preocupar com a higiene de geração de novo item AST
nós.

Os novos itens terão a mesma higiene que macro_rules! tem hoje. Elas vão
não seja higiênico. Novos itens adicionados ao AST entrarão no mesmo namespace que
outros itens do módulo.

A API proc_macro .

A fim de permitir tudo isso, a seguinte área de superfície será estabilizada para
a caixa proc_macro :

pub struct TokenStream(_);

impl TokenStream {
    pub fn empty() -> TokenStream;
    pub fn is_empty(&self) -> bool;
}

impl Clone for TokenStream { ... }
impl Debug for TokenStream { ... }
impl Display for TokenStream { ... }
impl FromStr for TokenStream { ... }
impl From<TokenTree> for TokenStream { ... }
impl FromIterator<TokenTree> for TokenStream { ... }
impl FromIterator<TokenStream> for TokenStream { ... }
impl !Send for TokenStream { ... }
impl !Sync for TokenStream { ... }

impl IntoIterator for TokenStream {
    type Item = TokenTree;
    type Iter = token_stream::IntoIter;
}

pub mod token_stream {
    pub struct IntoIter(_);

    impl Iterator for IntoIter {
        type Item = ::TokenTree;
    }
}

pub enum TokenTree {
    Op(Op),
    Term(Term),
    Literal(Literal),
    Group(Group),
}

impl TokenTree {
    pub fn span(&self) -> Span;
    pub fn set_span(&mut self, span: Span);
}

impl Clone for TokenTree { ... }
impl Debug for TokenTree { ... }
impl Display for TokenTree { ... }
impl From<Op> for TokenTree { ... }
impl From<Term> for TokenTree { ... }
impl From<Literal> for TokenTree { ... }
impl From<Group> for TokenTree { ... }
impl !Send for TokenTree { ... }
impl !Sync for TokenTree { ... }

pub struct Span(_);

impl Span {
    pub fn call_site() -> Span;
}

impl Clone for Span { ... }
impl Copy for Span { ... }
impl Debug for Span { ... }
impl !Send for Span { ... }
impl !Sync for Span { ... }

pub struct Group(_);

pub enum Delimiter {
    Parenthesis,
    Brace,
    Bracket,
    None,
}

impl Group {
    pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group;
    pub fn stream(&self) -> TokenStream;
    pub fn delimiter(&self) -> Delimiter;
    pub fn span(&self) -> Span;
    pub fn set_span(&mut self, span: Span);
}

impl Clone for Group { ... }
impl Debug for Group { ... }
impl Display for Group { ... }
impl !Send for Group { ... }
impl !Sync for Group { ... }

impl Copy for Delimiter { ... }
impl Clone for Delimiter { ... }
impl Debug for Delimiter { ... }
impl PartialEq for Delimiter { ... }
impl Eq for Delimeter { ... }

pub struct Term(_);

impl Term {
    pub fn new(s: &str, span: Span) -> Term;
    pub fn span(&self) -> Span;
    pub fn set_span(&mut self, span: Span);
}

impl Copy for Term { ... }
impl Clone for Term { ... }
impl Debug for Term { ... }
impl Display for Term { ... }
impl !Send for Term { ... }
impl !Sync for Term { ... }

pub struct Op(_);

pub enum Spacing {
   Alone,
   Joint,
}

impl Op {
    pub fn new(op: char, spacing: Spacing) -> Op;
    pub fn op(&self) -> char;
    pub fn spacing(&self) -> Spacing;
    pub fn span(&self) -> Span;
    pub fn set_span(&mut self, span: Span);
}

impl Debug for Op { ... }
impl Display for Op { ... }
impl Clone for Op { ... }
impl Copy for Op { ... }
impl !Send for Op { ... }
impl !Sync for Op { ... }

impl Copy for Spacing { ... }
impl Clone for Spacing { ... }
impl Debug for Spacing { ... }
impl PartialEq for Spacing { ... }
impl Eq for Spacing { ... }

pub struct Literal(_);

impl Literal {
  // panic on infinity and NaN
  pub fn f{32,64}_{un,}suffixed(f: f{32,64}) -> Literal;

  pub fn i{8,16,32,64,128,size}_{un,}suffixed(n: i{8,16,32,64,128,size}) -> Literal;
  pub fn u{8,16,32,64,128,size}_{un,}suffixed(n: u{8,16,32,64,128,size}) -> Literal;

  pub fn string(s: &str) -> Literal;
  pub fn character(c: char) -> Literal;
  pub fn byte_string(b: &[u8]) -> Literal;

  pub fn span(&self) -> Span;
  pub fn set_span(&mut self, span: Span) -> Span;
}

impl Clone for Literal { ... }
impl Debug for Literal { ... }
impl Display for Literal { ... }
impl !Send for Literal { ... }
impl !Sync for Literal { ... }

Mais informações sobre esta API podem ser encontradas online ou no original PR

Estratégia de teste

Os sistemas macros 1.1 e macros 2.0 foram amplamente utilizados em dogfood por toda a
o ecossistema já há algum tempo. Notavelmente, toda esta proposta também é
extensivamente testado por meio da versão 0.3 da caixa proc-macro2 como
bem como a caixa syn . Ao longo dos testes, vários bugs foram
identificado e corrigido e o sistema atual parece sólido o suficiente para começar a
estabilizado. (para não dizer que está livre de bugs!)

O membro da equipe @alexcrichton propôs fundir isso. A próxima etapa é revisada pelo restante das equipes marcadas:

  • [x] @Kimundi
  • [] @SimonSapin
  • [x] @alexcrichton
  • [x] @aturon
  • [x] @cramertj
  • [x] @dtolnay
  • [x] @eddyb
  • [x] @joshtriplett
  • [x] @nikomatsakis
  • [x] @nrc
  • [] @pnkfelix
  • [x] @scottmcm
  • [x] @sfackler
  • [x] @withoutboats

Nenhuma preocupação listada atualmente.

Assim que a maioria dos revisores aprovar (e nenhum objeção), isso entrará em seu período final para comentários. Se você identificar uma questão importante que não foi levantada em qualquer ponto deste processo, fale!

Consulte este documento para obter informações sobre quais comandos os membros da equipe marcados podem me dar.

cc @ rust-lang / compiler, vocês que eu sei também estão muito interessados ​​nisso e fiquem à vontade para levantar quaisquer objeções. Se você tiver uma objeção de bloqueio, também posso registrá-la para você

Parece que, usando a API fornecida proposta para estabilização, macros proc podem facilmente gerar todos os tipos de tokens, ou copiá-los de entrada para saída, mas não podem inspecionar facilmente um fluxo de tokens mesmo em um nível superficial. Por exemplo, Literal não implementa Eq ou PartialEq . Alguma razão particular para isso? (E mesmo isso só permitiria a comparação com valores constantes especificados, em vez de extrair um valor e processá-lo no código.)

Dentro de uma implementação de macro proc, dada um TokenStream , o que uma macro proc pode fazer para introspectar um literal? Ou, por falar nisso, extrair o valor de um? Isso simplesmente não é suportado ainda? Existe um plano para apoiá-lo no futuro?

(Não estou tentando bloquear a estabilização do subconjunto proposto; gostaria apenas de entender melhor esse subconjunto e seus recursos pretendidos.)

@alexcrichton Novos itens terão a mesma higiene que macro_rules! faz hoje. Elas vão
não seja higiênico. Novos itens adicionados ao AST entrarão no mesmo namespace que
outros itens do módulo.

Não estou na equipe principal do Rust e não examinei completamente todas as discussões anteriores, mas isso parece muito ruim para mim. E se a macro quiser gerar itens auxiliares que só podem ser acessados ​​pela macro?

Em vez de descartar a higiene, acho melhor aplicar 100% de higiene total, mas fornecer uma maneira para as macros desativarem explicitamente a higiene para uma variável específica.

@joshtriplett você usaria o Display impl para Literal.

@joshtriplett sim, como @dtolnay mencionou, você usaria Display que é sobre o que uma caixa como literalext é construída. Em geral, a API destina-se a ser o "mínimo", não uma API de primeira classe que tem todos os recursos. Isso provavelmente virá mais tarde!

Esta rodada de estabilização deve ser "mínima ao máximo" no sentido de que fornece o mínimo de funcionalidade para cobrir todos os casos de uso (também conhecido Display para Literal e nada mais para interpretá-lo )

@Pauan não é ótimo, sim, mas é exatamente o mesmo que macro_rules! que está estável há anos e funciona em muitos contextos diferentes. Queremos possibilitar higiene e melhor tratamento aqui, mas ainda não estamos prontos. O construtor Span::call_site() é o eixo central aqui, onde, ao usá-lo, você está solicitando "nenhuma higiene". Eventualmente, estabilizaremos mais opções.

Infelizmente, ainda faltam anos para "higiene 100% completa" e o objetivo desta proposta é obter algo estável para a Ferrugem 2018

@alexcrichton Então o plano é estabilizá-lo agora com um buraco de higiene e depois algumas épocas / eras / edições consertar esse buraco de higiene?

Já que Span::call_site() existe, há algum motivo para que não haja higiene completa agora? É uma falta de mão de obra / tempo, ou há preocupações teóricas / semânticas que ainda precisam ser concretizadas em uma RFC?

@Pauan para reiterar que não existe um "buraco" real no sentido de que já existe hoje. Esta é exatamente a mesma história de higiene com macro_rules! e derivação personalizada. Esta proposta é efetivamente uma extensão disso, não introduzindo nada de novo.

Dada uma "história de higiene perfeita", sempre desejaremos alguma forma de opt-out. Atualmente, esse cancelamento é Span::call_site() (efetivamente). Ao apenas estabilizar isso, estamos apenas estabilizando um mecanismo de opt-out para higiene. Eventualmente, estabilizaremos mais funcionalidade para tornar os itens totalmente higiênicos quando necessário.

A higiene estabilizadora está muito mais longe. Existem (AFAIK) questões de pesquisa não resolvidas, não é uma questão de mão de obra.

@dtolnay Ah, entendo. Então, você gera o texto e, em seguida, analisa novamente o texto? Suponho que funcione por enquanto.

cc @ rust-lang / compiler, vocês que eu sei também estão muito interessados ​​nisso e fiquem à vontade para levantar quaisquer objeções. Se você tiver uma objeção de bloqueio, também posso registrá-la para você

Eu ainda tenho algumas coisas para revisar e um PR com alguns ajustes de API proc-macro em andamento.

Esta é exatamente a mesma história de higiene com macro_rules! e derivação personalizada.

Claro, mas eu pensei que um dos objetivos era fazer um sistema macro melhor , então apontar para o sistema existente (quebrado e limitado) não parece muito convincente para mim.

Dada uma "história de higiene perfeita", sempre desejaremos alguma forma de opt-out.

Absolutamente, mas o meu ponto é que, uma vez que é possível usar Span::call_site() quebrar higiene (e, assim, ganhar o macro_rules! funcionalidade) se desejado, tornando-higiênica por padrão você ganha acesso a ambos higiene e não-higiene. Ao passo que, se ele se estabilizar com a falta de higiene, então ficará preso apenas com a falta de higiene.

Eventualmente, estabilizaremos mais funcionalidade para tornar os itens totalmente higiênicos quando necessário.

Isso soa como um band-aid bastante irritante (e confuso): macros proc seriam higiênicas na maioria das situações (mas não itens), mas você pode usar um novo recurso para ativar a higiene para itens. Estou entendendo isso certo?

Existem (AFAIK) questões de pesquisa não resolvidas, não é uma questão de mão de obra.

Ok, isso é justo o suficiente. Eu entendo o desejo de conveniência e pragmaticismo. Contanto que o plano seja, eventualmente, ter uma higiene sólida por padrão em uma época / era / edição posterior, então estou bem em ter uma higiene ruim temporariamente. Só não quero que estabilizemos algo de que nos arrependeremos mais tarde.

Em certo sentido, já é "higiênico por padrão", mas o padrão ainda não é suportado. Span::call_site é o opt-out. :)

@Pauan Para elaborar o que @alexcrichton disse:

A construção de um identificador requer um Span , que contém informações de higiene. (Veja Term::new )

No futuro, podemos expor muitas maneiras diferentes de construir Span s, que refletem diferentes opções de higiene. Mas, por enquanto, iremos expor apenas Span::call_site que leva o escopo do chamador.

Desta forma, podemos evitar a estabilização de todo o sistema de higiene de imediato, ao mesmo tempo que mantemos aberta a possibilidade de adicionar higiene mais tarde. Acho que é um truque muito inteligente para contornar o problema!

@rpjohnst Então você quer dizer que a única maneira de retornar novos itens de uma macro proc é usar Span::call_site ?

Se sim, então isso parece perfeito: nesse caso, a higiene pode de fato ser adicionada no futuro de uma forma limpa e compatível com as versões anteriores.

@lfairy Ahhh, entendo, não tinha percebido que era necessário usar Span::call_site , pensei que havia um escopo anti-higiênico padrão implícito. Obrigado pela explicação! Você está certo, esse é um truque muito inteligente.

Como tal, não tenho mais objeções.

@alexcrichton

Atributos, macro_rules e procedural
todas as macros residem no namespace maro.

A última vez que verifiquei com #[feature(proc_macro)] , havia uma alteração incompatível com versões anteriores no código estável para a caixa derive-error-chain conforme descrito aqui. Pela declaração citada, parece que continuará a ser assim nas macros 1.2. Isso é correto?

@Arnavion oh querido, isso soa mal! Não podemos realmente fazer grandes mudanças incompatíveis com versões anteriores, então teremos que descobrir uma maneira de fazer com que isso continue funcionando. Uma solução possível seria adiar os erros "isto não é um atributo" até depois da expansão #[derive] .

Parece que # 48644 e # 47786 foram concluídos. Alguém poderia atualizar o OP?

@alexcrichton Houve alguma discussão sobre permitir o acesso aos dados internos de Literal e amigos? Eu entendo que eles não são mais enums por motivos de compatibilidade com versões anteriores, mas há algum motivo para não termos algo assim por Literal para começar:

    impl Literal {
        pub fn as_str(&self) -> Option<&str> {}
        pub fn to_int(&self) -> Option<i64> {}
        pub fn to_uint(&self) -> Option<u64> {}
        pub fn to_float(&self) -> Option<f64> {}
    }

Poderíamos até ter um monte de TryFrom impls para os diferentes subtipos literais.

@abonander já foi discutido, mas a conclusão foi adicioná-los mais tarde, em vez de torná-los necessários para a primeira etapa de estabilização. Eles podem ser construídos em crates.io com a implementação Display e temos espaço para adicionar no futuro

: bell: Isso agora está entrando em seu período de comentários final , de acordo com a revisão acima . :Sino:

@alexcrichton
!Send e !Sync impls para tipos de API de macro proc não são escritos explicitamente, mas inferidos.
Para algumas estruturas (por exemplo, Op ), o impl inferido difere do que é especificado em https://github.com/rust-lang/rust/issues/38356#issuecomment -383693017.
Provavelmente faz sentido adicionar os impls explicitamente.

Opa, sim! Abri https://github.com/rust-lang/rust/pull/50453 para resolver isso

@alexcrichton Temos um problema desagradável com Term::as_str : eu estava tentando esboçar uma prova informal de que, devido ao escopo estrito, &'a Term -> &'a str pode ser implementado para emprestar a algum interno de escopo e 'a nunca poderia ser maior do que o escopo do interno em si (o que só importa se a função for bem-sucedida, ou seja, se for chamada dentro do escopo em que o interno está ativo).

AFAICT, Term::as_str é válido, mas apenas assumindo a condição 'a .

Que é sustentado por, por exemplo, thread_local! , porque, embora deixe você escapar do Term , dá a você 'a s de vida muito curta, que se Term::as_str bem-sucedidos, são estritamente mais curtos do que o escopo interno.
Em geral, a expansão de proc_macro acontece, e porque Term é thread-local, há muito poucas maneiras de escapar de Term , e assumi thread_local! foi efetivamente o único.

Mas Box::leak também existe! Ainda é instável, mas hoje, Box::leak(Box::new(term)).as_str() retorna &'static str . Eu me pergunto se existem outras abstrações quebradas por Box::leak (cc @RalfJung)

OTOH, isso só é complicado porque Term não pode possuir os dados da string, pois são Copy .
Se removermos Copy em Term , poderíamos manter um Option<Cell<Rc<String>> preguiçoso lá.

@eddyb oh Term::as_str está programado para remoção e não foi incluído na estabilização aqui, então nada para se preocupar! Ele está apenas ficando por aí para que não quebremos proc-macro2 , mas assim que isso se estabilizar, podemos liberar uma alteração de última hora para proc-macro2

@eddyb Não sei o contexto aqui, mas Box::leak se justifica no meu modelo formal. Se você possui alguma memória para sempre (ou seja, em uma caixa), pode dizer totalmente que ela tem qualquer duração.

Eu tenho algumas perguntas:

  1. quote! não está estabilizado?
  2. Será que quote! terá o proc_macro_non_items aplicado a ele? A confirmação 79630d4fdfc775b241cae0a209edec2687a29f0f agora exige, mas quote! ainda está marcado #[unstable(feature = "proc_macro" ... .
  3. Os problemas de rastreamento serão arquivados para a estabilização de proc_macro_path_invoc , proc_macro_mod , proc_macro_expr e proc_macro_non_items ?

Pergunta secundária não relacionada: onde quote! implementado?

@mjbshaw Tão divertida história: é implementado em proc_macro::quote .
rustc_metadata finge que qualquer caixa chamada proc_macro contém uma macro procedural chamada quote , mas a implementação usada é de proc_macro rustc_metadata links contra.

Eu ainda tenho algumas coisas para revisar e um PR com alguns ajustes de API proc-macro em andamento.

Revise o PR: https://github.com/rust-lang/rust/pull/50473

Solicitação de recurso para a API 1.2: adicione um delimitador para colchetes angulares ( < / > ) para que coisas como <T> (em fn foo<T>() {} ) sejam analisadas como a Group . Não fazer isso torna a análise, por exemplo, de genéricos, desnecessariamente complicada.

@mjbshaw Não acho que funcionaria no nível do token tentar determinar se dois < > estão agrupados um com o outro. Por exemplo, na seguinte entrada de macro:

m!($A<$B, $C>=$D);

Talvez sejam duas expressões booleanas $A < $B e $C >= $D , ou talvez represente genéricos em um alias de tipo, por exemplo, expandindo para type $A <$B,$C> = $D; .

assert_both!(a<AMAX, b>=BMIN);

define_type_alias!(SwappedResult<E, T>=std::result::Result<T, E>);

Atualizei um pouco o OP, mas temos o que me parecem dois possíveis bugs detonadores relacionados à higiene, dependendo de como eles acabam:

Para aqueles que seguem este tópico, é provável que a API seja alterada do comentário FCP original em https://github.com/rust-lang/rust/pull/50473 proposto por @petrochenkov. O resumo até agora é:

  • Renomeie Term::new para Term::ident e valide a entrada
  • Adicione Term::lifetime e valide a entrada
  • Adicione Term::raw_ident e valide a entrada
  • Renomear Op para Punct
  • Valide a entrada em Punct::new
  • Renomear Op::op para Punct::as_char

Uma pequena solicitação de API que eu faria (possivelmente em # 50473 - @petrochenkov) é uma forma de anexar tokens a um TokenStream. Possivelmente:

impl Extend<TokenTree> for TokenStream

Acredito que isso tornaria possível eliminar o tipo quote::Tokens (que é basicamente Vec<TokenTree> ) e reduzir a proliferação de diferentes tipos no ecossistema que significam "alguns tokens" - rastreados em https : //github.com/dtolnay/syn/issues/205.

+1 na sugestão acima de @dtolnay.

O período de comentários final, com uma disposição para mesclar , conforme a revisão acima , agora está completo .

Eu tenho duas perguntas:

  1. Como já foi perguntado aqui , o que é cerca de quote! ? Qual deve ser a maneira padrão de criar o TokenStream final? Deve ser feito manualmente? Ou deve-se usar a caixa quote ? proc_macro::quote! deve ser estabilizado em algum ponto no futuro?

  2. Meu entendimento está correto no sentido de que a diferença entre declarar uma macro do tipo função e uma macro do tipo atributo é apenas o número de argumentos? Ou seja:

    /// Invoked as `foo!()`
    #[proc_macro]
    pub fn foo(a: TokenStream) -> TokenStream {
        // ...
    }
    
    /// Invoked as `#[bar]`
    #[proc_macro]
    pub fn bar(attr: TokenStream, item: TokenStream) -> TokenStream {
        // ...
    }
    

    A única diferença é que um leva um TokenStream como argumento, enquanto o outro leva dois . Não é um pouco sutil? Por que não usar #[proc_macro_attribute] e #[proc_macro_function_like] ? Isso foi discutido em algum lugar? Nesse caso, ficaria feliz se alguém pudesse vincular a discussão.

Obrigado pelo seu trabalho nisso! :)

Atributos @LukasKalbertodt são declarados atualmente com #[proc_macro_attribute] . Não sei se há uma intenção deliberada de alterar isso ou se é um erro de digitação na proposta do FCP.

Problema: os tokens analisados ​​de strings não obtêm Span::call_site spans: https://github.com/rust-lang/rust/issues/50050#issuecomment -390520317.

Acho que precisamos mudar isso e também mudar o que Span::call_site retorna para corrigir backtraces de macro, clippy e higiene de edição para tokens usando spans de site de chamada.

@LukasKalbertodt a quote! na caixa proc_macro não está sendo estabilizada como parte deste FCP, mas a caixa quote em crates.io baseia-se na API proposta aqui. Além disso, como @abonander apontou, as macros de atributos são declaradas com #[proc_macro_attribute] e #[proc_macro] são reservadas exclusivamente para foo!() -style macros

#[proc_macro_attribute] não deveria ser nomeado #[proc_attribute_macro] ou #[attribute_proc_macro] ?

@Zoxc Já temos #[proc_macro_derive] estável, então seria estranho não seguir isso para macros de atributo.

Podemos obter PartialEq<char> e PartialEq<Punct> para Punct (semelhante às implementações de Ident PartialEq )? Parece que deve ser bastante seguro adicionar. Estou feliz em escrever o PR, mas não quero se a ideia for proibida.

@mjbshaw PartialEq<Punct> também compararia extensões? PartialEq<char> parece bom, OTOH.

@eddyb PartialEq para Ident não compara spans (eu sei que esta é uma fonte proc-macro2 e não uma fonte proc-macro). Estou ambivalente quanto a se os spans estão incluídos na comparação, mas esperaria que Punct e Ident se comportassem de maneira semelhante a esse respeito. Punct também tem seus próprios Spacing , que presumo que seria incluído na comparação (embora talvez outros pensem de forma diferente).

Eu ficaria bem implementando apenas PartialEq<char> por Punct por enquanto. PartialEq<Punct> pode ser eliminado posteriormente.

@mjbshaw a caixa proc-macro2 tem a liberdade de não ser exatamente o que proc_macro é upstream e nos dá alguma liberdade para experimentar vários ajustes. Suspeito que, se funcionar bem em crates.io, podemos adicioná-lo a proc_macro , mas é claro que é compatível com versões anteriores adicionar a proc_macro então estamos começando com o mínimo e podemos aumentar a partir daí, uma vez que esteja estável.

Eu fiz algum trabalho de triagem para todos os bugs relacionados às macros 1.2 . A maioria dos bugs pode ser classificada em "bugs graves" ou "todos os bugs relacionados com a amplitude". Atualmente os bugs relacionados ao span afetam puramente as mensagens de erro (já que a intenção das macros 1.2 não é alterar a resolução). O bug restante não relacionado ao span (também conhecido como sério) é https://github.com/rust-lang/rust/issues/50050 , para o qual @petrochenkov tem uma solução .

A classe Gnome foi interrompida com as mudanças em proc_macro2 / syn / quote, mais a porta de recursos para gerar módulos a partir de macros proc. Está consertado agora, felizmente.

O que devo monitorar para acompanhar as mudanças neste pequeno ecossistema?

@federicomenaquintero Se você usar recursos instáveis, considere ter um trabalho regular de CI (diário, semanal, o que funcionar para você) que compila seu código com o Nightly mais recente e o notifica se ele falhar. (Travis-CI suporta a ativação de um "cron job" em suas configurações.)

@SimonSapin obrigado, é uma boa ideia. Fixamos versões dessas caixas em nosso Cargo.toml, mas pode ser hora de remover os números de versão e apenas deixar o Cargo baixar o mais recente. Esta é a maneira correta de fazer isso?

@federicomenaquintero Isso está cada vez mais fora do tópico, então se você quiser continuar esta discussão, faça-o em outro lugar, como no IRC ou http://users.rust-lang.org/ , mas a recomendação geral é que os aplicativos (como oposto às bibliotecas) devem enviar Cargo.lock junto com seu código-fonte, o que efetivamente fixa as dependências. Em Cargo.toml , declarar uma dependência como foo = "1.2.3" é recomendado, implicitamente significa "essa versão ou posterior, se compatível de acordo com SemVer".

Então, acabei de encontrar um problema com macros procedurais que está me impedindo de desenvolver uma caixa que desejo fazer.

Considere o seguinte:

#[my_attribute]
struct MyStruct {
  #[other_attribute]
  field: String,
}

No momento, isso não funciona porque os recursos proc_macro e custom_attributes são necessários, mas não podem ser usados ​​ao mesmo tempo. Pelo que entendi, a estabilização de macros proc eliminaria a necessidade de sinalizadores de recursos?

A outra coisa é que desta forma #[my_attribute] poderia gerar um código que evita que #[other_attribute] jamais seja executado. O que seria muito legal se no atributo "outer" eu pudesse registrar atributos internos que funcionam da mesma forma que #[derive(Foo)] funcionam.

O que há sobre o comentário de @SimonSapin sobre a sessão fictícia ?

Seria possível / desejável substituir esse pânico com a criação implícita de uma "sessão" fictícia? Ou talvez adicione uma API pública (com um caminho para a estabilização) para criar uma?

Acho que seria muito útil ter uma sessão simulada. Escrever teste de unidade para proc-macro engradados é praticamente impossível ou pelo menos muito inconveniente de outra forma. Além disso, não é apenas TokenStream::from_str , mas também outras funções que requerem uma sessão.

@alexcrichton

Eu fiz algum trabalho de triagem para todos os bugs relacionados às macros 1.2. A maioria dos bugs pode ser classificada em "bugs graves" ou "todos os bugs relacionados com a amplitude".

Eu gostaria de atualizar https://github.com/rust-lang/rust/issues/50504 para o status "sério" - o problema do módulo descrito há apenas um sintoma de um problema mais profundo - IDs de expansão para macros proc não sendo registrado corretamente. Não sei que outras consequências isso pode ter.
Há um PR corrigindo o problema subjacente (https://github.com/rust-lang/rust/pull/51952), mas há regressões da correção e eu não as examinei ainda.

Publiquei um PR para estabilizar mais macros procedurais em https://github.com/rust-lang/rust/pull/52081

@petrochenkov soa bem para mim, vou comentar no PR

Acabei de postar sobre o problema de rastreamento de nomenclatura de macro sobre problemas com atributos proc_macro_derive filhos e como eles interagem com o sistema de nomenclatura. Há outro problema com a maneira proc_macro_derive atributos #[derive(foo::Parent)] poderia ter um atributo filho #[foo::Child] , mas a macro de derivação não teria como, em sua face, identificar se o atributo filho é realmente seu próprio filho, pois não pode realizar a pesquisa de nome. Por enquanto, não tenho uma solução fácil, mas acho que é algo que deveria estar no radar para o futuro dos atributos interdependentes; não há razão para que proc_macro_attribute atributos não possam querer interagir de uma maneira que esbarre em problemas de pesquisa semelhantes.

Tentei compilar meu projeto hoje, mas algo está quebrado que provavelmente está relacionado a esse problema. Todas as mensagens de erro apresentavam a mensagem: "(consulte o problema # 38356)". Foi assim que cheguei aqui.
Incluo aqui a mensagem de erro que recebi durante a compilação. Eu também incluo meu Cargo.toml.

Acho isso muito surpreendente, pois meu projeto está vinculado a uma versão noturna específica do Rust (rustc 1.29.0-nightly (9 Budap458c9 09/07/2018)) O que poderia ter mudado? Possivelmente uma biblioteca atualizada?

Cargo.toml

[[bin]]
name = "main"
path = "src/bin/main.rs"

[dependencies]
log = "0.4"
pretty_env_logger = "0.2"

rand = "0.4"
ring = "=0.13.0-alpha"
untrusted = "0.6"

bytes = "0.4"
futures = "0.1"
tokio-io = "0.1"
tokio-core = "0.1"
futures-await = "0.1"

capnp = "0.8"
rusqlite = "0.13"

async_mutex = { git = "https://github.com/realcr/async_mutex", rev = "a1d973ed7" }

num-bigint = "0.2.0"
num-traits = "0.2.4"

[dev-dependencies]

[dependencies.byteorder]
version = "1.1"
features = ["i128"]

[build-dependencies]
capnpc = "0.8"

[profile.release]
debug = true

Erros de compilação

$ cargo test
    Updating git repository `https://github.com/realcr/async_mutex`
   Compiling proc-macro2 v0.4.8                                                                                                                                                                                    
   Compiling cswitch v0.1.0 (file:///home/real/projects/d/cswitch)                                                                                                                                                 
error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)8: proc-macro2                                                                                                                        
  --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:33:40
   |
33 |     let works = panic::catch_unwind(|| proc_macro::Span::call_site()).is_ok();
   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:213:13
    |
213 |     Nightly(proc_macro::token_stream::IntoIter),
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:438:11
    |
438 | impl From<proc_macro::Span> for ::Span {
    |           ^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:284:13
    |
284 |     Nightly(proc_macro::SourceFile, FileName),
    |             ^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:332:13
    |
332 |     Nightly(proc_macro::Span),
    |             ^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:461:13
    |
461 |     Nightly(proc_macro::Ident),
    |             ^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:523:13
    |
523 |     Nightly(proc_macro::Literal),
    |             ^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:116:47
    |
116 |                     Delimiter::Parenthesis => proc_macro::Delimiter::Parenthesis,
    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:117:43
    |
117 |                     Delimiter::Bracket => proc_macro::Delimiter::Bracket,
    |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:118:41
    |
118 |                     Delimiter::Brace => proc_macro::Delimiter::Brace,
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:119:40
    |
119 |                     Delimiter::None => proc_macro::Delimiter::None,
    |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:122:33
    |
122 |                 let mut group = proc_macro::Group::new(delim, tt.stream.inner.unwrap_nightly());
    |                                 ^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:128:39
    |
128 |                     Spacing::Joint => proc_macro::Spacing::Joint,
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:129:39
    |
129 |                     Spacing::Alone => proc_macro::Spacing::Alone,
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:131:30
    |
131 |                 let mut op = proc_macro::Punct::new(tt.as_char(), spacing);
    |                              ^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:113:17
    |
113 |         let tt: proc_macro::TokenTree = match token {
    |                 ^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:238:13
    |
238 |             proc_macro::TokenTree::Group(tt) => {
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:240:21
    |
240 |                     proc_macro::Delimiter::Parenthesis => Delimiter::Parenthesis,
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:241:21
    |
241 |                     proc_macro::Delimiter::Bracket => Delimiter::Bracket,
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:242:21
    |
242 |                     proc_macro::Delimiter::Brace => Delimiter::Brace,
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:243:21
    |
243 |                     proc_macro::Delimiter::None => Delimiter::None,
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:250:13
    |
250 |             proc_macro::TokenTree::Punct(tt) => {
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:252:21
    |
252 |                     proc_macro::Spacing::Joint => Spacing::Joint,
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:253:21
    |
253 |                     proc_macro::Spacing::Alone => Spacing::Alone,
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:259:13
    |
259 |             proc_macro::TokenTree::Ident(s) => ::Ident::_new(Ident::Nightly(s)).into(),
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:260:13
    |
260 |             proc_macro::TokenTree::Literal(l) => ::Literal::_new(Literal::Nightly(l)).into(),
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:289:20
    |
289 |     fn nightly(sf: proc_macro::SourceFile) -> Self {
    |                    ^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:339:27
    |
339 |             Span::Nightly(proc_macro::Span::call_site())
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:347:27
    |
347 |             Span::Nightly(proc_macro::Span::def_site())
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:369:30
    |
369 |     pub fn unstable(self) -> proc_macro::Span {
    |                              ^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:430:32
    |
430 |     fn unwrap_nightly(self) -> proc_macro::Span {
    |                                ^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:439:24
    |
439 |     fn from(proc_span: proc_macro::Span) -> ::Span {
    |                        ^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:468:48
    |
468 |             Span::Nightly(s) => Ident::Nightly(proc_macro::Ident::new(string, s)),
    |                                                ^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:475:48
    |
475 |             Span::Nightly(s) => Ident::Nightly(proc_macro::Ident::new_raw(string, s)),
    |                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:495:32
    |
495 |     fn unwrap_nightly(self) -> proc_macro::Ident {
    |                                ^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:583:30
    |
583 |             Literal::Nightly(proc_macro::Literal::f32_unsuffixed(f))
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:591:30
    |
591 |             Literal::Nightly(proc_macro::Literal::f64_unsuffixed(f))
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:599:30
    |
599 |             Literal::Nightly(proc_macro::Literal::string(t))
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:607:30
    |
607 |             Literal::Nightly(proc_macro::Literal::character(t))
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:615:30
    |
615 |             Literal::Nightly(proc_macro::Literal::byte_string(bytes))
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:636:32
    |
636 |     fn unwrap_nightly(self) -> proc_macro::Literal {
    |                                ^^^^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/lib.rs:322:30
    |
322 |     pub fn unstable(self) -> proc_macro::Span {
    |                              ^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:531:34
    |
531 |                   Literal::Nightly(proc_macro::Literal::$name(n))
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
...
552 | /     suffixed_numbers! {
553 | |         u8_suffixed => u8,
554 | |         u16_suffixed => u16,
555 | |         u32_suffixed => u32,
...   |
565 | |         f64_suffixed => f64,
566 | |     }
    | |_____- in this macro invocation
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:543:34
    |
543 |                   Literal::Nightly(proc_macro::Literal::$name(n))
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
...
568 | /     unsuffixed_integers! {
569 | |         u8_unsuffixed => u8,
570 | |         u16_unsuffixed => u16,
571 | |         u32_unsuffixed => u32,
...   |
578 | |         isize_unsuffixed => isize,
579 | |     }
    | |_____- in this macro invocation
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
  --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:45:34
   |
45 |             TokenStream::Nightly(proc_macro::TokenStream::new())
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
  --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:53:46
   |
53 |             TokenStream::Nightly(tts) => tts.is_empty(),
   |                                              ^^^^^^^^
   |
   = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:123:23
    |
123 |                 group.set_span(span.inner.unwrap_nightly());
    |                       ^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:132:20
    |
132 |                 op.set_span(tt.span().inner.unwrap_nightly());
    |                    ^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:239:38
    |
239 |                 let delim = match tt.delimiter() {
    |                                      ^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:245:74
    |
245 |                 let stream = ::TokenStream::_new(TokenStream::Nightly(tt.stream()));
    |                                                                          ^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:247:58
    |
247 |                 g.set_span(::Span::_new(Span::Nightly(tt.span())));
    |                                                          ^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:251:40
    |
251 |                 let spacing = match tt.spacing() {
    |                                        ^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:255:43
    |
255 |                 let mut o = Punct::new(tt.as_char(), spacing);
    |                                           ^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:256:58
    |
256 |                 o.set_span(::Span::_new(Span::Nightly(tt.span())));
    |                                                          ^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:290:45
    |
290 |         let filename = stable::file_name(sf.path().to_string());
    |                                             ^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:304:44
    |
304 |             SourceFile::Nightly(a, _) => a.is_real(),
    |                                            ^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:355:69
    |
355 |             (Span::Nightly(a), Span::Nightly(b)) => Span::Nightly(a.resolved_at(b)),
    |                                                                     ^^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:363:69
    |
363 |             (Span::Nightly(a), Span::Nightly(b)) => Span::Nightly(a.located_at(b)),
    |                                                                     ^^^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:424:55
    |
424 |             (Span::Nightly(a), Span::Nightly(b)) => a.eq(b),
    |                                                       ^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:482:50
    |
482 |             Ident::Nightly(t) => Span::Nightly(t.span()),
    |                                                  ^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:489:56
    |
489 |             (Ident::Nightly(t), Span::Nightly(s)) => t.set_span(s),
    |                                                        ^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:623:56
    |
623 |             Literal::Nightly(lit) => Span::Nightly(lit.span()),
    |                                                        ^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)
   --> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:630:62
    |
630 |             (Literal::Nightly(lit), Span::Nightly(s)) => lit.set_span(s),
    |                                                              ^^^^^^^^
    |
    = help: add #![feature(proc_macro)] to the crate attributes to enable

error: aborting due to 63 previous errors

For more information about this error, try `rustc --explain E0658`.
error: Could not compile `proc-macro2`. 

Você tem uma ideia do que pode ter dado errado e como pode ser consertado? Obrigado!

@realcr para a caixa proc-macro2 você pode simplesmente executar cargo update e isso deve resolver o problema!

@alexcrichton Não acho que seja esse o problema aqui. Prefiro pensar que @realcr já atualizou proc-macro2 pois a mensagem de erro diz proc-macro2-0.4.8 todos os lugares. O problema é que a versão noturna foi corrigida para uma que não inclui o # 52081. Eu tive o mesmo problema hoje e me perguntei por que proc-macro2 apenas superou a versão secundária. Mas não estou muito familiarizado com a forma como proc-macro2 lida com a compatibilidade.

@realcr Tente atualizar seu compilador noturno ou imponha uma proc-macro-2 versão < 0.4.8 em seu projeto.

@alexcrichton , @LukasKalbertodt : Obrigado pela resposta rápida.
Acabei de atualizar meu compilador noturno para a versão mais recente. Isso eliminou os problemas proc-macro-2, mas recebi muitos novos erros de compilação. Exemplo:

error[E0277]: the trait bound `impl futures::Future: std::future::Future` is not satisfied
   --> src/networker/messenger/handler/handle_neighbor.rs:191:25
    |
191 |         let signature = await!(self.security_module_client.request_signature(failure_signature_buffer))
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::future::Future` is not implemented for `impl futures::Future`
    |
    = note: required by `std::future::poll_in_task_cx`
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

...

error[E0627]: yield statement outside of generator literal
   --> src/networker/messenger/handler/handle_neighbor.rs:403:13
    |
403 | /             await!(self.reply_with_failure(remote_public_key.clone(), 
404 | |                                            channel_index,
405 | |                                            request_send_msg.clone()))?
    | |_____________________________________________________________________^
    |
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

Para confirmar, minha versão atual do rustc:

rustc 1.29.0-nightly (1ecf6929d 2018-07-16)

Para rastrear a origem do problema, tentei compilar o exemplo básico de futures_await, mas também parou de funcionar. Vou registrar um problema lá para que possamos resolver esse problema.

@LukasKalbertodt https://github.com/alexcrichton/proc-macro2#unstable -features

Porque você está usando o recurso instável.

@realcr, seu novo problema de compilação não está relacionado a este problema, mantenha-se no tópico.

@TeXitoi : Sinta-se à vontade para editar ou remover qualquer coisa se achar que não é relevante. Eu tento o meu melhor para te ajudar, é difícil para mim saber o que está em questão e o que não está. A mensagem de erro "(veja o problema 38356)" é o que me trouxe aqui.

Tentei atualizar minha versão do compilador e recebi este erro. Meu código

#![no_std]
#![feature(proc_macro)]
#![feature(proc_macro_gen)]
#![feature(custom_attribute)]
#![feature(alloc)]

#[macro_use(eth_abi)]
extern crate pwasm_abi_derive;

e o erro que diz que não usei #![feature(proc_macro)] , mas usei!

error[E0658]: attribute procedural macros are experimental (see issue #38356)
  --> src\lib.rs:67:5
   |
8  | #[macro_use(eth_abi)]
   |             ------- procedural macro imported here
...
67 |     #[eth_abi(TokenEndpoint, TokenClient)]
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: add #![feature(proc_macro)] to the crate attributes to enable

@Pzixel você vai querer mudar #![feature(proc_macro)] para #![feature(use_extern_macros)] e isso deve resolver

Acho que você também precisará usar o sistema de módulo para importar macros procedurais (e certifique-se de ter um compilador noturno atualizado)

@alexcrichton sim, acabei de descobrir, graças ao artigo . No entanto, ainda não funciona:

error[E0433]: failed to resolve. Use of undeclared type or module `Vec`
  --> src\lib.rs:66:5
   |
66 |     #[eth_abi(TokenEndpoint, TokenClient)]
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use of undeclared type or module `Vec`

error[E0412]: cannot find type `Vec` in this scope
  --> src\lib.rs:66:5
   |
66 |     #[eth_abi(TokenEndpoint, TokenClient)]
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope

As regras de importação para macros também mudaram? Ou não consigo entender por que começou a reclamar aqui.

@Pzixel que pode ser um bug na macro procedural ou no compilador, você pode registrar um problema dedicado a isso?

Bem, acho que devo reescrever meu código primeiro para que pelo menos pareça estar funcionando :) No momento não, vocês mudaram muito com esse recurso. BRB quando terminar. Se não desaparecer, eu crio um problema.

@alexcrichton
Você sabe qual "pré-processamento" é aplicado aos tokens antes de serem passados ​​para uma macro procedural?
Eu sei que derives tem pelo menos $crate eliminado e cfg expandido antes de obter os tokens de entrada (mais as viagens de ida e volta através de strings não são totalmente isentas de perdas, mas isso pode ser corrigido).

Devemos ter certeza de que nada disso aconteça com macros procedurais recentemente estabilizadas e que elas obtenham tokens de entrada com precisão (bugs de módulo).

@alexcrichton desculpe, cargo expand não funciona nesta caixa por algum motivo. Não é possível confirmar se o problema está do meu lado ou no compilador um. Portanto, continuarei me culpando até que essa possibilidade não seja completamente excluída.

@petrochenkov a expansão de proc-macros foi muito bem testada até agora, então estou muito menos preocupado com isso do que com as peças de resolução de nomes. Não estou pessoalmente ciente do pré-processamento, mas sei que há uma ordem de expansão em que os derivados são executados por último, os cfgs são executados primeiro e, caso contrário, é principalmente iterativa.

Mas eu concordo que é bom fazer uma auditoria!

Talvez eu tenha perdido algo óbvio. Mas, não há como chamar uma função, usar um tipo, etc. da caixa de definição de proc_macro de uma expansão de macro procedural? (ou qualquer outra caixa conhecida da caixa proc_macro, FWIW)

Existem soluções alternativas , mas AFAIU elas não funcionarão se a caixa for renomeada pelo usuário da macro procedural.

As caixas construir as caixas que as utilizam. Eles não são dependências de tempo de execução. Você pode pensar em toda a caixa proc-macro como uma espécie de plugin do compilador, não uma biblioteca "normal".

Isso é especialmente visível durante a compilação cruzada: como scripts de construção, proc-macros são compiladas para a plataforma host, não para a plataforma de destino.

@SimonSapin Eu concordo com você, mas pode ser muito útil delegar parte do trabalho a uma função fornecida por uma caixa, mesmo que não seja a caixa proc-macro (por exemplo, em meu link acima, o X-derive crate tenta usar uma função de X crate). Do contrário, isso significaria que todo código gerado por proc-macros deve ser autocontido ou fazer suposições sobre o estado do site de chamada.

Então, eu posso entender que o rustc ainda não está pronto para este tipo de recurso (porque eu acho que funcionaria melhor sem a necessidade de separar as caixas proc-macro das não proc-macro, que parecem estar em algum lugar no longo prazo roteiro de prazo). Mas eu me pergunto, se a interface atual usando apenas TokenStreams estiver estabilizada, será possível atualizar esse recurso mais tarde? Não seria necessário algo como um tipo de token PathToBeResolvedFromTopOfGeneratingProcMacroCrate ? (o que seria uma alteração significativa se adicionado posteriormente, afaiu)

Talvez seja possível tornar as coisas mais flexíveis, mas isso parece um tanto distante.

Nesse ínterim, se você é um autor de biblioteca, considere ter uma caixa foo-proc-macros ou foo-derive para macros procedurais e uma biblioteca "normal" foo que contém tempo de execução código, mas também reexporta as macros procedurais. Dessa forma, a API voltada para o usuário pode ser mantida em uma única caixa. Isso é o que serde faz (em algumas configurações) https://github.com/serde-rs/serde/blob/v1.0.71/serde/src/lib.rs#L304

Vale a pena apontar, porém, que esta solução alternativa ainda não resolve o problema de o usuário renomear a caixa raiz (por exemplo

@Ekleog , o TokenStream é um fluxo de TokenTree s e cada TokenTree tem Span associado, que carrega as informações de escopo. Exceto que atualmente não há maneira de criar um Span para qualquer outro escopo que não seja “chamar site” (ou vazio). Basicamente, encontrar uma maneira razoavelmente ergonômica de criar Span s referindo-se a uma caixa específica é o que é necessário.

A razão pela qual perguntei é que a caixa de seleção não está marcada. Seria bom assinalá-lo então!

Com #![feature(proc_macro)] estabilizado, o que resta deste problema?

@ jan-hudec Oh, pensei que Span s eram apenas para relatórios de erros, pois as primeiras postagens de blog mencionavam uma estrutura Hygiene (ou com nome semelhante) que desempenhava essa função. Eu presumi que eles haviam desaparecido e aparentemente estava errado. Obrigado! :)

Com o #! [Feature (proc_macro)] estabilizado, o que resta desse problema?

Idealmente, novos problemas precisam ser registrados para todos os problemas individuais restantes e não para recursos estabilizados, e então esse problema pode ser fechado (da mesma forma como foi feito para https://github.com/rust-lang/rust/issues/ 44660).

Ah, pensei que os Spans serviam apenas para relatar erros, pois as primeiras postagens do blog mencionavam uma estrutura de Higiene (ou com nome semelhante) que desempenhava essa função. Eu presumi que eles haviam desaparecido e aparentemente estava errado. Obrigado! :)

IIUC, Spans são a principal forma de rastrear o contexto de higiene.

@ mark-im Tipo de. Eles incluem informações de localização do código-fonte (para mensagens / diagnósticos voltados para o usuário) e contexto de sintaxe (ou seja, informações de higiene).

Dada a quantidade de discussão / tráfego que esse problema recebe, faz sentido mover proc_macro_diagnostic para seu próprio problema de rastreamento? Também gostaria de descobrir quais são os bloqueadores para a estabilização desse recurso e ver se podemos empurrá-lo. Diesel usa, e tem sido ótimo até agora. A falta desse recurso no stable está levando a comunidade a criar soluções alternativas, como a versão mais recente do syn usando compile_error! em seu lugar.

Portanto, estou interessado em ajudar a estabilizar os métodos em Span e na estrutura LineColumn. Analisarei os problemas em aberto no primeiro comentário, mas se alguém quiser indicar a um novato o compilador em uma direção específica, eu agradeceria: +1:

O portão de recursos proc_macro_gen me apontou aqui, mas na lista de verificação no topo não vejo nada que obviamente se refira a uma macro (proc_) que gere outras definições de macro. Isso foi considerado (exceto no portão de recursos do rustc)?

@jjpe , provavelmente é melhor neste momento

@alexcrichton Estou perfeitamente bem com isso, é mais que no processo de olhar para o recurso proc_macro_gen , ele me referiu aqui apenas para encontrar quase nenhuma menção a ele. Isso é um pouco estranho para mim, então decidi mencioná-lo, pelo menos.

@xieyuheng Qual seria a CodeString / Code , isto é, qual seria sua semântica?
Lembre-se de que TokenStream nada mais é do que o código-fonte literalmente, exceto como uma série de valores de token em vez de um texto incômodo.

A API estendida para TokenStream (com TokenTree ) é estável em 1.29. A importação de macros proc semelhantes a funções será estável no 1.30.

Parece que desde rustc 1.30.0-nightly (63d51e89a 2018-09-28) não é mais possível percorrer o código dentro do módulo em um arquivo separado. Se você processar mod module; , obterá TokenStream contendo mod module; , WYSIWYG.

Eu entendo que isso é necessário para preservar vãos, higiene e consistência com o código processado. Existe ou haverá alguma maneira de trabalhar com o conteúdo dos módulos em arquivos separados? Sua falta pode ser confusa para os usuários finais quando a refatoração trivial da organização do módulo causa mudança no comportamento da macro.

Ok, este problema é enorme e chegou ao ponto que não acho muito útil manter aberto e rastrear APIs. Para esse fim, abri https://github.com/rust-lang/rust/pull/54728, que divide esse problema em vários problemas de rastreamento mais refinados:

Neste ponto, fecharei isso, mas se eu esqueci de separar qualquer outro problema de rastreamento, por favor me avise! Certamente posso abrir mais alguns acompanhamentos

@alexcrichton E quanto aos
https://github.com/rust-lang/rust/issues/38356#issuecomment -397095541
Existe um problema para isso?

@XX
Esses subatributos não precisam ser necessariamente um recurso de idioma?
No caso de derive registro de atributos personalizados é necessário porque as macros derivadas não podem remover atributos de sua entrada (a entrada é imutável).
Macros de atributos podem remover #[other_attribute] s de sua entrada, então eles nunca chegarão à resolução de nomes e nunca reportarão o erro de "atributo não resolvido".

(Além dos atributos personalizados instáveis ​​legados clássicos mencionados em https://github.com/rust-lang/rust/issues/38356#issuecomment-397095541 agora são compatíveis com macros proc.)

@petrochenkov Sim, obrigado pelo esclarecimento.

Esta página foi útil?
0 / 5 - 0 avaliações