Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6a2d2c9

Browse files
committedSep 21, 2016
Adds a ProcMacro form of syntax extension
This commit adds syntax extension forms matching the types for procedural macros 2.0 (RFC rust-lang#1566), these still require the usual syntax extension boiler plate, but this is a first step towards proper implementation and should be useful for macros 1.1 stuff too. Supports both attribute-like and function-like macros.
1 parent c772948 commit 6a2d2c9

File tree

9 files changed

+422
-18
lines changed

9 files changed

+422
-18
lines changed
 

‎src/librustc_plugin/registry.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ impl<'a> Registry<'a> {
111111
}
112112
MultiDecorator(ext) => MultiDecorator(ext),
113113
MultiModifier(ext) => MultiModifier(ext),
114+
SyntaxExtension::ProcMacro(ext) => SyntaxExtension::ProcMacro(ext),
115+
SyntaxExtension::AttrProcMacro(ext) => SyntaxExtension::AttrProcMacro(ext),
114116
}));
115117
}
116118

‎src/libsyntax/ext/base.rs

Lines changed: 195 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
pub use self::SyntaxExtension::*;
11+
pub use self::SyntaxExtension::{MultiDecorator, MultiModifier, NormalTT, IdentTT, MacroRulesTT};
1212

1313
use ast::{self, Attribute, Name, PatKind};
1414
use attr::HasAttrs;
@@ -19,7 +19,7 @@ use ext::expand::{self, Invocation, Expansion};
1919
use ext::hygiene::Mark;
2020
use ext::tt::macro_rules;
2121
use parse;
22-
use parse::parser;
22+
use parse::parser::{self, Parser};
2323
use parse::token;
2424
use parse::token::{InternedString, str_to_ident};
2525
use ptr::P;
@@ -31,7 +31,8 @@ use feature_gate;
3131
use std::collections::HashMap;
3232
use std::path::PathBuf;
3333
use std::rc::Rc;
34-
use tokenstream;
34+
use std::default::Default;
35+
use tokenstream::{self, TokenStream};
3536

3637

3738
#[derive(Debug,Clone)]
@@ -60,6 +61,14 @@ impl HasAttrs for Annotatable {
6061
}
6162

6263
impl Annotatable {
64+
pub fn span(&self) -> Span {
65+
match *self {
66+
Annotatable::Item(ref item) => item.span,
67+
Annotatable::TraitItem(ref trait_item) => trait_item.span,
68+
Annotatable::ImplItem(ref impl_item) => impl_item.span,
69+
}
70+
}
71+
6372
pub fn expect_item(self) -> P<ast::Item> {
6473
match self {
6574
Annotatable::Item(i) => i,
@@ -146,6 +155,173 @@ impl Into<Vec<Annotatable>> for Annotatable {
146155
}
147156
}
148157

158+
pub trait ProcMacro {
159+
fn expand<'cx>(&self,
160+
ecx: &'cx mut ExtCtxt,
161+
span: Span,
162+
ts: TokenStream)
163+
-> Box<MacResult+'cx>;
164+
}
165+
166+
impl<F> ProcMacro for F
167+
where F: Fn(TokenStream) -> TokenStream
168+
{
169+
fn expand<'cx>(&self,
170+
ecx: &'cx mut ExtCtxt,
171+
span: Span,
172+
ts: TokenStream)
173+
-> Box<MacResult+'cx> {
174+
let result = (*self)(ts);
175+
// FIXME setup implicit context in TLS before calling self.
176+
let parser = ecx.new_parser_from_tts(&result.to_tts());
177+
Box::new(TokResult { parser: parser, span: span })
178+
}
179+
}
180+
181+
pub trait AttrProcMacro {
182+
fn expand<'cx>(&self,
183+
ecx: &'cx mut ExtCtxt,
184+
span: Span,
185+
annotation: TokenStream,
186+
annotated: TokenStream)
187+
-> Box<MacResult+'cx>;
188+
}
189+
190+
impl<F> AttrProcMacro for F
191+
where F: Fn(TokenStream, TokenStream) -> TokenStream
192+
{
193+
fn expand<'cx>(&self,
194+
ecx: &'cx mut ExtCtxt,
195+
span: Span,
196+
annotation: TokenStream,
197+
annotated: TokenStream)
198+
-> Box<MacResult+'cx> {
199+
// FIXME setup implicit context in TLS before calling self.
200+
let parser = ecx.new_parser_from_tts(&(*self)(annotation, annotated).to_tts());
201+
Box::new(TokResult { parser: parser, span: span })
202+
}
203+
}
204+
205+
struct TokResult<'a> {
206+
parser: Parser<'a>,
207+
span: Span,
208+
}
209+
210+
impl<'a> MacResult for TokResult<'a> {
211+
fn make_items(mut self: Box<Self>) -> Option<SmallVector<P<ast::Item>>> {
212+
if self.parser.sess.span_diagnostic.has_errors() {
213+
return None;
214+
}
215+
216+
let mut items = SmallVector::zero();
217+
loop {
218+
match self.parser.parse_item() {
219+
Ok(Some(item)) => {
220+
// FIXME better span info.
221+
let mut item = item.unwrap();
222+
item.span = self.span;
223+
items.push(P(item));
224+
}
225+
Ok(None) => {
226+
return Some(items);
227+
}
228+
Err(mut e) => {
229+
e.emit();
230+
return None;
231+
}
232+
}
233+
}
234+
}
235+
236+
fn make_impl_items(mut self: Box<Self>) -> Option<SmallVector<ast::ImplItem>> {
237+
let mut items = SmallVector::zero();
238+
loop {
239+
match self.parser.parse_impl_item() {
240+
Ok(mut item) => {
241+
// FIXME better span info.
242+
item.span = self.span;
243+
items.push(item);
244+
245+
return Some(items);
246+
}
247+
Err(mut e) => {
248+
e.emit();
249+
return None;
250+
}
251+
}
252+
}
253+
}
254+
255+
fn make_trait_items(mut self: Box<Self>) -> Option<SmallVector<ast::TraitItem>> {
256+
let mut items = SmallVector::zero();
257+
loop {
258+
match self.parser.parse_trait_item() {
259+
Ok(mut item) => {
260+
// FIXME better span info.
261+
item.span = self.span;
262+
items.push(item);
263+
264+
return Some(items);
265+
}
266+
Err(mut e) => {
267+
e.emit();
268+
return None;
269+
}
270+
}
271+
}
272+
}
273+
274+
fn make_expr(mut self: Box<Self>) -> Option<P<ast::Expr>> {
275+
match self.parser.parse_expr() {
276+
Ok(e) => Some(e),
277+
Err(mut e) => {
278+
e.emit();
279+
return None;
280+
}
281+
}
282+
}
283+
284+
fn make_pat(mut self: Box<Self>) -> Option<P<ast::Pat>> {
285+
match self.parser.parse_pat() {
286+
Ok(e) => Some(e),
287+
Err(mut e) => {
288+
e.emit();
289+
return None;
290+
}
291+
}
292+
}
293+
294+
fn make_stmts(mut self: Box<Self>) -> Option<SmallVector<ast::Stmt>> {
295+
let mut stmts = SmallVector::zero();
296+
loop {
297+
if self.parser.token == token::Eof {
298+
return Some(stmts);
299+
}
300+
match self.parser.parse_full_stmt(true) {
301+
Ok(Some(mut stmt)) => {
302+
stmt.span = self.span;
303+
stmts.push(stmt);
304+
}
305+
Ok(None) => { /* continue */ }
306+
Err(mut e) => {
307+
e.emit();
308+
return None;
309+
}
310+
}
311+
}
312+
}
313+
314+
fn make_ty(mut self: Box<Self>) -> Option<P<ast::Ty>> {
315+
match self.parser.parse_ty() {
316+
Ok(e) => Some(e),
317+
Err(mut e) => {
318+
e.emit();
319+
return None;
320+
}
321+
}
322+
}
323+
}
324+
149325
/// Represents a thing that maps token trees to Macro Results
150326
pub trait TTMacroExpander {
151327
fn expand<'cx>(&self,
@@ -439,24 +615,35 @@ pub enum SyntaxExtension {
439615
/// based upon it.
440616
///
441617
/// `#[derive(...)]` is a `MultiItemDecorator`.
442-
MultiDecorator(Box<MultiItemDecorator + 'static>),
618+
///
619+
/// Prefer ProcMacro or MultiModifier since they are more flexible.
620+
MultiDecorator(Box<MultiItemDecorator>),
443621

444622
/// A syntax extension that is attached to an item and modifies it
445-
/// in-place. More flexible version than Modifier.
446-
MultiModifier(Box<MultiItemModifier + 'static>),
623+
/// in-place. Also allows decoration, i.e., creating new items.
624+
MultiModifier(Box<MultiItemModifier>),
625+
626+
/// A function-like procedural macro. TokenStream -> TokenStream.
627+
ProcMacro(Box<ProcMacro>),
628+
629+
/// An attribute-like procedural macro. TokenStream, TokenStream -> TokenStream.
630+
/// The first TokenSteam is the attribute, the second is the annotated item.
631+
/// Allows modification of the input items and adding new items, similar to
632+
/// MultiModifier, but uses TokenStreams, rather than AST nodes.
633+
AttrProcMacro(Box<AttrProcMacro>),
447634

448635
/// A normal, function-like syntax extension.
449636
///
450637
/// `bytes!` is a `NormalTT`.
451638
///
452639
/// The `bool` dictates whether the contents of the macro can
453640
/// directly use `#[unstable]` things (true == yes).
454-
NormalTT(Box<TTMacroExpander + 'static>, Option<Span>, bool),
641+
NormalTT(Box<TTMacroExpander>, Option<Span>, bool),
455642

456643
/// A function-like syntax extension that has an extra ident before
457644
/// the block.
458645
///
459-
IdentTT(Box<IdentMacroExpander + 'static>, Option<Span>, bool),
646+
IdentTT(Box<IdentMacroExpander>, Option<Span>, bool),
460647
}
461648

462649
pub type NamedSyntaxExtension = (Name, SyntaxExtension);

‎src/libsyntax/ext/expand.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ use feature_gate::{self, Features};
2222
use fold;
2323
use fold::*;
2424
use parse::token::{intern, keywords};
25+
use parse::span_to_tts;
2526
use ptr::P;
26-
use tokenstream::TokenTree;
27+
use tokenstream::{TokenTree, TokenStream};
2728
use util::small_vector::SmallVector;
2829
use visit::Visitor;
2930

@@ -308,6 +309,31 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
308309
items.push(item);
309310
kind.expect_from_annotatables(items)
310311
}
312+
SyntaxExtension::AttrProcMacro(ref mac) => {
313+
let attr_toks = TokenStream::from_tts(span_to_tts(&fld.cx.parse_sess,
314+
attr.span));
315+
let item_toks = TokenStream::from_tts(span_to_tts(&fld.cx.parse_sess,
316+
item.span()));
317+
let result = mac.expand(self.cx, attr.span, attr_toks, item_toks);
318+
let items = match item {
319+
Annotatable::Item(_) => result.make_items()
320+
.unwrap_or(SmallVector::zero())
321+
.into_iter()
322+
.map(|i| Annotatable::Item(i))
323+
.collect(),
324+
Annotatable::TraitItem(_) => result.make_trait_items()
325+
.unwrap_or(SmallVector::zero())
326+
.into_iter()
327+
.map(|i| Annotatable::TraitItem(P(i)))
328+
.collect(),
329+
Annotatable::ImplItem(_) => result.make_impl_items()
330+
.unwrap_or(SmallVector::zero())
331+
.into_iter()
332+
.map(|i| Annotatable::ImplItem(P(i)))
333+
.collect(),
334+
};
335+
kind.expect_from_annotatables(items)
336+
}
311337
_ => unreachable!(),
312338
}
313339
}
@@ -377,11 +403,34 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
377403
kind.make_from(expander.expand(self.cx, span, ident, marked_tts, attrs))
378404
}
379405

380-
MultiDecorator(..) | MultiModifier(..) => {
406+
MultiDecorator(..) | MultiModifier(..) | SyntaxExtension::AttrProcMacro(..) => {
381407
self.cx.span_err(path.span,
382408
&format!("`{}` can only be used in attributes", extname));
383409
return kind.dummy(span);
384410
}
411+
412+
SyntaxExtension::ProcMacro(ref expandfun) => {
413+
if ident.name != keywords::Invalid.name() {
414+
let msg =
415+
format!("macro {}! expects no ident argument, given '{}'", extname, ident);
416+
fld.cx.span_err(path.span, &msg);
417+
return None;
418+
}
419+
420+
fld.cx.bt_push(ExpnInfo {
421+
call_site: call_site,
422+
callee: NameAndSpan {
423+
format: MacroBang(extname),
424+
// FIXME procedural macros do not have proper span info
425+
// yet, when they do, we should use it here.
426+
span: None,
427+
// FIXME probably want to follow macro_rules macros here.
428+
allow_internal_unstable: false,
429+
},
430+
});
431+
432+
Some(expandfun.expand(fld.cx, call_site, TokenStream::from_tts(marked_tts)))
433+
}
385434
};
386435

387436
let expanded = if let Some(expanded) = opt_expanded {

‎src/libsyntax/ext/proc_macro_shim.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ use ext::base::*;
2424

2525
/// Take a `ExtCtxt`, `Span`, and `TokenStream`, and produce a Macro Result that parses
2626
/// the TokenStream as a block and returns it as an `Expr`.
27-
pub fn build_block_emitter<'cx>(cx: &'cx mut ExtCtxt, sp: Span, output: TokenStream)
27+
pub fn build_block_emitter<'cx>(cx: &'cx mut ExtCtxt,
28+
sp: Span,
29+
output: TokenStream)
2830
-> Box<MacResult + 'cx> {
2931
let parser = cx.new_parser_from_tts(&output.to_tts());
3032

@@ -60,7 +62,7 @@ pub fn build_block_emitter<'cx>(cx: &'cx mut ExtCtxt, sp: Span, output: TokenStr
6062
}
6163

6264
pub mod prelude {
63-
pub use ext::proc_macro_shim::build_block_emitter;
65+
pub use super::build_block_emitter;
6466
pub use ast::Ident;
6567
pub use codemap::{DUMMY_SP, Span};
6668
pub use ext::base::{ExtCtxt, MacResult};

‎src/libsyntax/parse/lexer/mod.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ pub struct StringReader<'a> {
8585
/// The last character to be read
8686
pub curr: Option<char>,
8787
pub filemap: Rc<syntax_pos::FileMap>,
88+
/// If Some, stop reading the source at this position (inclusive).
89+
pub terminator: Option<BytePos>,
90+
/// Whether to record new-lines in filemap. This is only necessary the first
91+
/// time a filemap is lexed. If part of a filemap is being re-lexed, this
92+
/// should be set to false.
93+
pub save_new_lines: bool,
8894
// cached:
8995
pub peek_tok: token::Token,
9096
pub peek_span: Span,
@@ -96,7 +102,15 @@ pub struct StringReader<'a> {
96102

97103
impl<'a> Reader for StringReader<'a> {
98104
fn is_eof(&self) -> bool {
99-
self.curr.is_none()
105+
if self.curr.is_none() {
106+
return true;
107+
}
108+
109+
match self.terminator {
110+
Some(t) => self.pos > t,
111+
None => false,
112+
}
113+
100114
}
101115
/// Return the next token. EFFECT: advances the string_reader.
102116
fn try_next_token(&mut self) -> Result<TokenAndSpan, ()> {
@@ -164,6 +178,14 @@ impl<'a> StringReader<'a> {
164178
pub fn new_raw<'b>(span_diagnostic: &'b Handler,
165179
filemap: Rc<syntax_pos::FileMap>)
166180
-> StringReader<'b> {
181+
let mut sr = StringReader::new_raw_internal(span_diagnostic, filemap);
182+
sr.bump();
183+
sr
184+
}
185+
186+
fn new_raw_internal<'b>(span_diagnostic: &'b Handler,
187+
filemap: Rc<syntax_pos::FileMap>)
188+
-> StringReader<'b> {
167189
if filemap.src.is_none() {
168190
span_diagnostic.bug(&format!("Cannot lex filemap \
169191
without source: {}",
@@ -172,21 +194,21 @@ impl<'a> StringReader<'a> {
172194

173195
let source_text = (*filemap.src.as_ref().unwrap()).clone();
174196

175-
let mut sr = StringReader {
197+
StringReader {
176198
span_diagnostic: span_diagnostic,
177199
pos: filemap.start_pos,
178200
last_pos: filemap.start_pos,
179201
col: CharPos(0),
180202
curr: Some('\n'),
181203
filemap: filemap,
204+
terminator: None,
205+
save_new_lines: true,
182206
// dummy values; not read
183207
peek_tok: token::Eof,
184208
peek_span: syntax_pos::DUMMY_SP,
185209
source_text: source_text,
186210
fatal_errs: Vec::new(),
187-
};
188-
sr.bump();
189-
sr
211+
}
190212
}
191213

192214
pub fn new<'b>(span_diagnostic: &'b Handler,
@@ -200,6 +222,28 @@ impl<'a> StringReader<'a> {
200222
sr
201223
}
202224

225+
pub fn from_span<'b>(span_diagnostic: &'b Handler,
226+
span: Span,
227+
codemap: &CodeMap)
228+
-> StringReader<'b> {
229+
let start_pos = codemap.lookup_byte_offset(span.lo);
230+
let last_pos = codemap.lookup_byte_offset(span.hi);
231+
assert!(start_pos.fm.name == last_pos.fm.name, "Attempt to lex span which crosses files");
232+
let mut sr = StringReader::new_raw_internal(span_diagnostic, start_pos.fm.clone());
233+
sr.pos = span.lo;
234+
sr.last_pos = span.lo;
235+
sr.terminator = Some(span.hi);
236+
sr.save_new_lines = false;
237+
238+
sr.bump();
239+
240+
if let Err(_) = sr.advance_token() {
241+
sr.emit_fatal_errors();
242+
panic!(FatalError);
243+
}
244+
sr
245+
}
246+
203247
pub fn curr_is(&self, c: char) -> bool {
204248
self.curr == Some(c)
205249
}
@@ -405,7 +449,9 @@ impl<'a> StringReader<'a> {
405449
self.curr = Some(ch);
406450
self.col = self.col + CharPos(1);
407451
if last_char == '\n' {
408-
self.filemap.next_line(self.last_pos);
452+
if self.save_new_lines {
453+
self.filemap.next_line(self.last_pos);
454+
}
409455
self.col = CharPos(0);
410456
}
411457

‎src/libsyntax/parse/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,13 @@ fn file_to_filemap(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
258258
}
259259
}
260260

261+
pub fn span_to_tts(sess: &ParseSess, span: Span) -> Vec<tokenstream::TokenTree> {
262+
let cfg = Vec::new();
263+
let srdr = lexer::StringReader::from_span(&sess.span_diagnostic, span, &sess.code_map);
264+
let mut p1 = Parser::new(sess, cfg, Box::new(srdr));
265+
panictry!(p1.parse_all_token_trees())
266+
}
267+
261268
/// Given a filemap, produce a sequence of token-trees
262269
pub fn filemap_to_tts(sess: &ParseSess, filemap: Rc<FileMap>)
263270
-> Vec<tokenstream::TokenTree> {

‎src/libsyntax/tokenstream.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
3333
use parse::lexer;
3434
use parse;
3535
use parse::token::{self, Token, Lit, Nonterminal};
36+
use print::pprust;
3637

3738
use std::fmt;
3839
use std::iter::*;
@@ -781,6 +782,12 @@ impl TokenStream {
781782
}
782783
}
783784

785+
impl fmt::Display for TokenStream {
786+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
787+
f.write_str(&pprust::tts_to_string(&self.to_tts()))
788+
}
789+
}
790+
784791
// FIXME Reimplement this iterator to hold onto a slice iterator for a leaf, getting the
785792
// next leaf's iterator when the current one is exhausted.
786793
pub struct Iter<'a> {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(plugin, plugin_registrar, rustc_private)]
12+
13+
extern crate proc_macro;
14+
extern crate rustc_plugin;
15+
extern crate syntax;
16+
17+
use proc_macro::prelude::*;
18+
use rustc_plugin::Registry;
19+
use syntax::ext::base::SyntaxExtension;
20+
use syntax::ext::proc_macro_shim::prelude::*;
21+
22+
#[plugin_registrar]
23+
pub fn plugin_registrar(reg: &mut Registry) {
24+
reg.register_syntax_extension(token::intern("attr_tru"),
25+
SyntaxExtension::AttrProcMacro(Box::new(attr_tru)));
26+
reg.register_syntax_extension(token::intern("attr_identity"),
27+
SyntaxExtension::AttrProcMacro(Box::new(attr_identity)));
28+
reg.register_syntax_extension(token::intern("tru"),
29+
SyntaxExtension::ProcMacro(Box::new(tru)));
30+
reg.register_syntax_extension(token::intern("ret_tru"),
31+
SyntaxExtension::ProcMacro(Box::new(ret_tru)));
32+
reg.register_syntax_extension(token::intern("identity"),
33+
SyntaxExtension::ProcMacro(Box::new(identity)));
34+
}
35+
36+
fn attr_tru(_attr: TokenStream, _item: TokenStream) -> TokenStream {
37+
lex("fn f1() -> bool { true }")
38+
}
39+
40+
fn attr_identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
41+
let source = item.to_string();
42+
lex(&source)
43+
}
44+
45+
fn tru(_ts: TokenStream) -> TokenStream {
46+
lex("true")
47+
}
48+
49+
fn ret_tru(_ts: TokenStream) -> TokenStream {
50+
lex("return true;")
51+
}
52+
53+
fn identity(ts: TokenStream) -> TokenStream {
54+
let source = ts.to_string();
55+
lex(&source)
56+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:proc_macro_def.rs
12+
// ignore-stage1
13+
// ignore-cross-compile
14+
15+
#![feature(plugin, custom_attribute)]
16+
#![feature(type_macros)]
17+
18+
#![plugin(proc_macro_def)]
19+
20+
#[attr_tru]
21+
fn f1() -> bool {
22+
return false;
23+
}
24+
25+
#[attr_identity]
26+
fn f2() -> bool {
27+
return identity!(true);
28+
}
29+
30+
fn f3() -> identity!(bool) {
31+
ret_tru!();
32+
}
33+
34+
fn f4(x: bool) -> bool {
35+
match x {
36+
identity!(true) => false,
37+
identity!(false) => true,
38+
}
39+
}
40+
41+
fn main() {
42+
assert!(f1());
43+
assert!(f2());
44+
assert!(tru!());
45+
assert!(f3());
46+
assert!(identity!(5 == 5));
47+
assert!(f4(false));
48+
}

0 commit comments

Comments
 (0)
Please sign in to comment.