diff --git a/src/doc/grammar.md b/src/doc/grammar.md index 3d9a5bafbd71e..542815e7afe3c 100644 --- a/src/doc/grammar.md +++ b/src/doc/grammar.md @@ -152,19 +152,19 @@ token : simple_token | ident | literal | symbol | whitespace token ;

-| | | | | | -|----------|----------|----------|----------|--------| -| abstract | alignof | as | become | box | -| break | const | continue | crate | do | -| else | enum | extern | false | final | -| fn | for | if | impl | in | -| let | loop | match | mod | move | -| mut | offsetof | once | override | priv | -| proc | pub | pure | ref | return | -| sizeof | static | self | struct | super | -| true | trait | type | typeof | unsafe | -| unsized | use | virtual | where | while | -| yield | | | | | +| | | | | | +|----------|----------|----------|----------|---------| +| abstract | alignof | as | become | box | +| break | const | continue | crate | do | +| else | enum | extern | false | final | +| fn | for | if | impl | in | +| let | loop | macro | match | mod | +| move | mut | offsetof | override | priv | +| proc | pub | pure | ref | return | +| Self | self | sizeof | static | struct | +| super | trait | true | type | typeof | +| unsafe | unsized | use | virtual | where | +| while | yield | | | | Each of these keywords has special meaning in its grammar, and all of them are @@ -524,6 +524,15 @@ array_elems : [expr [',' expr]*] | [expr ',' ".." expr] ; idx_expr : expr '[' expr ']' ; ``` +### Range expressions + +```antlr +range_expr : expr ".." expr | + expr ".." | + ".." expr | + ".." ; +``` + ### Unary operator expressions **FIXME:** grammar? @@ -610,7 +619,7 @@ lambda_expr : '|' ident_list '|' expr ; ### While loops ```antlr -while_expr : "while" no_struct_literal_expr '{' block '}' ; +while_expr : [ lifetime ':' ] "while" no_struct_literal_expr '{' block '}' ; ``` ### Infinite loops @@ -634,7 +643,7 @@ continue_expr : "continue" [ lifetime ]; ### For expressions ```antlr -for_expr : "for" pat "in" no_struct_literal_expr '{' block '}' ; +for_expr : [ lifetime ':' ] "for" pat "in" no_struct_literal_expr '{' block '}' ; ``` ### If expressions diff --git a/src/doc/reference.md b/src/doc/reference.md index 1cedbf299c327..059da89192576 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -29,41 +29,6 @@ You may also be interested in the [grammar]. # Notation -Rust's grammar is defined over Unicode code points, each conventionally denoted -`U+XXXX`, for 4 or more hexadecimal digits `X`. _Most_ of Rust's grammar is -confined to the ASCII range of Unicode, and is described in this document by a -dialect of Extended Backus-Naur Form (EBNF), specifically a dialect of EBNF -supported by common automated LL(k) parsing tools such as `llgen`, rather than -the dialect given in ISO 14977. The dialect can be defined self-referentially -as follows: - -```{.ebnf .notation} -grammar : rule + ; -rule : nonterminal ':' productionrule ';' ; -productionrule : production [ '|' production ] * ; -production : term * ; -term : element repeats ; -element : LITERAL | IDENTIFIER | '[' productionrule ']' ; -repeats : [ '*' | '+' ] NUMBER ? | NUMBER ? | '?' ; -``` - -Where: - -- Whitespace in the grammar is ignored. -- Square brackets are used to group rules. -- `LITERAL` is a single printable ASCII character, or an escaped hexadecimal - ASCII code of the form `\xQQ`, in single quotes, denoting the corresponding - Unicode code point `U+00QQ`. -- `IDENTIFIER` is a nonempty string of ASCII letters and underscores. -- The `repeat` forms apply to the adjacent `element`, and are as follows: - - `?` means zero or one repetition - - `*` means zero or more repetitions - - `+` means one or more repetitions - - NUMBER trailing a repeat symbol gives a maximum repetition count - - NUMBER on its own gives an exact repetition count - -This EBNF dialect should hopefully be familiar to many readers. - ## Unicode productions A few productions in Rust's grammar permit Unicode code points outside the ASCII @@ -132,13 +97,6 @@ Some productions are defined by exclusion of particular Unicode characters: ## Comments -```{.ebnf .gram} -comment : block_comment | line_comment ; -block_comment : "/*" block_comment_body * "*/" ; -block_comment_body : [block_comment | character] * ; -line_comment : "//" non_eol * ; -``` - Comments in Rust code follow the general C++ style of line and block-comment forms. Nested block comments are supported. @@ -159,11 +117,6 @@ Non-doc comments are interpreted as a form of whitespace. ## Whitespace -```{.ebnf .gram} -whitespace_char : '\x20' | '\x09' | '\x0a' | '\x0d' ; -whitespace : [ whitespace_char | comment ] + ; -``` - The `whitespace_char` production is any nonempty Unicode string consisting of any of the following Unicode characters: `U+0020` (space, `' '`), `U+0009` (tab, `'\t'`), `U+000A` (LF, `'\n'`), `U+000D` (CR, `'\r'`). @@ -176,41 +129,11 @@ with any other legal whitespace element, such as a single space character. ## Tokens -```{.ebnf .gram} -simple_token : keyword | unop | binop ; -token : simple_token | ident | literal | symbol | whitespace token ; -``` - Tokens are primitive productions in the grammar defined by regular (non-recursive) languages. "Simple" tokens are given in [string table production](#string-table-productions) form, and occur in the rest of the grammar as double-quoted strings. Other tokens have exact rules given. -### Keywords - -

- -| | | | | | -|----------|----------|----------|----------|---------| -| abstract | alignof | as | become | box | -| break | const | continue | crate | do | -| else | enum | extern | false | final | -| fn | for | if | impl | in | -| let | loop | macro | match | mod | -| move | mut | offsetof | override | priv | -| proc | pub | pure | ref | return | -| Self | self | sizeof | static | struct | -| super | trait | true | type | typeof | -| unsafe | unsized | use | virtual | where | -| while | yield | | | | - - -Each of these keywords has special meaning in its grammar, and all of them are -excluded from the `ident` rule. - -Note that some of these keywords are reserved, and do not currently do -anything. - ### Literals A literal is an expression consisting of a single token, rather than a sequence @@ -218,11 +141,6 @@ of tokens, that immediately and directly denotes the value it evaluates to, rather than referring to it by name or some other evaluation rule. A literal is a form of constant expression, so is evaluated (primarily) at compile time. -```{.ebnf .gram} -lit_suffix : ident; -literal : [ string_lit | char_lit | byte_string_lit | byte_lit | num_lit ] lit_suffix ?; -``` - The optional suffix is only used for certain numeric literals, but is reserved for future extension, that is, the above gives the lexical grammar, but a Rust parser will reject everything but the 12 special @@ -275,32 +193,6 @@ cases mentioned in [Number literals](#number-literals) below. #### Character and string literals -```{.ebnf .gram} -char_lit : '\x27' char_body '\x27' ; -string_lit : '"' string_body * '"' | 'r' raw_string ; - -char_body : non_single_quote - | '\x5c' [ '\x27' | common_escape | unicode_escape ] ; - -string_body : non_double_quote - | '\x5c' [ '\x22' | common_escape | unicode_escape ] ; -raw_string : '"' raw_string_body '"' | '#' raw_string '#' ; - -common_escape : '\x5c' - | 'n' | 'r' | 't' | '0' - | 'x' hex_digit 2 - -unicode_escape : 'u' '{' hex_digit+ 6 '}'; - -hex_digit : 'a' | 'b' | 'c' | 'd' | 'e' | 'f' - | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' - | dec_digit ; -oct_digit : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' ; -dec_digit : '0' | nonzero_dec ; -nonzero_dec: '1' | '2' | '3' | '4' - | '5' | '6' | '7' | '8' | '9' ; -``` - ##### Character literals A _character literal_ is a single Unicode character enclosed within two @@ -349,11 +241,10 @@ following forms: Raw string literals do not process any escapes. They start with the character `U+0072` (`r`), followed by zero or more of the character `U+0023` (`#`) and a -`U+0022` (double-quote) character. The _raw string body_ is not defined in the -EBNF grammar above: it can contain any sequence of Unicode characters and is -terminated only by another `U+0022` (double-quote) character, followed by the -same number of `U+0023` (`#`) characters that preceded the opening `U+0022` -(double-quote) character. +`U+0022` (double-quote) character. The _raw string body_ can contain any sequence +of Unicode characters and is terminated only by another `U+0022` (double-quote) +character, followed by the same number of `U+0023` (`#`) characters that preceded +the opening `U+0022` (double-quote) character. All Unicode characters contained in the raw string body represent themselves, the characters `U+0022` (double-quote) (except when followed by at least as @@ -375,19 +266,6 @@ r##"foo #"# bar"##; // foo #"# bar #### Byte and byte string literals -```{.ebnf .gram} -byte_lit : "b\x27" byte_body '\x27' ; -byte_string_lit : "b\x22" string_body * '\x22' | "br" raw_byte_string ; - -byte_body : ascii_non_single_quote - | '\x5c' [ '\x27' | common_escape ] ; - -byte_string_body : ascii_non_double_quote - | '\x5c' [ '\x22' | common_escape ] ; -raw_byte_string : '"' raw_byte_string_body '"' | '#' raw_byte_string '#' ; - -``` - ##### Byte literals A _byte literal_ is a single ASCII character (in the `U+0000` to `U+007F` @@ -403,7 +281,7 @@ preceded by the characters `U+0062` (`b`) and `U+0022` (double-quote), and followed by the character `U+0022`. If the character `U+0022` is present within the literal, it must be _escaped_ by a preceding `U+005C` (`\`) character. Alternatively, a byte string literal can be a _raw byte string literal_, defined -below. A byte string literal is equivalent to a `&'static [u8]` borrowed array +below. A byte string literal of length `n` is equivalent to a `&'static [u8; n]` borrowed fixed-sized array of unsigned 8-bit integers. Some additional _escapes_ are available in either byte or non-raw byte string @@ -424,11 +302,10 @@ following forms: Raw byte string literals do not process any escapes. They start with the character `U+0062` (`b`), followed by `U+0072` (`r`), followed by zero or more of the character `U+0023` (`#`), and a `U+0022` (double-quote) character. The -_raw string body_ is not defined in the EBNF grammar above: it can contain any -sequence of ASCII characters and is terminated only by another `U+0022` -(double-quote) character, followed by the same number of `U+0023` (`#`) -characters that preceded the opening `U+0022` (double-quote) character. A raw -byte string literal can not contain any non-ASCII byte. +_raw string body_ can contain any sequence of ASCII characters and is terminated +only by another `U+0022` (double-quote) character, followed by the same number of +`U+0023` (`#`) characters that preceded the opening `U+0022` (double-quote) +character. A raw byte string literal can not contain any non-ASCII byte. All characters contained in the raw string body represent their ASCII encoding, the characters `U+0022` (double-quote) (except when followed by at least as @@ -450,19 +327,6 @@ b"\\x52"; br"\x52"; // \x52 #### Number literals -```{.ebnf .gram} -num_lit : nonzero_dec [ dec_digit | '_' ] * float_suffix ? - | '0' [ [ dec_digit | '_' ] * float_suffix ? - | 'b' [ '1' | '0' | '_' ] + - | 'o' [ oct_digit | '_' ] + - | 'x' [ hex_digit | '_' ] + ] ; - -float_suffix : [ exponent | '.' dec_lit exponent ? ] ? ; - -exponent : ['E' | 'e'] ['-' | '+' ] ? dec_lit ; -dec_lit : [ dec_digit | '_' ] + ; -``` - A _number literal_ is either an _integer literal_ or a _floating-point literal_. The grammar for recognizing the two kinds of literals is mixed. @@ -540,12 +404,6 @@ The two values of the boolean type are written `true` and `false`. ### Symbols -```{.ebnf .gram} -symbol : "::" | "->" - | '#' | '[' | ']' | '(' | ')' | '{' | '}' - | ',' | ';' ; -``` - Symbols are a general class of printable [token](#tokens) that play structural roles in a variety of grammar productions. They are catalogued here for completeness as the set of remaining miscellaneous printable tokens that do not @@ -555,16 +413,6 @@ operators](#binary-operator-expressions), or [keywords](#keywords). ## Paths -```{.ebnf .gram} -expr_path : [ "::" ] ident [ "::" expr_path_tail ] + ; -expr_path_tail : '<' type_expr [ ',' type_expr ] + '>' - | expr_path ; - -type_path : ident [ type_path_tail ] + ; -type_path_tail : '<' type_expr [ ',' type_expr ] + '>' - | "::" type_path ; -``` - A _path_ is a sequence of one or more path components _logically_ separated by a namespace qualifier (`::`). If a path consists of only one component, it may refer to either an [item](#items) or a [variable](#variables) in a local control @@ -660,19 +508,6 @@ Users of `rustc` can define new syntax extensions in two ways: ## Macros -```{.ebnf .gram} -expr_macro_rules : "macro_rules" '!' ident '(' macro_rule * ')' ; -macro_rule : '(' matcher * ')' "=>" '(' transcriber * ')' ';' ; -matcher : '(' matcher * ')' | '[' matcher * ']' - | '{' matcher * '}' | '$' ident ':' ident - | '$' '(' matcher * ')' sep_token? [ '*' | '+' ] - | non_special_token ; -transcriber : '(' transcriber * ')' | '[' transcriber * ']' - | '{' transcriber * '}' | '$' ident - | '$' '(' transcriber * ')' sep_token? [ '*' | '+' ] - | non_special_token ; -``` - `macro_rules` allows users to define syntax extension in a declarative way. We call such extensions "macros by example" or simply "macros" — to be distinguished from the "procedural macros" defined in [compiler plugins][plugin]. @@ -811,12 +646,6 @@ Crates contain [items](#items), each of which may have some number of ## Items -```{.ebnf .gram} -item : extern_crate_decl | use_decl | mod_item | fn_item | type_item - | struct_item | enum_item | static_item | trait_item | impl_item - | extern_block ; -``` - An _item_ is a component of a crate. Items are organized within a crate by a nested set of [modules](#modules). Every crate has a single "outermost" anonymous module; all further items within the crate have [paths](#paths) @@ -863,11 +692,6 @@ no notion of type abstraction: there are no first-class "forall" types. ### Modules -```{.ebnf .gram} -mod_item : "mod" ident ( ';' | '{' mod '}' ); -mod : item * ; -``` - A module is a container for zero or more [items](#items). A _module item_ is a module, surrounded in braces, named, and prefixed with the @@ -928,11 +752,6 @@ mod thread { ##### Extern crate declarations -```{.ebnf .gram} -extern_crate_decl : "extern" "crate" crate_name -crate_name: ident | ( string_lit "as" ident ) -``` - An _`extern crate` declaration_ specifies a dependency on an external crate. The external crate is then bound into the declaring scope as the `ident` provided in the `extern_crate_decl`. @@ -958,17 +777,6 @@ extern crate std as ruststd; // linking to 'std' under another name ##### Use declarations -```{.ebnf .gram} -use_decl : "pub" ? "use" [ path "as" ident - | path_glob ] ; - -path_glob : ident [ "::" [ path_glob - | '*' ] ] ? - | '{' path_item [ ',' path_item ] * '}' ; - -path_item : ident | "self" ; -``` - A _use declaration_ creates one or more local name bindings synonymous with some other [path](#paths). Usually a `use` declaration is used to shorten the path required to refer to a module item. These declarations may appear at the @@ -1413,10 +1221,6 @@ it were `Bar(i32)`, this is disallowed. ### Constant items -```{.ebnf .gram} -const_item : "const" ident ':' type '=' expr ';' ; -``` - A *constant item* is a named _constant value_ which is not associated with a specific memory location in the program. Constants are essentially inlined wherever they are used, meaning that they are copied directly into the relevant @@ -1453,10 +1257,6 @@ const BITS_N_STRINGS: BitsNStrings<'static> = BitsNStrings { ### Static items -```{.ebnf .gram} -static_item : "static" ident ':' type '=' expr ';' ; -``` - A *static item* is similar to a *constant*, except that it represents a precise memory location in the program. A static is never "inlined" at the usage site, and all references to it refer to the same memory location. Static items have @@ -1711,11 +1511,6 @@ impl Seq for u32 { ### External blocks -```{.ebnf .gram} -extern_block_item : "extern" '{' extern_block '}' ; -extern_block : [ foreign_fn ] * ; -``` - External blocks form the basis for Rust's foreign function interface. Declarations in an external block describe symbols in external, non-Rust libraries. @@ -1915,13 +1710,6 @@ the namespace hierarchy as it normally would. ## Attributes -```{.ebnf .gram} -attribute : '#' '!' ? '[' meta_item ']' ; -meta_item : ident [ '=' literal - | '(' meta_seq ')' ] ? ; -meta_seq : meta_item [ ',' meta_seq ] ? ; -``` - Any item declaration may have an _attribute_ applied to it. Attributes in Rust are modeled on Attributes in ECMA-335, with the syntax coming from ECMA-334 (C#). An attribute is a general, free-form metadatum that is interpreted @@ -2503,7 +2291,7 @@ The currently implemented features of the reference compiler are: terms of encapsulation). If a feature is promoted to a language feature, then all existing programs will -start to receive compilation warnings about #[feature] directives which enabled +start to receive compilation warnings about `#![feature]` directives which enabled the new feature (because the directive is no longer necessary). However, if a feature is decided to be removed from the language, errors will be issued (if there isn't a parser error first). The directive in this case is no longer @@ -2554,11 +2342,6 @@ in meaning to declaring the item outside the statement block. #### Variable declarations -```{.ebnf .gram} -let_decl : "let" pat [':' type ] ? [ init ] ? ';' ; -init : [ '=' ] expr ; -``` - A _variable declaration_ introduces a new set of variable, given by a pattern. The pattern may be followed by a type annotation, and/or an initializer expression. When no type annotation is given, the compiler will infer the type, or signal @@ -2649,7 +2432,7 @@ parentheses. They are used to create [tuple-typed](#tuple-types) values. ```{.tuple} (0,); (0.0, 4.5); -("a", 4us, true); +("a", 4usize, true); ``` ### Unit expressions @@ -2659,15 +2442,6 @@ the same name. ### Structure expressions -```{.ebnf .gram} -struct_expr : expr_path '{' ident ':' expr - [ ',' ident ':' expr ] * - [ ".." expr ] '}' | - expr_path '(' expr - [ ',' expr ] * ')' | - expr_path ; -``` - There are several forms of structure expressions. A _structure expression_ consists of the [path](#paths) of a [structure item](#structures), followed by a brace-enclosed list of one or more comma-separated name-value pairs, @@ -2718,11 +2492,6 @@ Point3d {y: 0, z: 10, .. base}; ### Block expressions -```{.ebnf .gram} -block_expr : '{' [ stmt ';' | item ] * - [ expr ] '}' ; -``` - A _block expression_ is similar to a module in terms of the declarations that are possible. Each block conceptually introduces a new namespace scope. Use items can bring new names into scopes and declared items are in scope for only @@ -2745,10 +2514,6 @@ assert_eq!(5, x); ### Method-call expressions -```{.ebnf .gram} -method_call_expr : expr '.' ident paren_expr_list ; -``` - A _method call_ consists of an expression followed by a single dot, an identifier, and a parenthesized expression-list. Method calls are resolved to methods on specific traits, either statically dispatching to a method if the @@ -2757,10 +2522,6 @@ the left-hand-side expression is an indirect [trait object](#trait-objects). ### Field expressions -```{.ebnf .gram} -field_expr : expr '.' ident ; -``` - A _field expression_ consists of an expression followed by a single dot and an identifier, when not immediately followed by a parenthesized expression-list (the latter is a [method call expression](#method-call-expressions)). A field @@ -2781,12 +2542,6 @@ automatically dereferenced to make the field access possible. ### Array expressions -```{.ebnf .gram} -array_expr : '[' "mut" ? array_elems? ']' ; - -array_elems : [expr [',' expr]*] | [expr ';' expr] ; -``` - An [array](#array,-and-slice-types) _expression_ is written by enclosing zero or more comma-separated expressions of uniform type in square brackets. @@ -2803,10 +2558,6 @@ constant expression that can be evaluated at compile time, such as a ### Index expressions -```{.ebnf .gram} -idx_expr : expr '[' expr ']' ; -``` - [Array](#array,-and-slice-types)-typed expressions can be indexed by writing a square-bracket-enclosed expression (the index) after them. When the array is mutable, the resulting [lvalue](#lvalues,-rvalues-and-temporaries) can @@ -2823,13 +2574,6 @@ _panicked state_. ### Range expressions -```{.ebnf .gram} -range_expr : expr ".." expr | - expr ".." | - ".." expr | - ".." ; -``` - The `..` operator will construct an object of one of the `std::ops::Range` variants. ``` @@ -2872,10 +2616,6 @@ before the expression they apply to. ### Binary operator expressions -```{.ebnf .gram} -binop_expr : expr binop expr ; -``` - Binary operators expressions are given in terms of [operator precedence](#operator-precedence). @@ -3036,10 +2776,6 @@ An expression enclosed in parentheses evaluates to the result of the enclosed expression. Parentheses can be used to explicitly specify evaluation order within an expression. -```{.ebnf .gram} -paren_expr : '(' expr ')' ; -``` - An example of a parenthesized expression: ``` @@ -3049,12 +2785,6 @@ let x: i32 = (2 + 3) * 4; ### Call expressions -```{.ebnf .gram} -expr_list : [ expr [ ',' expr ]* ] ? ; -paren_expr_list : '(' expr_list ')' ; -call_expr : expr paren_expr_list ; -``` - A _call expression_ invokes a function, providing zero or more input variables and an optional location to move the function's output into. If the function eventually returns, then the expression completes. @@ -3070,11 +2800,6 @@ let pi: Result = "3.14".parse(); ### Lambda expressions -```{.ebnf .gram} -ident_list : [ ident [ ',' ident ]* ] ? ; -lambda_expr : '|' ident_list '|' expr ; -``` - A _lambda expression_ (sometimes called an "anonymous function expression") defines a function and denotes it as a value, in a single expression. A lambda expression is a pipe-symbol-delimited (`|`) list of identifiers followed by an @@ -3118,10 +2843,6 @@ ten_times(|j| println!("hello, {}", j)); A `loop` expression denotes an infinite loop. -```{.ebnf .gram} -loop_expr : [ lifetime ':' ] "loop" '{' block '}'; -``` - A `loop` expression may optionally have a _label_. The label is written as a lifetime preceding the loop expression, as in `'foo: loop{ }`. If a label is present, then labeled `break` and `continue` expressions nested @@ -3131,10 +2852,6 @@ expressions](#continue-expressions). ### Break expressions -```{.ebnf .gram} -break_expr : "break" [ lifetime ]; -``` - A `break` expression has an optional _label_. If the label is absent, then executing a `break` expression immediately terminates the innermost loop enclosing it. It is only permitted in the body of a loop. If the label is @@ -3143,10 +2860,6 @@ be the innermost label enclosing the `break` expression, but must enclose it. ### Continue expressions -```{.ebnf .gram} -continue_expr : "continue" [ lifetime ]; -``` - A `continue` expression has an optional _label_. If the label is absent, then executing a `continue` expression immediately terminates the current iteration of the innermost loop enclosing it, returning control to the loop *head*. In @@ -3160,10 +2873,6 @@ A `continue` expression is only permitted in the body of a loop. ### While loops -```{.ebnf .gram} -while_expr : [ lifetime ':' ] "while" no_struct_literal_expr '{' block '}' ; -``` - A `while` loop begins by evaluating the boolean loop conditional expression. If the loop conditional expression evaluates to `true`, the loop body block executes and control returns to the loop conditional expression. If the loop @@ -3187,26 +2896,22 @@ loops](#infinite-loops), [break expressions](#break-expressions), and ### For expressions -```{.ebnf .gram} -for_expr : [ lifetime ':' ] "for" pat "in" no_struct_literal_expr '{' block '}' ; -``` - A `for` expression is a syntactic construct for looping over elements provided -by an implementation of `std::iter::Iterator`. +by an implementation of `std::iter::IntoIterator`. An example of a for loop over the contents of an array: ``` # type Foo = i32; -# fn bar(f: Foo) { } +# fn bar(f: &Foo) { } # let a = 0; # let b = 0; # let c = 0; let v: &[Foo] = &[a, b, c]; -for e in v.iter() { - bar(*e); +for e in v { + bar(e); } ``` @@ -3226,14 +2931,6 @@ loops](#infinite-loops), [break expressions](#break-expressions), and ### If expressions -```{.ebnf .gram} -if_expr : "if" no_struct_literal_expr '{' block '}' - else_tail ? ; - -else_tail : "else" [ if_expr | if_let_expr - | '{' block '}' ] ; -``` - An `if` expression is a conditional branch in program control. The form of an `if` expression is a condition expression, followed by a consequent block, any number of `else if` conditions and blocks, and an optional trailing `else` @@ -3246,14 +2943,6 @@ if` condition is evaluated. If all `if` and `else if` conditions evaluate to ### Match expressions -```{.ebnf .gram} -match_expr : "match" no_struct_literal_expr '{' match_arm * '}' ; - -match_arm : attribute * match_pat "=>" [ expr "," | '{' block '}' ] ; - -match_pat : pat [ '|' pat ] * [ "if" expr ] ? ; -``` - A `match` expression branches on a *pattern*. The exact form of matching that occurs depends on the pattern. Patterns consist of some combination of literals, destructured arrays or enum constructors, structures and tuples, @@ -3370,12 +3059,6 @@ let message = match maybe_digit { ### If let expressions -```{.ebnf .gram} -if_let_expr : "if" "let" pat '=' expr '{' block '}' - else_tail ? ; -else_tail : "else" [ if_expr | if_let_expr | '{' block '}' ] ; -``` - An `if let` expression is semantically identical to an `if` expression but in place of a condition expression it expects a refutable let statement. If the value of the expression on the right hand side of the let statement matches the pattern, the corresponding @@ -3383,10 +3066,6 @@ block will execute, otherwise flow proceeds to the first `else` block that follo ### While let loops -```{.ebnf .gram} -while_let_expr : "while" "let" pat '=' expr '{' block '}' ; -``` - A `while let` loop is semantically identical to a `while` loop but in place of a condition expression it expects a refutable let statement. If the value of the expression on the right hand side of the let statement matches the pattern, the @@ -3395,10 +3074,6 @@ Otherwise, the while expression completes. ### Return expressions -```{.ebnf .gram} -return_expr : "return" expr ? ; -``` - Return expressions are denoted with the keyword `return`. Evaluating a `return` expression moves its argument into the designated output location for the current function call, destroys the current function activation frame, and @@ -3430,17 +3105,10 @@ User-defined types have limited capabilities. The primitive types are the following: -* The "unit" type `()`, having the single "unit" value `()` (occasionally called - "nil"). [^unittype] * The boolean type `bool` with values `true` and `false`. * The machine types. * The machine-dependent integer and floating-point types. -[^unittype]: The "unit" value `()` is *not* a sentinel "null pointer" value for - reference variables; the "unit" type is the implicit return type from functions - otherwise lacking a return type, and can be used in other contexts (such as - message-sending or type-parametric code) as a zero-size type.] - #### Machine types The machine types are the following: @@ -3481,7 +3149,7 @@ UTF-32 string. A value of type `str` is a Unicode string, represented as an array of 8-bit unsigned bytes holding a sequence of UTF-8 code points. Since `str` is of unknown size, it is not a _first-class_ type, but can only be instantiated -through a pointer type, such as `&str` or `String`. +through a pointer type, such as `&str`. ### Tuple types @@ -3537,7 +3205,7 @@ to an array or slice is always bounds-checked. A `struct` *type* is a heterogeneous product of other types, called the *fields* of the type.[^structtype] -[^structtype]: `struct` types are analogous `struct` types in C, +[^structtype]: `struct` types are analogous to `struct` types in C, the *record* types of the ML family, or the *structure* types of the Lisp family. @@ -3551,7 +3219,7 @@ a corresponding struct *expression*; the resulting `struct` value will always have the same memory layout. The fields of a `struct` may be qualified by [visibility -modifiers](#re-exporting-and-visibility), to allow access to data in a +modifiers](#visibility-and-privacy), to allow access to data in a structure outside a module. A _tuple struct_ type is just like a structure type, except that the fields are @@ -3619,18 +3287,18 @@ varieties of pointer in Rust: * References (`&`) : These point to memory _owned by some other value_. - A reference type is written `&type` for some lifetime-variable `f`, - or just `&'a type` when you need an explicit lifetime. + A reference type is written `&type`, + or `&'a type` when you need to specify an explicit lifetime. Copying a reference is a "shallow" operation: it involves only copying the pointer itself. - Releasing a reference typically has no effect on the value it points to, - with the exception of temporary values, which are released when the last - reference to them is released. + Releasing a reference has no effect on the value it points to, + but a reference of a temporary value will keep it alive during the scope + of the reference itself. * Raw pointers (`*`) : Raw pointers are pointers without safety or liveness guarantees. Raw pointers are written as `*const T` or `*mut T`, - for example `*const int` means a raw pointer to an integer. + for example `*const i32` means a raw pointer to a 32-bit integer. Copying or dropping a raw pointer has no effect on the lifecycle of any other value. Dereferencing a raw pointer or converting it to any other pointer type is an [`unsafe` operation](#unsafe-functions). @@ -3663,38 +3331,26 @@ x = bo(5,7); ### Closure types -```{.ebnf .notation} -closure_type := [ 'unsafe' ] [ '<' lifetime-list '>' ] '|' arg-list '|' - [ ':' bound-list ] [ '->' type ] -lifetime-list := lifetime | lifetime ',' lifetime-list -arg-list := ident ':' type | ident ':' type ',' arg-list -bound-list := bound | bound '+' bound-list -bound := path | lifetime -``` - -The type of a closure mapping an input of type `A` to an output of type `B` is -`|A| -> B`. A closure with no arguments or return values has type `||`. +A [lambda expression](#lambda-expressions) produces a closure value with +a unique, anonymous type that cannot be written out. -An example of creating and calling a closure: +Depending on the requirements of the closure, its type implements one or +more of the closure traits: -```rust -let captured_var = 10; +* `FnOnce` + : The closure can be called once. A closure called as `FnOnce` + can move out values from its environment. -let closure_no_args = || println!("captured_var={}", captured_var); +* `FnMut` + : The closure can be called multiple times as mutable. A closure called as + `FnMut` can mutate values from its environment. `FnMut` implies + `FnOnce`. -let closure_args = |arg: i32| -> i32 { - println!("captured_var={}, arg={}", captured_var, arg); - arg // Note lack of semicolon after 'arg' -}; +* `Fn` + : The closure can be called multiple times through a shared reference. + A closure called as `Fn` can neither move out from nor mutate values + from its environment. `Fn` implies `FnMut` and `FnOnce`. -fn call_closure i32>(c1: F, c2: G) { - c1(); - c2(2); -} - -call_closure(closure_no_args, closure_args); - -``` ### Trait objects @@ -3741,19 +3397,19 @@ Within the body of an item that has type parameter declarations, the names of its type parameters are types: ```ignore -fn map(f: |A| -> B, xs: &[A]) -> Vec { +fn to_vec(xs: &[A]) -> Vec { if xs.is_empty() { return vec![]; } - let first: B = f(xs[0].clone()); - let mut rest: Vec = map(f, xs.slice(1, xs.len())); + let first: A = xs[0].clone(); + let mut rest: Vec = to_vec(&xs[1..]); rest.insert(0, first); - return rest; + rest } ``` -Here, `first` has type `B`, referring to `map`'s `B` type parameter; and `rest` -has type `Vec`, a vector type with element type `B`. +Here, `first` has type `A`, referring to `to_vec`'s `A` type parameter; and `rest` +has type `Vec`, a vector with element type `A`. ### Self types diff --git a/src/doc/trpl/SUMMARY.md b/src/doc/trpl/SUMMARY.md index 7ce74e86fef63..695dc42cb6418 100644 --- a/src/doc/trpl/SUMMARY.md +++ b/src/doc/trpl/SUMMARY.md @@ -36,7 +36,6 @@ * [Strings](strings.md) * [Generics](generics.md) * [Traits](traits.md) - * [Operators and Overloading](operators-and-overloading.md) * [Drop](drop.md) * [if let](if-let.md) * [Trait Objects](trait-objects.md) @@ -50,6 +49,7 @@ * [Casting between types](casting-between-types.md) * [Associated Types](associated-types.md) * [Unsized Types](unsized-types.md) + * [Operators and Overloading](operators-and-overloading.md) * [Deref coercions](deref-coercions.md) * [Macros](macros.md) * [Raw Pointers](raw-pointers.md) diff --git a/src/doc/trpl/deref-coercions.md b/src/doc/trpl/deref-coercions.md index afacd30405521..b7011100971a8 100644 --- a/src/doc/trpl/deref-coercions.md +++ b/src/doc/trpl/deref-coercions.md @@ -1,3 +1,119 @@ % `Deref` coercions -Coming soon! +The standard library provides a special trait, [`Deref`][deref]. It’s normally +used to overload `*`, the dereference operator: + +```rust +use std::ops::Deref; + +struct DerefExample { + value: T, +} + +impl Deref for DerefExample { + type Target = T; + + fn deref(&self) -> &T { + &self.value + } +} + +fn main() { + let x = DerefExample { value: 'a' }; + assert_eq!('a', *x); +} +``` + +[deref]: ../std/ops/trait.Deref.html + +This is useful for writing custom pointer types. However, there’s a language +feature related to `Deref`: ‘deref coercions’. Here’s the rule: If you have a +type `U`, and it implements `Deref`, values of `&U` will +automatically coerce to a `&T`. Here’s an example: + +```rust +fn foo(s: &str) { + // borrow a string for a second +} + +// String implements Deref +let owned = "Hello".to_string(); + +// therefore, this works: +foo(&owned); +``` + +Using an ampersand in front of a value takes a reference to it. So `owned` is a +`String`, `&owned` is an `&String`, and since `impl Deref for +String`, `&String` will deref to `&str`, which `foo()` takes. + +That’s it. This rule is one of the only places in which Rust does an automatic +conversion for you, but it adds a lot of flexibility. For example, the `Rc` +type implements `Deref`, so this works: + +```rust +use std::rc::Rc; + +fn foo(s: &str) { + // borrow a string for a second +} + +// String implements Deref +let owned = "Hello".to_string(); +let counted = Rc::new(owned); + +// therefore, this works: +foo(&counted); +``` + +All we’ve done is wrap our `String` in an `Rc`. But we can now pass the +`Rc` around anywhere we’d have a `String`. The signature of `foo` +didn’t change, but works just as well with either type. This example has two +conversions: `Rc` to `String` and then `String` to `&str`. Rust will do +this as many times as possible until the types match. + +Another very common implementation provided by the standard library is: + +```rust +fn foo(s: &[i32]) { + // borrow a slice for a second +} + +// Vec implements Deref +let owned = vec![1, 2, 3]; + +foo(&owned); +``` + +Vectors can `Deref` to a slice. + +## Deref and method calls + +`Deref` will also kick in when calling a method. In other words, these are +the same two things in Rust: + +```rust +struct Foo; + +impl Foo { + fn foo(&self) { println!("Foo"); } +} + +let f = Foo; + +f.foo(); +``` + +Even though `f` isn’t a reference, and `foo` takes `&self`, this works. +That’s because these things are the same: + +```rust,ignore +f.foo(); +(&f).foo(); +(&&f).foo(); +(&&&&&&&&f).foo(); +``` + +A value of type `&&&&&&&&&&&&&&&&Foo` can still have methods defined on `Foo` +called, because the compiler will insert as many * operations as necessary to +get it right. And since it’s inserting `*`s, that uses `Deref`. diff --git a/src/doc/trpl/method-syntax.md b/src/doc/trpl/method-syntax.md index 5853f3d679c5f..1445d39fe8738 100644 --- a/src/doc/trpl/method-syntax.md +++ b/src/doc/trpl/method-syntax.md @@ -18,7 +18,7 @@ foo.bar().baz(); Luckily, as you may have guessed with the leading question, you can! Rust provides the ability to use this ‘method call syntax’ via the `impl` keyword. -## Method calls +# Method calls Here’s how it works: @@ -83,7 +83,7 @@ impl Circle { } ``` -## Chaining method calls +# Chaining method calls So, now we know how to call a method, such as `foo.bar()`. But what about our original example, `foo.bar().baz()`? This is called ‘method chaining’, and we @@ -127,7 +127,7 @@ fn grow(&self) -> Circle { We just say we’re returning a `Circle`. With this method, we can grow a new circle to any arbitrary size. -## Static methods +# Static methods You can also define methods that do not take a `self` parameter. Here’s a pattern that’s very common in Rust code: @@ -158,7 +158,7 @@ This ‘static method’ builds a new `Circle` for us. Note that static methods are called with the `Struct::method()` syntax, rather than the `ref.method()` syntax. -## Builder Pattern +# Builder Pattern Let’s say that we want our users to be able to create Circles, but we will allow them to only set the properties they care about. Otherwise, the `x` diff --git a/src/doc/trpl/mutability.md b/src/doc/trpl/mutability.md index ccb03c7f85f69..e7506dfe4fd7d 100644 --- a/src/doc/trpl/mutability.md +++ b/src/doc/trpl/mutability.md @@ -1,3 +1,179 @@ % Mutability -Coming Soon +Mutability, the ability to change something, works a bit differently in Rust +than in other languages. The first aspect of mutability is its non-default +status: + +```rust,ignore +let x = 5; +x = 6; // error! +``` + +We can introduce mutability with the `mut` keyword: + +```rust +let mut x = 5; + +x = 6; // no problem! +``` + +This is a mutable [variable binding][vb]. When a binding is mutable, it means +you’re allowed to change what the binding points to. So in the above example, +it’s not so much that the value at `x` is changing, but that the binding +changed from one `i32` to another. + +[vb]: variable-bindings.html + +If you want to change what the binding points to, you’ll need a [mutable reference][mr]: + +```rust +let mut x = 5; +let y = &mut x; +``` + +[mr]: references-and-borrowing.html + +`y` is an immutable binding to a mutable reference, which means that you can’t +bind `y` to something else (`y = &mut z`), but you can mutate the thing that’s +bound to `y`. (`*y = 5`) A subtle distinction. + +Of course, if you need both: + +```rust +let mut x = 5; +let mut y = &mut x; +``` + +Now `y` can be bound to another value, and the value it’s referencing can be +changed. + +It’s important to note that `mut` is part of a [pattern][pattern], so you +can do things like this: + +```rust +let (mut x, y) = (5, 6); + +fn foo(mut x: i32) { +# } +``` + +[pattern]: patterns.html + +# Interior vs. Exterior Mutability + +However, when we say something is ‘immutable’ in Rust, that doesn’t mean that +it’s not able to be changed: We mean something has ‘exterior mutability’. Consider, +for example, [`Arc`][arc]: + +```rust +use std::sync::Arc; + +let x = Arc::new(5); +let y = x.clone(); +``` + +[arc]: ../std/sync/struct.Arc.html + +When we call `clone()`, the `Arc` needs to update the reference count. Yet +we’ve not used any `mut`s here, `x` is an immutable binding, and we didn’t take +`&mut 5` or anything. So what gives? + +To this, we have to go back to the core of Rust’s guiding philosophy, memory +safety, and the mechanism by which Rust guarantees it, the +[ownership][ownership] system, and more specifically, [borrowing][borrowing]: + +> You may have one or the other of these two kinds of borrows, but not both at +> the same time: +> +> * 0 to N references (`&T`) to a resource. +> * exactly one mutable reference (`&mut T`) + +[ownership]: ownership.html +[borrowing]: borrowing.html#The-Rules + +So, that’s the real definition of ‘immutability’: is this safe to have two +pointers to? In `Arc`’s case, yes: the mutation is entirely contained inside +the structure itself. It’s not user facing. For this reason, it hands out `&T` +with `clone()`. If it handed out `&mut T`s, though, that would be a problem. + +Other types, like the ones in the [`std::cell`][stdcell] module, have the +opposite: interior mutability. For example: + +```rust +use std::cell::RefCell; + +let x = RefCell::new(42); + +let y = x.borrow_mut(); +``` + +[stdcell]: ../std/cell/index.html + +RefCell hands out `&mut` references to what’s inside of it with the +`borrow_mut()` method. Isn’t that dangerous? What if we do: + +```rust,ignore +use std::cell::RefCell; + +let x = RefCell::new(42); + +let y = x.borrow_mut(); +let z = x.borrow_mut(); +# (y, z); +``` + +This will in fact panic, at runtime. This is what `RefCell` does: it enforces +Rust’s borrowing rules at runtime, and `panic!`s if they’re violated. This +allows us to get around another aspect of Rust’s mutability rules. Let’s talk +about it first. + +## Field-level mutability + +Mutabilty is a property of either a borrow (`&mut`) or a binding (`let mut`). +This means that, for example, you cannot have a [`struct`][struct] with +some fields mutable and some immutable: + +```rust,ignore +struct Point { + x: i32, + mut y: i32, // nope +} +``` + +The mutability of a struct is in its binding: + +```rust,ignore +struct Point { + x: i32, + y: i32, +} + +let mut a = Point { x: 5, y: 6 }; + +a.x = 10; + +let b = Point { x: 5, y: 6}; + +b.x = 10; // error: cannot assign to immutable field `b.x` +``` + +[struct]: structs.html + +However, by using `Cell`, you can emulate field-level mutability: + +``` +use std::cell::Cell; + +struct Point { + x: i32, + y: Cell, +} + +let mut point = Point { x: 5, y: Cell::new(6) }; + +point.y.set(7); + +println!("y: {:?}", point.y); +``` + +This will print `y: Cell { value: 7 }`. We’ve successfully updated `y`. diff --git a/src/doc/trpl/operators-and-overloading.md b/src/doc/trpl/operators-and-overloading.md index f6f9d5cae1921..6a594659c37d2 100644 --- a/src/doc/trpl/operators-and-overloading.md +++ b/src/doc/trpl/operators-and-overloading.md @@ -1,3 +1,83 @@ % Operators and Overloading -Coming soon! +Rust allows for a limited form of operator overloading. There are certain +operators that are able to be overloaded. To support a particular operator +between types, there’s a specific trait that you can implement, which then +overloads the operator. + +For example, the `+` operator can be overloaded with the `Add` trait: + +```rust +use std::ops::Add; + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +impl Add for Point { + type Output = Point; + + fn add(self, other: Point) -> Point { + Point { x: self.x + other.x, y: self.y + other.y } + } +} + +fn main() { + let p1 = Point { x: 1, y: 0 }; + let p2 = Point { x: 2, y: 3 }; + + let p3 = p1 + p2; + + println!("{:?}", p3); +} +``` + +In `main`, we can use `+` on our two `Point`s, since we’ve implemented +`Add` for `Point`. + +There are a number of operators that can be overloaded this way, and all of +their associated traits live in the [`std::ops`][stdops] module. Check out its +documentation for the full list. + +[stdops]: ../std/ops/index.html + +Implementing these traits follows a pattern. Let’s look at [`Add`][add] in more +detail: + +```rust +# mod foo { +pub trait Add { + type Output; + + fn add(self, rhs: RHS) -> Self::Output; +} +# } +``` + +[add]: ../std/ops/trait.Add.html + +There’s three types in total involved here: the type you `impl Add` for, `RHS`, +which defaults to `Self`, and `Output`. For an expression `let z = x + y`, `x` +is the `Self` type, `y` is the RHS, and `z` is the `Self::Output` type. + +```rust +# struct Point; +# use std::ops::Add; +impl Add for Point { + type Output = f64; + + fn add(self, rhs: i32) -> f64 { + // add an i32 to a Point and get an f64 +# 1.0 + } +} +``` + +will let you do this: + +```rust,ignore +let p: Point = // ... +let x: f64 = p + 2i32; +``` diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index 1c1ad5fd33fb8..d9cda58d9ebed 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -11,7 +11,7 @@ //! Traits for conversions between types. //! //! The traits in this module provide a general way to talk about conversions from one type to -//! another. They follow the standard Rust conventions of `as`/`to`/`into`/`from`. +//! another. They follow the standard Rust conventions of `as`/`into`/`from`. //! //! Like many traits, these are often used as bounds for generic functions, to support arguments of //! multiple types. diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 9093b3b9f5c91..d4012f2057b5d 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -106,8 +106,10 @@ pub mod middle { pub mod entry; pub mod expr_use_visitor; pub mod fast_reject; + pub mod free_region; pub mod intrinsicck; pub mod infer; + pub mod implicator; pub mod lang_items; pub mod liveness; pub mod mem_categorization; diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs new file mode 100644 index 0000000000000..0c8a956f686a2 --- /dev/null +++ b/src/librustc/middle/free_region.rs @@ -0,0 +1,127 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This file defines + +use middle::implicator::Implication; +use middle::ty::{self, FreeRegion}; +use util::common::can_reach; +use util::nodemap::FnvHashMap; +use util::ppaux::Repr; + +#[derive(Clone)] +pub struct FreeRegionMap { + /// `free_region_map` maps from a free region `a` to a list of + /// free regions `bs` such that `a <= b for all b in bs` + map: FnvHashMap>, +} + +impl FreeRegionMap { + pub fn new() -> FreeRegionMap { + FreeRegionMap { map: FnvHashMap() } + } + + pub fn relate_free_regions_from_implications<'tcx>(&mut self, + tcx: &ty::ctxt<'tcx>, + implications: &[Implication<'tcx>]) + { + for implication in implications { + debug!("implication: {}", implication.repr(tcx)); + match *implication { + Implication::RegionSubRegion(_, ty::ReFree(free_a), ty::ReFree(free_b)) => { + self.relate_free_regions(free_a, free_b); + } + Implication::RegionSubRegion(..) | + Implication::RegionSubClosure(..) | + Implication::RegionSubGeneric(..) | + Implication::Predicate(..) => { + } + } + } + } + + pub fn relate_free_regions_from_predicates<'tcx>(&mut self, + tcx: &ty::ctxt<'tcx>, + predicates: &[ty::Predicate<'tcx>]) { + debug!("relate_free_regions_from_predicates(predicates={})", predicates.repr(tcx)); + for predicate in predicates { + match *predicate { + ty::Predicate::Projection(..) | + ty::Predicate::Trait(..) | + ty::Predicate::Equate(..) | + ty::Predicate::TypeOutlives(..) => { + // No region bounds here + } + ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => { + match (r_a, r_b) { + (ty::ReFree(fr_a), ty::ReFree(fr_b)) => { + // Record that `'a:'b`. Or, put another way, `'b <= 'a`. + self.relate_free_regions(fr_b, fr_a); + } + _ => { + // All named regions are instantiated with free regions. + tcx.sess.bug( + &format!("record_region_bounds: non free region: {} / {}", + r_a.repr(tcx), + r_b.repr(tcx))); + } + } + } + } + } + } + + pub fn relate_free_regions(&mut self, sub: FreeRegion, sup: FreeRegion) { + let mut sups = self.map.entry(sub).or_insert(Vec::new()); + if !sups.contains(&sup) { + sups.push(sup); + } + } + + /// Determines whether two free regions have a subregion relationship + /// by walking the graph encoded in `map`. Note that + /// it is possible that `sub != sup` and `sub <= sup` and `sup <= sub` + /// (that is, the user can give two different names to the same lifetime). + pub fn sub_free_region(&self, sub: FreeRegion, sup: FreeRegion) -> bool { + can_reach(&self.map, sub, sup) + } + + /// Determines whether one region is a subregion of another. This is intended to run *after + /// inference* and sadly the logic is somewhat duplicated with the code in infer.rs. + pub fn is_subregion_of(&self, + tcx: &ty::ctxt, + sub_region: ty::Region, + super_region: ty::Region) + -> bool { + debug!("is_subregion_of(sub_region={:?}, super_region={:?})", + sub_region, super_region); + + sub_region == super_region || { + match (sub_region, super_region) { + (ty::ReEmpty, _) | + (_, ty::ReStatic) => + true, + + (ty::ReScope(sub_scope), ty::ReScope(super_scope)) => + tcx.region_maps.is_subscope_of(sub_scope, super_scope), + + (ty::ReScope(sub_scope), ty::ReFree(ref fr)) => + tcx.region_maps.is_subscope_of(sub_scope, fr.scope.to_code_extent()), + + (ty::ReFree(sub_fr), ty::ReFree(super_fr)) => + self.sub_free_region(sub_fr, super_fr), + + _ => + false, + } + } + } +} + diff --git a/src/librustc_typeck/check/implicator.rs b/src/librustc/middle/implicator.rs similarity index 92% rename from src/librustc_typeck/check/implicator.rs rename to src/librustc/middle/implicator.rs index a4a18c7cfdea6..0d6a1df723788 100644 --- a/src/librustc_typeck/check/implicator.rs +++ b/src/librustc/middle/implicator.rs @@ -10,11 +10,10 @@ // #![warn(deprecated_mode)] -use astconv::object_region_bounds; use middle::infer::{InferCtxt, GenericKind}; use middle::subst::Substs; use middle::traits; -use middle::ty::{self, ToPolyTraitRef, Ty}; +use middle::ty::{self, RegionEscape, ToPolyTraitRef, Ty}; use middle::ty_fold::{TypeFoldable, TypeFolder}; use std::rc::Rc; @@ -423,6 +422,39 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> { } } +/// Given an object type like `SomeTrait+Send`, computes the lifetime +/// bounds that must hold on the elided self type. These are derived +/// from the declarations of `SomeTrait`, `Send`, and friends -- if +/// they declare `trait SomeTrait : 'static`, for example, then +/// `'static` would appear in the list. The hard work is done by +/// `ty::required_region_bounds`, see that for more information. +pub fn object_region_bounds<'tcx>( + tcx: &ty::ctxt<'tcx>, + principal: &ty::PolyTraitRef<'tcx>, + others: ty::BuiltinBounds) + -> Vec +{ + // Since we don't actually *know* the self type for an object, + // this "open(err)" serves as a kind of dummy standin -- basically + // a skolemized type. + let open_ty = ty::mk_infer(tcx, ty::FreshTy(0)); + + // Note that we preserve the overall binding levels here. + assert!(!open_ty.has_escaping_regions()); + let substs = tcx.mk_substs(principal.0.substs.with_self_ty(open_ty)); + let trait_refs = vec!(ty::Binder(Rc::new(ty::TraitRef::new(principal.0.def_id, substs)))); + + let param_bounds = ty::ParamBounds { + region_bounds: Vec::new(), + builtin_bounds: others, + trait_bounds: trait_refs, + projection_bounds: Vec::new(), // not relevant to computing region bounds + }; + + let predicates = ty::predicates(tcx, open_ty, ¶m_bounds); + ty::required_region_bounds(tcx, open_ty, predicates) +} + impl<'tcx> Repr<'tcx> for Implication<'tcx> { fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { match *self { diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index b0921a266f39f..63e4a5c8a2a4a 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -22,6 +22,7 @@ pub use middle::ty::IntVarValue; pub use self::freshen::TypeFreshener; pub use self::region_inference::GenericKind; +use middle::free_region::FreeRegionMap; use middle::subst; use middle::subst::Substs; use middle::ty::{TyVid, IntVid, FloatVid, RegionVid, UnconstrainedNumeric}; @@ -855,8 +856,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.region_vars.new_bound(debruijn) } - pub fn resolve_regions_and_report_errors(&self, subject_node_id: ast::NodeId) { - let errors = self.region_vars.resolve_regions(subject_node_id); + pub fn resolve_regions_and_report_errors(&self, + free_regions: &FreeRegionMap, + subject_node_id: ast::NodeId) { + let errors = self.region_vars.resolve_regions(free_regions, subject_node_id); self.report_region_errors(&errors); // see error_reporting.rs } diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs index e76468131e04d..9de362fe360a6 100644 --- a/src/librustc/middle/infer/region_inference/mod.rs +++ b/src/librustc/middle/infer/region_inference/mod.rs @@ -21,6 +21,7 @@ use self::Classification::*; use super::{RegionVariableOrigin, SubregionOrigin, TypeTrace, MiscVariable}; use rustc_data_structures::graph::{self, Direction, NodeIndex}; +use middle::free_region::FreeRegionMap; use middle::region; use middle::ty::{self, Ty}; use middle::ty::{BoundRegion, FreeRegion, Region, RegionVid}; @@ -711,19 +712,19 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { /// fixed-point iteration to find region values which satisfy all /// constraints, assuming such values can be found; if they cannot, /// errors are reported. - pub fn resolve_regions(&self, subject_node: ast::NodeId) -> Vec> { + pub fn resolve_regions(&self, + free_regions: &FreeRegionMap, + subject_node: ast::NodeId) + -> Vec> + { debug!("RegionVarBindings: resolve_regions()"); let mut errors = vec!(); - let v = self.infer_variable_values(&mut errors, subject_node); + let v = self.infer_variable_values(free_regions, &mut errors, subject_node); *self.values.borrow_mut() = Some(v); errors } - fn is_subregion_of(&self, sub: Region, sup: Region) -> bool { - self.tcx.region_maps.is_subregion_of(sub, sup) - } - - fn lub_concrete_regions(&self, a: Region, b: Region) -> Region { + fn lub_concrete_regions(&self, free_regions: &FreeRegionMap, a: Region, b: Region) -> Region { match (a, b) { (ReLateBound(..), _) | (_, ReLateBound(..)) | @@ -781,7 +782,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { } (ReFree(ref a_fr), ReFree(ref b_fr)) => { - self.lub_free_regions(a_fr, b_fr) + self.lub_free_regions(free_regions, a_fr, b_fr) } // For these types, we cannot define any additional @@ -796,23 +797,25 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { /// Computes a region that encloses both free region arguments. Guarantee that if the same two /// regions are given as argument, in any order, a consistent result is returned. fn lub_free_regions(&self, + free_regions: &FreeRegionMap, a: &FreeRegion, b: &FreeRegion) -> ty::Region { return match a.cmp(b) { - Less => helper(self, a, b), - Greater => helper(self, b, a), + Less => helper(self, free_regions, a, b), + Greater => helper(self, free_regions, b, a), Equal => ty::ReFree(*a) }; - fn helper(this: &RegionVarBindings, + fn helper(_this: &RegionVarBindings, + free_regions: &FreeRegionMap, a: &FreeRegion, b: &FreeRegion) -> ty::Region { - if this.tcx.region_maps.sub_free_region(*a, *b) { + if free_regions.sub_free_region(*a, *b) { ty::ReFree(*b) - } else if this.tcx.region_maps.sub_free_region(*b, *a) { + } else if free_regions.sub_free_region(*b, *a) { ty::ReFree(*a) } else { ty::ReStatic @@ -821,6 +824,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { } fn glb_concrete_regions(&self, + free_regions: &FreeRegionMap, a: Region, b: Region) -> RelateResult<'tcx, Region> @@ -878,7 +882,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { } (ReFree(ref a_fr), ReFree(ref b_fr)) => { - self.glb_free_regions(a_fr, b_fr) + self.glb_free_regions(free_regions, a_fr, b_fr) } // For these types, we cannot define any additional @@ -898,23 +902,25 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { /// if the same two regions are given as argument, in any order, a consistent result is /// returned. fn glb_free_regions(&self, + free_regions: &FreeRegionMap, a: &FreeRegion, b: &FreeRegion) -> RelateResult<'tcx, ty::Region> { return match a.cmp(b) { - Less => helper(self, a, b), - Greater => helper(self, b, a), + Less => helper(self, free_regions, a, b), + Greater => helper(self, free_regions, b, a), Equal => Ok(ty::ReFree(*a)) }; fn helper<'a, 'tcx>(this: &RegionVarBindings<'a, 'tcx>, + free_regions: &FreeRegionMap, a: &FreeRegion, b: &FreeRegion) -> RelateResult<'tcx, ty::Region> { - if this.tcx.region_maps.sub_free_region(*a, *b) { + if free_regions.sub_free_region(*a, *b) { Ok(ty::ReFree(*a)) - } else if this.tcx.region_maps.sub_free_region(*b, *a) { + } else if free_regions.sub_free_region(*b, *a) { Ok(ty::ReFree(*b)) } else { this.intersect_scopes(ty::ReFree(*a), ty::ReFree(*b), @@ -970,6 +976,7 @@ type RegionGraph = graph::Graph<(), Constraint>; impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { fn infer_variable_values(&self, + free_regions: &FreeRegionMap, errors: &mut Vec>, subject: ast::NodeId) -> Vec { @@ -980,12 +987,13 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { debug!("----() End constraint listing {:?}---", self.dump_constraints()); graphviz::maybe_print_constraints_for(self, subject); - self.expansion(&mut var_data); - self.contraction(&mut var_data); + self.expansion(free_regions, &mut var_data); + self.contraction(free_regions, &mut var_data); let values = - self.extract_values_and_collect_conflicts(&var_data[..], + self.extract_values_and_collect_conflicts(free_regions, + &var_data[..], errors); - self.collect_concrete_region_errors(&values, errors); + self.collect_concrete_region_errors(free_regions, &values, errors); values } @@ -1009,7 +1017,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { } } - fn expansion(&self, var_data: &mut [VarData]) { + fn expansion(&self, free_regions: &FreeRegionMap, var_data: &mut [VarData]) { self.iterate_until_fixed_point("Expansion", |constraint| { debug!("expansion: constraint={} origin={}", constraint.repr(self.tcx), @@ -1020,14 +1028,14 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { match *constraint { ConstrainRegSubVar(a_region, b_vid) => { let b_data = &mut var_data[b_vid.index as usize]; - self.expand_node(a_region, b_vid, b_data) + self.expand_node(free_regions, a_region, b_vid, b_data) } ConstrainVarSubVar(a_vid, b_vid) => { match var_data[a_vid.index as usize].value { NoValue | ErrorValue => false, Value(a_region) => { let b_node = &mut var_data[b_vid.index as usize]; - self.expand_node(a_region, b_vid, b_node) + self.expand_node(free_regions, a_region, b_vid, b_node) } } } @@ -1040,6 +1048,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { } fn expand_node(&self, + free_regions: &FreeRegionMap, a_region: Region, b_vid: RegionVid, b_data: &mut VarData) @@ -1072,7 +1081,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { } Value(cur_region) => { - let lub = self.lub_concrete_regions(a_region, cur_region); + let lub = self.lub_concrete_regions(free_regions, a_region, cur_region); if lub == cur_region { return false; } @@ -1093,6 +1102,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { } fn contraction(&self, + free_regions: &FreeRegionMap, var_data: &mut [VarData]) { self.iterate_until_fixed_point("Contraction", |constraint| { debug!("contraction: constraint={} origin={}", @@ -1111,19 +1121,20 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { NoValue | ErrorValue => false, Value(b_region) => { let a_data = &mut var_data[a_vid.index as usize]; - self.contract_node(a_vid, a_data, b_region) + self.contract_node(free_regions, a_vid, a_data, b_region) } } } ConstrainVarSubReg(a_vid, b_region) => { let a_data = &mut var_data[a_vid.index as usize]; - self.contract_node(a_vid, a_data, b_region) + self.contract_node(free_regions, a_vid, a_data, b_region) } } }) } fn contract_node(&self, + free_regions: &FreeRegionMap, a_vid: RegionVid, a_data: &mut VarData, b_region: Region) @@ -1143,19 +1154,23 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { Value(a_region) => { match a_data.classification { - Expanding => check_node(self, a_vid, a_data, a_region, b_region), - Contracting => adjust_node(self, a_vid, a_data, a_region, b_region), + Expanding => + check_node(self, free_regions, a_vid, a_data, a_region, b_region), + Contracting => + adjust_node(self, free_regions, a_vid, a_data, a_region, b_region), } } }; fn check_node(this: &RegionVarBindings, + free_regions: &FreeRegionMap, a_vid: RegionVid, a_data: &mut VarData, a_region: Region, b_region: Region) - -> bool { - if !this.is_subregion_of(a_region, b_region) { + -> bool + { + if !free_regions.is_subregion_of(this.tcx, a_region, b_region) { debug!("Setting {:?} to ErrorValue: {} not subregion of {}", a_vid, a_region.repr(this.tcx), @@ -1166,12 +1181,13 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { } fn adjust_node(this: &RegionVarBindings, + free_regions: &FreeRegionMap, a_vid: RegionVid, a_data: &mut VarData, a_region: Region, b_region: Region) -> bool { - match this.glb_concrete_regions(a_region, b_region) { + match this.glb_concrete_regions(free_regions, a_region, b_region) { Ok(glb) => { if glb == a_region { false @@ -1197,6 +1213,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { } fn collect_concrete_region_errors(&self, + free_regions: &FreeRegionMap, values: &Vec, errors: &mut Vec>) { @@ -1204,7 +1221,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { for verify in &*self.verifys.borrow() { match *verify { VerifyRegSubReg(ref origin, sub, sup) => { - if self.is_subregion_of(sub, sup) { + if free_regions.is_subregion_of(self.tcx, sub, sup) { continue; } @@ -1222,7 +1239,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { let sub = normalize(values, sub); if sups.iter() .map(|&sup| normalize(values, sup)) - .any(|sup| self.is_subregion_of(sub, sup)) + .any(|sup| free_regions.is_subregion_of(self.tcx, sub, sup)) { continue; } @@ -1239,6 +1256,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { fn extract_values_and_collect_conflicts( &self, + free_regions: &FreeRegionMap, var_data: &[VarData], errors: &mut Vec>) -> Vec @@ -1304,12 +1322,12 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { match var_data[idx].classification { Expanding => { self.collect_error_for_expanding_node( - graph, var_data, &mut dup_vec, + free_regions, graph, var_data, &mut dup_vec, node_vid, errors); } Contracting => { self.collect_error_for_contracting_node( - graph, var_data, &mut dup_vec, + free_regions, graph, var_data, &mut dup_vec, node_vid, errors); } } @@ -1355,13 +1373,13 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { return graph; } - fn collect_error_for_expanding_node( - &self, - graph: &RegionGraph, - var_data: &[VarData], - dup_vec: &mut [u32], - node_idx: RegionVid, - errors: &mut Vec>) + fn collect_error_for_expanding_node(&self, + free_regions: &FreeRegionMap, + graph: &RegionGraph, + var_data: &[VarData], + dup_vec: &mut [u32], + node_idx: RegionVid, + errors: &mut Vec>) { // Errors in expanding nodes result from a lower-bound that is // not contained by an upper-bound. @@ -1394,8 +1412,9 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { for lower_bound in &lower_bounds { for upper_bound in &upper_bounds { - if !self.is_subregion_of(lower_bound.region, - upper_bound.region) { + if !free_regions.is_subregion_of(self.tcx, + lower_bound.region, + upper_bound.region) { debug!("pushing SubSupConflict sub: {:?} sup: {:?}", lower_bound.region, upper_bound.region); errors.push(SubSupConflict( @@ -1420,6 +1439,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { fn collect_error_for_contracting_node( &self, + free_regions: &FreeRegionMap, graph: &RegionGraph, var_data: &[VarData], dup_vec: &mut [u32], @@ -1438,7 +1458,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { for upper_bound_1 in &upper_bounds { for upper_bound_2 in &upper_bounds { - match self.glb_concrete_regions(upper_bound_1.region, + match self.glb_concrete_regions(free_regions, + upper_bound_1.region, upper_bound_2.region) { Ok(_) => {} Err(_) => { diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 2f7296051c566..2c510b5f1d3b5 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -17,9 +17,8 @@ //! `middle/typeck/infer/region_inference.rs` use session::Session; -use middle::ty::{self, Ty, FreeRegion}; +use middle::ty::{self, Ty}; use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap}; -use util::common::can_reach; use std::cell::RefCell; use syntax::codemap::{self, Span}; @@ -234,14 +233,6 @@ pub struct RegionMaps { /// which that variable is declared. var_map: RefCell>, - /// `free_region_map` maps from a free region `a` to a list of - /// free regions `bs` such that `a <= b for all b in bs` - /// - /// NB. the free region map is populated during type check as we - /// check each function. See the function `relate_free_regions` - /// for more information. - free_region_map: RefCell>>, - /// `rvalue_scopes` includes entries for those expressions whose cleanup scope is /// larger than the default. The map goes from the expression id /// to the cleanup scope id. For rvalues not present in this @@ -390,13 +381,6 @@ impl RegionMaps { e(child, parent) } } - pub fn each_encl_free_region(&self, mut e:E) where E: FnMut(&FreeRegion, &FreeRegion) { - for (child, parents) in self.free_region_map.borrow().iter() { - for parent in parents.iter() { - e(child, parent) - } - } - } pub fn each_rvalue_scope(&self, mut e:E) where E: FnMut(&ast::NodeId, &CodeExtent) { for (child, parent) in self.rvalue_scopes.borrow().iter() { e(child, parent) @@ -408,21 +392,6 @@ impl RegionMaps { } } - pub fn relate_free_regions(&self, sub: FreeRegion, sup: FreeRegion) { - match self.free_region_map.borrow_mut().get_mut(&sub) { - Some(sups) => { - if !sups.iter().any(|x| x == &sup) { - sups.push(sup); - } - return; - } - None => {} - } - - debug!("relate_free_regions(sub={:?}, sup={:?})", sub, sup); - self.free_region_map.borrow_mut().insert(sub, vec!(sup)); - } - /// Records that `sub_fn` is defined within `sup_fn`. These ids /// should be the id of the block that is the fn body, which is /// also the root of the region hierarchy for that fn. @@ -567,56 +536,6 @@ impl RegionMaps { return true; } - /// Determines whether two free regions have a subregion relationship - /// by walking the graph encoded in `free_region_map`. Note that - /// it is possible that `sub != sup` and `sub <= sup` and `sup <= sub` - /// (that is, the user can give two different names to the same lifetime). - pub fn sub_free_region(&self, sub: FreeRegion, sup: FreeRegion) -> bool { - can_reach(&*self.free_region_map.borrow(), sub, sup) - } - - /// Determines whether one region is a subregion of another. This is intended to run *after - /// inference* and sadly the logic is somewhat duplicated with the code in infer.rs. - pub fn is_subregion_of(&self, - sub_region: ty::Region, - super_region: ty::Region) - -> bool { - debug!("is_subregion_of(sub_region={:?}, super_region={:?})", - sub_region, super_region); - - sub_region == super_region || { - match (sub_region, super_region) { - (ty::ReEmpty, _) | - (_, ty::ReStatic) => { - true - } - - (ty::ReScope(sub_scope), ty::ReScope(super_scope)) => { - self.is_subscope_of(sub_scope, super_scope) - } - - (ty::ReScope(sub_scope), ty::ReFree(ref fr)) => { - self.is_subscope_of(sub_scope, fr.scope.to_code_extent()) - } - - (ty::ReFree(sub_fr), ty::ReFree(super_fr)) => { - self.sub_free_region(sub_fr, super_fr) - } - - (ty::ReEarlyBound(data_a), ty::ReEarlyBound(data_b)) => { - // This case is used only to make sure that explicitly- - // specified `Self` types match the real self type in - // implementations. Yuck. - data_a == data_b - } - - _ => { - false - } - } - } - } - /// Finds the nearest common ancestor (if any) of two scopes. That is, finds the smallest /// scope which is greater than or equal to both `scope_a` and `scope_b`. pub fn nearest_common_ancestor(&self, @@ -1291,7 +1210,6 @@ pub fn resolve_crate(sess: &Session, krate: &ast::Crate) -> RegionMaps { let maps = RegionMaps { scope_map: RefCell::new(FnvHashMap()), var_map: RefCell::new(NodeMap()), - free_region_map: RefCell::new(FnvHashMap()), rvalue_scopes: RefCell::new(NodeMap()), terminating_scopes: RefCell::new(FnvHashSet()), fn_tree: RefCell::new(NodeMap()), diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 8809abdd70e62..b221c4bb685a8 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -15,6 +15,7 @@ pub use self::FulfillmentErrorCode::*; pub use self::Vtable::*; pub use self::ObligationCauseCode::*; +use middle::free_region::FreeRegionMap; use middle::subst; use middle::ty::{self, HasProjectionTypes, Ty}; use middle::ty_fold::TypeFoldable; @@ -424,7 +425,8 @@ pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvi } }; - infcx.resolve_regions_and_report_errors(body_id); + let free_regions = FreeRegionMap::new(); + infcx.resolve_regions_and_report_errors(&free_regions, body_id); let predicates = match infcx.fully_resolve(&predicates) { Ok(predicates) => predicates, Err(fixup_err) => { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 2c94399f92138..232e962ed4d23 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -45,12 +45,14 @@ use middle::check_const; use middle::const_eval; use middle::def::{self, DefMap, ExportMap}; use middle::dependency_format; +use middle::free_region::FreeRegionMap; use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem}; use middle::mem_categorization as mc; use middle::region; use middle::resolve_lifetime; use middle::infer; use middle::pat_util; +use middle::region::RegionMaps; use middle::stability; use middle::subst::{self, ParamSpace, Subst, Substs, VecPerParamSpace}; use middle::traits; @@ -620,7 +622,14 @@ pub struct ctxt<'tcx> { pub named_region_map: resolve_lifetime::NamedRegionMap, - pub region_maps: middle::region::RegionMaps, + pub region_maps: RegionMaps, + + // For each fn declared in the local crate, type check stores the + // free-region relationships that were deduced from its where + // clauses and parameter types. These are then read-again by + // borrowck. (They are not used during trans, and hence are not + // serialized or needed for cross-crate fns.) + free_region_maps: RefCell>, /// Stores the types for various nodes in the AST. Note that this table /// is not guaranteed to be populated until after typeck. See @@ -795,6 +804,15 @@ impl<'tcx> ctxt<'tcx> { pub fn node_type_insert(&self, id: NodeId, ty: Ty<'tcx>) { self.node_types.borrow_mut().insert(id, ty); } + + pub fn store_free_region_map(&self, id: NodeId, map: FreeRegionMap) { + self.free_region_maps.borrow_mut() + .insert(id, map); + } + + pub fn free_region_map(&self, id: NodeId) -> FreeRegionMap { + self.free_region_maps.borrow()[&id].clone() + } } // Flags that we track on types. These flags are propagated upwards @@ -2546,7 +2564,7 @@ pub fn mk_ctxt<'tcx>(s: Session, named_region_map: resolve_lifetime::NamedRegionMap, map: ast_map::Map<'tcx>, freevars: RefCell, - region_maps: middle::region::RegionMaps, + region_maps: RegionMaps, lang_items: middle::lang_items::LanguageItems, stability: stability::Index) -> ctxt<'tcx> { @@ -2561,11 +2579,12 @@ pub fn mk_ctxt<'tcx>(s: Session, region_interner: RefCell::new(FnvHashMap()), types: common_types, named_region_map: named_region_map, + region_maps: region_maps, + free_region_maps: RefCell::new(FnvHashMap()), item_variance_map: RefCell::new(DefIdMap()), variance_computed: Cell::new(false), sess: s, def_map: def_map, - region_maps: region_maps, node_types: RefCell::new(FnvHashMap()), item_substs: RefCell::new(NodeMap()), impl_trait_refs: RefCell::new(NodeMap()), @@ -6537,14 +6556,6 @@ pub fn construct_parameter_environment<'a,'tcx>( let bounds = liberate_late_bound_regions(tcx, free_id_outlive, &ty::Binder(bounds)); let predicates = bounds.predicates.into_vec(); - // - // Compute region bounds. For now, these relations are stored in a - // global table on the tcx, so just enter them there. I'm not - // crazy about this scheme, but it's convenient, at least. - // - - record_region_bounds(tcx, &*predicates); - debug!("construct_parameter_environment: free_id={:?} free_subst={:?} predicates={:?}", free_id, free_substs.repr(tcx), @@ -6573,37 +6584,7 @@ pub fn construct_parameter_environment<'a,'tcx>( }; let cause = traits::ObligationCause::misc(span, free_id); - return traits::normalize_param_env_or_error(unnormalized_env, cause); - - fn record_region_bounds<'tcx>(tcx: &ty::ctxt<'tcx>, predicates: &[ty::Predicate<'tcx>]) { - debug!("record_region_bounds(predicates={:?})", predicates.repr(tcx)); - - for predicate in predicates { - match *predicate { - Predicate::Projection(..) | - Predicate::Trait(..) | - Predicate::Equate(..) | - Predicate::TypeOutlives(..) => { - // No region bounds here - } - Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => { - match (r_a, r_b) { - (ty::ReFree(fr_a), ty::ReFree(fr_b)) => { - // Record that `'a:'b`. Or, put another way, `'b <= 'a`. - tcx.region_maps.relate_free_regions(fr_b, fr_a); - } - _ => { - // All named regions are instantiated with free regions. - tcx.sess.bug( - &format!("record_region_bounds: non free region: {} / {}", - r_a.repr(tcx), - r_b.repr(tcx))); - } - } - } - } - } - } + traits::normalize_param_env_or_error(unnormalized_env, cause) } impl BorrowKind { diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 502321d07598c..aedc0d23cfe1d 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -27,9 +27,11 @@ use rustc::middle::dataflow::DataFlowOperator; use rustc::middle::dataflow::KillFrom; use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization as mc; +use rustc::middle::free_region::FreeRegionMap; use rustc::middle::region; use rustc::middle::ty::{self, Ty}; use rustc::util::ppaux::{note_and_explain_region, Repr, UserString}; +use std::mem; use std::rc::Rc; use std::string::String; use syntax::ast; @@ -56,7 +58,20 @@ pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> { fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, b: &'v Block, s: Span, id: ast::NodeId) { - borrowck_fn(self, fk, fd, b, s, id); + match fk { + visit::FkItemFn(..) | + visit::FkMethod(..) => { + let new_free_region_map = self.tcx.free_region_map(id); + let old_free_region_map = + mem::replace(&mut self.free_region_map, new_free_region_map); + borrowck_fn(self, fk, fd, b, s, id); + self.free_region_map = old_free_region_map; + } + + visit::FkFnBlock => { + borrowck_fn(self, fk, fd, b, s, id); + } + } } fn visit_item(&mut self, item: &ast::Item) { @@ -67,6 +82,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> { pub fn check_crate(tcx: &ty::ctxt) { let mut bccx = BorrowckCtxt { tcx: tcx, + free_region_map: FreeRegionMap::new(), stats: BorrowStats { loaned_paths_same: 0, loaned_paths_imm: 0, @@ -129,11 +145,13 @@ fn borrowck_fn(this: &mut BorrowckCtxt, let cfg = cfg::CFG::new(this.tcx, body); let AnalysisData { all_loans, loans: loan_dfcx, - move_data:flowed_moves } = + move_data: flowed_moves } = build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id); move_data::fragments::instrument_move_fragments(&flowed_moves.move_data, - this.tcx, sp, id); + this.tcx, + sp, + id); check_loans::check_loans(this, &loan_dfcx, @@ -152,7 +170,9 @@ fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>, cfg: &cfg::CFG, body: &ast::Block, sp: Span, - id: ast::NodeId) -> AnalysisData<'a, 'tcx> { + id: ast::NodeId) + -> AnalysisData<'a, 'tcx> +{ // Check the body of fn items. let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id); let (all_loans, move_data) = @@ -203,10 +223,13 @@ impl<'a> FnPartsWithCFG<'a> { /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer. pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>( tcx: &'a ty::ctxt<'tcx>, - input: FnPartsWithCFG<'a>) -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>) { + input: FnPartsWithCFG<'a>) + -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>) +{ let mut bccx = BorrowckCtxt { tcx: tcx, + free_region_map: FreeRegionMap::new(), stats: BorrowStats { loaned_paths_same: 0, loaned_paths_imm: 0, @@ -234,6 +257,18 @@ pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>( pub struct BorrowckCtxt<'a, 'tcx: 'a> { tcx: &'a ty::ctxt<'tcx>, + // Hacky. As we visit various fns, we have to load up the + // free-region map for each one. This map is computed by during + // typeck for each fn item and stored -- closures just use the map + // from the fn item that encloses them. Since we walk the fns in + // order, we basically just overwrite this field as we enter a fn + // item and restore it afterwards in a stack-like fashion. Then + // the borrow checking code can assume that `free_region_map` is + // always the correct map for the current fn. Feels like it'd be + // better to just recompute this, rather than store it, but it's a + // bit of a pain to factor that code out at the moment. + free_region_map: FreeRegionMap, + // Statistics: stats: BorrowStats } @@ -518,8 +553,9 @@ pub enum MovedValueUseKind { impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region) - -> bool { - self.tcx.region_maps.is_subregion_of(r_sub, r_sup) + -> bool + { + self.free_region_map.is_subregion_of(self.tcx, r_sub, r_sup) } pub fn report(&self, err: BckError<'tcx>) { diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 12b16e95a71a4..a8cf753317996 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -16,6 +16,7 @@ use driver; use rustc_lint; use rustc_resolve as resolve; use rustc_typeck::middle::lang_items; +use rustc_typeck::middle::free_region::FreeRegionMap; use rustc_typeck::middle::region::{self, CodeExtent, DestructionScopeData}; use rustc_typeck::middle::resolve_lifetime; use rustc_typeck::middle::stability; @@ -138,7 +139,8 @@ fn test_env(source_string: &str, stability::Index::new(krate)); let infcx = infer::new_infer_ctxt(&tcx); body(Env { infcx: &infcx }); - infcx.resolve_regions_and_report_errors(ast::CRATE_NODE_ID); + let free_regions = FreeRegionMap::new(); + infcx.resolve_regions_and_report_errors(&free_regions, ast::CRATE_NODE_ID); assert_eq!(tcx.sess.err_count(), expected_err_count); } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 78797d086c677..9dbf918ddb5c3 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -51,6 +51,7 @@ use middle::astconv_util::{prim_ty_to_ty, check_path_args, NO_TPS, NO_REGIONS}; use middle::const_eval; use middle::def; +use middle::implicator::object_region_bounds; use middle::resolve_lifetime as rl; use middle::privacy::{AllPublic, LastMod}; use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs}; @@ -2087,39 +2088,6 @@ fn compute_object_lifetime_bound<'tcx>( return r; } -/// Given an object type like `SomeTrait+Send`, computes the lifetime -/// bounds that must hold on the elided self type. These are derived -/// from the declarations of `SomeTrait`, `Send`, and friends -- if -/// they declare `trait SomeTrait : 'static`, for example, then -/// `'static` would appear in the list. The hard work is done by -/// `ty::required_region_bounds`, see that for more information. -pub fn object_region_bounds<'tcx>( - tcx: &ty::ctxt<'tcx>, - principal: &ty::PolyTraitRef<'tcx>, - others: ty::BuiltinBounds) - -> Vec -{ - // Since we don't actually *know* the self type for an object, - // this "open(err)" serves as a kind of dummy standin -- basically - // a skolemized type. - let open_ty = ty::mk_infer(tcx, ty::FreshTy(0)); - - // Note that we preserve the overall binding levels here. - assert!(!open_ty.has_escaping_regions()); - let substs = tcx.mk_substs(principal.0.substs.with_self_ty(open_ty)); - let trait_refs = vec!(ty::Binder(Rc::new(ty::TraitRef::new(principal.0.def_id, substs)))); - - let param_bounds = ty::ParamBounds { - region_bounds: Vec::new(), - builtin_bounds: others, - trait_bounds: trait_refs, - projection_bounds: Vec::new(), // not relevant to computing region bounds - }; - - let predicates = ty::predicates(tcx, open_ty, ¶m_bounds); - ty::required_region_bounds(tcx, open_ty, predicates) -} - pub struct PartitionedBounds<'a> { pub builtin_bounds: ty::BuiltinBounds, pub trait_bounds: Vec<&'a ast::PolyTraitRef>, diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 532277d75b2e0..c4ee7e79570da 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use middle::free_region::FreeRegionMap; use middle::infer; use middle::traits; use middle::ty::{self}; @@ -354,9 +355,19 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, Ok(_) => {} } - // Finally, resolve all regions. This catches wily misuses of lifetime - // parameters. - infcx.resolve_regions_and_report_errors(impl_m_body_id); + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. We have to build up a plausible lifetime + // environment based on what we find in the trait. We could also + // include the obligations derived from the method argument types, + // but I don't think it's necessary -- after all, those are still + // in effect when type-checking the body, and all the + // where-clauses in the header etc should be implied by the trait + // anyway, so it shouldn't be needed there either. Anyway, we can + // always add more relations later (it's backwards compat). + let mut free_regions = FreeRegionMap::new(); + free_regions.relate_free_regions_from_predicates(tcx, &trait_param_env.caller_bounds); + + infcx.resolve_regions_and_report_errors(&free_regions, impl_m_body_id); fn check_region_bounds_on_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 348846b8ad401..e87deba280804 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -132,7 +132,6 @@ pub mod dropck; pub mod _match; pub mod vtable; pub mod writeback; -pub mod implicator; pub mod regionck; pub mod coercion; pub mod demand; diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 6dfc1e0ea6c26..2e8c5730e67cb 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -85,8 +85,9 @@ use astconv::AstConv; use check::dropck; use check::FnCtxt; -use check::implicator; use check::vtable; +use middle::free_region::FreeRegionMap; +use middle::implicator; use middle::mem_categorization as mc; use middle::region::CodeExtent; use middle::subst::Substs; @@ -124,6 +125,8 @@ pub fn regionck_expr(fcx: &FnCtxt, e: &ast::Expr) { pub fn regionck_item(fcx: &FnCtxt, item: &ast::Item) { let mut rcx = Rcx::new(fcx, RepeatingScope(item.id), item.id, Subject(item.id)); + let tcx = fcx.tcx(); + rcx.free_region_map.relate_free_regions_from_predicates(tcx, &fcx.inh.param_env.caller_bounds); rcx.visit_region_obligations(item.id); rcx.resolve_regions_and_report_errors(); } @@ -135,12 +138,21 @@ pub fn regionck_fn(fcx: &FnCtxt, blk: &ast::Block) { debug!("regionck_fn(id={})", fn_id); let mut rcx = Rcx::new(fcx, RepeatingScope(blk.id), blk.id, Subject(fn_id)); + if fcx.err_count_since_creation() == 0 { // regionck assumes typeck succeeded rcx.visit_fn_body(fn_id, decl, blk, fn_span); } + let tcx = fcx.tcx(); + rcx.free_region_map.relate_free_regions_from_predicates(tcx, &fcx.inh.param_env.caller_bounds); + rcx.resolve_regions_and_report_errors(); + + // For the top-level fn, store the free-region-map. We don't store + // any map for closures; they just share the same map as the + // function that created them. + fcx.tcx().store_free_region_map(fn_id, rcx.free_region_map); } /// Checks that the types in `component_tys` are well-formed. This will add constraints into the @@ -167,6 +179,8 @@ pub struct Rcx<'a, 'tcx: 'a> { region_bound_pairs: Vec<(ty::Region, GenericKind<'tcx>)>, + free_region_map: FreeRegionMap, + // id of innermost fn body id body_id: ast::NodeId, @@ -191,7 +205,8 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { repeating_scope: initial_repeating_scope, body_id: initial_body_id, subject: subject, - region_bound_pairs: Vec::new() + region_bound_pairs: Vec::new(), + free_region_map: FreeRegionMap::new(), } } @@ -277,13 +292,16 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { } }; - let len = self.region_bound_pairs.len(); + let old_region_bounds_pairs_len = self.region_bound_pairs.len(); + let old_body_id = self.set_body_id(body.id); self.relate_free_regions(&fn_sig[..], body.id, span); link_fn_args(self, CodeExtent::from_node_id(body.id), &fn_decl.inputs[..]); self.visit_block(body); self.visit_region_obligations(body.id); - self.region_bound_pairs.truncate(len); + + self.region_bound_pairs.truncate(old_region_bounds_pairs_len); + self.set_body_id(old_body_id); } @@ -340,14 +358,16 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { let body_scope = ty::ReScope(body_scope); let implications = implicator::implications(self.fcx.infcx(), self.fcx, body_id, ty, body_scope, span); + + // Record any relations between free regions that we observe into the free-region-map. + self.free_region_map.relate_free_regions_from_implications(tcx, &implications); + + // But also record other relationships, such as `T:'x`, + // that don't go into the free-region-map but which we use + // here. for implication in implications { debug!("implication: {}", implication.repr(tcx)); match implication { - implicator::Implication::RegionSubRegion(_, - ty::ReFree(free_a), - ty::ReFree(free_b)) => { - tcx.region_maps.relate_free_regions(free_a, free_b); - } implicator::Implication::RegionSubRegion(_, ty::ReFree(free_a), ty::ReInfer(ty::ReVar(vid_b))) => { @@ -388,7 +408,8 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { } }; - self.fcx.infcx().resolve_regions_and_report_errors(subject_node_id); + self.fcx.infcx().resolve_regions_and_report_errors(&self.free_region_map, + subject_node_id); } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 5ed93703d977f..23959d578bf03 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -68,6 +68,7 @@ use astconv::{self, AstConv, ty_of_arg, ast_ty_to_ty, ast_region_to_region}; use middle::def; use constrained_type_params as ctp; use middle::lang_items::SizedTraitLangItem; +use middle::free_region::FreeRegionMap; use middle::region; use middle::resolve_lifetime; use middle::subst::{Substs, FnSpace, ParamSpace, SelfSpace, TypeSpace, VecPerParamSpace}; @@ -2158,7 +2159,16 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>( format!("mismatched self type: expected `{}`", ppaux::ty_to_string(tcx, required_type)) })); - infcx.resolve_regions_and_report_errors(body_id); + + // We could conceviably add more free-reion relations here, + // but since this code is just concerned with checking that + // the `&Self` types etc match up, it's not really necessary. + // It would just allow people to be more approximate in some + // cases. In any case, we can do it later as we feel the need; + // I'd like this function to go away eventually. + let free_regions = FreeRegionMap::new(); + + infcx.resolve_regions_and_report_errors(&free_regions, body_id); } fn liberate_early_bound_regions<'tcx,T>( diff --git a/src/libstd/sys/common/net2.rs b/src/libstd/sys/common/net2.rs index 7d42d65d360f5..2b2c31d92edad 100644 --- a/src/libstd/sys/common/net2.rs +++ b/src/libstd/sys/common/net2.rs @@ -202,15 +202,19 @@ impl TcpStream { setsockopt(&self.inner, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, seconds as c_int) } - #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "linux"))] fn set_tcp_keepalive(&self, seconds: u32) -> io::Result<()> { setsockopt(&self.inner, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, seconds as c_int) } + #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "freebsd", - target_os = "dragonfly")))] + target_os = "dragonfly", + target_os = "linux")))] fn set_tcp_keepalive(&self, _seconds: u32) -> io::Result<()> { Ok(()) } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index d0975c76e9351..fa95f667c15af 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -394,7 +394,7 @@ impl<'a> Context<'a> { are reserved for internal compiler diagnostics"); } else if name.starts_with("derive_") { self.gate_feature("custom_derive", attr.span, - "attributes of the form `#[derive_*]` are reserved + "attributes of the form `#[derive_*]` are reserved \ for the compiler"); } else { self.gate_feature("custom_attribute", attr.span, @@ -620,7 +620,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { pattern.span, "multiple-element slice matches anywhere \ but at the end of a slice (e.g. \ - `[0, ..xs, 0]` are experimental") + `[0, ..xs, 0]`) are experimental") } ast::PatVec(..) => { self.gate_feature("slice_patterns", diff --git a/src/rustbook/javascript.rs b/src/rustbook/javascript.rs index d5483593aa888..26303d13b6cfc 100644 --- a/src/rustbook/javascript.rs +++ b/src/rustbook/javascript.rs @@ -52,7 +52,7 @@ document.addEventListener("DOMContentLoaded", function(event) { } for (var i = 0; i < toc.length; i++) { - if (toc[i].attributes['href'].value === href) { + if (toc[i].attributes['href'].value.split('/').pop() === href) { var nav = document.createElement('p'); if (i > 0) { var prevNode = toc[i-1].cloneNode(true); diff --git a/src/test/compile-fail/feature-gate-negate-unsigned.rs b/src/test/compile-fail/feature-gate-negate-unsigned.rs new file mode 100644 index 0000000000000..7dc654fe1c8d5 --- /dev/null +++ b/src/test/compile-fail/feature-gate-negate-unsigned.rs @@ -0,0 +1,17 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that negating unsigned integers is gated by `negate_unsigned` feature +// gate + +const MAX: usize = -1; +//~^ ERROR unary negation of unsigned integers may be removed in the future + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-on-unimplemented.rs b/src/test/compile-fail/feature-gate-on-unimplemented.rs new file mode 100644 index 0000000000000..5d32bba6a766c --- /dev/null +++ b/src/test/compile-fail/feature-gate-on-unimplemented.rs @@ -0,0 +1,19 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that `#[rustc_on_unimplemented]` is gated by `on_unimplemented` feature +// gate. + +#[rustc_on_unimplemented = "test error `{Self}` with `{Bar}`"] +//~^ ERROR the `#[rustc_on_unimplemented]` attribute is an experimental feature +trait Foo +{} + +fn main() {} diff --git a/src/test/compile-fail/gated-simd-ffi.rs b/src/test/compile-fail/feature-gate-optin-builtin-traits.rs similarity index 54% rename from src/test/compile-fail/gated-simd-ffi.rs rename to src/test/compile-fail/feature-gate-optin-builtin-traits.rs index 883e1be04b228..59d7473a741d6 100644 --- a/src/test/compile-fail/gated-simd-ffi.rs +++ b/src/test/compile-fail/feature-gate-optin-builtin-traits.rs @@ -8,20 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Test that the use of smid types in the ffi is gated by `smid_ffi` feature gate. +// Test that default and negative trait implementations are gated by +// `optin_builtin_traits` feature gate -#![feature(simd)] +struct DummyStruct; -#[repr(C)] -#[derive(Copy, Clone)] -#[simd] -pub struct f32x4(f32, f32, f32, f32); - -#[allow(dead_code)] -extern { - fn foo(x: f32x4); - //~^ ERROR use of SIMD type `f32x4` in FFI is highly experimental and may result in invalid code - //~| HELP add #![feature(simd_ffi)] to the crate attributes to enable +trait DummyTrait { + fn dummy(&self) {} } +impl DummyTrait for .. {} +//~^ ERROR default trait implementations are experimental and possibly buggy + +impl !DummyTrait for DummyStruct {} +//~^ ERROR negative trait bounds are not yet fully implemented; use marker types for now + fn main() {} diff --git a/src/test/compile-fail/feature-gate-plugin.rs b/src/test/compile-fail/feature-gate-plugin.rs new file mode 100644 index 0000000000000..3b5d7626ce309 --- /dev/null +++ b/src/test/compile-fail/feature-gate-plugin.rs @@ -0,0 +1,16 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that `#![plugin(...)]` attribute is gated by `plugin` feature gate + +#![plugin(foo)] +//~^ ERROR compiler plugins are experimental and possibly buggy + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-rustc-attrs.rs b/src/test/compile-fail/feature-gate-rustc-attrs.rs new file mode 100644 index 0000000000000..dab44b655fce8 --- /dev/null +++ b/src/test/compile-fail/feature-gate-rustc-attrs.rs @@ -0,0 +1,21 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength + +// Test that `#[rustc_*]` attributes are gated by `rustc_attrs` feature gate. + +#[rustc_variance] //~ ERROR the `#[rustc_variance]` attribute is an experimental feature +#[rustc_error] //~ ERROR the `#[rustc_error]` attribute is an experimental feature +#[rustc_move_fragments] //~ ERROR the `#[rustc_move_fragments]` attribute is an experimental feature +#[rustc_foo] +//~^ ERROR unless otherwise specified, attributes with the prefix `rustc_` are reserved for internal compiler diagnostics + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-rustc-diagnostic-macros.rs b/src/test/compile-fail/feature-gate-rustc-diagnostic-macros.rs new file mode 100644 index 0000000000000..8286d833e8d22 --- /dev/null +++ b/src/test/compile-fail/feature-gate-rustc-diagnostic-macros.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that diagnostic macros are gated by `rustc_diagnostic_macros` feature +// gate + +__register_diagnostic!(E0001); +//~^ ERROR macro undefined: '__register_diagnostic!' + +fn main() { + __diagnostic_used!(E0001); + //~^ ERROR macro undefined: '__diagnostic_used!' +} + +__build_diagnostic_array!(DIAGNOSTICS); +//~^ ERROR macro undefined: '__build_diagnostic_array!' diff --git a/src/test/compile-fail/gated-box-patterns.rs b/src/test/compile-fail/feature-gate-slice-patterns.rs similarity index 70% rename from src/test/compile-fail/gated-box-patterns.rs rename to src/test/compile-fail/feature-gate-slice-patterns.rs index d82d0dec72bba..625cb2d351553 100644 --- a/src/test/compile-fail/gated-box-patterns.rs +++ b/src/test/compile-fail/feature-gate-slice-patterns.rs @@ -8,14 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Test that patterns including the box syntax are gated by `box_patterns` feature gate. +// Test that slice pattern syntax is gated by `slice_patterns` feature gate fn main() { - let x = Box::new(1); - + let x = [1, 2, 3, 4, 5]; match x { - box 1 => (), - //~^ box pattern syntax is experimental - _ => () - }; + [1, 2, xs..] => {} //~ ERROR slice pattern syntax is experimental + } } diff --git a/src/test/compile-fail/region-bound-extra-bound-in-impl.rs b/src/test/compile-fail/region-bound-extra-bound-in-impl.rs new file mode 100644 index 0000000000000..5bcc6be4c3d3b --- /dev/null +++ b/src/test/compile-fail/region-bound-extra-bound-in-impl.rs @@ -0,0 +1,25 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for issue #22779. An extra where clause was +// permitted on the impl that is not present on the trait. + +trait Tr<'a, T> { + fn renew<'b: 'a>(self) -> &'b mut [T]; +} + +impl<'a, T> Tr<'a, T> for &'a mut [T] { + fn renew<'b: 'a>(self) -> &'b mut [T] where 'a: 'b { + //~^ ERROR lifetime bound not satisfied + &mut self[..] + } +} + +fn main() { } diff --git a/src/test/compile-fail/region-bound-extra-bound-in-inherent-impl.rs b/src/test/compile-fail/region-bound-extra-bound-in-inherent-impl.rs new file mode 100644 index 0000000000000..c1df057b39609 --- /dev/null +++ b/src/test/compile-fail/region-bound-extra-bound-in-inherent-impl.rs @@ -0,0 +1,26 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test related to #22779. In this case, the impl is an inherent impl, +// so it doesn't have to match any trait, so no error results. + +#![feature(rustc_attrs)] +#![allow(dead_code)] + +struct MySlice<'a, T:'a>(&'a mut [T]); + +impl<'a, T> MySlice<'a, T> { + fn renew<'b: 'a>(self) -> &'b mut [T] where 'a: 'b { + &mut self.0[..] + } +} + +#[rustc_error] +fn main() { } //~ ERROR compilation successful diff --git a/src/test/compile-fail/region-bound-same-bounds-in-trait-and-impl.rs b/src/test/compile-fail/region-bound-same-bounds-in-trait-and-impl.rs new file mode 100644 index 0000000000000..3115e5a9a4376 --- /dev/null +++ b/src/test/compile-fail/region-bound-same-bounds-in-trait-and-impl.rs @@ -0,0 +1,27 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test related to #22779, but where the `'a:'b` relation +// appears in the trait too. No error here. + +#![feature(rustc_attrs)] + +trait Tr<'a, T> { + fn renew<'b: 'a>(self) -> &'b mut [T] where 'a: 'b; +} + +impl<'a, T> Tr<'a, T> for &'a mut [T] { + fn renew<'b: 'a>(self) -> &'b mut [T] where 'a: 'b { + &mut self[..] + } +} + +#[rustc_error] +fn main() { } //~ ERROR compilation successful diff --git a/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs b/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs index 278ccd3c11936..abffd33e3f83e 100644 --- a/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs +++ b/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs @@ -50,7 +50,9 @@ impl<'a, 't> Foo<'a, 't> for &'a isize { fn okay_bound<'b,'c,'e:'b+'c>(self, b: Inv<'b>, c: Inv<'c>, e: Inv<'e>) { } - fn another_bound<'x: 't>(self, x: Inv<'x>, y: Inv<'t>) {} + fn another_bound<'x: 't>(self, x: Inv<'x>, y: Inv<'t>) { + //~^ ERROR lifetime bound not satisfied + } } fn main() { }