Rust: RFC 1566 的跟踪问题:程序宏

创建于 2016-12-14  ·  184评论  ·  资料来源: rust-lang/rust

当前状态

此问题已被关闭以支持更细粒度的跟踪问题

~更新说明~

下一步:

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

    • 在火山口等待

  • [ ] 稳定proc_macro功能

可能的稳定措施

原始描述

RFC

这个 RFC 提出了 Rust 的过程宏系统(又名语法)的演变
扩展,又名编译器插件)。 此 RFC 指定了定义的语法
过程宏,它们在编译器中实现的高级视图,
并概述它们如何与编译过程交互。

在最高级别,宏是通过实现标记为的函数来定义的
#[macro]属性。 宏对由提供的令牌列表进行操作
编译器并返回宏使用的标记列表。 我们
提供用于操作这些代币的低级设施。 更高层次
工具(例如,用于将令牌解析为 AST)应该作为库包存在。

路线图: https :


任务

  • [x] 实现#[proc_macro_attribute] (PR #38842)。

    • [x] 修复 #39347(PR #39572)。

    • [x] 修复 #39336(PR #44528)。

  • [x] 实现#[proc_macro] (PR #40129)。
  • [x] 在InvocationCollector (PR #39391) 中识别和收集proc_macro_derive的使用。
  • [x] 支持宏扩展的proc_macro_derive导入。

    • 例如:

#[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!();
  • [ ] 在扩展应用proc_macro_derive之前扩展项目(PR #48465)。
  • [x] 对未使用的#[macro_use]导入实施警告 (PR #39060)。
  • [x] 重构解析器以使用令牌树(PR #39118)。
  • [x] 清理TokenStream以准备进一步重构(PR #39173)。
  • [x] 删除TokenTree::Sequence (PR #39419)。
  • [x] 在tokenstream::TokenTreeDelimited变体中使用TokenStream代替Vec<TokenTree> (PR #40202)。
  • [x] 在ast::Attribute使用Path s 和TokenStream ast::Attribute s (PR #40346)。

    • [x] 支持属性/派生宏中的非平凡路径(例如#[foo::bar]#[derive(foo::Bar)] )。

  • [x] 包括所有令牌的卫生信息,而不仅仅是标识符 (PR #40597)。
  • [x] 如 RFC (PR #40939) 中所述,为proc_macro::TokenStream实现一个最小的 API。

    • [x] 在Token::Interpolated令牌中包含插入 AST 片段的源TokenStream s。

    • [x] 在proc_macro功能门后面包含一个TokenStream引用器proc_macro::quote!

  • [x] 为proc_macro作者提供一种方法来创建使用预定 crate foo项目的扩展,而无需宏用户在 crate 根目录中包含extern crate foo; (PR # 40939)。

    • [ ] 改善人体工程学。

  • [ ] 为 AST 中的项目包括源TokenStream s。
  • [ ] 稳定性检查 (proc-) 宏(问题 #34079)。
  • [x] 允许 proc 宏使用 def_site 值初始化私有字段(问题 #47311)。 (公关#48082)
  • [x] 在 proc 宏中访问花括号结构与元组结构的字段不一致(问题 #47312)。 (公关#48083)
  • [ ] 在阶段 1 中使 std 可用于 proc 宏根(问题 #47314)。
  • [x] 改进proc_macro::quote!无效语法的错误(问题 #47315)。
  • [ ] 包含模块的 TokenStream 的 Display 和 IntoIterator 之间的不一致(问题 #47627)。
  • [x] #[cfg_attr] 使 .to_string() 和 TokenStream 不一致(问题 #48644)。
  • [x] libproc_macro 的愿望清单(#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

最有用的评论

好吧,这个问题很大,并且已经到了我认为保持开放和跟踪 API 没有太大用处的地步。 为此,我打开了https://github.com/rust-lang/rust/pull/54728 ,它将这个问题分解为许多更细粒度的跟踪问题:

在这一点上,我将关闭它,但如果我忘记拆分任何其他跟踪问题,请告诉我! 我当然可以打开更多的后续

所有184条评论

抄送@nrc @jseyfried

我希望尽快实施#[proc_macro_attribute] 。 在意识到还没有编译器支持之前,我已经有了一个原型和测试用法,我已经把它搞砸了 :unamed: :

原型: https :
示例/测试: https :

任务

(dtolnay 编辑:将清单移至 OP)

cc @nrc @petrochenkov @durka @Ralith

@jseyfried我遇到了一个问题,如果将旧宏和具有相同名称的属性导入到同一范围内,则尝试使用该属性会引发错误,即宏不能用作属性。 我们能否使这项工作能够使两者在同一范围内并按预期使用?

@abonander所有的宏(bang、attribute 和

对不起,我参加聚会迟到了。 我对公开令牌而不是 AST 的方向感到满意,但我对 RFC 中提出的特定TokenStream API 有一些担忧:

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,
}

目前尚不清楚此 API 是否旨在作为一个在 RFC 合并时被接受的完整计划,或者只是一个稍后制定的示例。

通常,这似乎与编译器在宏之外接受的“正常”Rust 语法相去甚远。 虽然一些宏想要解析一些特定领域的特定语言,但其他宏想要解析“实际的 Rust”语法并理解它。

  1. (次要)我不认为Eof是必要的。 一个Iterator大概会被用来迭代TokenStream并且Iterator::next已经返回None来表示迭代结束。

  2. (次要)我不认为ExclamationDollarSemicolon是必要的。 例如,匹配Punctuation('!')并不困难。

  3. (次要)正如其他人在 RFC PR 中提到的,我们可能希望省略不是文档注释的注释。 (任何想要保留注释的用例也可能希望保留空格。)

  4. 据我所知,如何处理多字符运算符(每个运算符可能应该是一个标记)仍然是一个悬而未决的问题。 PR 评论中讨论了一个可能的解决方案,但看起来它没有进入 RFC 文本。

  5. 数字文字丢失。 宏是否应该自己解析[Punct('1'), Punct('_'), Punct('2'), Punct('3'), Punct('4'), Punct('.'), Punct('5'), Punct('e'), Punct('6')]来评估文字? 他们甚至不能使用str::parse::<f32>来做到这一点,因为它接受的语法与 Rust 文字语法不同(例如,中间可以有_ )。

    我想这里存在稳定性问题。 我们能否引入新的数字类型,如u128 / i128 (并且可能在未来f128u256 ,...)及其文字,而不会破坏对令牌API? 使这成为可能的一种方法可能是:

    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
    

    或者也许是别的东西。 但我不认为“假装数字不存在”是一个好方法。

  6. // 单词由 Unicode 标准附件 31 定义 -

    这个定义需要比这更精确。 UAX 31 指定了标识符语法的几种不同变体,它们都称为“词”。 但是选择我们想要的确切变化是非 ASCII 标识符目前被特征门控的原因。

    相反,我认为这应该定义为“当前编译器接受的任何标识符或关键字”(可以根据 #28979 进行更改)。 也许在 libmacro 中有一个pub fn is_identifier(&str) -> bool公共 API。

  7. Unicode 字符串和字节字符串文字共享一个标记变体,我认为这是错误的,因为它们的值的内存表示具有不同的类型( str[u8] )。 也不清楚text: Symbol组件是打算作为源代码的文字切片还是解析反斜杠转义后的值。 我认为绝对应该是后者。 (相比之下, Char(char)必须是后者,因为\u{A0}需要多个char来表示。)

另一种编写高级宏的方法是使用类似宏的 lisp,但这需要整个 rust ast 的 s 表达式表示。

@西蒙萨平

正如其他人在 RFC PR 中提到的,我们可能希望省略不是文档注释的注释。 (任何想要保留注释的用例也可能希望保留空格。)

请不要。 我有一个用例,我想在语法中使用(虽然不保留——它们将被写入单独的编译产品)注释。

具体来说,我想创建一个翻译宏,从一个单独的源文件加载一个字符串的翻译,我想生成一个字符串列表,在调试版本中作为副产品进行翻译。 并且需要有一种方法来包含要发送到该列表中的注释(rust-locale/rust-locale#19)。 所以使用注释语法是有意义的,宏需要看到它们。

我同意那篇文章中的其他观点。

@jan-hudec
即使我们没有TokenKind::Comment ,您仍然可以通过查看连续标记之间的跨度内容来使用注释。

我认为我们不应该用TokenKind::Comment来鼓励程序宏忽略注释,以便用户可以自由地向宏调用添加注释,而不必担心更改语义。

@jan-hudec 是否有原因属性不适用于您的解决方案?

@abonander ,属性绝对没有意义。 可翻译的字符串充当文字,而不是项目。 但是在编译期间提取它们只是为了方便——它总是可以作为单独的解析来完成(事实上,最终可能会如此,因为我需要在包中看到它们的_所有_,而增量编译会破坏它)。

我想制作一个基于 serde 派生的程序宏(因此直接调用 serde 令牌流函数),但没有办法说我想将 serde 派生用作库而不是程序宏。 这不是派生宏所独有的,我可以看到“正常”程序宏也需要类似的东西。

我现在唯一的解决方案似乎是分叉 serde_derive。

问题是来自 rustc 的这个错误消息:

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

删除它并使其工作很容易,但也有一些我不知道如何解决的复杂性 - 一个程序宏 crate 可能想要使用从另一个程序宏 crate 派生的 proc-macro,以及调用函数为下游用户生成派生。 那会是什么样子? 目前有没有类似的东西,一个 crate 可以根据消费 crate 的要求以两种不同的方式链接到它?

@aidanhs

一个程序宏 crate 可能希望使用从另一个程序宏 crate 派生的 proc-macro,以及调用函数来为下游用户生成派生。 那会是什么样子?

您无法从proc-macro板条箱访问函数(或除程序宏之外的任何其他内容)。 如果要使用TokenStream -> TokenStream函数和相应的过程宏,则需要将TokenStream -> TokenStream函数放在单独的非proc-macro板条箱中,然后还有一个proc-macro板条箱,只委托给这些函数。

一旦#40939 登陆,这个 RFC 将主要实施。

proc_macro作者提供一种方法来创建使用预定 crate foo项目的扩展,而无需宏用户在 crate 根中包含extern crate foo;

假设我想展示一个单独的箱子,它包含非宏项目和一个引用所述项目的程序宏。 当#40939 登陆时,这种三箱模式会是实现这一目标的惯用方式吗?

  1. 将所有非宏项放入foo_runtime
  2. 实现foo_macros的程序宏,根据需要引用foo_runtime的符号
  3. 添加最终的“façade”板条箱foo ,其中pub use是来自foo_runtimefoo_macros

    • 这是用户将直接导入的唯一 crate

    • 这是有效的,因为卫生系统修复了宏以指向正确的箱子

我问是因为我的用例涉及导入两个板条箱,如果我可以只使用一个

@lairy我认为“双板条箱”模式将是惯用的方式:

  1. 将所有非宏项目放入foo
  2. foo_macros实现程序宏,根据需要引用foo符号,例如
#[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 foo_macros中的foo

这是有效的,因为卫生系统修复了宏以指向正确的箱子

在不同的 crate 中重新导出程序宏不会影响程序宏中的名称解析方式。

@jseyfried :你知道这个再导出技巧是否也适用于自定义派生? 因为这些板条箱具有完全相同的限制,即无法导出任何物品。

@科林-基格尔
自定义派生箱只是碰巧只有#[proc_macro_derive]的 proc 宏箱。
使用#[feature(proc_macro)] ,您可以重新导出普通板条箱中的自定义派生,就像您可以重新导出其他 proc 宏一样。

@jseyfried我知道目前的情况,我提出这个问题是因为我认为这并不理想,并希望就此进行讨论。 在您描述的情况下,“委托”/重用另一个板条箱的程序宏成为说服宏作者(在本例中为 serde)将其程序宏分成两个板条箱的问题。 如果我可以像普通函数一样调用程序宏,上游 crate 作者甚至不需要知道我正在使用他们的 crate。

也就是说,我认识到兼容性风险——宏生成的确切令牌树成为稳定接口的一部分,所以如果 serde 在补丁版本中更改了它们生成Derive并且我编写了一个脆弱的宏,我的对于我的 crate 中的每个新用户,宏都会被破坏(与当前情况下的脆弱宏相反,在最坏的情况下,它仅适用于特定输入,但始终如一)。

@杰西弗里德

这会从当前的货物清单中提取foo吗? 这听起来很糟糕(即,如果有 2 个名为foo crate 链接到当前二进制文件中,它会做任何特别愚蠢的事情吗?)。

@aidanhs这将是一个主要的语言更改/添加,将保证其自己的 RFC。

@arielb1

这会从当前的货物清单中提取foo吗? 听起来很糟糕

是的 - 遗憾的是,引用的extern crate名称不卫生,即分辨率取决于哪些 crate 名称恰好在使用过程宏的范围内。 我们可以使用重新导出技巧来缓解这种情况(即重新导出foo_macros中的foo以便我们知道foo将在范围内),但这并不能保护当有两个名为foo crate 时,可以防止歧义错误。

我认为这里最好的解决方案是通过--target-extern命令行参数向Cargo.tomlproc-macro crates 添加阶段 1(即目标 wrt 主机与目标)依赖项。 这将使我们能够明确地列出extern crate范围内的名字quote!

@杰西弗里德

这个想法是 proc-macro crate 会依赖它的“目标”元数据,对吧?

@arielb1是的,没错。

一旦#40939 登陆,这个 RFC 将主要实施。

@jseyfried就像,准备好在公关落地时稳定下来吗? 如果不是,什么会阻碍稳定? 我只是不希望这成为另一个功能,感觉我们已经完成了 95% 的实施,人们都很兴奋,然后事情就虎头蛇尾地消失了。

比如,准备好在 PR 登陆时稳定下来吗?

不,我们希望在稳定extern crate名称(即解决@arielb1指出的这个问题)之前获得一些 API 经验,并可能在未来证明卫生。

我们可能希望对此 API 进行重大更改; @eddyb已提议/考虑将OpKind推广到所有令牌树。 此外,我们可能会改变我们处理文档注释、浮点文字等的方式。总的来说,这个 PR 中的 API 还不够成熟,无法考虑稳定。

@bstrie遗憾的是,RFC 可以快速跟踪 proc 宏稳定(具有有限的 api,其中例如令牌流只能通过其字符串表示访问),例如派生宏稳定已失败: https :

@est31推迟,更像是 - 在对这个 API 有了一点经验之后,我们可能会同意一个子集,我们可以同意快速跟踪到稳定。

基于String的 API 与声明性宏 2.0 的交互很差,并且今天已经受到限制,即使没有宏 2.0 而只有#[derive] s。 我们希望尽可能避免基于String的 API 的扩散,以避免人们迁移到宏 2.0 时出现问题。

我已经为#[proc_macro_attribute]打开了一个问题,似乎没有在 trait 方法上进行扩展(也许是一般的 trait 项目?)

由于这现在是proc_macro板条箱及其新 API 的跟踪问题,我想我也会写下一些想法。 我发布了一个名为proc-macro2 crate,它与树中的proc_macro crate 完全相同,只是它提供了在稳定的 Rust 上编译的能力。 然后它也有能力使用一个特性在每晚 Rust 上进行编译,以获得更好的跨度信息的好处。 该库旨在成为syn等其他库的基础,在syn的开发中,我们发现了一些我们可能希望直接在proc_macro解决的缺点:

  • 有几种文字没有Literal构造函数。 这是通过字符串化然后解析来解决的,但是能够直接构造这些而不必通过字符串 API 会很棒。

    • 原始字符串 - r###" foo "###

    • 原始字节字符串 - rb#" foo "#

    • 字节文字 - b'x'

    • 文档注释 - 这些当前表示为Literal标记。

  • 无法检查Literal并提取其值。 现在我们依赖于literalext crateto_string一个文字并重新解析它,但理论上这个信息已经存储在Literal ,如果能够访问它。
  • 在某些情况下,令牌的映射可以解释为有点奇怪。 即现在文档注释映射到Literal类型。

我相信从这里开始的所有其他问题都已得到解决。

我在使用#![feature(proc_macro)]进行测试时遇到了一个损坏,它会影响具有#[proc_macro_derive(foo, attributes(foo))]自定义派生。 即,具有与自定义派生相同的属性名称的自定义派生。 一个这样的箱子是我的 - 派生错误链,它有#[derive(error_chain)] #[error_chain(...)] struct ErrorKind { ... } 。 另一个是衍生新,它有#[derive(new)] #[new] struct S; 。 我不知道还有没有其他人。

对于这样的代码,编译器会抱怨"foo" is a derive mode的第二个属性。 这是故意的还是可以修复的? 如果有意,我需要准备将我的自定义派生重命名为ErrorChain或其他名称。

@Arnavion
这通常是有意的——因为proc_macro_attribute必须在派生之前扩展,如果newproc_macro_attribute那么扩展将是不明确的。 有可能专门允许new成为proc_macro_derive ,但我不确定它是否值得(也可能是未来兼容性的风险)。

这通常是有意的——因为proc_macro_attributes必须在派生之前扩展,如果newproc_macro_attribute那么扩展将是不明确的。

好的,我将#[derive(error_chain)]重命名#[derive(ErrorChain)]

有可能专门允许new成为proc_macro_derive ,但我不确定它是否值得(也可能是未来兼容性的风险)。

当然,我并不是要求new是特殊情况。 这只是我所知道的两个proc_macro_derive之一的一个例子,它被这个破坏了。

@Arnavion抱歉,我的最后一条评论并不是最清楚的——我并不是特别指特殊情况new而是在some_attr解析为proc_macro_derive时允许#[derive(some_macro)] #[some_attr] struct S; proc_macro_derive 。 当some_attr解析为proc_macro_attribute ,这将需要是一个歧义错误; 今天,如果some_attr解析为任何宏,这是一个歧义错误。

是的,我知道了。

我希望这是提出此类问题的正确位置。

这是什么状态?

  • [ ] 为proc_macro作者提供一种方法来创建使用预定 crate foo项目的扩展,而无需宏用户在 crate 根目录中包含extern crate foo; (PR #40939 )。

PR 已落地,但该框仍未选中。 @jseyfried这里提到了一些东西,它似乎有效。 但是它似乎根本不适用于use

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;
}

我错过了什么吗? 从宏中导入的 extern crate 中use符号的惯用方法是什么?

您不能使用use参见https://github.com/rust-lang/rfcs/issues/959。 但是对于宏来说,每次使用完全限定的路径并不是真正的缺点。 (除了性格,我认为)

@parched感谢您将其他问题联系起来。 我的用例如下:

在我的宏中,我想让用户编写类似于匹配匹配器的东西。 具体地,用户写入一个Term并且这可以是一个枚举结合所述匹配值的简单变量名的变体。 要使用macro_rules!语法编写一些伪代码:

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

现在我希望用户能够只指定没有枚举名称的变体名称。 因此我会在生成的代码中插入一个use my_crate::AnEnum::*;语句。 但由于这是不可能的(现在),我需要自己检查$matcher是否是枚举的变体。

我希望我的解释是可以理解的。 我只是想在宏生成的代码中为use提供另一个用例。

@LukasKalbertodt你能在match使用my_crate::AnEnum::$matcher => {} match吗?
没关系,我是你的问题——我相信我们需要https://github.com/rust-lang/rfcs/issues/959来解决这个问题。

@jseyfried否: $matcher可以是变体名称(在这种情况下您的解决方案将起作用)或简单的变量名称,如match x { simple_var_name => {} } 。 在后一种情况下它不会工作 AFAICT。 (顺便说一句,我只是想提到另一个用例来表明使用use很重要)

@杰西弗里德

这通常是有意的——因为proc_macro_attributes必须在派生之前扩展,如果newproc_macro_attribute那么扩展将是不明确的。

好的,我将#[derive(error_chain)]重命名#[derive(ErrorChain)]

似乎自定义派生的属性也与macro_rules宏冲突,而不是像自定义派生那样根据导入顺序覆盖它们。 也就是说,这段代码编译:

#![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,
}

这与当前稳定版 Rust 的行为相匹配,除了(1)确实在稳定版#[macro_use]error-chain板条箱一起使用的用户需要导入derive-error-chain之前先导入它。

但即使我将自定义派生重命名为ErrorChain以使(1)proc_macro功能一起使用(这已经是稳定代码的一个重大更改):

#![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,
}

它仍然无法编译 - (2)处的属性产生错误: macro `error_chain` may not be used in attributes因为macro_rules宏显然与自定义派生注册的属性冲突,而不是像这样被覆盖在第一种情况下。

所以我要重命名的自定义派生它的属性两者。 与自定义派生(每个枚举一个)相比,该属性被使用得更多(一个在枚举的每个变体上),所以这是一个比我预期的更大的破坏性变化。 我确实理解这是我自己构建的一个棘手情况(将macro_rules宏的名称重用于自定义派生及其属性),但这也是自自定义派生以来一直在稳定编译的代码稳定下来,所以我没有理由认为六个月后会出现问题。

是否可以使自定义派生的属性覆盖macro_rules宏,就像自定义派生本身覆盖macro_rules宏一样? 实际上我看不出它们之间有什么歧义,但我认为这与在同名的自定义派生之后导入macro_rules宏的原因相同 - 所有宏都放在相同的命名空间,而不考虑它们是什么类型的宏。

是否有一些不太正式的“地方”来谈论 proc 宏? 喜欢#rust-proc-macro IRC 频道? 我很想时不时地问一些有关该功能的小问题,但是发送此线程的垃圾邮件感觉是错误的 :see_no_evil: 在#rust频道中,大多数人还没有使用过特别是新的proc_macro API(因为它不稳定)。 所以:知道在哪里讨论这个话题吗?

@LukasKalbertodt #rust-internals ,也许,或者只是在 /r/rust 上开始一个新线程。

TokenStream::from_str在程序宏之外使用时会出现恐慌(例如在构建脚本中):

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

是否可以/希望通过隐式创建虚拟“会话”来替换这种恐慌? 或者添加一个公共 API(带有稳定路径)来创建一个?

有没有人看过有关其他系统宏的文献? 我想听听人们对此的看法。 我将在这里谈论Scheme,因为这是我最熟悉的。

我个人致力于在我自己的项目中为 R7RS Scheme 实施syntax-rules ,我发现syntax-case可以构成支持不卫生和卫生宏观系统的基础( defmacrosyntax-rules )。 GNU Guile 就是这样做的。 syntax-case还支持 fenders,它可以对语法对象列表(或者,Scheme 中TokenStream行中的某些内容)执行额外的谓词验证。 我可以看到Mark正在处理中,看起来它的灵感来自Bindings as Sets of Scopes

另外,我们是否还应该讨论是否应该支持编译时的任意计算? Racket 实际上采用了一个完整的“阶段”方法,看起来像begin-for-syntax允许在宏扩展期间在编译时级别进行定义和计算(?)。 .

使用 Scheme 中的(datum->syntax <thing-to-copy-scope-from> <thing-to-apply-scope-to>)可以很好地控制卫生,允许您转义宏的作用域,转而采用直接作用域之外的对象作用域。

The Scheme Programming Language, 3rd ed 中的这个例子为例http : (include "filename.scm")syntax-case宏,并允许解释器使用宏来设置运行时以读取文件并继续计算。 这里更深层次的问题是我们是否想要一个宏宏系统允许这样的事情在宏扩展时发生,并触发编译时计算,例如触发文件导入(尽管这似乎发生在直接编译器功能中,所以也许我们不想这样做)。

宏的限制应该是什么? 我想 Rust 想要减少编译时间,想要限制编译时评估(尤其是避免无限循环)。 Racket 采用了 Lisp in Small Pieces 中提到的“准备器和扩展器之塔”方法和阶段。 我们是否希望允许访问编译时 API 以执行文件 I/O 和有限的递归计算? 我们是否应该允许程序宏能够将 CSV 电子表格规范转换为 switch 语句?

我很想知道其他系统! 我听说 Template Haskell 有一种有趣的方法,用定义明确的类型来表示它们的 AST,Haskell 中的惰性可以取代宏的许多用于控制结构的用途。

对不起,如果我越界了。

宏的限制应该是什么?

对于本期讨论的程序宏, none 。 过程宏是编译器扩展。 它可能需要一些 C++ 代码,通过 clang 运行它并将结果对象添加到编译中。 可以用一些SQL,查询数据库,找到对应的结果类型,生成合适的结果集。 这些是人们想要做的实际用例!

请注意,Rust 有另一个宏系统。 它的更新已被批准为RFC 1584 ,其实现由https://github.com/rust-lang/rust/issues/39412跟踪

@VermillionAzure ,快速查看您引用的 Scheme 表单:

macro_rules及其根据RFC 1584的更新类似于syntax-rules 。 如果您有增强这些建议的建议, https://github.com/rust-lang/rust/issues/39412可能是讨论该问题的最佳场所。

这个问题所涉及的 proc-macros 就像define-syntax的一般形式。 而这个 RFC ( 1566 ) 非常有意地没有定义任何类似syntax-case 。 只有一个接口调用一个函数来转换令牌流。

接口的定义方式是,像syntax-case可以在单独的箱子(库)中实现,目的是这样做。 如果您愿意,请随意玩耍。 任何关于 API 使用难易程度的原型和报告肯定都会受到欢迎。

我认为这个想法是,像函数一样定义宏,就像在 lisp 中一样,但是有一个宏,它返回macro_rules!定义的宏。

所以以下是等效的:

    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`
}

这就是syntax-rulessyntax-case似乎在方案中工作的方式。

@VermillionAzure
这是,你想要什么?

@porky11不,似乎并非如此。 我只是想看看,如果计划宏将是一个相关的想法添加到讨论-这是显而易见的,因为程序上的宏意图是远远超过了更强大的syntax-case方案宏系统,这是微不足道的根据此处提供的任意功率实现所有宏系统。

@jan-hudec 在没有任何安全保证的情况下允许任意计算作为编译器扩展是否明智? 我完全被过程宏在这里如此强大的想法所震撼,但是 Rust 的任何潜在用户会认为这是使用包的缺点吗? 我无论如何都不是安全专家,但是编译器扩展中使用的任何库中的漏洞不能很容易地被用来恶意地将 Rust 编译器变成攻击向量吗? 此外,如果在程序宏中使用的库中出现任何错误(例如,由错误的 C 库代码触发的段错误),这是否意味着段错误会逐渐蔓延并使编译器在没有正确错误消息的情况下失败?

有没有一种方法可以封装程序宏中发生的错误,而不会影响编译器的任何其他部分?

另一个想法:程序宏何时执行? 如果过程宏可以与具有相关副作用的代码交互(例如与有状态的外部服务器通信、改变外部 SQL 数据库、获取安全密钥以登录到外部系统),那么这并不意味着编译过程触发程序宏的顺序很重要?

@VermillionAzure Cargo 包已经可以有在编译时执行任意代码的构建脚本,因此过程宏不会让事情变得更糟:您已经需要信任您的依赖项。 (由于 crates.io 是不可变的/仅附加的,并且如果您有Cargo.lock文件,依赖项不会自动更新,这会更容易一些:您只需要信任特定版本。)即使构建脚本没有存在,您的依赖项本质上仍然可以在运行时执行任意代码。 编译时间更糟吗?

这个讨论让我想到了一个相关但不同的问题。

假设一个 crate 定义了两个 proc 宏: foo!()写入一个临时文件,而bar!()读取同一个文件。 此 crate 的使用者在同一模块中同时调用foo!()bar!() 。 那么,编译成功与否取决于先扩展foo!()bar!()哪一个。 这种排序是实现定义的,如果有足够多的人编写这样的代码,可能会成为事实上的标准。

不过,我不确定这是一个多大的问题。 只是担心这是否会导致重复 struct 字段排序恶作剧。

@西蒙萨平

虽然我同意你的立场,但我应该指出编译时执行和运行时执行之间存在一个显着差异:

它提供了一种不同的威胁模型,因为事物往往被编译一次,然后部署到多台机器上。 (例如,在 Linux 发行版构建集群上利用维护者的疏忽加上沙盒缺陷。)

@lairy是的,这正是 Racket 十多年前在 2002 年遇到的问题“可组合和可编译的宏:你想要它什么时候? . R. Kent Dybvig,曾参与 Chez Scheme 的工作,还在“R6RS 库中的隐式分阶段”中写了一篇关于库/模块评估阶段的论文。

@SimonSapin编译时可能会更糟。 如果您的编译器随机崩溃或执行由编译器触发的恶意行为,那么我敢打赌,有人最终会在 Reddit 上写一篇标题为“Rust 的模块本质上不安全”或类似内容的帖子。

@VermillionAzure ,我没有很仔细地阅读文章,但我认为它们与讨论无关,因为 Rust 面临的问题与 Scheme 面临的问题非常不同。

在 Scheme 中,一个库可以同时提供函数和宏,因此编译器必须正确区分哪些函数需要编译时,哪些需要运行时。 然而,在 Rust 中,一个 crate 要么提供程序宏,要么提供运行时函数,所以这种划分(暂时)是显而易见的。

(请注意,提供运行时功能的 crate 也可以提供基于规则(卫生)的宏,但这些在 Rust 中是单独的机制)

@lairy谈论的问题是扩展器函数的执行顺序之一。 在 Rust 中,编译对于单独的文件可能是并行的,也可能是增量的,因此扩展器的执行顺序是不确定的。 但是这两篇文章真的解决了这个问题吗? 我没有看到。

@jan-hudec 是的,我想你是对的。 但是如果在编译时允许副作用,评估的顺序肯定会很重要,除非你能保证某个模块不会产生副作用。 模块是否“可类型化”?

我认为程序宏“可能不应该”有副作用,因为不能依赖某些细节(见下文),但我们可能不会在语言中使用类似类型系统的机制来强制它们成为纯粹的功能。

这些细节包括与其他 proc 宏相比的执行顺序和并发性,以及 proc 宏是否在增量构建中重新执行。 对于后者,我们可能希望在构建脚本中添加类似于rerun-if-changed依赖项来声明依赖项。 和构建脚本一样,这些声明可能不完整或有问题。 我们不能静态地防止所有错误。

我认为我们应该避免保证任何关于副作用的事情(即不允许 proc 宏包依赖于副作用工作,也不允许他们依赖于除了应用它们的模块中的代码更改之外的任何事情重新触发(所以,没有全局状态)。

我们稍后可以通过一种指定 rerun-if-changed 和其他内容的方式来放宽此要求。

(我有一个半生不熟的通用构建脚本/proc 宏安全建议,它会在这里有所帮助,但我还没有真正写下它)

IMO proc 宏/自定义派生应放入沙盒环境中,没有任何 I/O 或其他与外部的连接,并由 miri 评估,可能使用起重机 JIT。

@est31这是个好主意,但像柴油 infer_schema 之类的东西! 已经
存在,bindgen 需要读取文件和运行程序,甚至包括! 和
环境! 使用 I/O。

2017 年 11 月 9 日 06:19,“est31” [email protected]写道:

IMO proc 宏/自定义派生应放入沙盒环境中
没有任何 I/O 或其他与外部的连接,并由
美里,也许有起重机JIT。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/rust-lang/rust/issues/38356#issuecomment-343124957
或静音线程
https://github.com/notifications/unsubscribe-auth/AAC3n5VOPdKBsu81Sp3tp2XlIQ05L865ks5s0t_PgaJpZM4LMWlc
.

看起来#40939 和#44528 已经合并了... @jseyfried你能更新清单吗?

@mark-im 更新了。

程序宏可以生成程序宏吗?

@VermillionAzure我还没有尝试过,但我看不出他们为什么不能这样做的原因。 当然,就像 proc 宏生成“运行时”代码一样,它们需要放在与使用它们的地方分开的板条箱中。

在同步中,我们今天遇到了 proc_macro::TokenNode 的限制——块分隔符{ ... }只与输入 TokenStream 中的一个 Span 相关联,因此无法触发仅指向结束} 。 Rustc 似乎没有这个限制。

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

我们可以在 proc 宏中做的最好的事情是指向块内的最后一个标记,指向整个块,或指向块之后的下一个标记,对于上述错误,这些都不是您真正想要的。

一个通用的解决方案是让Span::startSpan::end返回一个 1 个字符的Span而不是他们目前所做的LineColumn ,然后公开一种方法从Span到跨度的第一行/列。

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

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

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

提到在 #43604 中添加该 API 的@abonander

让 Span::start 和 Span::end 返回一个 1 个字符的 Span 而不是它们目前所做的 LineColumn,然后公开一种从 Span 到第一行/第一列的方法。

这将使 Span 采用分隔列表所需的特殊行为,但对所有其他 span 来说都是错误的。 一般是不对的。 考虑通过加入东西来获得像foo(hi)这样的表达式的跨度。 现在您想指向foo并取sp.begin() ,但sp.begin()仅指向foo的第一个字符。

我认为更好的解决方案是将两个跨度添加到proc_macro::TokenNode::Group或允许创建任意跨度。

Span::begin / end返回类型可能需要更改: https :

我试图让Span::def_site()解决我的程序宏中范围内的问题。 我误解了这应该如何工作吗?

如果我使用Span::call_site()并且在 main.rs 中定义了MySend ,几乎相同的代码可以工作,正如我所期望的那样。 一直无法让它与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`?

在 Syn 端跟踪这个: https :

@dtolnay
这里的问题是MySend是在阶段 0 中导入的(即交叉编译时用于主机架构),因此它在阶段 1 中不可用(即为目标架构编译时)。

这里的解决方案是允许 proc-macro crate 具有阶段 1(目标架构)依赖关系,以便我们可以将阶段 1 项导入范围。

今天,一个解决方法是返回:

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
    }
} 

您也可以手动构建它,我只是为了方便使用quote!

由于卫生, dummy / std / MySend永远不会与作用域内的任何其他东西发生冲突,所以例如在同一个模块中多次使用这个宏是安全的, ident是“MySend”等是安全的。

这个问题,以及对mod dummy的需求和解决方案,在https://github.com/rust-lang/rust/issues/45934#issuecomment -344497531 中有更详细的描述。

遗憾的是,在第一阶段的依赖得到实现之前,这将是不符合人体工程学的。

谢谢@jseyfried! 那个有效。 一些后续问题:

  • 在我之前评论的代码中,如果我更改impl_mysend_for以生成Send而不是MySend那么一切都会编译。 Send这是解决什么问题,为什么它没有达到阶段 0 与阶段 1 的区别? 这是有意的还是偶然的?

  • 我的def_site()令牌可以使用的范围还有什么,例如Send

  • 如果MySend需要来自一个库(就像想象我们正在派生serde::Serialize )那么最终用户仍然需要serde在他们的 Cargo.toml 中,即使我们没有需要他们写extern crate serde 。 是否有可能使extern cratedef_site()对程序宏的Cargo.toml IDENT决心, extern cratecall_site()对下游的ident决心货物.toml?

对于 extern 板条箱,我假设需要通过 proc 宏明确地使板条箱可用于阶段 1。

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

@dtolnay

在我之前评论的代码中,如果我更改 impl_mysend_for 以生成用于 Send 而不是 MySend 的 impl,那么一切都会编译。 这解决了什么发送,为什么它没有达到阶段 0 与阶段 1 的区别? 这是有意的还是偶然的?

好问题。 现在,前奏是在定义站点的范围内(除非 proc-macro crate 是#![no_implicit_prelude] )并且这是一个意外(在某种意义上)由于阶段 0 与阶段 1 的区别,正如您所指出的.

但是,对于人体工程学,我认为第 1 阶段应该在 proc-macro 根中隐式包含std (以便您始终可以quote!(use std::...); )以及方便/人体工程学的前奏,因为这些已经是隐式的在第 0 阶段。很快就会有一个 PR 在第 1 阶段的根中添加std

我的 def_site() 令牌可以使用的范围还有什么,例如发送?

除了前面讨论的前奏和(很快) std之外,阶段 1 范围内唯一的其他东西是 proc-macro 本身(不是 proc-macro函数,它们是阶段 0)。

例如,

#[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
    }
}

是否可以使用 def_site() ident 解析程序宏的 Cargo.toml 来生成 extern crate,并使用 call_site() ident 解析下游 Cargo.toml 的 extern crate?

是的,除了我相信带有Span::def_site()的 extern crate 应该解决程序宏Cargo.toml的阶段 1(目标)依赖项——今天的阶段 0 依赖项链接到为主机平台编译的库. 由于阶段 1 依赖项尚不存在,因此 extern crate 的名称解析不卫生,正如您指出的那样,这很烦人。

一旦我们有了第 1 阶段的依赖,我们就不需要在每个扩展中引用extern crate开始,所以这将不是一个问题。 但是,我们仍然应该修复它——当前的计划是尝试首先解决 proc-macro crate 的目标依赖项,然后返回到具有低优先级警告周期的不卫生解决方案,以避免流失。

对于 extern 板条箱,我假设需要通过 proc 宏明确地使板条箱可用于阶段 1。
#[proc_macro_derive(Serialize, attributes(serde), crates(serde))]

有趣的是,我们可以通过这种方式实现它。

相反,我认为第 1 阶段的 crate 将在 proc-macro crate 根(例如#[phase(1)] extern crate foo; )中声明,以便它可以在所有 proc 宏(例如quote!(use foo::bar); )中自动可用。 由于extern crate无论如何都要退出,我们可以避免完全声明阶段 1 crate——来自 Cargo.toml 的所有目标依赖项将自动在阶段 1 的 proc-macro crate 根的范围内。

在我的代码中,我发现我似乎将跨度用于两个目的:控制名称解析和控制错误消息。 这两者是密不可分的,还是可以制作一个跨度,将一个跨度的名称解析方面与不同跨度的行/列错误消息位置混合在一起? 我希望这是一个普遍的需求。

更具体地说,我在自定义派生的 def_site 范围内引入了一个特征,我想在用户结构的字段上调用特征方法。 如果字段类型没有实现正确的特征,我希望错误消息在相应的结构字段下划线。

我可以使用 def_site 跨度生成方法调用,该跨度可以编译和运行,但遗憾的是,错误消息总是指向派生属性,就像我们在宏 1.1 中看到的那样。

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

或者我可以生成与结构字段的标识或类型具有相同跨度的方法调用,它显示了正确的下划线,但无法解析到我的 def_site 范围内的特征。

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

如何正确解决并以我想要的方式显示错误?

@dtolnay这是一个很好的观点,谢谢。

我认为修复https://github.com/rust-lang/rust/issues/46489的正确方法可能是使生成的#[derive(…)]令牌具有相同的名称解析跨度范围作为类型定义,错误消息跨度位于创建它们的quote! {}宏调用处。

目前的卫生情况如何? 我有像程序宏这样的函数,它曾经可以工作(4 个月前),但从 rustc 1.24.0-nightly(b65f0bedd 2018-01-01)开始,它抱怨在范围内找不到参数。

抱歉,应该先搜索问题跟踪器,好像我刚刚点击了https://github.com/rust-lang/rust/issues/46489。

我提交了 #47311,我相信它目前阻止了正确的派生(反序列化)实现。 过程宏不能构造具有私有字段的结构。

提交了另一个,#47312,其中访问未命名的元组结构字段(如self.0.令牌的跨度的要求与访问命名结构字段(如self.x .

47311 和#47312 分别固定在#48082 和#48083 中。

我有上面两个 PR 等待审核/评论。

看起来https://github.com/rust-lang/rust/pull/41029现在完成了吗?

该 PR 已被放弃但已恢复并在 #48465 中继续工作。 目前正在等待火山口。

@petrochenkov @nrc

查看syntax::ext::expand ,似乎proc_macro_attribute当前没有在多种上下文中进行处理(非详尽无遗):

  • 在块上( fold_block()是一个 noop)
  • 关于语句/表达式 (#41475, #43988)
  • extern {}块内 (#48747)

RFC 1566 没有列出可以应用属性宏的特定 AST 节点类型,这表明它们应该适用于任何事物。 但这可能会变得有点荒谬,所以我们应该清楚地确定需要处理但不需要处理的内容,以及永远不应该允许但当前可能允许的属性 (#43988)

@abonander的意图是 proc 宏属性可以在常规属性可以使用的任何地方使用,而不能在其他任何地方使用,我认为这应该涵盖以上所有内容(尽管有些不稳定,如果我们稳定 proc 宏,我们应该小心仅稳定对其他属性稳定的用途)。

@nrc是否存在枚举这些位置的任何地方,因为该引用仅说明属性可以应用于任何项目。 但是,我几乎可以肯定 lint 属性也可以应用于块和语句。

@nrc是否存在枚举这些位置的任何地方,因为该引用仅说明属性可以应用于任何项目。 但是,我几乎可以肯定 lint 属性也可以应用于块和语句。

没有 afaik - 对于任何表达式的属性都有一个公认的 RFC 和一个不稳定的特性标志,但我认为我们只在语句和块上稳定下来。 参考数据不足。

这个问题:

稳定性检查 (proc-) 宏(问题 #34079)。

现已关闭 WRT proc-macros。 我的 PR 只是没有为 Macros 2.0 宏添加稳定性检查,这就是问题仍然存在的原因(尽管它可能只是一个新问题)。

@rfcbot fcp 合并

我想提议将宏 2.0 故事的一个子集稳定为 Rust 1.28 版本的宏 1.2。 Rust 1.28 将于 2018 年 5 月 10 日(从本文开始约 2.5 周)每晚进入,并将在 2018 年 8 月 2 日变得稳定。我认为 FCP 可能会在 1.27 进入测试版的 5 月 10 日截止之前完成,但我想持有关闭这里的任何稳定,直到发生截止之后,并将其延迟到 1.28 版本。

最近已经在内部讨论了这个问题以及@petrochenkov注册的一些问题,现在应该修复(但还没有在每晚发布)。 我认为在这里回顾一下会有所帮助,因此这具体是将稳定的子集。

但请记住,这是一个子集。 此处缺少的功能并不意味着它永远不会稳定或从编译器中删除。 相反,在此提议的稳定通过之后,此功能将保持不稳定,以便在以后稳定。

宏和模块系统

主要由https://github.com/rust-lang/rust/issues/35896和现在覆盖
完成其 FCP,主要思想是您可以使用use语句来
导入宏。 例如这样的代码:

use some_proc_macro_crate::bar;

#[bar]
fn baz() {}

或者

use some_proc_macro_crate::bar;
bar!();

甚至

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

这在 Rust 中引入了第三个命名空间(除了值/类型
命名空间),宏命名空间。 属性、宏规则和程序
宏都驻留在 maro 命名空间中。

与成熟的模块系统不同的是,只有一个元素路径将被允许调用
例如#[foo::bar]::bar::baz!()将被禁止。 这个
限制可能有一天会取消,但这是一个很好的保守途径
从...开始。

哪里可以扩张?

属性只能应用于非模块项目
此处的“项目”包括特征项目、实现项目和外部模块等内容
项目。 由于卫生和
实施后果。 剩下来指定和稳定它在一个
稍后日期。

语句和表达式属性宏还不稳定。 这是
主要是由于表达水平上卫生的真正必要性(如
与项目级别相反)。 这将在以后稳定下来。

最后,属性宏内部必须有参数分隔符
例如#[foo]#[foo(bar)]#[foo { bar baz ... @ | ^ hello }]
是有效的调用。 像#[foo = "baz"]#[foo bar]
#[foo ... = ( baz )]最初不会稳定。

程序宏是什么样的?

与自定义派生一样,它们在proc-macro板条箱类型的板条箱中定义。
程序宏和属性定义如下:

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 {
    // ...
}

卫生呢?

上面可以看到自定义属性和宏只能在
项目上下文,特别是仅生成作为项目的新 AST 节点。 这个
意味着我们只需要担心生成新 AS​​T 项的卫生
节点。

新项目将具有与今天macro_rules!相同的卫生。 他们会
不卫生。 添加到 AST 的新项目将进入与
模块中的其他项目。

proc_macro API。

为了实现所有这些,以下表面积将被稳定
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 { ... }

有关此 API 的更多信息可以在线或在原始公关

测试策略

宏 1.1 和宏 2.0 系统已在整个过程中进行了广泛的测试
生态系统已经有一段时间了。 值得注意的是,整个提案也是
通过proc-macro2板条箱的 0.3 版本广泛测试为
以及syn板条箱。 在整个测试过程中发现了许多错误
确定并修复,当前系统感觉足够稳固,可以开始
稳定了。 (并不是说它没有错误!)

团队成员@alexcrichton已提议将其合并。 下一步是由其他标记团队进行审查:

  • [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

当前未列出任何问题。

一旦大多数审稿人批准(并且没有人反对),这将进入其最终评论期。 如果您发现在此过程中任何时候都没有提出的重大问题,请说出来!

有关标记的团队成员可以给我的命令的信息,请参阅此文档

cc @rust-lang/compiler,我知道你们也对此非常感兴趣,请随时提出任何反对意见。 如果您有阻止异议,我也可以为您注册

看起来,使用提供的用于稳定的 API,proc 宏可以轻松生成各种令牌,或将它们从输入复制到输出,但无法轻松检查即使是表面级别的令牌流。 例如, Literal不实现EqPartialEq 。 有什么特别的原因吗? (即使那样也只允许与指定的常量值进行比较,而不是提取一个值并在代码中处理它。)

在 proc 宏实现中,给定TokenStream ,proc 宏可以做什么来内省文字? 或者,就此而言,提取 1 的值? 这还不支持吗? 未来有计划支持吗?

(我不是试图阻止建议的子集的稳定性;我只是想更好地了解该子集及其预期功能。)

@alexcrichton新项目将具有与 macro_rules 相同的卫生! 今天做。 他们会
不卫生。 添加到 AST 的新项目将进入与
模块中的其他项目。

我不是 Rust 核心团队的成员,也没有彻底查看过去的所有讨论,但这对我来说听起来很糟糕。 如果宏想要生成只能由宏访问的帮助项怎么办?

与其放弃卫生,我认为最好执行完整的 100% 卫生,但为宏提供一种方法来明确选择退出特定变量的卫生。

@joshtriplett您将使用 Display impl for Literal。

@joshtriplett是的,正如@dtolnay提到的那样,你会使用Display ,这是像literalext这样的板条箱。 一般来说,API 旨在成为“最低限度”,而不是具有所有花里胡哨的一流 API。 那些可能会在以后出现!

这一轮稳定旨在“最大限度地减少”,因为它提供了最少的功能来涵盖所有用例(又名Display用于Literal并且没有其他解释它)。

@Pauan不是很好,是的,但它与macro_rules!完全相同,它已经稳定多年并且在许多不同的环境中工作。 我们在这里实现卫生和更好的治疗,但我们还没有准备好。 Span::call_site()构造函数是这里的关键,使用它你要求“不卫生”。 最终我们会稳定更多的选择。

不幸的是,“完全 100% 卫生”还需要数年时间,此提案的目的是为 Rust 2018 提供稳定的东西

@alexcrichton那么现在计划用一个卫生漏洞来稳定它,然后几个时代/时代/版本之后修复那个卫生漏洞吗?

既然Span::call_site()存在,是否有什么原因它现在不能完全卫生? 是缺乏人力/时间,还是仍然需要在 RFC 中充实理论/语义问题?

@Pauan重申没有真正的“漏洞”,因为它今天已经存在。 这是与macro_rules!和自定义派生完全相同的卫生故事。 该提案实际上是该提案的延伸,并未引入任何新内容。

鉴于“完美的卫生故事”,我们总是希望某种形式的退出。 目前,选择退出是Span::call_site() (有效)。 通过稳定我们只是稳定了卫生的选择退出机制。 最终,我们将稳定更多功能,以便在必要时使物品完全卫生。

稳定卫生还有很远的路要走。 有(AFAIK)未解决的研究问题,这不是人力问题。

@dtolnay啊,我明白了。 那么,您生成文本然后重新解析文本? 我想这暂时有效。

cc @rust-lang/compiler,我知道你们也对此非常感兴趣,请随时提出任何反对意见。 如果您有阻止异议,我也可以为您注册

我仍然有一些事情需要审查,并且正在进行 PR 和几个 proc-macro API 调整。

这是与 macro_rules 完全相同的卫生故事! 和自定义派生。

当然,但我认为其中一个目标是制作一个更好的宏观系统,因此指出现有(损坏和受限)系统对我来说听起来不太令人信服。

鉴于“完美的卫生故事”,我们总是希望某种形式的退出。

当然,但我的观点是,因为它可以使用Span::call_site()打破卫生(并因此获得macro_rules!如果需要的功能),通过使卫生默认情况下,您可以访问卫生和不卫生。 而如果它在不卫生的情况下稳定下来,那么它只会在不卫生的情况下被卡住。

最终,我们将稳定更多功能,以便在必要时使物品完全卫生。

这听起来像是一个相当烦人(且令人困惑)的创可贴:proc 宏在大多数情况下(但不是物品)是卫生的,但是您可以使用新功能选择对物品进行卫生。 我的理解对吗?

有(AFAIK)未解决的研究问题,这不是人力问题。

好吧,这很公平。 我理解对权宜之计和务实主义的渴望。 只要计划是在以后的时代/时代/版本中最终默认情况下保持良好的卫生,那么我可以暂时保持卫生状况不佳。 我只是不想让我们稳定一些我们以后会后悔的事情。

从某种意义上说,它已经“默认卫生”,只是尚不支持默认设置。 Span::call_site选择退出。 :)

@Pauan详细说明@alexcrichton所说的内容:

构造标识符需要Span ,其中包含卫生信息。 (见Term::new

将来,我们可能会公开许多不同的构造Span的方法,它们反映了不同的卫生选择。 但是现在,我们将只公开Span::call_site ,它接受调用者的范围。

这样,我们可以避免立即稳定整个卫生系统,同时保持以后添加卫生的能力。 我认为这是解决这个问题的一个非常聪明的技巧!

@rpjohnst所以你的意思是从 proc 宏返回新项目的唯一方法是使用Span::call_site

如果是这样,那么这听起来很完美:在这种情况下,将来确实可以以干净且向后兼容的方式添加卫生。

@lairy Ahhh,我明白了,我没有意识到有必要使用Span::call_site ,我认为有一个隐含的默认不卫生范围。 谢谢你的解释! 你说得对,这是一个非常聪明的技巧。

因此,我没有更多的反对意见。

@alexcrichton

属性、宏规则和程序
宏都驻留在 maro 命名空间中。

最后我检查了#[feature(proc_macro)] ,它在此处描述的导出错误链板条箱的稳定代码中有向后不兼容的更改 从引用的语句来看,它似乎在宏 1.2 中将继续如此。 那是对的吗?

@Arnavion哦,亲爱的,这听起来很糟糕! 我们真的不能做任何大的向后不兼容的更改,所以我们必须想办法让它继续工作。 一种可能的解决方案是将“这不是属性”错误推迟到#[derive]扩展之后。

看来#48644 和#47786 已经完成了。 有人可以更新OP吗?

@alexcrichton有没有讨论过允许访问Literal和朋友的内部数据? 我知道出于向后兼容性的原因,它们不再是枚举,但是有什么理由让Literal不能像这样开始:

    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> {}
    }

我们甚至可以为不同的文字子类型使用一堆TryFrom实现。

@abonander已经讨论过了,是的,但结论是稍后添加它们,而不是使它们在第一次稳定过程中必不可少。 它们可以使用Display实现在 crates.io 上构建,我们有空间在将来添加后者

:bell: 根据上面的评论

@alexcrichton
proc 宏 API 类型的!Send!Sync impls 不是明确编写的,而是推断出来的。
对于某些结构(例如Op ),推断的 impl 与https://github.com/rust-lang/rust/issues/38356#issuecomment -383693017 中指定的不同。
显式添加 impls 可能是有意义的。

哎呀确实是! 我打开了https://github.com/rust-lang/rust/pull/50453来解决这个问题

@alexcrichton我们有一个关于Term::as_str的令人讨厌的问题:我试图草拟一个非正式的证明,由于严格的范围,可以实现&'a Term -> &'a str以借用一些范围内的内部人员,而'a永远不会大于内部人员本身的范围(这仅在函数成功时才重要,即如果它在内部人员所在的范围内被调用)。

AFAICT, Term::as_str是合理的,但假设'a条件。

例如thread_local!支持这一点,因为虽然它可以让您转义Term值,但它会给您非常短暂的'a s,如果Term::as_str成功,严格短于内部范围。
一般来说, proc_macro扩展发生的方式,并且因为Term是线程本地的,很少有转义Term ,我假设thread_local!实际上是唯一的。

但是Box::leak也存在! 它仍然不稳定,但今天, Box::leak(Box::new(term)).as_str()返回&'static str 。 我想知道是否还有其他抽象被Box::leak (cc @RalfJung) 打破

OTOH,这很复杂,因为Term不能拥有字符串数据,因为它是Copy
如果我们删除Term Copy上的Term ,我们可以在那里保留一个懒惰的Option<Cell<Rc<String>>

@eddyb哦, Term::as_str将被移除,并没有包含在此处的稳定性中,所以无需担心! 它只是坚持,所以我们不会破坏proc-macro2 ,但是一旦稳定下来,我们就可以发布proc-macro2的重大更改

@eddyb我不知道这里的上下文,但Box::leak在我的正式模型中是合理的。 如果你永远拥有一些记忆(即在一个盒子里),你完全可以说它有任何生命周期。

我有几个问题:

  1. quote!没有稳定吗?
  2. quote!是否会应用proc_macro_non_items特征门? Commit 79630d4fdfc775b241cae0a209edec2687a29f0f 现在需要它,但quote!仍然标记为#[unstable(feature = "proc_macro" ...
  3. 是否会提交跟踪问题以稳定proc_macro_path_invocproc_macro_modproc_macro_exprproc_macro_non_items

不相关的问题: quote!在哪里实现?

@mjbshaw很有趣的故事:它proc_macro::quote
rustc_metadata假装任何名为proc_macro crate包含一个名为quote的程序宏,但使用的实现来自proc_macro rustc_metadata链接反对。

我仍然有一些事情需要审查,并且正在进行 PR 和几个 proc-macro API 调整。

审查公关: https :

对 1.2 API 的功能请求:为尖括号添加分隔符( < / > ),因此诸如<T> (在fn foo<T>() {} )之类的内容将被解析为一个Group 。 不这样做会使解析例如泛型变得不必要地复杂。

@mjbshaw我认为在令牌级别尝试确定两个< >是否相互分组是行不通的。 例如在以下宏输入中:

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

也许这是两个布尔表达式$A < $B$C >= $D ,或者它可能代表类型别名中的泛型,例如扩展为type $A <$B,$C> = $D;

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

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

我已经稍微更新了 OP,但在我看来,有两个可能的与卫生相关的 showstopper 错误,具体取决于它们的结果:

对于关注此线程的人,API 可能会从 @petrochenkov 提出的https://github.com/rust-lang/rust/pull/50473 中的原始 FCP 评论中更改。 到目前为止的总结是:

  • Term::new重命名Term::ident并验证输入
  • 添加Term::lifetime并验证输入
  • 添加Term::raw_ident并验证输入
  • Op重命名Punct
  • 验证Punct::new
  • Op::op重命名Punct::as_char

我将提出的一个次要 API 请求(可能在 #50473 -- @petrochenkov 中)是将令牌附加到 TokenStream 的某种方式。 可能:

impl Extend<TokenTree> for TokenStream

我相信这将使消除quote::Tokens类型(基本上是Vec<TokenTree> )并减少生态系统中不同类型的扩散成为可能,这些类型都意味着“一些令牌”——在https 中跟踪

+1 @dtolnay 的上述建议。

最后的征求意见期,有倾向合并,具体根据上述审查,现已完成

我有两个问题:

  1. 正如这里已经问quote!什么? 创建最终TokenStream的默认方式应该是什么? 应该手动完成吗? 还是应该使用quote板条箱? proc_macro::quote!应该在未来某个时候稳定下来?

  2. 我的理解是否正确,声明类函数宏和类属性宏之间的区别仅在于参数的数量? IE:

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

    唯一的区别是,一个带一个TokenStream作为参数,其中另外一个需要两个。 这是不是有点微妙? 为什么不改用#[proc_macro_attribute]#[proc_macro_function_like]呢? 这是否在某处讨论过? 如果是这样,如果有人可以链接讨论,我会很高兴。

感谢您在这方面所做的工作! :)

@LukasKalbertodt属性当前用#[proc_macro_attribute] 。 我不知道是有意改变它还是 FCP 提案中的错字。

问题:从字符串解析的令牌没有得到Span::call_site跨度: https :

我认为我们需要改变这一点,并改变Span::call_site返回的内容,以修复使用调用站点跨度的令牌的宏回溯、剪辑和版本卫生。

@LukasKalbertodt proc_macro板条箱中的quote!宏未作为此 FCP 的一部分进行稳定,但 crates.io 上的quote板条箱建立在此处提议的 API 上。 同样@abonander指出属性宏是用#[proc_macro_attribute]声明的,而#[proc_macro]专为foo!()样式的宏保留

不应该将#[proc_macro_attribute]命名为#[proc_attribute_macro]#[attribute_proc_macro]吗?

@Zoxc我们已经有了#[proc_macro_derive]稳定版,所以不遵循属性宏的做法会很奇怪。

我们可以得到PartialEq<char>PartialEq<Punct>Punct (类似于IdentPartialEq实现)? 看起来添加起来应该很安全。 我很高兴写 PR,但如果这个想法行不通,我不想写。

@mjbshaw PartialEq<Punct>也会比较跨度吗? PartialEq<char>看起来不错,OTOH。

@eddyb PartialEq for Ident不比较跨度(我知道这是 proc-macro2 源而不是 proc-macro 源)。 我对比较中是否包含跨度持矛盾态度,但我希望PunctIdent在这方面表现相似。 Punct也有自己的Spacing ,我认为它会包含在比较中(尽管其他人可能有不同的想法)。

我现在只为Punct实现PartialEq<char>就可以了。 PartialEq<Punct>可以稍后散列。

@mjbshaw proc-macro2 板条箱具有不完全是上游的proc_macro自由,并为我们提供了一些尝试各种调整的自由。 我怀疑如果它在 crates.io 上运行良好,我们可以将它添加到proc_macro ,但是添加到proc_macro当然是向后兼容的,所以我们从最低限度开始,一旦稳定,我们就可以从那里扩大规模。

Gnome-class 因 proc_macro2/syn/quote 的变化以及从 proc 宏生成模块的特性门而中断。 幸运的是,现在已经修复了。

我应该监控哪些事情以了解这个小生态系统中的变化?

@federicomenaquintero如果您使用不稳定的功能,请考虑定期进行 CI 工作(每天、每周、任何适合您的工作),使用最新的 Nightly 编译您的代码,并在失败时通知您。 (Travis-CI 支持在其设置中启用这样的“cron 作业”。)

@SimonSapin谢谢,这是个好主意。 我们将这些 crate 的版本固定在 Cargo.toml 中,但可能是时候删除版本号并让 Cargo 下载最新版本了。 这是正确的方法吗?

@federicomenaquintero这越来越离题了,所以如果你想继续这个讨论,请在 IRC 或http://users.rust-lang.org/等其他地方继续讨论,但一般建议是应用程序(如与库相反)应该将Cargo.lock与其源代码一起提供,从而有效地固定依赖项。 在Cargo.toml ,建议声明像foo = "1.2.3"这样的依赖项,它隐含地表示“该版本或更高版本,如果根据 SemVer 兼容”。

所以,我刚刚遇到了一个程序宏问题,该问题阻碍了我开发我想要制作的板条箱。

考虑以下:

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

这目前不起作用,因为proc_macrocustom_attributes都是必需的,但不能同时使用。 据我了解,proc 宏的稳定性将消除对功能标志的需要?

另一件事是,这种方式#[my_attribute]可能会生成阻止#[other_attribute]运行的代码。 如果在“外部”属性上,我可以注册内部属性,其工作方式与#[derive(Foo)]工作方式相同,那将会非常酷。

@SimonSapin对虚拟会话的评论如何

是否可以/希望通过隐式创建虚拟“会话”来替换这种恐慌? 或者添加一个公共 API(带有稳定路径)来创建一个?

我认为有一个虚拟会话会非常有用。 为proc-macro crates 编写单元测试几乎是不可能的,或者至少非常不方便。 此外,它不仅是TokenStream::from_str ,还有其他需要会话的函数。

@alexcrichton

我已经为与宏 1.2 相关的所有错误做了一些分类工作。 大多数错误可以归类为“严重错误”或“所有跨度相关错误”。

我想将https://github.com/rust-lang/rust/issues/50504升级到“严重”状态 - 描述的模块问题只是一个更深层次问题的症状 - proc 宏的扩展 ID 不是被正确注册。 我不知道这可能会产生什么其他后果。
有一个 PR 修复了潜在的问题(https://github.com/rust-lang/rust/pull/51952),但是修复有一些回归,我还没有仔细研究它们。

我在https://github.com/rust-lang/rust/pull/52081发布了一个 PR 来稳定更多的程序宏

@petrochenkov对我来说听起来不错,我会评论公关

我刚刚发布关于proc_macro_derive子属性问题以及它们如何与命名系统交互的宏命名跟踪问题proc_macro_derive子属性与作用域和命名交互的方式还有另一个问题,但在这里提出似乎更相关。 由于属性中的路径当前未在稳定过程中,我们得到#[derive(foo::Parent)]可能具有子属性#[foo::Child]的可能性,但从表面上看,派生宏无法确定子属性是否实际上是它自己的子属性,因为它无法执行名称查找。 目前,我没有一个简单的解决方案,但我认为对于相互依赖的属性的未来,这应该引起注意; proc_macro_attribute属性没有理由不想以遇到类似查找问题的方式进行交互。

我今天试图编译我的项目,但有些东西坏了,这可能与这个问题有关。 所有错误消息都包含以下消息:“(请参阅问题 #38356)”。 这就是我到这里的方式。
我在这里包含了我在编译过程中得到的错误消息。 我还包括我的 Cargo.toml。

我觉得这非常令人惊讶,因为我的项目被固定到特定的 Rust nightly 版本 (rustc 1.29.0-nightly (9bd8458c9 2018-07-09)) 有什么可以改变的? 可能是图书馆更新了?

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

编译错误

$ 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`. 

您是否知道可能出现的问题,以及如何修复? 谢谢!

@realcr对于proc-macro2板条箱,您只需运行cargo update就可以了!

@alexcrichton我不认为这是这里的问题。 我宁愿认为@realcr已经更新了proc-macro2因为错误消息到处都是proc-macro2-0.4.8 。 问题在于每晚的版本被修复为不包含 #52081 的版本。 我今天遇到了同样的问题,想知道为什么proc-macro2只碰到了次要版本。 但我不太熟悉proc-macro2处理兼容性。

@realcr尝试更新您的夜间编译器或在您的项目中强制执行proc-macro-2版本 < 0.4.8

@alexcrichton@LukasKalbertodt :感谢您的快速回复。
我刚刚将我的夜间编译器更新到了最新版本。 它确实消除了 proc-macro-2 问题,但我遇到了很多新的编译错误。 例子:

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)

为了确认,我当前的 rustc 版本:

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

为了追踪问题的根源,我尝试从futures_await编译基本示例,但它也停止工作。 我会在那里提交一个问题,以便我们可以解决这个问题。

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

因为您正在使用不稳定的功能。

@realcr您的新编译问题与此问题无关,请继续讨论。

@TeXitoi :如果您认为不相关,请随意编辑或删除任何内容。 我尽力帮助你,我很难知道什么是主题,什么不是。 错误消息“(参见问题 #38356)”是我来到这里的原因。

我试图升级我的编译器版本,但出现此错误。 我的代码

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

#[macro_use(eth_abi)]
extern crate pwasm_abi_derive;

和错误说我没有使用#![feature(proc_macro)] ,但我使用了!

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您想将#![feature(proc_macro)]切换#![feature(use_extern_macros)] ,这应该可以解决问题

我认为您还需要使用模块系统来导入过程宏(并确保您有一个最新的夜间编译器)

@alexcrichton是的,多亏了这篇文章,我才弄明白了。 但是,它仍然不起作用:

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

宏的导入规则也发生了变化? 或者我不明白为什么它开始在这里抱怨。

@Pzixel可能是程序宏或编译器中的错误,您可以为此提交专门的问题吗?

好吧,我想我应该先重写我的代码,让它至少看起来可以工作:) 现在还不行,你们用这个功能改变了很多。 完成后 BRB。 如果它没有消失,我就会创建一个问题。

@alexcrichton
您知道在将令牌传递给程序宏之前对令牌应用了什么“预处理”吗?
我知道派生在获得输入令牌之前至少消除了$crate并扩展了cfg (加上通过字符串的往返不是完全无损的,但这是可以修复的)。

我们应该确保新稳定的程序宏不会发生这种情况,并且它们可以准确地获取输入标记(模错误)。

@alexcrichton抱歉, cargo expand由于某种原因在这个板条箱上不起作用。 无法确认问题是在我这边还是在编译器上。 所以我会继续自责,直到这种可能性没有被完全排除。

@petrochenkov proc-macros 的扩展迄今为止已经得到了很好的审查,所以与名称解析部分相比,我更担心这一点。 我个人不知道预处理,但我知道有一个扩展顺序,派生最后运行,cfgs 首先运行,否则它主要是迭代的。

我同意尽管审计很好!

也许我错过了一些明显的东西。 但是,有没有办法从过程宏扩展的 proc_macro 定义包中调用函数、使用类型等? (或从 proc_macro 板条箱,FWIW 中已知的任何其他板条箱)

一些解决方法,但是 AFAIU 如果程序宏的用户重命名了 crate,它们将不起作用。

@Ekleog proc-macro 构建使用它们的 crate。 它们不是运行时依赖项。 您可以将整个 proc-macro crate 视为一种编译器插件,而不是“普通”库。

这在交叉编译时尤其明显:像构建脚本一样,proc-macros 是为主机平台编译的,而不是目标平台。

@SimonSapin我同意你的看法,但是将部分工作委托给 crate 提供的函数可能真的很有用,即使它不是 proc-macro crate(例如,在我上面的链接中, X-derive crate 尝试使用X crate 中的函数)。 因为否则这意味着 proc-macros 生成的所有代码必须是自包含的或对调用站点的状态进行假设。

然后,我可以理解 rustc 还没有为这种功能做好准备(因为我想它最好不需要将 proc-macro 与非 proc-macro crates 分开,这似乎是长期的某个地方 -术语路线图)。 但是我想知道,如果目前只使用TokenStreams界面稳定了,以后能不能改造这个功能? 它不需要像PathToBeResolvedFromTopOfGeneratingProcMacroCrate令牌类型的东西吗? (如果稍后添加,这将是一个重大变化,afaiu)

也许最终可以让事情变得更加灵活,但这似乎还很遥远。

同时,如果您是库作者,请考虑为程序宏使用foo-proc-macrosfoo-derive板条箱,以及包含运行时的“普通” foo库代码,但也重新导出程序宏。 这样,面向用户的 API 就可以保存在一个单独的 crate 中。 这就是serde所做的(在某些配置中) https://github.com/serde-rs/serde/blob/v1.0.71/serde/src/lib.rs#L304

值得指出的是,这种变通方法仍然没有解决用户重命名根 crate的问题(例如

@EkleogTokenStreamTokenTree的流,每个TokenTree都有关联的Span ,它携带范围信息。 除了目前无法为“呼叫站点”(或空)之外的任何其他范围创建跨度。 基本上想出一种合理的符合人体工程学的方式来创建Span s 指的是特定的板条箱是需要的。

我问的原因是没有勾选复选框。 那就太好了!

#![feature(proc_macro)]稳定后,这个问题还剩下什么?

@jan-hudec 哦,我认为Span仅用于错误报告,因为早期的博客文章提到了Hygiene (或类似名称的)结构体起到了这个作用。 我原以为这些已经消失了,显然是错误的。 谢谢! :)

#![feature(proc_macro)] 稳定后,这个问题还剩下什么?

理想情况下,需要为所有单独的剩余问题和未稳定的功能提交新问题,然后可以关闭此问题(就像对 https://github.com/rust-lang/rust/issues/ 所做的一样) 44660)。

哦,我认为 Span 仅用于错误报告,因为早期的博客文章提到了扮演此角色的 Hygiene(或类似名称)结构。 我原以为这些已经消失了,显然是错误的。 谢谢! :)

IIUC,跨度是跟踪卫生上下文的主要方式。

@mark-im 有点。 它们包括源代码位置信息(用于面向用户的消息/诊断)和语法上下文(即卫生信息)。

鉴于此问题获得的讨论/流量量,将proc_macro_diagnostic移至其自己的跟踪问题是否有意义? 我还想弄清楚阻止该功能稳定的因素是什么,看看我们是否可以推动它。 Diesel 使用它,到目前为止它一直很棒。 稳定版缺少此功能导致社区创建了时髦的解决方法,例如 Syn 的最新版本使用compile_error!代替。

@sgrif我打开了这样一个问题: https :

所以我对帮助稳定 Span 和 LineColumn 结构中的方法很感兴趣。 我会在第一条评论中查看未解决的问题,但是如果有人想将新手指向特定方向的编译器,我将不胜感激:+1:

proc_macro_gen功能门将我指向这里,但在顶部的检查列表中,我看不到任何明显指代生成其他宏定义的 (proc_) 宏的内容。 这是否被考虑在内(除了在 r​​ustc 的功能门中)?

@jjpe目前最好是我们为这些特征门分拆专门的跟踪问题,这个问题主要是针对最初的稳定浪潮

@alexcrichton我对此非常满意,更多的是在查看proc_macro_gen功能的过程中,它把我推荐到这里只是为了找到和没有提及它一样好。 这对我来说有点奇怪,所以我决定至少提一下。

@xieyuheng CodeString / Code甚至会是什么样子,即它的语义是什么?
请记住, TokenStream只不过是逐字逐句的源代码,只是作为一系列标记值而不是烦人的文本。

TokenStream (带有TokenTree )的扩展 API 在 1.29 中是稳定的。 导入类似函数的 proc 宏将在 1.30 中稳定。

似乎因为rustc 1.30.0-nightly (63d51e89a 2018-09-28)不再可能在单独的文件中遍历模块内的代码。 如果你处理mod module; ,你会得到TokenStream包含mod module; ,所见即所得。

我明白,为了保持跨度、卫生和与处理代码的一致性,这是必要的。 是否有或将有任何方法可以处理单独文件中的模块内容? 当模块组织的琐碎重构导致宏行为发生变化时,它的缺乏可能会让最终用户感到困惑。

好吧,这个问题很大,并且已经到了我认为保持开放和跟踪 API 没有太大用处的地步。 为此,我打开了https://github.com/rust-lang/rust/pull/54728 ,它将这个问题分解为许多更细粒度的跟踪问题:

在这一点上,我将关闭它,但如果我忘记拆分任何其他跟踪问题,请告诉我! 我当然可以打开更多的后续

@alexcrichton类属性宏的子属性呢?
https://github.com/rust-lang/rust/issues/38356#issuecomment -397095541
这有问题吗?

@XX
这样的子属性不一定是语言特征?
如果derive注册自定义属性,因为派生宏无法从其输入中删除属性(输入是不可变的)。
属性宏可以从其输入中删除#[other_attribute] s,因此它们永远不会进行名称解析,也永远不会报告“未解析的属性”错误。

(除了 https://github.com/rust-lang/rust/issues/38356#issuecomment-397095541 中提到的经典遗留的不稳定自定义属性现在与 proc 宏兼容。)

@petrochenkov是的,感谢您的澄清。

此页面是否有帮助?
0 / 5 - 0 等级