From ffc5f1ccd8b0c7ca07414c324729ecac97f47e6a Mon Sep 17 00:00:00 2001 From: Ignacio Corderi Date: Wed, 26 Nov 2014 16:40:56 -0800 Subject: [PATCH 001/211] Added src/doc/grammar.md to hold Rust grammar --- src/doc/grammar.md | 1514 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1514 insertions(+) create mode 100644 src/doc/grammar.md diff --git a/src/doc/grammar.md b/src/doc/grammar.md new file mode 100644 index 0000000000000..6b700210201d4 --- /dev/null +++ b/src/doc/grammar.md @@ -0,0 +1,1514 @@ +# **This is a work in progress** + +% The Rust Grammar + +# Introduction + +This document is the primary reference for the Rust programming language grammar. It +provides only one kind of material: + + - Chapters that formally define the language grammar and, for each + construct. + +This document does not serve as an introduction to the language. Background +familiarity with the language is assumed. A separate [guide] is available to +help acquire such background familiarity. + +This document also does not serve as a reference to the [standard] library +included in the language distribution. Those libraries are documented +separately by extracting documentation attributes from their source code. Many +of the features that one might expect to be language features are library +features in Rust, so what you're looking for may be there, not here. + +[guide]: guide.html +[standard]: std/index.html + +# Notation + +Rust's grammar is defined over Unicode codepoints, 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: + +```antlr +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 codepoint `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 codepoints outside the ASCII +range. We define these productions in terms of character properties specified +in the Unicode standard, rather than in terms of ASCII-range codepoints. The +section [Special Unicode Productions](#special-unicode-productions) lists these +productions. + +## String table productions + +Some rules in the grammar — notably [unary +operators](#unary-operator-expressions), [binary +operators](#binary-operator-expressions), and [keywords](#keywords) — are +given in a simplified form: as a listing of a table of unquoted, printable +whitespace-separated strings. These cases form a subset of the rules regarding +the [token](#tokens) rule, and are assumed to be the result of a +lexical-analysis phase feeding the parser, driven by a DFA, operating over the +disjunction of all such string table entries. + +When such a string enclosed in double-quotes (`"`) occurs inside the grammar, +it is an implicit reference to a single member of such a string table +production. See [tokens](#tokens) for more information. + +# Lexical structure + +## Input format + +Rust input is interpreted as a sequence of Unicode codepoints encoded in UTF-8. +Most Rust grammar rules are defined in terms of printable ASCII-range +codepoints, but a small number are defined in terms of Unicode properties or +explicit codepoint lists. [^inputformat] + +[^inputformat]: Substitute definitions for the special Unicode productions are + provided to the grammar verifier, restricted to ASCII range, when verifying the + grammar in this document. + +## Special Unicode Productions + +The following productions in the Rust grammar are defined in terms of Unicode +properties: `ident`, `non_null`, `non_star`, `non_eol`, `non_slash_or_star`, +`non_single_quote` and `non_double_quote`. + +### Identifiers + +The `ident` production is any nonempty Unicode string of the following form: + +- The first character has property `XID_start` +- The remaining characters have property `XID_continue` + +that does _not_ occur in the set of [keywords](#keywords). + +> **Note**: `XID_start` and `XID_continue` as character properties cover the +> character ranges used to form the more familiar C and Java language-family +> identifiers. + +### Delimiter-restricted productions + +Some productions are defined by exclusion of particular Unicode characters: + +- `non_null` is any single Unicode character aside from `U+0000` (null) +- `non_eol` is `non_null` restricted to exclude `U+000A` (`'\n'`) +- `non_star` is `non_null` restricted to exclude `U+002A` (`*`) +- `non_slash_or_star` is `non_null` restricted to exclude `U+002F` (`/`) and `U+002A` (`*`) +- `non_single_quote` is `non_null` restricted to exclude `U+0027` (`'`) +- `non_double_quote` is `non_null` restricted to exclude `U+0022` (`"`) + +## Comments + +```antlr +comment : block_comment | line_comment ; +block_comment : "/*" block_comment_body * "*/" ; +block_comment_body : [block_comment | character] * ; +line_comment : "//" non_eol * ; +``` + +**FIXME:** add doc grammar? + +## Whitespace + +```antlr +whitespace_char : '\x20' | '\x09' | '\x0a' | '\x0d' ; +whitespace : [ whitespace_char | comment ] + ; +``` + +## Tokens + +```antlr +simple_token : keyword | unop | binop ; +token : simple_token | ident | literal | symbol | whitespace token ; +``` + +### Keywords + +

+ +| | | | | | +|----------|----------|----------|----------|--------| +| abstract | alignof | as | be | 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 | | | | | + + +Each of these keywords has special meaning in its grammar, and all of them are +excluded from the `ident` rule. + +### Literals + +```antlr +lit_suffix : ident; +literal : [ string_lit | char_lit | byte_string_lit | byte_lit | num_lit ] lit_suffix ?; +``` + +#### Character and string literals + +```antlr +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 4 + | 'U' hex_digit 8 ; + +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' ; +``` + +#### Byte and byte string literals + +```antlr +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 '#' ; + +``` + +#### Number literals + +```antlr +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 | '_' ] + ; +``` + +#### Boolean literals + +**FIXME:** write grammar + +The two values of the boolean type are written `true` and `false`. + +### Symbols + +```antlr +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 +otherwise appear as [unary operators](#unary-operator-expressions), [binary +operators](#binary-operator-expressions), or [keywords](#keywords). + +## Paths + +```antlr +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 ; +``` + +# Syntax extensions + +## Macros + +```antlr +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 ; +``` + +# Crates and source files + +**FIXME:** grammar? What production covers #![crate_id = "foo"] ? + +# Items and attributes + +**FIXME:** grammar? + +## Items + +```antlr +item : mod_item | fn_item | type_item | struct_item | enum_item + | static_item | trait_item | impl_item | extern_block ; +``` + +### Type Parameters + +**FIXME:** grammar? + +### Modules + +```antlr +mod_item : "mod" ident ( ';' | '{' mod '}' ); +mod : [ view_item | item ] * ; +``` + +#### View items + +```antlr +view_item : extern_crate_decl | use_decl ; +``` + +##### Extern crate declarations + +```antlr +extern_crate_decl : "extern" "crate" crate_name +crate_name: ident | ( string_lit as ident ) +``` + +##### Use declarations + +```antlr +use_decl : "pub" ? "use" [ path "as" ident + | path_glob ] ; + +path_glob : ident [ "::" [ path_glob + | '*' ] ] ? + | '{' path_item [ ',' path_item ] * '}' ; + +path_item : ident | "mod" ; +``` + +### Functions + +**FIXME:** grammar? + +#### Generic functions + +**FIXME:** grammar? + +#### Unsafety + +**FIXME:** grammar? + +##### Unsafe functions + +**FIXME:** grammar? + +##### Unsafe blocks + +**FIXME:** grammar? + +#### Diverging functions + +**FIXME:** grammar? + +### Type definitions + +**FIXME:** grammar? + +### Structures + +**FIXME:** grammar? + +### Constant items + +```antlr +const_item : "const" ident ':' type '=' expr ';' ; +``` + +### Static items + +```antlr +static_item : "static" ident ':' type '=' expr ';' ; +``` + +#### Mutable statics + +**FIXME:** grammar? + +### Traits + +**FIXME:** grammar? + +### Implementations + +**FIXME:** grammar? + +### External blocks + +```antlr +extern_block_item : "extern" '{' extern_block '}' ; +extern_block : [ foreign_fn ] * ; +``` + +## Visibility and Privacy + +**FIXME:** grammar? + +### Re-exporting and Visibility + +**FIXME:** grammar? + +## Attributes + +```antlr +attribute : "#!" ? '[' meta_item ']' ; +meta_item : ident [ '=' literal + | '(' meta_seq ')' ] ? ; +meta_seq : meta_item [ ',' meta_seq ] ? ; +``` + +# Statements and expressions + +## Statements + +**FIXME:** grammar? + +### Declaration statements + +**FIXME:** grammar? + +A _declaration statement_ is one that introduces one or more *names* into the +enclosing statement block. The declared names may denote new slots or new +items. + +#### Item declarations + +**FIXME:** grammar? + +An _item declaration statement_ has a syntactic form identical to an +[item](#items) declaration within a module. Declaring an item — a +function, enumeration, structure, type, static, trait, implementation or module +— locally within a statement block is simply a way of restricting its +scope to a narrow region containing all of its uses; it is otherwise identical +in meaning to declaring the item outside the statement block. + +#### Slot declarations + +```antlr +let_decl : "let" pat [':' type ] ? [ init ] ? ';' ; +init : [ '=' ] expr ; +``` + +### Expression statements + +**FIXME:** grammar? + +## Expressions + +**FIXME:** grammar? + +#### Lvalues, rvalues and temporaries + +**FIXME:** grammar? + +#### Moved and copied types + +**FIXME:** Do we want to capture this in the grammar as different productions? + +### Literal expressions + +**FIXME:** grammar? + +### Path expressions + +**FIXME:** grammar? + +### Tuple expressions + +**FIXME:** grammar? + +### Unit expressions + +**FIXME:** grammar? + +### Structure expressions + +```antlr +struct_expr : expr_path '{' ident ':' expr + [ ',' ident ':' expr ] * + [ ".." expr ] '}' | + expr_path '(' expr + [ ',' expr ] * ')' | + expr_path ; +``` + +### Block expressions + +```antlr +block_expr : '{' [ view_item ] * + [ stmt ';' | item ] * + [ expr ] '}' ; +``` + +### Method-call expressions + +```antlr +method_call_expr : expr '.' ident paren_expr_list ; +``` + +### Field expressions + +```antlr +field_expr : expr '.' ident ; +``` + +### Array expressions + +```antlr +array_expr : '[' "mut" ? vec_elems? ']' ; + +array_elems : [expr [',' expr]*] | [expr ',' ".." expr] ; +``` + +### Index expressions + +```antlr +idx_expr : expr '[' expr ']' ; +``` + +### Unary operator expressions + +**FIXME:** grammar? + +### Binary operator expressions + +```antlr +binop_expr : expr binop expr ; +``` + +#### Arithmetic operators + +**FIXME:** grammar? + +#### Bitwise operators + +**FIXME:** grammar? + +#### Lazy boolean operators + +**FIXME:** grammar? + +#### Comparison operators + +**FIXME:** grammar? + +#### Type cast expressions + +**FIXME:** grammar? + +#### Assignment expressions + +**FIXME:** grammar? + +#### Compound assignment expressions + +**FIXME:** grammar? + +#### Operator precedence + +The precedence of Rust binary operators is ordered as follows, going from +strong to weak: + +``` +* / % +as ++ - +<< >> +& +^ +| +< > <= >= +== != +&& +|| += +``` + +Operators at the same precedence level are evaluated left-to-right. [Unary +operators](#unary-operator-expressions) have the same precedence level and it +is stronger than any of the binary operators'. + +### Grouped expressions + +```antlr +paren_expr : '(' expr ')' ; +``` + +### Call expressions + +```antlr +expr_list : [ expr [ ',' expr ]* ] ? ; +paren_expr_list : '(' expr_list ')' ; +call_expr : expr paren_expr_list ; +``` + +### Lambda expressions + +```antlr +ident_list : [ ident [ ',' ident ]* ] ? ; +lambda_expr : '|' ident_list '|' expr ; +``` + +### While loops + +```antlr +while_expr : "while" no_struct_literal_expr '{' block '}' ; +``` + +### Infinite loops + +```antlr +loop_expr : [ lifetime ':' ] "loop" '{' block '}'; +``` + +### Break expressions + +```antlr +break_expr : "break" [ lifetime ]; +``` + +### Continue expressions + +```antlr +continue_expr : "continue" [ lifetime ]; +``` + +### For expressions + +```antlr +for_expr : "for" pat "in" no_struct_literal_expr '{' block '}' ; +``` + +### If expressions + +```antlr +if_expr : "if" no_struct_literal_expr '{' block '}' + else_tail ? ; + +else_tail : "else" [ if_expr | if_let_expr + | '{' block '}' ] ; +``` + +### Match expressions + +```antlr +match_expr : "match" no_struct_literal_expr '{' match_arm * '}' ; + +match_arm : attribute * match_pat "=>" [ expr "," | '{' block '}' ] ; + +match_pat : pat [ '|' pat ] * [ "if" expr ] ? ; +``` + +### If let expressions + +```antlr +if_let_expr : "if" "let" pat '=' expr '{' block '}' + else_tail ? ; +else_tail : "else" [ if_expr | if_let_expr | '{' block '}' ] ; +``` + +### While let loops + +```antlr +while_let_expr : "while" "let" pat '=' expr '{' block '}' ; +``` + +### Return expressions + +```antlr +return_expr : "return" expr ? ; +``` + +# Type system + +## Types + +Every slot, item and value in a Rust program has a type. The _type_ of a +*value* defines the interpretation of the memory holding it. + +Built-in types and type-constructors are tightly integrated into the language, +in nontrivial ways that are not possible to emulate in user-defined types. +User-defined types have limited capabilities. + +### Primitive types + +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 slots; 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: + +* The unsigned word types `u8`, `u16`, `u32` and `u64`, with values drawn from + the integer intervals [0, 2^8 - 1], [0, 2^16 - 1], [0, 2^32 - 1] and + [0, 2^64 - 1] respectively. + +* The signed two's complement word types `i8`, `i16`, `i32` and `i64`, with + values drawn from the integer intervals [-(2^(7)), 2^7 - 1], + [-(2^(15)), 2^15 - 1], [-(2^(31)), 2^31 - 1], [-(2^(63)), 2^63 - 1] + respectively. + +* The IEEE 754-2008 `binary32` and `binary64` floating-point types: `f32` and + `f64`, respectively. + +#### Machine-dependent integer types + +The `uint` type is an unsigned integer type with the same number of bits as the +platform's pointer type. It can represent every memory address in the process. + +The `int` type is a signed integer type with the same number of bits as the +platform's pointer type. The theoretical upper bound on object and array size +is the maximum `int` value. This ensures that `int` can be used to calculate +differences between pointers into an object or array and can address every byte +within an object along with one byte past the end. + +### Textual types + +The types `char` and `str` hold textual data. + +A value of type `char` is a [Unicode scalar value]( +http://www.unicode.org/glossary/#unicode_scalar_value) (ie. a code point that +is not a surrogate), represented as a 32-bit unsigned word in the 0x0000 to +0xD7FF or 0xE000 to 0x10FFFF range. A `[char]` array is effectively an UCS-4 / +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 codepoints. 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`. + +### Tuple types + +A tuple *type* is a heterogeneous product of other types, called the *elements* +of the tuple. It has no nominal name and is instead structurally typed. + +Tuple types and values are denoted by listing the types or values of their +elements, respectively, in a parenthesized, comma-separated list. + +Because tuple elements don't have a name, they can only be accessed by +pattern-matching. + +The members of a tuple are laid out in memory contiguously, in order specified +by the tuple type. + +An example of a tuple type and its use: + +``` +type Pair<'a> = (int, &'a str); +let p: Pair<'static> = (10, "hello"); +let (a, b) = p; +assert!(b != "world"); +``` + +### Array, and Slice types + +Rust has two different types for a list of items: + +* `[T ..N]`, an 'array' +* `&[T]`, a 'slice'. + +An array has a fixed size, and can be allocated on either the stack or the +heap. + +A slice is a 'view' into an array. It doesn't own the data it points +to, it borrows it. + +An example of each kind: + +```{rust} +let vec: Vec = vec![1, 2, 3]; +let arr: [int, ..3] = [1, 2, 3]; +let s: &[int] = vec.as_slice(); +``` + +As you can see, the `vec!` macro allows you to create a `Vec` easily. The +`vec!` macro is also part of the standard library, rather than the language. + +All in-bounds elements of arrays, and slices are always initialized, and access +to an array or slice is always bounds-checked. + +### Structure types + +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, + the *record* types of the ML family, + or the *structure* types of the Lisp family. + +New instances of a `struct` can be constructed with a [struct +expression](#structure-expressions). + +The memory layout of a `struct` is undefined by default to allow for compiler +optimizations like field reordering, but it can be fixed with the +`#[repr(...)]` attribute. In either case, fields may be given in any order in +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 +structure outside a module. + +A _tuple struct_ type is just like a structure type, except that the fields are +anonymous. + +A _unit-like struct_ type is like a structure type, except that it has no +fields. The one value constructed by the associated [structure +expression](#structure-expressions) is the only value that inhabits such a +type. + +### Enumerated types + +An *enumerated type* is a nominal, heterogeneous disjoint union type, denoted +by the name of an [`enum` item](#enumerations). [^enumtype] + +[^enumtype]: The `enum` type is analogous to a `data` constructor declaration in + ML, or a *pick ADT* in Limbo. + +An [`enum` item](#enumerations) declares both the type and a number of *variant +constructors*, each of which is independently named and takes an optional tuple +of arguments. + +New instances of an `enum` can be constructed by calling one of the variant +constructors, in a [call expression](#call-expressions). + +Any `enum` value consumes as much memory as the largest variant constructor for +its corresponding `enum` type. + +Enum types cannot be denoted *structurally* as types, but must be denoted by +named reference to an [`enum` item](#enumerations). + +### Recursive types + +Nominal types — [enumerations](#enumerated-types) and +[structures](#structure-types) — may be recursive. That is, each `enum` +constructor or `struct` field may refer, directly or indirectly, to the +enclosing `enum` or `struct` type itself. Such recursion has restrictions: + +* Recursive types must include a nominal type in the recursion + (not mere [type definitions](#type-definitions), + or other structural types such as [arrays](#array,-and-slice-types) or [tuples](#tuple-types)). +* A recursive `enum` item must have at least one non-recursive constructor + (in order to give the recursion a basis case). +* The size of a recursive type must be finite; + in other words the recursive fields of the type must be [pointer types](#pointer-types). +* Recursive type definitions can cross module boundaries, but not module *visibility* boundaries, + or crate boundaries (in order to simplify the module system and type checker). + +An example of a *recursive* type and its use: + +``` +enum List { + Nil, + Cons(T, Box>) +} + +let a: List = List::Cons(7, box List::Cons(13, box List::Nil)); +``` + +### Pointer types + +All pointers in Rust are explicit first-class values. They can be copied, +stored into data structures, and returned from functions. There are two +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. + 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. + +* 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. + 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). + Raw pointers are generally discouraged in Rust code; + they exist to support interoperability with foreign code, + and writing performance-critical or low-level functions. + +The standard library contains additional 'smart pointer' types beyond references +and raw pointers. + +### Function types + +The function type constructor `fn` forms new function types. A function type +consists of a possibly-empty set of function-type modifiers (such as `unsafe` +or `extern`), a sequence of input types and an output type. + +An example of a `fn` type: + +``` +fn add(x: int, y: int) -> int { + return x + y; +} + +let mut x = add(5,7); + +type Binop<'a> = |int,int|: 'a -> int; +let bo: Binop = add; +x = bo(5,7); +``` + +### Closure types + +```{.ebnf .notation} +closure_type := [ 'unsafe' ] [ '<' lifetime-list '>' ] '|' arg-list '|' + [ ':' bound-list ] [ '->' type ] +procedure_type := 'proc' [ '<' 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 `||`. +Similarly, a procedure mapping `A` to `B` is `proc(A) -> B` and a no-argument +and no-return value closure has type `proc()`. + +An example of creating and calling a closure: + +```rust +let captured_var = 10i; + +let closure_no_args = || println!("captured_var={}", captured_var); + +let closure_args = |arg: int| -> int { + println!("captured_var={}, arg={}", captured_var, arg); + arg // Note lack of semicolon after 'arg' +}; + +fn call_closure(c1: ||, c2: |int| -> int) { + c1(); + c2(2); +} + +call_closure(closure_no_args, closure_args); + +``` + +Unlike closures, procedures may only be invoked once, but own their +environment, and are allowed to move out of their environment. Procedures are +allocated on the heap (unlike closures). An example of creating and calling a +procedure: + +```rust +let string = "Hello".to_string(); + +// Creates a new procedure, passing it to the `spawn` function. +spawn(proc() { + println!("{} world!", string); +}); + +// the variable `string` has been moved into the previous procedure, so it is +// no longer usable. + + +// Create an invoke a procedure. Note that the procedure is *moved* when +// invoked, so it cannot be invoked again. +let f = proc(n: int) { n + 22 }; +println!("answer: {}", f(20)); + +``` + +### Object types + +Every trait item (see [traits](#traits)) defines a type with the same name as +the trait. This type is called the _object type_ of the trait. Object types +permit "late binding" of methods, dispatched using _virtual method tables_ +("vtables"). Whereas most calls to trait methods are "early bound" (statically +resolved) to specific implementations at compile time, a call to a method on an +object type is only resolved to a vtable entry at compile time. The actual +implementation for each vtable entry can vary on an object-by-object basis. + +Given a pointer-typed expression `E` of type `&T` or `Box`, where `T` +implements trait `R`, casting `E` to the corresponding pointer type `&R` or +`Box` results in a value of the _object type_ `R`. This result is +represented as a pair of pointers: the vtable pointer for the `T` +implementation of `R`, and the pointer value of `E`. + +An example of an object type: + +``` +trait Printable { + fn stringify(&self) -> String; +} + +impl Printable for int { + fn stringify(&self) -> String { self.to_string() } +} + +fn print(a: Box) { + println!("{}", a.stringify()); +} + +fn main() { + print(box 10i as Box); +} +``` + +In this example, the trait `Printable` occurs as an object type in both the +type signature of `print`, and the cast expression in `main`. + +### Type parameters + +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 { + if xs.len() == 0 { + return vec![]; + } + let first: B = f(xs[0].clone()); + let mut rest: Vec = map(f, xs.slice(1, xs.len())); + rest.insert(0, first); + return 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`. + +### Self types + +The special type `self` has a meaning within methods inside an impl item. It +refers to the type of the implicit `self` argument. For example, in: + +``` +trait Printable { + fn make_string(&self) -> String; +} + +impl Printable for String { + fn make_string(&self) -> String { + (*self).clone() + } +} +``` + +`self` refers to the value of type `String` that is the receiver for a call to +the method `make_string`. + +## Type kinds + +Types in Rust are categorized into kinds, based on various properties of the +components of the type. The kinds are: + +* `Send` + : Types of this kind can be safely sent between tasks. + This kind includes scalars, boxes, procs, and + structural types containing only other owned types. + All `Send` types are `'static`. +* `Copy` + : Types of this kind consist of "Plain Old Data" + which can be copied by simply moving bits. + All values of this kind can be implicitly copied. + This kind includes scalars and immutable references, + as well as structural types containing other `Copy` types. +* `'static` + : Types of this kind do not contain any references (except for + references with the `static` lifetime, which are allowed). + This can be a useful guarantee for code + that breaks borrowing assumptions + using [`unsafe` operations](#unsafe-functions). +* `Drop` + : This is not strictly a kind, + but its presence interacts with kinds: + the `Drop` trait provides a single method `drop` + that takes no parameters, + and is run when values of the type are dropped. + Such a method is called a "destructor", + and are always executed in "top-down" order: + a value is completely destroyed + before any of the values it owns run their destructors. + Only `Send` types can implement `Drop`. + +* _Default_ + : Types with destructors, closure environments, + and various other _non-first-class_ types, + are not copyable at all. + Such types can usually only be accessed through pointers, + or in some cases, moved between mutable locations. + +Kinds can be supplied as _bounds_ on type parameters, like traits, in which +case the parameter is constrained to types satisfying that kind. + +By default, type parameters do not carry any assumed kind-bounds at all. When +instantiating a type parameter, the kind bounds on the parameter are checked to +be the same or narrower than the kind of the type that it is instantiated with. + +Sending operations are not part of the Rust language, but are implemented in +the library. Generic functions that send values bound the kind of these values +to sendable. + +# Memory and concurrency models + +Rust has a memory model centered around concurrently-executing _tasks_. Thus +its memory model and its concurrency model are best discussed simultaneously, +as parts of each only make sense when considered from the perspective of the +other. + +When reading about the memory model, keep in mind that it is partitioned in +order to support tasks; and when reading about tasks, keep in mind that their +isolation and communication mechanisms are only possible due to the ownership +and lifetime semantics of the memory model. + +## Memory model + +A Rust program's memory consists of a static set of *items*, a set of +[tasks](#tasks) each with its own *stack*, and a *heap*. Immutable portions of +the heap may be shared between tasks, mutable portions may not. + +Allocations in the stack consist of *slots*, and allocations in the heap +consist of *boxes*. + +### Memory allocation and lifetime + +The _items_ of a program are those functions, modules and types that have their +value calculated at compile-time and stored uniquely in the memory image of the +rust process. Items are neither dynamically allocated nor freed. + +A task's _stack_ consists of activation frames automatically allocated on entry +to each function as the task executes. A stack allocation is reclaimed when +control leaves the frame containing it. + +The _heap_ is a general term that describes boxes. The lifetime of an +allocation in the heap depends on the lifetime of the box values pointing to +it. Since box values may themselves be passed in and out of frames, or stored +in the heap, heap allocations may outlive the frame they are allocated within. + +### Memory ownership + +A task owns all memory it can *safely* reach through local variables, as well +as boxes and references. + +When a task sends a value that has the `Send` trait to another task, it loses +ownership of the value sent and can no longer refer to it. This is statically +guaranteed by the combined use of "move semantics", and the compiler-checked +_meaning_ of the `Send` trait: it is only instantiated for (transitively) +sendable kinds of data constructor and pointers, never including references. + +When a stack frame is exited, its local allocations are all released, and its +references to boxes are dropped. + +When a task finishes, its stack is necessarily empty and it therefore has no +references to any boxes; the remainder of its heap is immediately freed. + +### Memory slots + +A task's stack contains slots. + +A _slot_ is a component of a stack frame, either a function parameter, a +[temporary](#lvalues,-rvalues-and-temporaries), or a local variable. + +A _local variable_ (or *stack-local* allocation) holds a value directly, +allocated within the stack's memory. The value is a part of the stack frame. + +Local variables are immutable unless declared otherwise like: `let mut x = ...`. + +Function parameters are immutable unless declared with `mut`. The `mut` keyword +applies only to the following parameter (so `|mut x, y|` and `fn f(mut x: +Box, y: Box)` declare one mutable variable `x` and one immutable +variable `y`). + +Methods that take either `self` or `Box` can optionally place them in a +mutable slot by prefixing them with `mut` (similar to regular arguments): + +``` +trait Changer { + fn change(mut self) -> Self; + fn modify(mut self: Box) -> Box; +} +``` + +Local variables are not initialized when allocated; the entire frame worth of +local variables are allocated at once, on frame-entry, in an uninitialized +state. Subsequent statements within a function may or may not initialize the +local variables. Local variables can be used only after they have been +initialized; this is enforced by the compiler. + +### Boxes + +A _box_ is a reference to a heap allocation holding another value, which is +constructed by the prefix operator `box`. When the standard library is in use, +the type of a box is `std::owned::Box`. + +An example of a box type and value: + +``` +let x: Box = box 10; +``` + +Box values exist in 1:1 correspondence with their heap allocation, copying a +box value makes a shallow copy of the pointer. Rust will consider a shallow +copy of a box to move ownership of the value. After a value has been moved, +the source location cannot be used unless it is reinitialized. + +``` +let x: Box = box 10; +let y = x; +// attempting to use `x` will result in an error here +``` + +## Tasks + +An executing Rust program consists of a tree of tasks. A Rust _task_ consists +of an entry function, a stack, a set of outgoing communication channels and +incoming communication ports, and ownership of some portion of the heap of a +single operating-system process. + +### Communication between tasks + +Rust tasks are isolated and generally unable to interfere with one another's +memory directly, except through [`unsafe` code](#unsafe-functions). All +contact between tasks is mediated by safe forms of ownership transfer, and data +races on memory are prohibited by the type system. + +When you wish to send data between tasks, the values are restricted to the +[`Send` type-kind](#type-kinds). Restricting communication interfaces to this +kind ensures that no references move between tasks. Thus access to an entire +data structure can be mediated through its owning "root" value; no further +locking or copying is required to avoid data races within the substructure of +such a value. + +### Task lifecycle + +The _lifecycle_ of a task consists of a finite set of states and events that +cause transitions between the states. The lifecycle states of a task are: + +* running +* blocked +* panicked +* dead + +A task begins its lifecycle — once it has been spawned — in the +*running* state. In this state it executes the statements of its entry +function, and any functions called by the entry function. + +A task may transition from the *running* state to the *blocked* state any time +it makes a blocking communication call. When the call can be completed — +when a message arrives at a sender, or a buffer opens to receive a message +— then the blocked task will unblock and transition back to *running*. + +A task may transition to the *panicked* state at any time, due being killed by +some external event or internally, from the evaluation of a `panic!()` macro. +Once *panicking*, a task unwinds its stack and transitions to the *dead* state. +Unwinding the stack of a task is done by the task itself, on its own control +stack. If a value with a destructor is freed during unwinding, the code for the +destructor is run, also on the task's control stack. Running the destructor +code causes a temporary transition to a *running* state, and allows the +destructor code to cause any subsequent state transitions. The original task +of unwinding and panicking thereby may suspend temporarily, and may involve +(recursive) unwinding of the stack of a failed destructor. Nonetheless, the +outermost unwinding activity will continue until the stack is unwound and the +task transitions to the *dead* state. There is no way to "recover" from task +panics. Once a task has temporarily suspended its unwinding in the *panicking* +state, a panic occurring from within this destructor results in *hard* panic. +A hard panic currently results in the process aborting. + +A task in the *dead* state cannot transition to other states; it exists only to +have its termination status inspected by other tasks, and/or to await +reclamation when the last reference to it drops. + +# Runtime services, linkage and debugging + +The Rust _runtime_ is a relatively compact collection of Rust code that +provides fundamental services and datatypes to all Rust tasks at run-time. It +is smaller and simpler than many modern language runtimes. It is tightly +integrated into the language's execution model of memory, tasks, communication +and logging. + +### Memory allocation + +The runtime memory-management system is based on a _service-provider +interface_, through which the runtime requests blocks of memory from its +environment and releases them back to its environment when they are no longer +needed. The default implementation of the service-provider interface consists +of the C runtime functions `malloc` and `free`. + +The runtime memory-management system, in turn, supplies Rust tasks with +facilities for allocating releasing stacks, as well as allocating and freeing +heap data. + +### Built in types + +The runtime provides C and Rust code to assist with various built-in types, +such as arrays, strings, and the low level communication system (ports, +channels, tasks). + +Support for other built-in types such as simple types, tuples and enums is +open-coded by the Rust compiler. + +### Task scheduling and communication + +The runtime provides code to manage inter-task communication. This includes +the system of task-lifecycle state transitions depending on the contents of +queues, as well as code to copy values between queues and their recipients and +to serialize values for transmission over operating-system inter-process +communication facilities. + +### Linkage + +The Rust compiler supports various methods to link crates together both +statically and dynamically. This section will explore the various methods to +link Rust crates together, and more information about native libraries can be +found in the [ffi guide][ffi]. + +In one session of compilation, the compiler can generate multiple artifacts +through the usage of either command line flags or the `crate_type` attribute. +If one or more command line flag is specified, all `crate_type` attributes will +be ignored in favor of only building the artifacts specified by command line. + +* `--crate-type=bin`, `#[crate_type = "bin"]` - A runnable executable will be + produced. This requires that there is a `main` function in the crate which + will be run when the program begins executing. This will link in all Rust and + native dependencies, producing a distributable binary. + +* `--crate-type=lib`, `#[crate_type = "lib"]` - A Rust library will be produced. + This is an ambiguous concept as to what exactly is produced because a library + can manifest itself in several forms. The purpose of this generic `lib` option + is to generate the "compiler recommended" style of library. The output library + will always be usable by rustc, but the actual type of library may change from + time-to-time. The remaining output types are all different flavors of + libraries, and the `lib` type can be seen as an alias for one of them (but the + actual one is compiler-defined). + +* `--crate-type=dylib`, `#[crate_type = "dylib"]` - A dynamic Rust library will + be produced. This is different from the `lib` output type in that this forces + dynamic library generation. The resulting dynamic library can be used as a + dependency for other libraries and/or executables. This output type will + create `*.so` files on linux, `*.dylib` files on osx, and `*.dll` files on + windows. + +* `--crate-type=staticlib`, `#[crate_type = "staticlib"]` - A static system + library will be produced. This is different from other library outputs in that + the Rust compiler will never attempt to link to `staticlib` outputs. The + purpose of this output type is to create a static library containing all of + the local crate's code along with all upstream dependencies. The static + library is actually a `*.a` archive on linux and osx and a `*.lib` file on + windows. This format is recommended for use in situations such as linking + Rust code into an existing non-Rust application because it will not have + dynamic dependencies on other Rust code. + +* `--crate-type=rlib`, `#[crate_type = "rlib"]` - A "Rust library" file will be + produced. This is used as an intermediate artifact and can be thought of as a + "static Rust library". These `rlib` files, unlike `staticlib` files, are + interpreted by the Rust compiler in future linkage. This essentially means + that `rustc` will look for metadata in `rlib` files like it looks for metadata + in dynamic libraries. This form of output is used to produce statically linked + executables as well as `staticlib` outputs. + +Note that these outputs are stackable in the sense that if multiple are +specified, then the compiler will produce each form of output at once without +having to recompile. However, this only applies for outputs specified by the +same method. If only `crate_type` attributes are specified, then they will all +be built, but if one or more `--crate-type` command line flag is specified, +then only those outputs will be built. + +With all these different kinds of outputs, if crate A depends on crate B, then +the compiler could find B in various different forms throughout the system. The +only forms looked for by the compiler, however, are the `rlib` format and the +dynamic library format. With these two options for a dependent library, the +compiler must at some point make a choice between these two formats. With this +in mind, the compiler follows these rules when determining what format of +dependencies will be used: + +1. If a static library is being produced, all upstream dependencies are + required to be available in `rlib` formats. This requirement stems from the + reason that a dynamic library cannot be converted into a static format. + + Note that it is impossible to link in native dynamic dependencies to a static + library, and in this case warnings will be printed about all unlinked native + dynamic dependencies. + +2. If an `rlib` file is being produced, then there are no restrictions on what + format the upstream dependencies are available in. It is simply required that + all upstream dependencies be available for reading metadata from. + + The reason for this is that `rlib` files do not contain any of their upstream + dependencies. It wouldn't be very efficient for all `rlib` files to contain a + copy of `libstd.rlib`! + +3. If an executable is being produced and the `-C prefer-dynamic` flag is not + specified, then dependencies are first attempted to be found in the `rlib` + format. If some dependencies are not available in an rlib format, then + dynamic linking is attempted (see below). + +4. If a dynamic library or an executable that is being dynamically linked is + being produced, then the compiler will attempt to reconcile the available + dependencies in either the rlib or dylib format to create a final product. + + A major goal of the compiler is to ensure that a library never appears more + than once in any artifact. For example, if dynamic libraries B and C were + each statically linked to library A, then a crate could not link to B and C + together because there would be two copies of A. The compiler allows mixing + the rlib and dylib formats, but this restriction must be satisfied. + + The compiler currently implements no method of hinting what format a library + should be linked with. When dynamically linking, the compiler will attempt to + maximize dynamic dependencies while still allowing some dependencies to be + linked in via an rlib. + + For most situations, having all libraries available as a dylib is recommended + if dynamically linking. For other situations, the compiler will emit a + warning if it is unable to determine which formats to link each library with. + +In general, `--crate-type=bin` or `--crate-type=lib` should be sufficient for +all compilation needs, and the other options are just available if more +fine-grained control is desired over the output format of a Rust crate. + +# Appendix: Rationales and design tradeoffs + +*TODO*. + +# Appendix: Influences and further references + +## Influences + +> The essential problem that must be solved in making a fault-tolerant +> software system is therefore that of fault-isolation. Different programmers +> will write different modules, some modules will be correct, others will have +> errors. We do not want the errors in one module to adversely affect the +> behaviour of a module which does not have any errors. +> +> — Joe Armstrong + +> In our approach, all data is private to some process, and processes can +> only communicate through communications channels. *Security*, as used +> in this paper, is the property which guarantees that processes in a system +> cannot affect each other except by explicit communication. +> +> When security is absent, nothing which can be proven about a single module +> in isolation can be guaranteed to hold when that module is embedded in a +> system [...] +> +> — Robert Strom and Shaula Yemini + +> Concurrent and applicative programming complement each other. The +> ability to send messages on channels provides I/O without side effects, +> while the avoidance of shared data helps keep concurrent processes from +> colliding. +> +> — Rob Pike + +Rust is not a particularly original language. It may however appear unusual by +contemporary standards, as its design elements are drawn from a number of +"historical" languages that have, with a few exceptions, fallen out of favour. +Five prominent lineages contribute the most, though their influences have come +and gone during the course of Rust's development: + +* The NIL (1981) and Hermes (1990) family. These languages were developed by + Robert Strom, Shaula Yemini, David Bacon and others in their group at IBM + Watson Research Center (Yorktown Heights, NY, USA). + +* The Erlang (1987) language, developed by Joe Armstrong, Robert Virding, Claes + Wikström, Mike Williams and others in their group at the Ericsson Computer + Science Laboratory (Älvsjö, Stockholm, Sweden) . + +* The Sather (1990) language, developed by Stephen Omohundro, Chu-Cheow Lim, + Heinz Schmidt and others in their group at The International Computer + Science Institute of the University of California, Berkeley (Berkeley, CA, + USA). + +* The Newsqueak (1988), Alef (1995), and Limbo (1996) family. These + languages were developed by Rob Pike, Phil Winterbottom, Sean Dorward and + others in their group at Bell Labs Computing Sciences Research Center + (Murray Hill, NJ, USA). + +* The Napier (1985) and Napier88 (1988) family. These languages were + developed by Malcolm Atkinson, Ron Morrison and others in their group at + the University of St. Andrews (St. Andrews, Fife, UK). + +Additional specific influences can be seen from the following languages: + +* The structural algebraic types and compilation manager of SML. +* The attribute and assembly systems of C#. +* The references and deterministic destructor system of C++. +* The memory region systems of the ML Kit and Cyclone. +* The typeclass system of Haskell. +* The lexical identifier rule of Python. +* The block syntax of Ruby. + +[ffi]: guide-ffi.html +[plugin]: guide-plugin.html From ab24ffe21a742287ee12d5992d4c90e83abb374d Mon Sep 17 00:00:00 2001 From: Ignacio Corderi Date: Wed, 26 Nov 2014 16:52:36 -0800 Subject: [PATCH 002/211] Copied all the grammar productions from reference.md to grammar.md --- src/doc/grammar.md | 773 ++------------------------------------------- 1 file changed, 18 insertions(+), 755 deletions(-) diff --git a/src/doc/grammar.md b/src/doc/grammar.md index 6b700210201d4..c2cbb3ae3fb2f 100644 --- a/src/doc/grammar.md +++ b/src/doc/grammar.md @@ -683,254 +683,53 @@ return_expr : "return" expr ? ; # Type system -## Types - -Every slot, item and value in a Rust program has a type. The _type_ of a -*value* defines the interpretation of the memory holding it. +**FIXME:** is this entire chapter relevant here? Or should it all have been covered by some production already? -Built-in types and type-constructors are tightly integrated into the language, -in nontrivial ways that are not possible to emulate in user-defined types. -User-defined types have limited capabilities. +## Types ### Primitive types -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 slots; 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.] +**FIXME:** grammar? #### Machine types -The machine types are the following: - -* The unsigned word types `u8`, `u16`, `u32` and `u64`, with values drawn from - the integer intervals [0, 2^8 - 1], [0, 2^16 - 1], [0, 2^32 - 1] and - [0, 2^64 - 1] respectively. - -* The signed two's complement word types `i8`, `i16`, `i32` and `i64`, with - values drawn from the integer intervals [-(2^(7)), 2^7 - 1], - [-(2^(15)), 2^15 - 1], [-(2^(31)), 2^31 - 1], [-(2^(63)), 2^63 - 1] - respectively. - -* The IEEE 754-2008 `binary32` and `binary64` floating-point types: `f32` and - `f64`, respectively. +**FIXME:** grammar? #### Machine-dependent integer types -The `uint` type is an unsigned integer type with the same number of bits as the -platform's pointer type. It can represent every memory address in the process. - -The `int` type is a signed integer type with the same number of bits as the -platform's pointer type. The theoretical upper bound on object and array size -is the maximum `int` value. This ensures that `int` can be used to calculate -differences between pointers into an object or array and can address every byte -within an object along with one byte past the end. +**FIXME:** grammar? ### Textual types -The types `char` and `str` hold textual data. - -A value of type `char` is a [Unicode scalar value]( -http://www.unicode.org/glossary/#unicode_scalar_value) (ie. a code point that -is not a surrogate), represented as a 32-bit unsigned word in the 0x0000 to -0xD7FF or 0xE000 to 0x10FFFF range. A `[char]` array is effectively an UCS-4 / -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 codepoints. 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`. +**FIXME:** grammar? ### Tuple types -A tuple *type* is a heterogeneous product of other types, called the *elements* -of the tuple. It has no nominal name and is instead structurally typed. - -Tuple types and values are denoted by listing the types or values of their -elements, respectively, in a parenthesized, comma-separated list. - -Because tuple elements don't have a name, they can only be accessed by -pattern-matching. - -The members of a tuple are laid out in memory contiguously, in order specified -by the tuple type. - -An example of a tuple type and its use: - -``` -type Pair<'a> = (int, &'a str); -let p: Pair<'static> = (10, "hello"); -let (a, b) = p; -assert!(b != "world"); -``` +**FIXME:** grammar? ### Array, and Slice types -Rust has two different types for a list of items: - -* `[T ..N]`, an 'array' -* `&[T]`, a 'slice'. - -An array has a fixed size, and can be allocated on either the stack or the -heap. - -A slice is a 'view' into an array. It doesn't own the data it points -to, it borrows it. - -An example of each kind: - -```{rust} -let vec: Vec = vec![1, 2, 3]; -let arr: [int, ..3] = [1, 2, 3]; -let s: &[int] = vec.as_slice(); -``` - -As you can see, the `vec!` macro allows you to create a `Vec` easily. The -`vec!` macro is also part of the standard library, rather than the language. - -All in-bounds elements of arrays, and slices are always initialized, and access -to an array or slice is always bounds-checked. +**FIXME:** grammar? ### Structure types -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, - the *record* types of the ML family, - or the *structure* types of the Lisp family. - -New instances of a `struct` can be constructed with a [struct -expression](#structure-expressions). - -The memory layout of a `struct` is undefined by default to allow for compiler -optimizations like field reordering, but it can be fixed with the -`#[repr(...)]` attribute. In either case, fields may be given in any order in -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 -structure outside a module. - -A _tuple struct_ type is just like a structure type, except that the fields are -anonymous. - -A _unit-like struct_ type is like a structure type, except that it has no -fields. The one value constructed by the associated [structure -expression](#structure-expressions) is the only value that inhabits such a -type. +**FIXME:** grammar? ### Enumerated types -An *enumerated type* is a nominal, heterogeneous disjoint union type, denoted -by the name of an [`enum` item](#enumerations). [^enumtype] - -[^enumtype]: The `enum` type is analogous to a `data` constructor declaration in - ML, or a *pick ADT* in Limbo. - -An [`enum` item](#enumerations) declares both the type and a number of *variant -constructors*, each of which is independently named and takes an optional tuple -of arguments. - -New instances of an `enum` can be constructed by calling one of the variant -constructors, in a [call expression](#call-expressions). - -Any `enum` value consumes as much memory as the largest variant constructor for -its corresponding `enum` type. - -Enum types cannot be denoted *structurally* as types, but must be denoted by -named reference to an [`enum` item](#enumerations). - -### Recursive types - -Nominal types — [enumerations](#enumerated-types) and -[structures](#structure-types) — may be recursive. That is, each `enum` -constructor or `struct` field may refer, directly or indirectly, to the -enclosing `enum` or `struct` type itself. Such recursion has restrictions: - -* Recursive types must include a nominal type in the recursion - (not mere [type definitions](#type-definitions), - or other structural types such as [arrays](#array,-and-slice-types) or [tuples](#tuple-types)). -* A recursive `enum` item must have at least one non-recursive constructor - (in order to give the recursion a basis case). -* The size of a recursive type must be finite; - in other words the recursive fields of the type must be [pointer types](#pointer-types). -* Recursive type definitions can cross module boundaries, but not module *visibility* boundaries, - or crate boundaries (in order to simplify the module system and type checker). - -An example of a *recursive* type and its use: - -``` -enum List { - Nil, - Cons(T, Box>) -} - -let a: List = List::Cons(7, box List::Cons(13, box List::Nil)); -``` +**FIXME:** grammar? ### Pointer types -All pointers in Rust are explicit first-class values. They can be copied, -stored into data structures, and returned from functions. There are two -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. - 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. - -* 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. - 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). - Raw pointers are generally discouraged in Rust code; - they exist to support interoperability with foreign code, - and writing performance-critical or low-level functions. - -The standard library contains additional 'smart pointer' types beyond references -and raw pointers. +**FIXME:** grammar? ### Function types -The function type constructor `fn` forms new function types. A function type -consists of a possibly-empty set of function-type modifiers (such as `unsafe` -or `extern`), a sequence of input types and an output type. - -An example of a `fn` type: - -``` -fn add(x: int, y: int) -> int { - return x + y; -} - -let mut x = add(5,7); - -type Binop<'a> = |int,int|: 'a -> int; -let bo: Binop = add; -x = bo(5,7); -``` +**FIXME:** grammar? ### Closure types -```{.ebnf .notation} +```antlr closure_type := [ 'unsafe' ] [ '<' lifetime-list '>' ] '|' arg-list '|' [ ':' bound-list ] [ '->' type ] procedure_type := 'proc' [ '<' lifetime-list '>' ] '(' arg-list ')' @@ -941,574 +740,38 @@ 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 `||`. -Similarly, a procedure mapping `A` to `B` is `proc(A) -> B` and a no-argument -and no-return value closure has type `proc()`. - -An example of creating and calling a closure: - -```rust -let captured_var = 10i; - -let closure_no_args = || println!("captured_var={}", captured_var); - -let closure_args = |arg: int| -> int { - println!("captured_var={}, arg={}", captured_var, arg); - arg // Note lack of semicolon after 'arg' -}; - -fn call_closure(c1: ||, c2: |int| -> int) { - c1(); - c2(2); -} - -call_closure(closure_no_args, closure_args); - -``` - -Unlike closures, procedures may only be invoked once, but own their -environment, and are allowed to move out of their environment. Procedures are -allocated on the heap (unlike closures). An example of creating and calling a -procedure: - -```rust -let string = "Hello".to_string(); - -// Creates a new procedure, passing it to the `spawn` function. -spawn(proc() { - println!("{} world!", string); -}); - -// the variable `string` has been moved into the previous procedure, so it is -// no longer usable. - - -// Create an invoke a procedure. Note that the procedure is *moved* when -// invoked, so it cannot be invoked again. -let f = proc(n: int) { n + 22 }; -println!("answer: {}", f(20)); - -``` - ### Object types -Every trait item (see [traits](#traits)) defines a type with the same name as -the trait. This type is called the _object type_ of the trait. Object types -permit "late binding" of methods, dispatched using _virtual method tables_ -("vtables"). Whereas most calls to trait methods are "early bound" (statically -resolved) to specific implementations at compile time, a call to a method on an -object type is only resolved to a vtable entry at compile time. The actual -implementation for each vtable entry can vary on an object-by-object basis. - -Given a pointer-typed expression `E` of type `&T` or `Box`, where `T` -implements trait `R`, casting `E` to the corresponding pointer type `&R` or -`Box` results in a value of the _object type_ `R`. This result is -represented as a pair of pointers: the vtable pointer for the `T` -implementation of `R`, and the pointer value of `E`. - -An example of an object type: - -``` -trait Printable { - fn stringify(&self) -> String; -} - -impl Printable for int { - fn stringify(&self) -> String { self.to_string() } -} - -fn print(a: Box) { - println!("{}", a.stringify()); -} - -fn main() { - print(box 10i as Box); -} -``` - -In this example, the trait `Printable` occurs as an object type in both the -type signature of `print`, and the cast expression in `main`. +**FIXME:** grammar? ### Type parameters -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 { - if xs.len() == 0 { - return vec![]; - } - let first: B = f(xs[0].clone()); - let mut rest: Vec = map(f, xs.slice(1, xs.len())); - rest.insert(0, first); - return 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`. +**FIXME:** grammar? ### Self types -The special type `self` has a meaning within methods inside an impl item. It -refers to the type of the implicit `self` argument. For example, in: - -``` -trait Printable { - fn make_string(&self) -> String; -} - -impl Printable for String { - fn make_string(&self) -> String { - (*self).clone() - } -} -``` - -`self` refers to the value of type `String` that is the receiver for a call to -the method `make_string`. +**FIXME:** grammar? ## Type kinds -Types in Rust are categorized into kinds, based on various properties of the -components of the type. The kinds are: - -* `Send` - : Types of this kind can be safely sent between tasks. - This kind includes scalars, boxes, procs, and - structural types containing only other owned types. - All `Send` types are `'static`. -* `Copy` - : Types of this kind consist of "Plain Old Data" - which can be copied by simply moving bits. - All values of this kind can be implicitly copied. - This kind includes scalars and immutable references, - as well as structural types containing other `Copy` types. -* `'static` - : Types of this kind do not contain any references (except for - references with the `static` lifetime, which are allowed). - This can be a useful guarantee for code - that breaks borrowing assumptions - using [`unsafe` operations](#unsafe-functions). -* `Drop` - : This is not strictly a kind, - but its presence interacts with kinds: - the `Drop` trait provides a single method `drop` - that takes no parameters, - and is run when values of the type are dropped. - Such a method is called a "destructor", - and are always executed in "top-down" order: - a value is completely destroyed - before any of the values it owns run their destructors. - Only `Send` types can implement `Drop`. - -* _Default_ - : Types with destructors, closure environments, - and various other _non-first-class_ types, - are not copyable at all. - Such types can usually only be accessed through pointers, - or in some cases, moved between mutable locations. - -Kinds can be supplied as _bounds_ on type parameters, like traits, in which -case the parameter is constrained to types satisfying that kind. - -By default, type parameters do not carry any assumed kind-bounds at all. When -instantiating a type parameter, the kind bounds on the parameter are checked to -be the same or narrower than the kind of the type that it is instantiated with. - -Sending operations are not part of the Rust language, but are implemented in -the library. Generic functions that send values bound the kind of these values -to sendable. +**FIXME:** this this probably not relevant to the grammar... # Memory and concurrency models -Rust has a memory model centered around concurrently-executing _tasks_. Thus -its memory model and its concurrency model are best discussed simultaneously, -as parts of each only make sense when considered from the perspective of the -other. - -When reading about the memory model, keep in mind that it is partitioned in -order to support tasks; and when reading about tasks, keep in mind that their -isolation and communication mechanisms are only possible due to the ownership -and lifetime semantics of the memory model. +**FIXME:** is this entire chapter relevant here? Or should it all have been covered by some production already? ## Memory model -A Rust program's memory consists of a static set of *items*, a set of -[tasks](#tasks) each with its own *stack*, and a *heap*. Immutable portions of -the heap may be shared between tasks, mutable portions may not. - -Allocations in the stack consist of *slots*, and allocations in the heap -consist of *boxes*. - ### Memory allocation and lifetime -The _items_ of a program are those functions, modules and types that have their -value calculated at compile-time and stored uniquely in the memory image of the -rust process. Items are neither dynamically allocated nor freed. - -A task's _stack_ consists of activation frames automatically allocated on entry -to each function as the task executes. A stack allocation is reclaimed when -control leaves the frame containing it. - -The _heap_ is a general term that describes boxes. The lifetime of an -allocation in the heap depends on the lifetime of the box values pointing to -it. Since box values may themselves be passed in and out of frames, or stored -in the heap, heap allocations may outlive the frame they are allocated within. - ### Memory ownership -A task owns all memory it can *safely* reach through local variables, as well -as boxes and references. - -When a task sends a value that has the `Send` trait to another task, it loses -ownership of the value sent and can no longer refer to it. This is statically -guaranteed by the combined use of "move semantics", and the compiler-checked -_meaning_ of the `Send` trait: it is only instantiated for (transitively) -sendable kinds of data constructor and pointers, never including references. - -When a stack frame is exited, its local allocations are all released, and its -references to boxes are dropped. - -When a task finishes, its stack is necessarily empty and it therefore has no -references to any boxes; the remainder of its heap is immediately freed. - ### Memory slots -A task's stack contains slots. - -A _slot_ is a component of a stack frame, either a function parameter, a -[temporary](#lvalues,-rvalues-and-temporaries), or a local variable. - -A _local variable_ (or *stack-local* allocation) holds a value directly, -allocated within the stack's memory. The value is a part of the stack frame. - -Local variables are immutable unless declared otherwise like: `let mut x = ...`. - -Function parameters are immutable unless declared with `mut`. The `mut` keyword -applies only to the following parameter (so `|mut x, y|` and `fn f(mut x: -Box, y: Box)` declare one mutable variable `x` and one immutable -variable `y`). - -Methods that take either `self` or `Box` can optionally place them in a -mutable slot by prefixing them with `mut` (similar to regular arguments): - -``` -trait Changer { - fn change(mut self) -> Self; - fn modify(mut self: Box) -> Box; -} -``` - -Local variables are not initialized when allocated; the entire frame worth of -local variables are allocated at once, on frame-entry, in an uninitialized -state. Subsequent statements within a function may or may not initialize the -local variables. Local variables can be used only after they have been -initialized; this is enforced by the compiler. - ### Boxes -A _box_ is a reference to a heap allocation holding another value, which is -constructed by the prefix operator `box`. When the standard library is in use, -the type of a box is `std::owned::Box`. - -An example of a box type and value: - -``` -let x: Box = box 10; -``` - -Box values exist in 1:1 correspondence with their heap allocation, copying a -box value makes a shallow copy of the pointer. Rust will consider a shallow -copy of a box to move ownership of the value. After a value has been moved, -the source location cannot be used unless it is reinitialized. - -``` -let x: Box = box 10; -let y = x; -// attempting to use `x` will result in an error here -``` - ## Tasks -An executing Rust program consists of a tree of tasks. A Rust _task_ consists -of an entry function, a stack, a set of outgoing communication channels and -incoming communication ports, and ownership of some portion of the heap of a -single operating-system process. - ### Communication between tasks -Rust tasks are isolated and generally unable to interfere with one another's -memory directly, except through [`unsafe` code](#unsafe-functions). All -contact between tasks is mediated by safe forms of ownership transfer, and data -races on memory are prohibited by the type system. - -When you wish to send data between tasks, the values are restricted to the -[`Send` type-kind](#type-kinds). Restricting communication interfaces to this -kind ensures that no references move between tasks. Thus access to an entire -data structure can be mediated through its owning "root" value; no further -locking or copying is required to avoid data races within the substructure of -such a value. - ### Task lifecycle - -The _lifecycle_ of a task consists of a finite set of states and events that -cause transitions between the states. The lifecycle states of a task are: - -* running -* blocked -* panicked -* dead - -A task begins its lifecycle — once it has been spawned — in the -*running* state. In this state it executes the statements of its entry -function, and any functions called by the entry function. - -A task may transition from the *running* state to the *blocked* state any time -it makes a blocking communication call. When the call can be completed — -when a message arrives at a sender, or a buffer opens to receive a message -— then the blocked task will unblock and transition back to *running*. - -A task may transition to the *panicked* state at any time, due being killed by -some external event or internally, from the evaluation of a `panic!()` macro. -Once *panicking*, a task unwinds its stack and transitions to the *dead* state. -Unwinding the stack of a task is done by the task itself, on its own control -stack. If a value with a destructor is freed during unwinding, the code for the -destructor is run, also on the task's control stack. Running the destructor -code causes a temporary transition to a *running* state, and allows the -destructor code to cause any subsequent state transitions. The original task -of unwinding and panicking thereby may suspend temporarily, and may involve -(recursive) unwinding of the stack of a failed destructor. Nonetheless, the -outermost unwinding activity will continue until the stack is unwound and the -task transitions to the *dead* state. There is no way to "recover" from task -panics. Once a task has temporarily suspended its unwinding in the *panicking* -state, a panic occurring from within this destructor results in *hard* panic. -A hard panic currently results in the process aborting. - -A task in the *dead* state cannot transition to other states; it exists only to -have its termination status inspected by other tasks, and/or to await -reclamation when the last reference to it drops. - -# Runtime services, linkage and debugging - -The Rust _runtime_ is a relatively compact collection of Rust code that -provides fundamental services and datatypes to all Rust tasks at run-time. It -is smaller and simpler than many modern language runtimes. It is tightly -integrated into the language's execution model of memory, tasks, communication -and logging. - -### Memory allocation - -The runtime memory-management system is based on a _service-provider -interface_, through which the runtime requests blocks of memory from its -environment and releases them back to its environment when they are no longer -needed. The default implementation of the service-provider interface consists -of the C runtime functions `malloc` and `free`. - -The runtime memory-management system, in turn, supplies Rust tasks with -facilities for allocating releasing stacks, as well as allocating and freeing -heap data. - -### Built in types - -The runtime provides C and Rust code to assist with various built-in types, -such as arrays, strings, and the low level communication system (ports, -channels, tasks). - -Support for other built-in types such as simple types, tuples and enums is -open-coded by the Rust compiler. - -### Task scheduling and communication - -The runtime provides code to manage inter-task communication. This includes -the system of task-lifecycle state transitions depending on the contents of -queues, as well as code to copy values between queues and their recipients and -to serialize values for transmission over operating-system inter-process -communication facilities. - -### Linkage - -The Rust compiler supports various methods to link crates together both -statically and dynamically. This section will explore the various methods to -link Rust crates together, and more information about native libraries can be -found in the [ffi guide][ffi]. - -In one session of compilation, the compiler can generate multiple artifacts -through the usage of either command line flags or the `crate_type` attribute. -If one or more command line flag is specified, all `crate_type` attributes will -be ignored in favor of only building the artifacts specified by command line. - -* `--crate-type=bin`, `#[crate_type = "bin"]` - A runnable executable will be - produced. This requires that there is a `main` function in the crate which - will be run when the program begins executing. This will link in all Rust and - native dependencies, producing a distributable binary. - -* `--crate-type=lib`, `#[crate_type = "lib"]` - A Rust library will be produced. - This is an ambiguous concept as to what exactly is produced because a library - can manifest itself in several forms. The purpose of this generic `lib` option - is to generate the "compiler recommended" style of library. The output library - will always be usable by rustc, but the actual type of library may change from - time-to-time. The remaining output types are all different flavors of - libraries, and the `lib` type can be seen as an alias for one of them (but the - actual one is compiler-defined). - -* `--crate-type=dylib`, `#[crate_type = "dylib"]` - A dynamic Rust library will - be produced. This is different from the `lib` output type in that this forces - dynamic library generation. The resulting dynamic library can be used as a - dependency for other libraries and/or executables. This output type will - create `*.so` files on linux, `*.dylib` files on osx, and `*.dll` files on - windows. - -* `--crate-type=staticlib`, `#[crate_type = "staticlib"]` - A static system - library will be produced. This is different from other library outputs in that - the Rust compiler will never attempt to link to `staticlib` outputs. The - purpose of this output type is to create a static library containing all of - the local crate's code along with all upstream dependencies. The static - library is actually a `*.a` archive on linux and osx and a `*.lib` file on - windows. This format is recommended for use in situations such as linking - Rust code into an existing non-Rust application because it will not have - dynamic dependencies on other Rust code. - -* `--crate-type=rlib`, `#[crate_type = "rlib"]` - A "Rust library" file will be - produced. This is used as an intermediate artifact and can be thought of as a - "static Rust library". These `rlib` files, unlike `staticlib` files, are - interpreted by the Rust compiler in future linkage. This essentially means - that `rustc` will look for metadata in `rlib` files like it looks for metadata - in dynamic libraries. This form of output is used to produce statically linked - executables as well as `staticlib` outputs. - -Note that these outputs are stackable in the sense that if multiple are -specified, then the compiler will produce each form of output at once without -having to recompile. However, this only applies for outputs specified by the -same method. If only `crate_type` attributes are specified, then they will all -be built, but if one or more `--crate-type` command line flag is specified, -then only those outputs will be built. - -With all these different kinds of outputs, if crate A depends on crate B, then -the compiler could find B in various different forms throughout the system. The -only forms looked for by the compiler, however, are the `rlib` format and the -dynamic library format. With these two options for a dependent library, the -compiler must at some point make a choice between these two formats. With this -in mind, the compiler follows these rules when determining what format of -dependencies will be used: - -1. If a static library is being produced, all upstream dependencies are - required to be available in `rlib` formats. This requirement stems from the - reason that a dynamic library cannot be converted into a static format. - - Note that it is impossible to link in native dynamic dependencies to a static - library, and in this case warnings will be printed about all unlinked native - dynamic dependencies. - -2. If an `rlib` file is being produced, then there are no restrictions on what - format the upstream dependencies are available in. It is simply required that - all upstream dependencies be available for reading metadata from. - - The reason for this is that `rlib` files do not contain any of their upstream - dependencies. It wouldn't be very efficient for all `rlib` files to contain a - copy of `libstd.rlib`! - -3. If an executable is being produced and the `-C prefer-dynamic` flag is not - specified, then dependencies are first attempted to be found in the `rlib` - format. If some dependencies are not available in an rlib format, then - dynamic linking is attempted (see below). - -4. If a dynamic library or an executable that is being dynamically linked is - being produced, then the compiler will attempt to reconcile the available - dependencies in either the rlib or dylib format to create a final product. - - A major goal of the compiler is to ensure that a library never appears more - than once in any artifact. For example, if dynamic libraries B and C were - each statically linked to library A, then a crate could not link to B and C - together because there would be two copies of A. The compiler allows mixing - the rlib and dylib formats, but this restriction must be satisfied. - - The compiler currently implements no method of hinting what format a library - should be linked with. When dynamically linking, the compiler will attempt to - maximize dynamic dependencies while still allowing some dependencies to be - linked in via an rlib. - - For most situations, having all libraries available as a dylib is recommended - if dynamically linking. For other situations, the compiler will emit a - warning if it is unable to determine which formats to link each library with. - -In general, `--crate-type=bin` or `--crate-type=lib` should be sufficient for -all compilation needs, and the other options are just available if more -fine-grained control is desired over the output format of a Rust crate. - -# Appendix: Rationales and design tradeoffs - -*TODO*. - -# Appendix: Influences and further references - -## Influences - -> The essential problem that must be solved in making a fault-tolerant -> software system is therefore that of fault-isolation. Different programmers -> will write different modules, some modules will be correct, others will have -> errors. We do not want the errors in one module to adversely affect the -> behaviour of a module which does not have any errors. -> -> — Joe Armstrong - -> In our approach, all data is private to some process, and processes can -> only communicate through communications channels. *Security*, as used -> in this paper, is the property which guarantees that processes in a system -> cannot affect each other except by explicit communication. -> -> When security is absent, nothing which can be proven about a single module -> in isolation can be guaranteed to hold when that module is embedded in a -> system [...] -> -> — Robert Strom and Shaula Yemini - -> Concurrent and applicative programming complement each other. The -> ability to send messages on channels provides I/O without side effects, -> while the avoidance of shared data helps keep concurrent processes from -> colliding. -> -> — Rob Pike - -Rust is not a particularly original language. It may however appear unusual by -contemporary standards, as its design elements are drawn from a number of -"historical" languages that have, with a few exceptions, fallen out of favour. -Five prominent lineages contribute the most, though their influences have come -and gone during the course of Rust's development: - -* The NIL (1981) and Hermes (1990) family. These languages were developed by - Robert Strom, Shaula Yemini, David Bacon and others in their group at IBM - Watson Research Center (Yorktown Heights, NY, USA). - -* The Erlang (1987) language, developed by Joe Armstrong, Robert Virding, Claes - Wikström, Mike Williams and others in their group at the Ericsson Computer - Science Laboratory (Älvsjö, Stockholm, Sweden) . - -* The Sather (1990) language, developed by Stephen Omohundro, Chu-Cheow Lim, - Heinz Schmidt and others in their group at The International Computer - Science Institute of the University of California, Berkeley (Berkeley, CA, - USA). - -* The Newsqueak (1988), Alef (1995), and Limbo (1996) family. These - languages were developed by Rob Pike, Phil Winterbottom, Sean Dorward and - others in their group at Bell Labs Computing Sciences Research Center - (Murray Hill, NJ, USA). - -* The Napier (1985) and Napier88 (1988) family. These languages were - developed by Malcolm Atkinson, Ron Morrison and others in their group at - the University of St. Andrews (St. Andrews, Fife, UK). - -Additional specific influences can be seen from the following languages: - -* The structural algebraic types and compilation manager of SML. -* The attribute and assembly systems of C#. -* The references and deterministic destructor system of C++. -* The memory region systems of the ML Kit and Cyclone. -* The typeclass system of Haskell. -* The lexical identifier rule of Python. -* The block syntax of Ruby. - -[ffi]: guide-ffi.html -[plugin]: guide-plugin.html From a72bd699cff26175be7156db13758d17bb88813e Mon Sep 17 00:00:00 2001 From: Daniel Raloff Date: Tue, 16 Dec 2014 00:43:57 -0800 Subject: [PATCH 003/211] added optional method chain indentations for emacs major mode --- src/etc/emacs/rust-mode.el | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/etc/emacs/rust-mode.el b/src/etc/emacs/rust-mode.el index 6917e9ee354ee..f66df6c056560 100644 --- a/src/etc/emacs/rust-mode.el +++ b/src/etc/emacs/rust-mode.el @@ -49,6 +49,11 @@ :type 'integer :group 'rust-mode) +(defcustom rust-indent-method-chain nil + "Indent Rust method chains, aligned by the '.' operators" + :type 'boolean + :group 'rust-mode) + (defun rust-paren-level () (nth 0 (syntax-ppss))) (defun rust-in-str-or-cmnt () (nth 8 (syntax-ppss))) (defun rust-rewind-past-str-cmnt () (goto-char (nth 8 (syntax-ppss)))) @@ -72,6 +77,15 @@ (backward-word 1)) (current-column)))) +(defun rust-align-to-method-chain () + (save-excursion + (previous-line) + (end-of-line) + (backward-word 1) + (backward-char) + (when (looking-at "\\..+\(.*\)\n") + (- (current-column) rust-indent-offset)))) + (defun rust-rewind-to-beginning-of-current-level-expr () (let ((current-level (rust-paren-level))) (back-to-indentation) @@ -94,10 +108,13 @@ ;; the inside of it correctly relative to the outside. (if (= 0 level) 0 + (or + (when rust-indent-method-chain + (rust-align-to-method-chain)) (save-excursion (backward-up-list) (rust-rewind-to-beginning-of-current-level-expr) - (+ (current-column) rust-indent-offset))))) + (+ (current-column) rust-indent-offset)))))) (cond ;; A function return type is indented to the corresponding function arguments ((looking-at "->") @@ -109,6 +126,16 @@ ;; A closing brace is 1 level unindended ((looking-at "}") (- baseline rust-indent-offset)) + ;;Line up method chains by their .'s + ((when (and rust-indent-method-chain + (looking-at "\..+\(.*\);?\n")) + (or + (let ((method-indent (rust-align-to-method-chain))) + (when method-indent + (+ method-indent rust-indent-offset))) + (+ baseline rust-indent-offset)))) + + ;; Doc comments in /** style with leading * indent to line up the *s ((and (nth 4 (syntax-ppss)) (looking-at "*")) (+ 1 baseline)) From d0863adf24b316d8ecaca5a1b8866ee5db902313 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 11 Jan 2015 00:14:03 +0100 Subject: [PATCH 004/211] Update Token::can_begin_expr() to make it consistent with the grammar: * add Token::AndAnd (double borrow) * add Token::DotDot (range notation) * remove Token::Pound and Token::At Fixes a syntax error when parsing "fn f() -> RangeTo { return ..1; }". Also, remove "fn_expr_lookahead". It's from the fn~ days and seems to no longer be necessary. --- src/libsyntax/parse/parser.rs | 12 +++--------- src/libsyntax/parse/token.rs | 4 ++-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 33f9e35d8b7af..8d1599e176e87 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2167,6 +2167,7 @@ impl<'a> Parser<'a> { let ex: Expr_; + // Note: when adding new syntax here, don't forget to adjust Token::can_begin_expr(). match self.token { token::OpenDelim(token::Paren) => { self.bump(); @@ -2773,6 +2774,7 @@ impl<'a> Parser<'a> { let lo = self.span.lo; let hi; + // Note: when adding new unary operators, don't forget to adjust Token::can_begin_expr() let ex; match self.token { token::Not => { @@ -5590,13 +5592,6 @@ impl<'a> Parser<'a> { (id, ItemEnum(enum_definition, generics), None) } - fn fn_expr_lookahead(tok: &token::Token) -> bool { - match *tok { - token::OpenDelim(token::Paren) | token::At | token::Tilde | token::BinOp(_) => true, - _ => false - } - } - /// Parses a string as an ABI spec on an extern type or module. Consumes /// the `extern` keyword, if one is found. fn parse_opt_abi(&mut self) -> Option { @@ -5779,8 +5774,7 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return IoviItem(item); } - if self.token.is_keyword(keywords::Fn) && - self.look_ahead(1, |f| !Parser::fn_expr_lookahead(f)) { + if self.token.is_keyword(keywords::Fn) { // FUNCTION ITEM self.bump(); let (ident, item_, extra_attrs) = diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 4b3573f84c571..e5aef12e82795 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -183,14 +183,14 @@ impl Token { Underscore => true, Tilde => true, Literal(_, _) => true, - Pound => true, - At => true, Not => true, BinOp(Minus) => true, BinOp(Star) => true, BinOp(And) => true, BinOp(Or) => true, // in lambda syntax OrOr => true, // in lambda syntax + AndAnd => true, // double borrow + DotDot => true, // range notation ModSep => true, Interpolated(NtExpr(..)) => true, Interpolated(NtIdent(..)) => true, From 602a7d1436cc880e65f7d4dfb3f5eaaa5953d5f9 Mon Sep 17 00:00:00 2001 From: Willson Mock Date: Sat, 10 Jan 2015 23:35:33 -0500 Subject: [PATCH 005/211] update mit-license and copyright --- COPYRIGHT | 2 +- LICENSE-MIT | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/COPYRIGHT b/COPYRIGHT index 52039ea7e5734..da8ac7b3adde7 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -6,7 +6,7 @@ terms. Longer version: -The Rust Project is copyright 2014, The Rust Project +The Rust Project is copyright 2015, The Rust Project Developers (given in the file AUTHORS.txt). Licensed under the Apache License, Version 2.0 diff --git a/LICENSE-MIT b/LICENSE-MIT index 39d4bdb5acd31..e69282e381bc0 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2014 The Rust Project Developers +Copyright (c) 2015 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated From ca8578a953d0563dbef499ea2c2c853c9b71887c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 11 Jan 2015 16:09:31 +0100 Subject: [PATCH 006/211] Add test case that RangeTo notation works in return statements. --- src/test/run-pass/range.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/run-pass/range.rs b/src/test/run-pass/range.rs index 90e8716899082..bfd3e43768f50 100644 --- a/src/test/run-pass/range.rs +++ b/src/test/run-pass/range.rs @@ -12,6 +12,9 @@ fn foo() -> int { 42 } +// Test that range syntax works in return statements +fn return_range_to() -> ::std::ops::RangeTo { return ..1; } + pub fn main() { let mut count = 0; for i in 0u..10 { From 1c0acb9d91e50131d9914991dbf71f69466e7cba Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Mon, 12 Jan 2015 16:18:02 -0500 Subject: [PATCH 007/211] Match prose with code when discussing Ordering values Now both the enum values and the prose describing them mention the values in the same order. --- src/doc/trpl/compound-data-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/trpl/compound-data-types.md b/src/doc/trpl/compound-data-types.md index afa890b84b401..9f7fa374e6990 100644 --- a/src/doc/trpl/compound-data-types.md +++ b/src/doc/trpl/compound-data-types.md @@ -253,7 +253,7 @@ things from the standard library if you need them. Okay, let's talk about the actual code in the example. `cmp` is a function that compares two things, and returns an `Ordering`. We return either `Ordering::Less`, `Ordering::Greater`, or `Ordering::Equal`, depending on if -the two values are greater, less, or equal. Note that each variant of the +the two values are less, greater, or equal. Note that each variant of the `enum` is namespaced under the `enum` itself: it's `Ordering::Greater` not `Greater`. From f071f3b18551ae93dc2d28d8035b3e0a81697399 Mon Sep 17 00:00:00 2001 From: KernelJ Date: Mon, 12 Jan 2015 23:26:22 +0000 Subject: [PATCH 008/211] Fixes to cfg .mk files for Windows: removed use of unused LDPATH variable on Windows as is done for other platforms, and added GCC flag to ensure MINGW's ANSI compatible STDIO functions are used wherever available (required by jemalloc). --- mk/cfg/i686-pc-windows-gnu.mk | 10 +++++----- mk/cfg/x86_64-pc-windows-gnu.mk | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mk/cfg/i686-pc-windows-gnu.mk b/mk/cfg/i686-pc-windows-gnu.mk index ecb405b76f010..357a321688bca 100644 --- a/mk/cfg/i686-pc-windows-gnu.mk +++ b/mk/cfg/i686-pc-windows-gnu.mk @@ -8,8 +8,8 @@ CFG_LIB_NAME_i686-pc-windows-gnu=$(1).dll CFG_STATIC_LIB_NAME_i686-pc-windows-gnu=$(1).lib CFG_LIB_GLOB_i686-pc-windows-gnu=$(1)-*.dll CFG_LIB_DSYM_GLOB_i686-pc-windows-gnu=$(1)-*.dylib.dSYM -CFG_JEMALLOC_CFLAGS_i686-pc-windows-gnu := -march=i686 -m32 -D_WIN32_WINNT=0x0600 $(CFLAGS) -CFG_GCCISH_CFLAGS_i686-pc-windows-gnu := -Wall -Werror -g -m32 -D_WIN32_WINNT=0x0600 $(CFLAGS) +CFG_JEMALLOC_CFLAGS_i686-pc-windows-gnu := -march=i686 -m32 -D_WIN32_WINNT=0x0600 -D__USE_MINGW_ANSI_STDIO=1 $(CFLAGS) +CFG_GCCISH_CFLAGS_i686-pc-windows-gnu := -Wall -Werror -g -m32 -D_WIN32_WINNT=0x0600 -D__USE_MINGW_ANSI_STDIO=1 $(CFLAGS) CFG_GCCISH_CXXFLAGS_i686-pc-windows-gnu := -fno-rtti $(CXXFLAGS) CFG_GCCISH_LINK_FLAGS_i686-pc-windows-gnu := -shared -g -m32 CFG_GCCISH_DEF_FLAG_i686-pc-windows-gnu := @@ -22,7 +22,7 @@ CFG_EXE_SUFFIX_i686-pc-windows-gnu := .exe CFG_WINDOWSY_i686-pc-windows-gnu := 1 CFG_UNIXY_i686-pc-windows-gnu := CFG_PATH_MUNGE_i686-pc-windows-gnu := -CFG_LDPATH_i686-pc-windows-gnu :=$(CFG_LDPATH_i686-pc-windows-gnu):$(PATH) -CFG_RUN_i686-pc-windows-gnu=PATH="$(CFG_LDPATH_i686-pc-windows-gnu):$(1)" $(2) -CFG_RUN_TARG_i686-pc-windows-gnu=$(call CFG_RUN_i686-pc-windows-gnu,$(HLIB$(1)_H_$(CFG_BUILD)),$(2)) +CFG_LDPATH_i686-pc-windows-gnu := +CFG_RUN_i686-pc-windows-gnu=$(2) +CFG_RUN_TARG_i686-pc-windows-gnu=$(call CFG_RUN_i686-pc-windows-gnu,,$(2)) CFG_GNU_TRIPLE_i686-pc-windows-gnu := i686-w64-mingw32 diff --git a/mk/cfg/x86_64-pc-windows-gnu.mk b/mk/cfg/x86_64-pc-windows-gnu.mk index a23e292c7bd5d..e9e5f04ea5433 100644 --- a/mk/cfg/x86_64-pc-windows-gnu.mk +++ b/mk/cfg/x86_64-pc-windows-gnu.mk @@ -8,8 +8,8 @@ CFG_LIB_NAME_x86_64-pc-windows-gnu=$(1).dll CFG_STATIC_LIB_NAME_x86_64-pc-windows-gnu=$(1).lib CFG_LIB_GLOB_x86_64-pc-windows-gnu=$(1)-*.dll CFG_LIB_DSYM_GLOB_x86_64-pc-windows-gnu=$(1)-*.dylib.dSYM -CFG_JEMALLOC_CFLAGS_x86_64-pc-windows-gnu := -m64 -D_WIN32_WINNT=0x0600 $(CFLAGS) -CFG_GCCISH_CFLAGS_x86_64-pc-windows-gnu := -Wall -Werror -g -m64 -D_WIN32_WINNT=0x0600 $(CFLAGS) +CFG_JEMALLOC_CFLAGS_x86_64-pc-windows-gnu := -m64 -D_WIN32_WINNT=0x0600 -D__USE_MINGW_ANSI_STDIO=1 $(CFLAGS) +CFG_GCCISH_CFLAGS_x86_64-pc-windows-gnu := -Wall -Werror -g -m64 -D_WIN32_WINNT=0x0600 -D__USE_MINGW_ANSI_STDIO=1 $(CFLAGS) CFG_GCCISH_CXXFLAGS_x86_64-pc-windows-gnu := -fno-rtti $(CXXFLAGS) CFG_GCCISH_LINK_FLAGS_x86_64-pc-windows-gnu := -shared -g -m64 CFG_GCCISH_DEF_FLAG_x86_64-pc-windows-gnu := @@ -22,7 +22,7 @@ CFG_EXE_SUFFIX_x86_64-pc-windows-gnu := .exe CFG_WINDOWSY_x86_64-pc-windows-gnu := 1 CFG_UNIXY_x86_64-pc-windows-gnu := CFG_PATH_MUNGE_x86_64-pc-windows-gnu := -CFG_LDPATH_x86_64-pc-windows-gnu :=$(CFG_LDPATH_x86_64-pc-windows-gnu):$(PATH) -CFG_RUN_x86_64-pc-windows-gnu=PATH="$(CFG_LDPATH_x86_64-pc-windows-gnu):$(1)" $(2) -CFG_RUN_TARG_x86_64-pc-windows-gnu=$(call CFG_RUN_x86_64-pc-windows-gnu,$(HLIB$(1)_H_$(CFG_BUILD)),$(2)) +CFG_LDPATH_x86_64-pc-windows-gnu := +CFG_RUN_x86_64-pc-windows-gnu=$(2) +CFG_RUN_TARG_x86_64-pc-windows-gnu=$(call CFG_RUN_x86_64-pc-windows-gnu,,$(2)) CFG_GNU_TRIPLE_x86_64-pc-windows-gnu := x86_64-w64-mingw32 From af506fa5d15b892c2fa6df442e106e1afcb7abca Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 13 Jan 2015 17:05:04 +1100 Subject: [PATCH 009/211] typeck: move method errors/suggestions to their own file. --- src/librustc_typeck/check/method/mod.rs | 105 +---------------- src/librustc_typeck/check/method/suggest.rs | 122 ++++++++++++++++++++ 2 files changed, 126 insertions(+), 101 deletions(-) create mode 100644 src/librustc_typeck/check/method/suggest.rs diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 0c53a16a8118b..8637a915305db 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -12,7 +12,6 @@ use astconv::AstConv; use check::{FnCtxt}; -use check::{impl_self_ty}; use check::vtable; use check::vtable::select_new_fcx_obligations; use middle::subst; @@ -20,7 +19,7 @@ use middle::traits; use middle::ty::*; use middle::ty; use middle::infer; -use util::ppaux::{Repr, UserString}; +use util::ppaux::Repr; use std::rc::Rc; use syntax::ast::{DefId}; @@ -30,9 +29,12 @@ use syntax::codemap::Span; pub use self::MethodError::*; pub use self::CandidateSource::*; +pub use self::suggest::report_error; + mod confirm; mod doc; mod probe; +mod suggest; pub enum MethodError { // Did not find an applicable method, but we did find various @@ -294,105 +296,6 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, Some(callee) } -pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - span: Span, - rcvr_ty: Ty<'tcx>, - method_name: ast::Name, - error: MethodError) -{ - match error { - NoMatch(static_sources) => { - let cx = fcx.tcx(); - let method_ustring = method_name.user_string(cx); - - // True if the type is a struct and contains a field with - // the same name as the not-found method - let is_field = match rcvr_ty.sty { - ty_struct(did, _) => - ty::lookup_struct_fields(cx, did) - .iter() - .any(|f| f.name.user_string(cx) == method_ustring), - _ => false - }; - - fcx.type_error_message( - span, - |actual| { - format!("type `{}` does not implement any \ - method in scope named `{}`", - actual, - method_ustring) - }, - rcvr_ty, - None); - - // If the method has the name of a field, give a help note - if is_field { - cx.sess.span_note(span, - &format!("use `(s.{0})(...)` if you meant to call the \ - function stored in the `{0}` field", method_ustring)[]); - } - - if static_sources.len() > 0 { - fcx.tcx().sess.fileline_note( - span, - "found defined static methods, maybe a `self` is missing?"); - - report_candidates(fcx, span, method_name, static_sources); - } - } - - Ambiguity(sources) => { - span_err!(fcx.sess(), span, E0034, - "multiple applicable methods in scope"); - - report_candidates(fcx, span, method_name, sources); - } - } - - fn report_candidates(fcx: &FnCtxt, - span: Span, - method_name: ast::Name, - mut sources: Vec) { - sources.sort(); - sources.dedup(); - - for (idx, source) in sources.iter().enumerate() { - match *source { - ImplSource(impl_did) => { - // Provide the best span we can. Use the method, if local to crate, else - // the impl, if local to crate (method may be defaulted), else the call site. - let method = impl_method(fcx.tcx(), impl_did, method_name).unwrap(); - let impl_span = fcx.tcx().map.def_id_span(impl_did, span); - let method_span = fcx.tcx().map.def_id_span(method.def_id, impl_span); - - let impl_ty = impl_self_ty(fcx, span, impl_did).ty; - - let insertion = match impl_trait_ref(fcx.tcx(), impl_did) { - None => format!(""), - Some(trait_ref) => format!(" of the trait `{}`", - ty::item_path_str(fcx.tcx(), - trait_ref.def_id)), - }; - - span_note!(fcx.sess(), method_span, - "candidate #{} is defined in an impl{} for the type `{}`", - idx + 1u, - insertion, - impl_ty.user_string(fcx.tcx())); - } - TraitSource(trait_did) => { - let (_, method) = trait_method(fcx.tcx(), trait_did, method_name).unwrap(); - let method_span = fcx.tcx().map.def_id_span(method.def_id, span); - span_note!(fcx.sess(), method_span, - "candidate #{} is defined in the trait `{}`", - idx + 1u, - ty::item_path_str(fcx.tcx(), trait_did)); - } - } - } - } -} /// Find method with name `method_name` defined in `trait_def_id` and return it, along with its /// index (or `None`, if no such method). diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs new file mode 100644 index 0000000000000..b6d97d2df9dec --- /dev/null +++ b/src/librustc_typeck/check/method/suggest.rs @@ -0,0 +1,122 @@ +// 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. + +//! Give useful errors and suggestions to users when a method can't be +//! found or is otherwise invalid. + +use astconv::AstConv; +use check::{self, FnCtxt}; +use middle::ty::{self, Ty}; +use util::ppaux::UserString; + +use syntax::ast; +use syntax::codemap::Span; + +use super::{MethodError, CandidateSource, impl_method, trait_method}; + +pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + span: Span, + rcvr_ty: Ty<'tcx>, + method_name: ast::Name, + error: MethodError) +{ + match error { + MethodError::NoMatch(static_sources) => { + let cx = fcx.tcx(); + let method_ustring = method_name.user_string(cx); + + // True if the type is a struct and contains a field with + // the same name as the not-found method + let is_field = match rcvr_ty.sty { + ty::ty_struct(did, _) => + ty::lookup_struct_fields(cx, did) + .iter() + .any(|f| f.name.user_string(cx) == method_ustring), + _ => false + }; + + fcx.type_error_message( + span, + |actual| { + format!("type `{}` does not implement any \ + method in scope named `{}`", + actual, + method_ustring) + }, + rcvr_ty, + None); + + // If the method has the name of a field, give a help note + if is_field { + cx.sess.span_note(span, + &format!("use `(s.{0})(...)` if you meant to call the \ + function stored in the `{0}` field", method_ustring)[]); + } + + if static_sources.len() > 0 { + fcx.tcx().sess.fileline_note( + span, + "found defined static methods, maybe a `self` is missing?"); + + report_candidates(fcx, span, method_name, static_sources); + } + } + + MethodError::Ambiguity(sources) => { + span_err!(fcx.sess(), span, E0034, + "multiple applicable methods in scope"); + + report_candidates(fcx, span, method_name, sources); + } + } + + fn report_candidates(fcx: &FnCtxt, + span: Span, + method_name: ast::Name, + mut sources: Vec) { + sources.sort(); + sources.dedup(); + + for (idx, source) in sources.iter().enumerate() { + match *source { + CandidateSource::ImplSource(impl_did) => { + // Provide the best span we can. Use the method, if local to crate, else + // the impl, if local to crate (method may be defaulted), else the call site. + let method = impl_method(fcx.tcx(), impl_did, method_name).unwrap(); + let impl_span = fcx.tcx().map.def_id_span(impl_did, span); + let method_span = fcx.tcx().map.def_id_span(method.def_id, impl_span); + + let impl_ty = check::impl_self_ty(fcx, span, impl_did).ty; + + let insertion = match ty::impl_trait_ref(fcx.tcx(), impl_did) { + None => format!(""), + Some(trait_ref) => format!(" of the trait `{}`", + ty::item_path_str(fcx.tcx(), + trait_ref.def_id)), + }; + + span_note!(fcx.sess(), method_span, + "candidate #{} is defined in an impl{} for the type `{}`", + idx + 1u, + insertion, + impl_ty.user_string(fcx.tcx())); + } + CandidateSource::TraitSource(trait_did) => { + let (_, method) = trait_method(fcx.tcx(), trait_did, method_name).unwrap(); + let method_span = fcx.tcx().map.def_id_span(method.def_id, span); + span_note!(fcx.sess(), method_span, + "candidate #{} is defined in the trait `{}`", + idx + 1u, + ty::item_path_str(fcx.tcx(), trait_did)); + } + } + } + } +} From 06ad8bb872c93ed0aa1db00cf02b31b0f30d57b3 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 14 Jan 2015 10:49:17 +1100 Subject: [PATCH 010/211] Implement suggestions for traits to import. For a call like `foo.bar()` where the method `bar` can't be resolved, the compiler will search for traits that have methods with name `bar` to give a more informative error, providing a list of possibilities. Closes #7643. --- src/librustc/session/mod.rs | 3 + src/librustc_typeck/check/method/mod.rs | 2 +- src/librustc_typeck/check/method/suggest.rs | 167 +++++++++++++++++- src/librustc_typeck/lib.rs | 8 + src/libsyntax/diagnostic.rs | 3 + .../auxiliary/no_method_suggested_traits.rs | 38 ++++ .../no-method-suggested-traits.rs | 30 ++++ 7 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 src/test/auxiliary/no_method_suggested_traits.rs create mode 100644 src/test/compile-fail/no-method-suggested-traits.rs diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 27acc39c77863..8a7c7b38287ec 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -174,6 +174,9 @@ impl Session { pub fn fileline_note(&self, sp: Span, msg: &str) { self.diagnostic().fileline_note(sp, msg) } + pub fn fileline_help(&self, sp: Span, msg: &str) { + self.diagnostic().fileline_help(sp, msg) + } pub fn note(&self, msg: &str) { self.diagnostic().handler().note(msg) } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 8637a915305db..a28b4ac34753b 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -29,7 +29,7 @@ use syntax::codemap::Span; pub use self::MethodError::*; pub use self::CandidateSource::*; -pub use self::suggest::report_error; +pub use self::suggest::{report_error, AllTraitsVec}; mod confirm; mod doc; diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index b6d97d2df9dec..aab1fa2a95817 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -11,14 +11,21 @@ //! Give useful errors and suggestions to users when a method can't be //! found or is otherwise invalid. +use CrateCtxt; + use astconv::AstConv; use check::{self, FnCtxt}; use middle::ty::{self, Ty}; +use middle::def; +use metadata::{csearch, cstore, decoder}; use util::ppaux::UserString; -use syntax::ast; +use syntax::{ast, ast_util}; use syntax::codemap::Span; +use std::cell; +use std::cmp::Ordering; + use super::{MethodError, CandidateSource, impl_method, trait_method}; pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, @@ -67,6 +74,8 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, report_candidates(fcx, span, method_name, static_sources); } + + suggest_traits_to_import(fcx, span, rcvr_ty, method_name) } MethodError::Ambiguity(sources) => { @@ -120,3 +129,159 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } } } + + +pub type AllTraitsVec = Vec; + +fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + span: Span, + _rcvr_ty: Ty<'tcx>, + method_name: ast::Name) +{ + let tcx = fcx.tcx(); + + let mut candidates = all_traits(fcx.ccx) + .filter(|info| trait_method(tcx, info.def_id, method_name).is_some()) + .collect::>(); + + if candidates.len() > 0 { + // sort from most relevant to least relevant + candidates.sort_by(|a, b| a.cmp(b).reverse()); + + let method_ustring = method_name.user_string(tcx); + + span_help!(fcx.sess(), span, + "methods from traits can only be called if the trait is implemented \ + and in scope; the following trait{s} define{inv_s} a method `{name}`:", + s = if candidates.len() == 1 {""} else {"s"}, + inv_s = if candidates.len() == 1 {"s"} else {""}, + name = method_ustring); + + for (i, trait_info) in candidates.iter().enumerate() { + // provide a good-as-possible span; the span of + // the trait if it is local, or the span of the + // method call itself if not + let trait_span = fcx.tcx().map.def_id_span(trait_info.def_id, span); + + fcx.sess().fileline_help(trait_span, + &*format!("candidate #{}: `{}`", + i + 1, + ty::item_path_str(fcx.tcx(), trait_info.def_id))) + } + } +} + +#[derive(Copy)] +pub struct TraitInfo { + def_id: ast::DefId, +} + +impl TraitInfo { + fn new(def_id: ast::DefId) -> TraitInfo { + TraitInfo { + def_id: def_id, + } + } +} +impl PartialEq for TraitInfo { + fn eq(&self, other: &TraitInfo) -> bool { + self.cmp(other) == Ordering::Equal + } +} +impl Eq for TraitInfo {} +impl PartialOrd for TraitInfo { + fn partial_cmp(&self, other: &TraitInfo) -> Option { Some(self.cmp(other)) } +} +impl Ord for TraitInfo { + fn cmp(&self, other: &TraitInfo) -> Ordering { + // accessible traits are more important/relevant than + // inaccessible ones, local crates are more important than + // remote ones (local: cnum == 0), and NodeIds just for + // totality. + + let lhs = (other.def_id.krate, other.def_id.node); + let rhs = (self.def_id.krate, self.def_id.node); + lhs.cmp(&rhs) + } +} + +/// Retrieve all traits in this crate and any dependent crates. +fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> { + if ccx.all_traits.borrow().is_none() { + use syntax::visit; + + let mut traits = vec![]; + + // Crate-local: + // + // meh. + struct Visitor<'a, 'b: 'a, 'tcx: 'a + 'b> { + traits: &'a mut AllTraitsVec, + } + impl<'v,'a, 'b, 'tcx> visit::Visitor<'v> for Visitor<'a, 'b, 'tcx> { + fn visit_item(&mut self, i: &'v ast::Item) { + match i.node { + ast::ItemTrait(..) => { + self.traits.push(TraitInfo::new(ast_util::local_def(i.id))); + } + _ => {} + } + visit::walk_item(self, i) + } + } + visit::walk_crate(&mut Visitor { + traits: &mut traits + }, ccx.tcx.map.krate()); + + // Cross-crate: + fn handle_external_def(traits: &mut AllTraitsVec, + ccx: &CrateCtxt, + cstore: &cstore::CStore, + dl: decoder::DefLike) { + match dl { + decoder::DlDef(def::DefTrait(did)) => { + traits.push(TraitInfo::new(did)); + } + decoder::DlDef(def::DefMod(did)) => { + csearch::each_child_of_item(cstore, did, |dl, _, _| { + handle_external_def(traits, ccx, cstore, dl) + }) + } + _ => {} + } + } + let cstore = &ccx.tcx.sess.cstore; + cstore.iter_crate_data(|&mut: cnum, _| { + csearch::each_top_level_item_of_crate(cstore, cnum, |dl, _, _| { + handle_external_def(&mut traits, ccx, cstore, dl) + }) + }); + + *ccx.all_traits.borrow_mut() = Some(traits); + } + + let borrow = ccx.all_traits.borrow(); + assert!(borrow.is_some()); + AllTraits { + borrow: borrow, + idx: 0 + } +} + +struct AllTraits<'a> { + borrow: cell::Ref<'a Option>, + idx: usize +} + +impl<'a> Iterator for AllTraits<'a> { + type Item = TraitInfo; + + fn next(&mut self) -> Option { + let AllTraits { ref borrow, ref mut idx } = *self; + // ugh. + borrow.as_ref().unwrap().get(*idx).map(|info| { + *idx += 1; + *info + }) + } +} diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 68b152dee233b..88fe88bf26540 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -108,6 +108,8 @@ use syntax::print::pprust::*; use syntax::{ast, ast_map, abi}; use syntax::ast_util::local_def; +use std::cell::RefCell; + mod check; mod rscope; mod astconv; @@ -123,6 +125,11 @@ struct TypeAndSubsts<'tcx> { struct CrateCtxt<'a, 'tcx: 'a> { // A mapping from method call sites to traits that have that method. trait_map: ty::TraitMap, + /// A vector of every trait accessible in the whole crate + /// (i.e. including those from subcrates). This is used only for + /// error reporting, and so is lazily initialised and generally + /// shouldn't taint the common path (hence the RefCell). + all_traits: RefCell>, tcx: &'a ty::ctxt<'tcx>, } @@ -320,6 +327,7 @@ pub fn check_crate(tcx: &ty::ctxt, trait_map: ty::TraitMap) { let time_passes = tcx.sess.time_passes(); let ccx = CrateCtxt { trait_map: trait_map, + all_traits: RefCell::new(None), tcx: tcx }; diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs index 3f81dac2b0d62..36058b694eac4 100644 --- a/src/libsyntax/diagnostic.rs +++ b/src/libsyntax/diagnostic.rs @@ -118,6 +118,9 @@ impl SpanHandler { pub fn fileline_note(&self, sp: Span, msg: &str) { self.handler.custom_emit(&self.cm, FileLine(sp), msg, Note); } + pub fn fileline_help(&self, sp: Span, msg: &str) { + self.handler.custom_emit(&self.cm, FileLine(sp), msg, Help); + } pub fn span_bug(&self, sp: Span, msg: &str) -> ! { self.handler.emit(Some((&self.cm, sp)), msg, Bug); panic!(ExplicitBug); diff --git a/src/test/auxiliary/no_method_suggested_traits.rs b/src/test/auxiliary/no_method_suggested_traits.rs new file mode 100644 index 0000000000000..fdeace00d4ca2 --- /dev/null +++ b/src/test/auxiliary/no_method_suggested_traits.rs @@ -0,0 +1,38 @@ +// 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. + +pub use reexport::Reexported; + +pub mod foo { + pub trait PubPub { + fn method(&self); + } +} +pub mod bar { + trait PubPriv { + fn method(&self); + } +} +mod qux { + pub trait PrivPub { + fn method(&self); + } +} +mod quz { + trait PrivPriv { + fn method(&self); + } +} + +mod reexport { + pub trait Reexported { + fn method(&self); + } +} diff --git a/src/test/compile-fail/no-method-suggested-traits.rs b/src/test/compile-fail/no-method-suggested-traits.rs new file mode 100644 index 0000000000000..2565d2b730267 --- /dev/null +++ b/src/test/compile-fail/no-method-suggested-traits.rs @@ -0,0 +1,30 @@ +// 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. + +// aux-build:no_method_suggested_traits.rs + +extern crate no_method_suggested_traits; + +mod foo { + trait Bar { //~ HELP `foo::Bar` + fn method(&self); + } +} + +fn main() { + 1u32.method(); + //~^ ERROR does not implement + //~^^ HELP the following traits define a method `method` + //~^^^ HELP `no_method_suggested_traits::foo::PubPub` + //~^^^^ HELP `no_method_suggested_traits::reexport::Reexported` + //~^^^^^ HELP `no_method_suggested_traits::bar::PubPriv` + //~^^^^^^ HELP `no_method_suggested_traits::qux::PrivPub` + //~^^^^^^^ HELP `no_method_suggested_traits::quz::PrivPriv` +} From 935b37a460faf6eb54194f3653daf31efb0b9b3b Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 14 Jan 2015 09:45:41 +0800 Subject: [PATCH 011/211] fix string multi line connector '\' for kate example: let m = "hello \ world"; --- src/etc/kate/rust.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/etc/kate/rust.xml b/src/etc/kate/rust.xml index 925034eaa00b0..3ceec0f250a33 100644 --- a/src/etc/kate/rust.xml +++ b/src/etc/kate/rust.xml @@ -249,7 +249,7 @@ - + From 4ebde950f5afc16366b04b4c191f1c6198726da7 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 15 Jan 2015 00:23:43 +1100 Subject: [PATCH 012/211] Document, tweak and refactor some trans code. --- src/librustc_llvm/lib.rs | 2 +- src/librustc_trans/trans/cabi_x86_64.rs | 43 +++++++++++++------------ src/librustc_trans/trans/type_.rs | 7 ++++ 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index a4f9b1f98d4c9..e7d613f67b6f6 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -304,7 +304,7 @@ pub enum RealPredicate { // The LLVM TypeKind type - must stay in sync with the def of // LLVMTypeKind in llvm/include/llvm-c/Core.h -#[derive(Copy, PartialEq)] +#[derive(Copy, PartialEq, Show)] #[repr(C)] pub enum TypeKind { Void = 0, diff --git a/src/librustc_trans/trans/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs index 86190b1e56639..c0e80ff08a22e 100644 --- a/src/librustc_trans/trans/cabi_x86_64.rs +++ b/src/librustc_trans/trans/cabi_x86_64.rs @@ -34,6 +34,7 @@ enum RegClass { SSEDs, SSEDv, SSEInt, + /// Data that can appear in the upper half of an SSE register. SSEUp, X87, X87Up, @@ -155,26 +156,28 @@ fn classify_ty(ty: Type) -> Vec { fn unify(cls: &mut [RegClass], i: uint, newv: RegClass) { - if cls[i] == newv { - return; - } else if cls[i] == NoClass { - cls[i] = newv; - } else if newv == NoClass { - return; - } else if cls[i] == Memory || newv == Memory { - cls[i] = Memory; - } else if cls[i] == Int || newv == Int { - cls[i] = Int; - } else if cls[i] == X87 || - cls[i] == X87Up || - cls[i] == ComplexX87 || - newv == X87 || - newv == X87Up || - newv == ComplexX87 { - cls[i] = Memory; - } else { - cls[i] = newv; - } + if cls[i] == newv { return } + + let to_write = match (cls[i], newv) { + (NoClass, _) => newv, + (_, NoClass) => return, + + (Memory, _) | + (_, Memory) => Memory, + + (Int, _) | + (_, Int) => Int, + + (X87, _) | + (X87Up, _) | + (ComplexX87, _) | + (_, X87) | + (_, X87Up) | + (_, ComplexX87) => Memory, + + (_, _) => newv + }; + cls[i] = to_write; } fn classify_struct(tys: &[Type], diff --git a/src/librustc_trans/trans/type_.rs b/src/librustc_trans/trans/type_.rs index 9cae142c03ac1..acb1623db330c 100644 --- a/src/librustc_trans/trans/type_.rs +++ b/src/librustc_trans/trans/type_.rs @@ -284,6 +284,13 @@ impl Type { } } + /// Return the number of elements in `self` if it is a LLVM vector type. + pub fn vector_length(&self) -> uint { + unsafe { + llvm::LLVMGetVectorSize(self.to_ref()) as uint + } + } + pub fn array_length(&self) -> uint { unsafe { llvm::LLVMGetArrayLength(self.to_ref()) as uint From 5b6d0245b8055f176c606604ac6cc0f50ef79b45 Mon Sep 17 00:00:00 2001 From: Tristan Storch Date: Tue, 13 Jan 2015 17:45:04 +0100 Subject: [PATCH 013/211] Small Readability Update --- src/libcore/str/mod.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 94ee9b7dcf6ad..cf8a96f439649 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -678,18 +678,15 @@ struct TwoWaySearcher { */ impl TwoWaySearcher { fn new(needle: &[u8]) -> TwoWaySearcher { - let (crit_pos1, period1) = TwoWaySearcher::maximal_suffix(needle, false); - let (crit_pos2, period2) = TwoWaySearcher::maximal_suffix(needle, true); - - let crit_pos; - let period; - if crit_pos1 > crit_pos2 { - crit_pos = crit_pos1; - period = period1; - } else { - crit_pos = crit_pos2; - period = period2; - } + let (crit_pos_false, period_false) = TwoWaySearcher::maximal_suffix(needle, false); + let (crit_pos_true, period_true) = TwoWaySearcher::maximal_suffix(needle, true); + + let (crit_pos, period) = + if crit_pos_false > crit_pos_true { + (crit_pos_false, period_false) + } else { + (crit_pos_true, period_true) + }; // This isn't in the original algorithm, as far as I'm aware. let byteset = needle.iter() From 195fd9a2b41ed9b5294f8803aeb84c1ace673e5e Mon Sep 17 00:00:00 2001 From: "NODA, Kai" Date: Thu, 15 Jan 2015 13:51:29 +0800 Subject: [PATCH 014/211] reference.md: change "mod" to "self" in "use" declaration. This should have been done together with 56dcbd17fdad5d39b7b02e22a7490d2468718d08 for rust-lang/rust#20361 Signed-off-by: NODA, Kai --- src/doc/reference.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index 623097b2fc90f..07bb6e5132a26 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -971,7 +971,7 @@ path_glob : ident [ "::" [ path_glob | '*' ] ] ? | '{' path_item [ ',' path_item ] * '}' ; -path_item : ident | "mod" ; +path_item : ident | "self" ; ``` A _use declaration_ creates one or more local name bindings synonymous with @@ -991,15 +991,15 @@ Use declarations support a number of convenient shortcuts: * Binding all paths matching a given prefix, using the asterisk wildcard syntax `use a::b::*;` * Simultaneously binding a list of paths differing only in their final element - and their immediate parent module, using the `mod` keyword, such as - `use a::b::{mod, c, d};` + and their immediate parent module, using the `self` keyword, such as + `use a::b::{self, c, d};` An example of `use` declarations: ``` use std::iter::range_step; use std::option::Option::{Some, None}; -use std::collections::hash_map::{mod, HashMap}; +use std::collections::hash_map::{self, HashMap}; fn foo(_: T){} fn bar(map1: HashMap, map2: hash_map::HashMap){} From 716effa34900781ddb201fbd50ee679a4964cb25 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Wed, 14 Jan 2015 18:56:17 -0500 Subject: [PATCH 015/211] support deriving `Hash` for nullary structs fixes #16530 --- src/libsyntax/ext/deriving/hash.rs | 8 ++------ src/test/run-pass/issue-16530.rs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 src/test/run-pass/issue-16530.rs diff --git a/src/libsyntax/ext/deriving/hash.rs b/src/libsyntax/ext/deriving/hash.rs index db99c14244324..0c3ceb2b90bf3 100644 --- a/src/libsyntax/ext/deriving/hash.rs +++ b/src/libsyntax/ext/deriving/hash.rs @@ -63,7 +63,7 @@ pub fn expand_deriving_hash(cx: &mut ExtCtxt, fn hash_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P { let state_expr = match substr.nonself_args { [ref state_expr] => state_expr, - _ => cx.span_bug(trait_span, "incorrect number of arguments in `deriving(Hash)`") + _ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`") }; let hash_ident = substr.method_ident; let call_hash = |&: span, thing_expr| { @@ -86,16 +86,12 @@ fn hash_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) fs } - _ => cx.span_bug(trait_span, "impossible substructure in `deriving(Hash)`") + _ => cx.span_bug(trait_span, "impossible substructure in `derive(Hash)`") }; for &FieldInfo { ref self_, span, .. } in fields.iter() { stmts.push(call_hash(span, self_.clone())); } - if stmts.len() == 0 { - cx.span_bug(trait_span, "#[derive(Hash)] needs at least one field"); - } - cx.expr_block(cx.block(trait_span, stmts, None)) } diff --git a/src/test/run-pass/issue-16530.rs b/src/test/run-pass/issue-16530.rs new file mode 100644 index 0000000000000..7e3b796235d26 --- /dev/null +++ b/src/test/run-pass/issue-16530.rs @@ -0,0 +1,18 @@ +// 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. + +use std::hash::{SipHasher, hash}; + +#[derive(Hash)] +struct Empty; + +pub fn main() { + assert!(hash::<_, SipHasher>(&Empty) == hash::<_, SipHasher>(&Empty)); +} From 5eb254b1ccf196ea0856782eba96f92ae0786617 Mon Sep 17 00:00:00 2001 From: Sebastian Gesemann Date: Fri, 16 Jan 2015 05:44:19 +0100 Subject: [PATCH 016/211] libcore: implement many operators for references as well --- src/libcore/ops.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index e7eb307689fbe..144019147af7e 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -96,6 +96,80 @@ pub trait Drop { fn drop(&mut self); } +// implements the unary operator "op &T" +// based on "op T" where T is expected to be `Copy`able +macro_rules! forward_ref_unop { + (impl $imp:ident, $method:ident for $t:ty) => { + #[unstable] + impl<'a> $imp for &'a $t { + type Output = <$t as $imp>::Output; + + #[inline] + fn $method(self) -> <$t as $imp>::Output { + $imp::$method(*self) + } + } + } +} + +// implements the binary operator "&T op U" +// based on "T + U" where T and U are expected `Copy`able +macro_rules! forward_ref_val_binop { + (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { + #[unstable] + impl<'a> $imp<$u> for &'a $t { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + fn $method(self, other: $u) -> <$t as $imp<$u>>::Output { + $imp::$method(*self, other) + } + } + } +} + +// implements the binary operator "T op &U" +// based on "T + U" where T and U are expected `Copy`able +macro_rules! forward_val_ref_binop { + (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { + #[unstable] + impl<'a> $imp<&'a $u> for $t { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output { + $imp::$method(self, *other) + } + } + } +} + +// implements the binary operator "&T op &U" +// based on "T + U" where T and U are expected `Copy`able +macro_rules! forward_ref_ref_binop { + (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { + #[unstable] + impl<'a, 'b> $imp<&'a $u> for &'b $t { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output { + $imp::$method(*self, *other) + } + } + } +} + +// implements binary operators "&T op U", "T op &U", "&T op &U" +// based on "T + U" where T and U are expected `Copy`able +macro_rules! forward_ref_binop { + (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { + forward_ref_val_binop! { impl $imp, $method for $t, $u } + forward_val_ref_binop! { impl $imp, $method for $t, $u } + forward_ref_ref_binop! { impl $imp, $method for $t, $u } + } +} + /// The `Add` trait is used to specify the functionality of `+`. /// /// # Example @@ -144,6 +218,8 @@ macro_rules! add_impl { #[inline] fn add(self, other: $t) -> $t { self + other } } + + forward_ref_binop! { impl Add, add for $t, $t } )*) } @@ -197,6 +273,8 @@ macro_rules! sub_impl { #[inline] fn sub(self, other: $t) -> $t { self - other } } + + forward_ref_binop! { impl Sub, sub for $t, $t } )*) } @@ -250,6 +328,8 @@ macro_rules! mul_impl { #[inline] fn mul(self, other: $t) -> $t { self * other } } + + forward_ref_binop! { impl Mul, mul for $t, $t } )*) } @@ -303,6 +383,8 @@ macro_rules! div_impl { #[inline] fn div(self, other: $t) -> $t { self / other } } + + forward_ref_binop! { impl Div, div for $t, $t } )*) } @@ -356,6 +438,8 @@ macro_rules! rem_impl { #[inline] fn rem(self, other: $t) -> $t { self % other } } + + forward_ref_binop! { impl Rem, rem for $t, $t } )*) } @@ -371,6 +455,8 @@ macro_rules! rem_float_impl { unsafe { $fmod(self, other) } } } + + forward_ref_binop! { impl Rem, rem for $t, $t } } } @@ -429,6 +515,8 @@ macro_rules! neg_impl { #[stable] fn neg(self) -> $t { -self } } + + forward_ref_unop! { impl Neg, neg for $t } )*) } @@ -441,6 +529,8 @@ macro_rules! neg_uint_impl { #[inline] fn neg(self) -> $t { -(self as $t_signed) as $t } } + + forward_ref_unop! { impl Neg, neg for $t } } } @@ -502,6 +592,8 @@ macro_rules! not_impl { #[inline] fn not(self) -> $t { !self } } + + forward_ref_unop! { impl Not, not for $t } )*) } @@ -555,6 +647,8 @@ macro_rules! bitand_impl { #[inline] fn bitand(self, rhs: $t) -> $t { self & rhs } } + + forward_ref_binop! { impl BitAnd, bitand for $t, $t } )*) } @@ -608,6 +702,8 @@ macro_rules! bitor_impl { #[inline] fn bitor(self, rhs: $t) -> $t { self | rhs } } + + forward_ref_binop! { impl BitOr, bitor for $t, $t } )*) } @@ -661,6 +757,8 @@ macro_rules! bitxor_impl { #[inline] fn bitxor(self, other: $t) -> $t { self ^ other } } + + forward_ref_binop! { impl BitXor, bitxor for $t, $t } )*) } @@ -716,6 +814,8 @@ macro_rules! shl_impl { self << other } } + + forward_ref_binop! { impl Shl, shl for $t, $f } ) } @@ -795,6 +895,8 @@ macro_rules! shr_impl { self >> other } } + + forward_ref_binop! { impl Shr, shr for $t, $f } ) } From 39676c8bf0ad0fe7249f788ab6ab6790360af73c Mon Sep 17 00:00:00 2001 From: Sebastian Gesemann Date: Fri, 16 Jan 2015 08:31:01 +0100 Subject: [PATCH 017/211] Fix/update core::ops module documentation w.r.t. operator traits * Not all traits are part of the prelude anymore * We switched from pass-by-reference to pass-by-value for most traits * Add some explanations around pass-by-value traits in the context of generic code and additional implementations for reference types. --- src/libcore/ops.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 144019147af7e..88e0aac2b1231 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -13,12 +13,20 @@ //! Implementing these traits allows you to get an effect similar to //! overloading operators. //! -//! The values for the right hand side of an operator are automatically -//! borrowed, so `a + b` is sugar for `a.add(&b)`. -//! -//! All of these traits are imported by the prelude, so they are available in +//! Some of these traits are imported by the prelude, so they are available in //! every Rust program. //! +//! Many of the operators take their operands by value. In non-generic +//! contexts involving built-in types, this is usually not a problem. +//! However, using these operators in generic code, requires some +//! attention if values have to be reused as opposed to letting the operators +//! consume them. One option is to occasionally use `clone()`. +//! Another option is to rely on the types involved providing additional +//! operator implementations for references. For example, for a user-defined +//! type `T` which is supposed to support addition, it is probably a good +//! idea to have both `T` and `&T` implement the traits `Add` and `Add<&T>` +//! so that generic code can be written without unnecessary cloning. +//! //! # Example //! //! This example creates a `Point` struct that implements `Add` and `Sub`, and then From c6c14b928f45302238f4dd4e32ee97e50af1bd6e Mon Sep 17 00:00:00 2001 From: Sebastian Gesemann Date: Fri, 16 Jan 2015 11:22:46 +0100 Subject: [PATCH 018/211] inline forward_xxx_xxx_binop macros as per suggestion --- src/libcore/ops.rs | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 88e0aac2b1231..2007971d84dfb 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -120,9 +120,9 @@ macro_rules! forward_ref_unop { } } -// implements the binary operator "&T op U" -// based on "T + U" where T and U are expected `Copy`able -macro_rules! forward_ref_val_binop { +// implements binary operators "&T op U", "T op &U", "&T op &U" +// based on "T op U" where T and U are expected to be `Copy`able +macro_rules! forward_ref_binop { (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { #[unstable] impl<'a> $imp<$u> for &'a $t { @@ -133,13 +133,7 @@ macro_rules! forward_ref_val_binop { $imp::$method(*self, other) } } - } -} -// implements the binary operator "T op &U" -// based on "T + U" where T and U are expected `Copy`able -macro_rules! forward_val_ref_binop { - (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { #[unstable] impl<'a> $imp<&'a $u> for $t { type Output = <$t as $imp<$u>>::Output; @@ -149,13 +143,7 @@ macro_rules! forward_val_ref_binop { $imp::$method(self, *other) } } - } -} -// implements the binary operator "&T op &U" -// based on "T + U" where T and U are expected `Copy`able -macro_rules! forward_ref_ref_binop { - (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { #[unstable] impl<'a, 'b> $imp<&'a $u> for &'b $t { type Output = <$t as $imp<$u>>::Output; @@ -168,16 +156,6 @@ macro_rules! forward_ref_ref_binop { } } -// implements binary operators "&T op U", "T op &U", "&T op &U" -// based on "T + U" where T and U are expected `Copy`able -macro_rules! forward_ref_binop { - (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { - forward_ref_val_binop! { impl $imp, $method for $t, $u } - forward_val_ref_binop! { impl $imp, $method for $t, $u } - forward_ref_ref_binop! { impl $imp, $method for $t, $u } - } -} - /// The `Add` trait is used to specify the functionality of `+`. /// /// # Example From 3d59a476e56b855fb27405016f0592ca7913b624 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 15 Jan 2015 00:27:38 +1100 Subject: [PATCH 019/211] Support SSE types in extern {} better. This seems to work on x86-64, but I am not able to test on other platforms. cc #20043 --- src/librustc_trans/trans/cabi_aarch64.rs | 13 ++++- src/librustc_trans/trans/cabi_arm.rs | 18 ++++++- src/librustc_trans/trans/cabi_mips.rs | 15 +++++- src/librustc_trans/trans/cabi_x86_64.rs | 66 +++++++++++++++++++++--- 4 files changed, 102 insertions(+), 10 deletions(-) diff --git a/src/librustc_trans/trans/cabi_aarch64.rs b/src/librustc_trans/trans/cabi_aarch64.rs index 66308503b818d..ca9b3791ea980 100644 --- a/src/librustc_trans/trans/cabi_aarch64.rs +++ b/src/librustc_trans/trans/cabi_aarch64.rs @@ -11,7 +11,7 @@ #![allow(non_upper_case_globals)] use llvm; -use llvm::{Integer, Pointer, Float, Double, Struct, Array}; +use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector}; use llvm::{StructRetAttribute, ZExtAttribute}; use trans::cabi::{FnType, ArgType}; use trans::context::CrateContext; @@ -50,6 +50,11 @@ fn ty_align(ty: Type) -> uint { let elt = ty.element_type(); ty_align(elt) } + Vector => { + let len = ty.vector_length(); + let elt = ty.element_type(); + ty_align(elt) * len + } _ => panic!("ty_align: unhandled type") } } @@ -80,6 +85,12 @@ fn ty_size(ty: Type) -> uint { let eltsz = ty_size(elt); len * eltsz } + Vector => { + let len = ty.vector_length(); + let elt = ty.element_type(); + let eltsz = ty_size(elt); + len * eltsz + } _ => panic!("ty_size: unhandled type") } } diff --git a/src/librustc_trans/trans/cabi_arm.rs b/src/librustc_trans/trans/cabi_arm.rs index 830771d7397e2..2da0c3787e8c5 100644 --- a/src/librustc_trans/trans/cabi_arm.rs +++ b/src/librustc_trans/trans/cabi_arm.rs @@ -11,7 +11,7 @@ #![allow(non_upper_case_globals)] use llvm; -use llvm::{Integer, Pointer, Float, Double, Struct, Array}; +use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector}; use llvm::{StructRetAttribute, ZExtAttribute}; use trans::cabi::{FnType, ArgType}; use trans::context::CrateContext; @@ -57,6 +57,11 @@ fn general_ty_align(ty: Type) -> uint { let elt = ty.element_type(); general_ty_align(elt) } + Vector => { + let len = ty.vector_length(); + let elt = ty.element_type(); + general_ty_align(elt) * len + } _ => panic!("ty_align: unhandled type") } } @@ -90,6 +95,11 @@ fn ios_ty_align(ty: Type) -> uint { let elt = ty.element_type(); ios_ty_align(elt) } + Vector => { + let len = ty.vector_length(); + let elt = ty.element_type(); + ios_ty_align(elt) * len + } _ => panic!("ty_align: unhandled type") } } @@ -123,6 +133,12 @@ fn ty_size(ty: Type, align_fn: TyAlignFn) -> uint { let eltsz = ty_size(elt, align_fn); len * eltsz } + Vector => { + let len = ty.vector_length(); + let elt = ty.element_type(); + let eltsz = ty_size(elt, align_fn); + len * eltsz + } _ => panic!("ty_size: unhandled type") } } diff --git a/src/librustc_trans/trans/cabi_mips.rs b/src/librustc_trans/trans/cabi_mips.rs index 4dfe8daf33946..0a0ab784f57fc 100644 --- a/src/librustc_trans/trans/cabi_mips.rs +++ b/src/librustc_trans/trans/cabi_mips.rs @@ -13,7 +13,7 @@ use libc::c_uint; use std::cmp; use llvm; -use llvm::{Integer, Pointer, Float, Double, Struct, Array}; +use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector}; use llvm::{StructRetAttribute, ZExtAttribute}; use trans::cabi::{ArgType, FnType}; use trans::context::CrateContext; @@ -50,7 +50,12 @@ fn ty_align(ty: Type) -> uint { let elt = ty.element_type(); ty_align(elt) } - _ => panic!("ty_size: unhandled type") + Vector => { + let len = ty.vector_length(); + let elt = ty.element_type(); + ty_align(elt) * len + } + _ => panic!("ty_align: unhandled type") } } @@ -80,6 +85,12 @@ fn ty_size(ty: Type) -> uint { let eltsz = ty_size(elt); len * eltsz } + Vector => { + let len = ty.vector_length(); + let elt = ty.element_type(); + let eltsz = ty_size(elt); + len * eltsz + } _ => panic!("ty_size: unhandled type") } } diff --git a/src/librustc_trans/trans/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs index c0e80ff08a22e..72ace5f95617d 100644 --- a/src/librustc_trans/trans/cabi_x86_64.rs +++ b/src/librustc_trans/trans/cabi_x86_64.rs @@ -16,7 +16,7 @@ use self::RegClass::*; use llvm; use llvm::{Integer, Pointer, Float, Double}; -use llvm::{Struct, Array, Attribute}; +use llvm::{Struct, Array, Attribute, Vector}; use llvm::{StructRetAttribute, ByValAttribute, ZExtAttribute}; use trans::cabi::{ArgType, FnType}; use trans::context::CrateContext; @@ -114,7 +114,12 @@ fn classify_ty(ty: Type) -> Vec { let elt = ty.element_type(); ty_align(elt) } - _ => panic!("ty_size: unhandled type") + Vector => { + let len = ty.vector_length(); + let elt = ty.element_type(); + ty_align(elt) * len + } + _ => panic!("ty_align: unhandled type") } } @@ -143,6 +148,13 @@ fn classify_ty(ty: Type) -> Vec { let eltsz = ty_size(elt); len * eltsz } + Vector => { + let len = ty.vector_length(); + let elt = ty.element_type(); + let eltsz = ty_size(elt); + len * eltsz + } + _ => panic!("ty_size: unhandled type") } } @@ -175,6 +187,12 @@ fn classify_ty(ty: Type) -> Vec { (_, X87Up) | (_, ComplexX87) => Memory, + (SSEFv, SSEUp) | + (SSEFs, SSEUp) | + (SSEDv, SSEUp) | + (SSEDs, SSEUp) | + (SSEInt(_), SSEUp) => return, + (_, _) => newv }; cls[i] = to_write; @@ -240,6 +258,27 @@ fn classify_ty(ty: Type) -> Vec { i += 1u; } } + Vector => { + let len = ty.vector_length(); + let elt = ty.element_type(); + let eltsz = ty_size(elt); + let mut reg = match elt.kind() { + Integer => SSEInt, + Float => SSEFv, + Double => SSEDv, + _ => panic!("classify: unhandled vector element type") + }; + + let mut i = 0u; + while i < len { + unify(cls, ix + (off + i * eltsz) / 8, reg); + + // everything after the first one is the upper + // half of a register. + reg = SSEUp; + i += 1u; + } + } _ => panic!("classify: unhandled type") } } @@ -248,7 +287,7 @@ fn classify_ty(ty: Type) -> Vec { let mut i = 0u; let ty_kind = ty.kind(); let e = cls.len(); - if cls.len() > 2u && (ty_kind == Struct || ty_kind == Array) { + if cls.len() > 2u && (ty_kind == Struct || ty_kind == Array || ty_kind == Vector) { if cls[i].is_sse() { i += 1u; while i < e { @@ -320,9 +359,19 @@ fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type { Int => { tys.push(Type::i64(ccx)); } - SSEFv => { + SSEFv | SSEDv | SSEInt => { + let (elts_per_word, elt_ty) = match cls[i] { + SSEFv => (2, Type::f32(ccx)), + SSEDv => (1, Type::f64(ccx)), + // FIXME: need to handle the element types, since + // C doesn't distinguish between the contained + // types of the vector at all; normalise to u8, + // maybe? + SSEInt => panic!("llregtype: SSEInt not yet supported"), + _ => unreachable!(), + }; let vec_len = llvec_len(&cls[(i + 1u)..]); - let vec_ty = Type::vector(&Type::f32(ccx), (vec_len * 2u) as u64); + let vec_ty = Type::vector(&elt_ty, (vec_len * elts_per_word) as u64); tys.push(vec_ty); i += vec_len; continue; @@ -337,7 +386,12 @@ fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type { } i += 1u; } - return Type::struct_(ccx, tys.as_slice(), false); + if tys.len() == 1 && tys[0].kind() == Vector { + // if the type contains only a vector, pass it as that vector. + tys[0] + } else { + Type::struct_(ccx, tys.as_slice(), false) + } } pub fn compute_abi_info(ccx: &CrateContext, From 5edbe1f5ddab26a5a8ea75d447d5a37d8f7a3347 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 15 Jan 2015 01:08:22 +1100 Subject: [PATCH 020/211] Add `Type::int_width` for retrieving integer's bit width. --- src/librustc_trans/trans/base.rs | 32 +++++++++++------------- src/librustc_trans/trans/cabi_aarch64.rs | 13 ++-------- src/librustc_trans/trans/cabi_arm.rs | 19 +++----------- src/librustc_trans/trans/cabi_mips.rs | 12 ++------- src/librustc_trans/trans/cabi_x86_64.rs | 13 ++-------- src/librustc_trans/trans/expr.rs | 22 ++++++++-------- src/librustc_trans/trans/type_.rs | 7 ++++++ 7 files changed, 41 insertions(+), 77 deletions(-) diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index ea98d6bb74e95..cee6018d9c074 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -847,26 +847,24 @@ pub fn cast_shift_rhs(op: ast::BinOp, G: FnOnce(ValueRef, Type) -> ValueRef, { // Shifts may have any size int on the rhs - unsafe { - if ast_util::is_shift_binop(op) { - let mut rhs_llty = val_ty(rhs); - let mut lhs_llty = val_ty(lhs); - if rhs_llty.kind() == Vector { rhs_llty = rhs_llty.element_type() } - if lhs_llty.kind() == Vector { lhs_llty = lhs_llty.element_type() } - let rhs_sz = llvm::LLVMGetIntTypeWidth(rhs_llty.to_ref()); - let lhs_sz = llvm::LLVMGetIntTypeWidth(lhs_llty.to_ref()); - if lhs_sz < rhs_sz { - trunc(rhs, lhs_llty) - } else if lhs_sz > rhs_sz { - // FIXME (#1877: If shifting by negative - // values becomes not undefined then this is wrong. - zext(rhs, lhs_llty) - } else { - rhs - } + if ast_util::is_shift_binop(op) { + let mut rhs_llty = val_ty(rhs); + let mut lhs_llty = val_ty(lhs); + if rhs_llty.kind() == Vector { rhs_llty = rhs_llty.element_type() } + if lhs_llty.kind() == Vector { lhs_llty = lhs_llty.element_type() } + let rhs_sz = rhs_llty.int_width(); + let lhs_sz = lhs_llty.int_width(); + if lhs_sz < rhs_sz { + trunc(rhs, lhs_llty) + } else if lhs_sz > rhs_sz { + // FIXME (#1877: If shifting by negative + // values becomes not undefined then this is wrong. + zext(rhs, lhs_llty) } else { rhs } + } else { + rhs } } diff --git a/src/librustc_trans/trans/cabi_aarch64.rs b/src/librustc_trans/trans/cabi_aarch64.rs index ca9b3791ea980..3485e29707aa2 100644 --- a/src/librustc_trans/trans/cabi_aarch64.rs +++ b/src/librustc_trans/trans/cabi_aarch64.rs @@ -10,7 +10,6 @@ #![allow(non_upper_case_globals)] -use llvm; use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector}; use llvm::{StructRetAttribute, ZExtAttribute}; use trans::cabi::{FnType, ArgType}; @@ -30,11 +29,7 @@ fn align(off: uint, ty: Type) -> uint { fn ty_align(ty: Type) -> uint { match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } + Integer => ((ty.int_width() as uint) + 7) / 8, Pointer => 8, Float => 4, Double => 8, @@ -61,11 +56,7 @@ fn ty_align(ty: Type) -> uint { fn ty_size(ty: Type) -> uint { match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } + Integer => ((ty.int_width() as uint) + 7) / 8, Pointer => 8, Float => 4, Double => 8, diff --git a/src/librustc_trans/trans/cabi_arm.rs b/src/librustc_trans/trans/cabi_arm.rs index 2da0c3787e8c5..13c70875f689a 100644 --- a/src/librustc_trans/trans/cabi_arm.rs +++ b/src/librustc_trans/trans/cabi_arm.rs @@ -10,7 +10,6 @@ #![allow(non_upper_case_globals)] -use llvm; use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector}; use llvm::{StructRetAttribute, ZExtAttribute}; use trans::cabi::{FnType, ArgType}; @@ -37,11 +36,7 @@ fn align(off: uint, ty: Type, align_fn: TyAlignFn) -> uint { fn general_ty_align(ty: Type) -> uint { match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } + Integer => ((ty.int_width() as uint) + 7) / 8, Pointer => 4, Float => 4, Double => 8, @@ -75,11 +70,7 @@ fn general_ty_align(ty: Type) -> uint { // /iPhoneOSABIReference/Articles/ARMv6FunctionCallingConventions.html fn ios_ty_align(ty: Type) -> uint { match ty.kind() { - Integer => { - unsafe { - cmp::min(4, ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8) - } - } + Integer => cmp::min(4, ((ty.int_width() as uint) + 7) / 8), Pointer => 4, Float => 4, Double => 4, @@ -106,11 +97,7 @@ fn ios_ty_align(ty: Type) -> uint { fn ty_size(ty: Type, align_fn: TyAlignFn) -> uint { match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } + Integer => ((ty.int_width() as uint) + 7) / 8, Pointer => 4, Float => 4, Double => 8, diff --git a/src/librustc_trans/trans/cabi_mips.rs b/src/librustc_trans/trans/cabi_mips.rs index 0a0ab784f57fc..70b29b5fb758e 100644 --- a/src/librustc_trans/trans/cabi_mips.rs +++ b/src/librustc_trans/trans/cabi_mips.rs @@ -30,11 +30,7 @@ fn align(off: uint, ty: Type) -> uint { fn ty_align(ty: Type) -> uint { match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } + Integer => ((ty.int_width() as uint) + 7) / 8, Pointer => 4, Float => 4, Double => 8, @@ -61,11 +57,7 @@ fn ty_align(ty: Type) -> uint { fn ty_size(ty: Type) -> uint { match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } + Integer => ((ty.int_width() as uint) + 7) / 8, Pointer => 4, Float => 4, Double => 8, diff --git a/src/librustc_trans/trans/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs index 72ace5f95617d..d8bd57785d213 100644 --- a/src/librustc_trans/trans/cabi_x86_64.rs +++ b/src/librustc_trans/trans/cabi_x86_64.rs @@ -14,7 +14,6 @@ #![allow(non_upper_case_globals)] use self::RegClass::*; -use llvm; use llvm::{Integer, Pointer, Float, Double}; use llvm::{Struct, Array, Attribute, Vector}; use llvm::{StructRetAttribute, ByValAttribute, ZExtAttribute}; @@ -94,11 +93,7 @@ fn classify_ty(ty: Type) -> Vec { fn ty_align(ty: Type) -> uint { match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } + Integer => ((ty.int_width() as uint) + 7) / 8, Pointer => 8, Float => 4, Double => 8, @@ -125,11 +120,7 @@ fn classify_ty(ty: Type) -> Vec { fn ty_size(ty: Type) -> uint { match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } + Integer => (ty.int_width() as uint + 7) / 8, Pointer => 8, Float => 4, Double => 8, diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index ac50445be2f9b..433d989f22c96 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1906,18 +1906,16 @@ fn int_cast(bcx: Block, signed: bool) -> ValueRef { let _icx = push_ctxt("int_cast"); - unsafe { - let srcsz = llvm::LLVMGetIntTypeWidth(llsrctype.to_ref()); - let dstsz = llvm::LLVMGetIntTypeWidth(lldsttype.to_ref()); - return if dstsz == srcsz { - BitCast(bcx, llsrc, lldsttype) - } else if srcsz > dstsz { - TruncOrBitCast(bcx, llsrc, lldsttype) - } else if signed { - SExtOrBitCast(bcx, llsrc, lldsttype) - } else { - ZExtOrBitCast(bcx, llsrc, lldsttype) - }; + let srcsz = llsrctype.int_width(); + let dstsz = lldsttype.int_width(); + return if dstsz == srcsz { + BitCast(bcx, llsrc, lldsttype) + } else if srcsz > dstsz { + TruncOrBitCast(bcx, llsrc, lldsttype) + } else if signed { + SExtOrBitCast(bcx, llsrc, lldsttype) + } else { + ZExtOrBitCast(bcx, llsrc, lldsttype) } } diff --git a/src/librustc_trans/trans/type_.rs b/src/librustc_trans/trans/type_.rs index acb1623db330c..0124ab72f6b08 100644 --- a/src/librustc_trans/trans/type_.rs +++ b/src/librustc_trans/trans/type_.rs @@ -333,6 +333,13 @@ impl Type { _ => panic!("llvm_float_width called on a non-float type") } } + + /// Retrieve the bit width of the integer type `self`. + pub fn int_width(&self) -> u64 { + unsafe { + llvm::LLVMGetIntTypeWidth(self.to_ref()) as u64 + } + } } From 7d4f358de7de97b443a97e1f18a16781d472bbda Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 15 Jan 2015 13:49:41 +1100 Subject: [PATCH 021/211] Support SSE with integer types in x86-64 FFI. Unlike the intrinics in C, this types the SSE values base on integer size. This matches the LLVM intrinsics which have concrete vector types (`<4 x i32>` etc.), and is no loss of expressivity: if one is using a C function that really takes an untyped integral SSE value, just give it whatever Rust type makes most sense. --- src/librustc_trans/trans/cabi_x86_64.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/librustc_trans/trans/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs index d8bd57785d213..980a70256e936 100644 --- a/src/librustc_trans/trans/cabi_x86_64.rs +++ b/src/librustc_trans/trans/cabi_x86_64.rs @@ -32,7 +32,7 @@ enum RegClass { SSEFv, SSEDs, SSEDv, - SSEInt, + SSEInt(/* bitwidth */ u64), /// Data that can appear in the upper half of an SSE register. SSEUp, X87, @@ -57,7 +57,7 @@ impl TypeMethods for Type { impl RegClass { fn is_sse(&self) -> bool { match *self { - SSEFs | SSEFv | SSEDs | SSEDv => true, + SSEFs | SSEFv | SSEDs | SSEDv | SSEInt(_) => true, _ => false } } @@ -254,7 +254,7 @@ fn classify_ty(ty: Type) -> Vec { let elt = ty.element_type(); let eltsz = ty_size(elt); let mut reg = match elt.kind() { - Integer => SSEInt, + Integer => SSEInt(elt.int_width()), Float => SSEFv, Double => SSEDv, _ => panic!("classify: unhandled vector element type") @@ -350,19 +350,19 @@ fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type { Int => { tys.push(Type::i64(ccx)); } - SSEFv | SSEDv | SSEInt => { + SSEFv | SSEDv | SSEInt(_) => { let (elts_per_word, elt_ty) = match cls[i] { SSEFv => (2, Type::f32(ccx)), SSEDv => (1, Type::f64(ccx)), - // FIXME: need to handle the element types, since - // C doesn't distinguish between the contained - // types of the vector at all; normalise to u8, - // maybe? - SSEInt => panic!("llregtype: SSEInt not yet supported"), + SSEInt(bits) => { + assert!(bits == 8 || bits == 16 || bits == 32 || bits == 64, + "llreg_ty: unsupported SSEInt width {}", bits); + (64 / bits, Type::ix(ccx, bits)) + } _ => unreachable!(), }; let vec_len = llvec_len(&cls[(i + 1u)..]); - let vec_ty = Type::vector(&elt_ty, (vec_len * elts_per_word) as u64); + let vec_ty = Type::vector(&elt_ty, vec_len as u64 * elts_per_word); tys.push(vec_ty); i += vec_len; continue; From 9e83ae931c802608962fb5f9c90220d80d2eaa1c Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 15 Jan 2015 14:43:24 +1100 Subject: [PATCH 022/211] Put vector types in regs for arm & mips FFI. This seems to match what clang does on arm, but I cannot do any experimentation with mips, but it matches how the LLVM intrinsics are defined in any case... --- src/librustc_trans/trans/cabi_aarch64.rs | 3 ++- src/librustc_trans/trans/cabi_arm.rs | 3 ++- src/librustc_trans/trans/cabi_mips.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/librustc_trans/trans/cabi_aarch64.rs b/src/librustc_trans/trans/cabi_aarch64.rs index 3485e29707aa2..0d8ef9e2fc92e 100644 --- a/src/librustc_trans/trans/cabi_aarch64.rs +++ b/src/librustc_trans/trans/cabi_aarch64.rs @@ -139,7 +139,8 @@ fn is_reg_ty(ty: Type) -> bool { Integer | Pointer | Float - | Double => true, + | Double + | Vector => true, _ => false } } diff --git a/src/librustc_trans/trans/cabi_arm.rs b/src/librustc_trans/trans/cabi_arm.rs index 13c70875f689a..7d1a8ab1452ec 100644 --- a/src/librustc_trans/trans/cabi_arm.rs +++ b/src/librustc_trans/trans/cabi_arm.rs @@ -169,7 +169,8 @@ fn is_reg_ty(ty: Type) -> bool { Integer | Pointer | Float - | Double => true, + | Double + | Vector => true, _ => false } } diff --git a/src/librustc_trans/trans/cabi_mips.rs b/src/librustc_trans/trans/cabi_mips.rs index 70b29b5fb758e..776be8855cbf7 100644 --- a/src/librustc_trans/trans/cabi_mips.rs +++ b/src/librustc_trans/trans/cabi_mips.rs @@ -123,7 +123,8 @@ fn is_reg_ty(ty: Type) -> bool { Integer | Pointer | Float - | Double => true, + | Double + | Vector => true, _ => false }; } From 0a55aacc077f26f5703035f7c5395d083db3d355 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 16 Jan 2015 22:27:40 +1100 Subject: [PATCH 023/211] Prefer implemented traits in suggestions. If `a.method();` can't be resolved, we first look for implemented traits globally and suggest those. If there are no such traits found, we only then fall back to suggesting from the unfiltered list of traits. --- src/librustc_typeck/check/method/mod.rs | 7 +- src/librustc_typeck/check/method/probe.rs | 64 ++++++++++++++++++- src/librustc_typeck/check/method/suggest.rs | 57 +++++++++++------ .../auxiliary/no_method_suggested_traits.rs | 7 +- .../no-method-suggested-traits.rs | 44 +++++++++++-- 5 files changed, 148 insertions(+), 31 deletions(-) diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index a28b4ac34753b..345bc5fd2aa60 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -38,8 +38,9 @@ mod suggest; pub enum MethodError { // Did not find an applicable method, but we did find various - // static methods that may apply. - NoMatch(Vec), + // static methods that may apply, as well as a list of + // not-in-scope traits which may work. + NoMatch(Vec, Vec), // Multiple methods might apply. Ambiguity(Vec), @@ -65,7 +66,7 @@ pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, { match probe::probe(fcx, span, method_name, self_ty, call_expr_id) { Ok(_) => true, - Err(NoMatch(_)) => false, + Err(NoMatch(_, _)) => false, Err(Ambiguity(_)) => true, } } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index dc4d6c9a826c9..9df8875152e3b 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -11,6 +11,7 @@ use super::{MethodError,Ambiguity,NoMatch}; use super::MethodIndex; use super::{CandidateSource,ImplSource,TraitSource}; +use super::suggest; use check; use check::{FnCtxt, NoPreference}; @@ -25,6 +26,7 @@ use middle::infer::InferCtxt; use syntax::ast; use syntax::codemap::{Span, DUMMY_SP}; use std::collections::HashSet; +use std::mem; use std::rc::Rc; use util::ppaux::Repr; @@ -42,6 +44,7 @@ struct ProbeContext<'a, 'tcx:'a> { extension_candidates: Vec>, impl_dups: HashSet, static_candidates: Vec, + all_traits_search: bool, } struct CandidateStep<'tcx> { @@ -127,7 +130,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // take place in the `fcx.infcx().probe` below. let steps = match create_steps(fcx, span, self_ty) { Some(steps) => steps, - None => return Err(NoMatch(Vec::new())), + None => return Err(NoMatch(Vec::new(), Vec::new())), }; // Create a list of simplified self types, if we can. @@ -208,9 +211,17 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { steps: Rc::new(steps), opt_simplified_steps: opt_simplified_steps, static_candidates: Vec::new(), + all_traits_search: false, } } + fn reset(&mut self) { + self.inherent_candidates.clear(); + self.extension_candidates.clear(); + self.impl_dups.clear(); + self.static_candidates.clear(); + } + fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.fcx.tcx() } @@ -446,6 +457,15 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { } } + fn assemble_extension_candidates_for_all_traits(&mut self) { + let mut duplicates = HashSet::new(); + for trait_info in suggest::all_traits(self.fcx.ccx) { + if duplicates.insert(trait_info.def_id) { + self.assemble_extension_candidates_for_trait(trait_info.def_id) + } + } + } + fn assemble_extension_candidates_for_trait(&mut self, trait_def_id: ast::DefId) { debug!("assemble_extension_candidates_for_trait(trait_def_id={})", @@ -715,7 +735,47 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { } } - Err(NoMatch(self.static_candidates)) + let static_candidates = mem::replace(&mut self.static_candidates, vec![]); + + let out_of_scope_traits = if !self.all_traits_search { + // things failed, and we haven't yet looked through all + // traits, so lets do that now: + self.reset(); + self.all_traits_search = true; + + let span = self.span; + let tcx = self.tcx(); + + self.assemble_extension_candidates_for_all_traits(); + + match self.pick() { + Ok(p) => vec![p.method_ty.container.id()], + Err(Ambiguity(v)) => v.into_iter().map(|source| { + match source { + TraitSource(id) => id, + ImplSource(impl_id) => { + match ty::trait_id_of_impl(tcx, impl_id) { + Some(id) => id, + None => tcx.sess.span_bug(span, + "found inherent method when looking \ + at traits") + } + } + } + }).collect(), + // it'd be really weird for this assertion to trigger, + // given the `vec![]` in the else branch below + Err(NoMatch(_, others)) => { + assert!(others.is_empty()); + vec![] + } + } + } else { + // we've just looked through all traits and didn't find + // anything at all. + vec![] + }; + Err(NoMatch(static_candidates, out_of_scope_traits)) } fn pick_step(&mut self, step: &CandidateStep<'tcx>) -> Option> { diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index aab1fa2a95817..013c6e2f953a0 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -35,7 +35,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, error: MethodError) { match error { - MethodError::NoMatch(static_sources) => { + MethodError::NoMatch(static_sources, out_of_scope_traits) => { let cx = fcx.tcx(); let method_ustring = method_name.user_string(cx); @@ -75,7 +75,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, report_candidates(fcx, span, method_name, static_sources); } - suggest_traits_to_import(fcx, span, rcvr_ty, method_name) + suggest_traits_to_import(fcx, span, rcvr_ty, method_name, out_of_scope_traits) } MethodError::Ambiguity(sources) => { @@ -136,10 +136,35 @@ pub type AllTraitsVec = Vec; fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, span: Span, _rcvr_ty: Ty<'tcx>, - method_name: ast::Name) + method_name: ast::Name, + valid_out_of_scope_traits: Vec) { let tcx = fcx.tcx(); + let method_ustring = method_name.user_string(tcx); + + if !valid_out_of_scope_traits.is_empty() { + let mut candidates = valid_out_of_scope_traits; + candidates.sort(); + let msg = format!( + "methods from traits can only be called if the trait is in scope; \ + the following {traits_are} implemented and {define} a method `{name}`:", + traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"}, + define = if candidates.len() == 1 {"defines"} else {"define"}, + name = method_ustring); + + fcx.sess().fileline_help(span, &msg[]); + + for (i, trait_did) in candidates.iter().enumerate() { + fcx.sess().fileline_help(span, + &*format!("candidate #{}: `{}`", + i + 1, + ty::item_path_str(fcx.tcx(), *trait_did))) + + } + return + } + // there's no implemented traits, so lets suggest some traits to implement let mut candidates = all_traits(fcx.ccx) .filter(|info| trait_method(tcx, info.def_id, method_name).is_some()) .collect::>(); @@ -148,22 +173,16 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // sort from most relevant to least relevant candidates.sort_by(|a, b| a.cmp(b).reverse()); - let method_ustring = method_name.user_string(tcx); + let msg = format!( + "methods from traits can only be called if the trait is implemented and \ + in scope; no such traits are but the following {traits_define} a method `{name}`:", + traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"}, + name = method_ustring); - span_help!(fcx.sess(), span, - "methods from traits can only be called if the trait is implemented \ - and in scope; the following trait{s} define{inv_s} a method `{name}`:", - s = if candidates.len() == 1 {""} else {"s"}, - inv_s = if candidates.len() == 1 {"s"} else {""}, - name = method_ustring); + fcx.sess().fileline_help(span, &msg[]); for (i, trait_info) in candidates.iter().enumerate() { - // provide a good-as-possible span; the span of - // the trait if it is local, or the span of the - // method call itself if not - let trait_span = fcx.tcx().map.def_id_span(trait_info.def_id, span); - - fcx.sess().fileline_help(trait_span, + fcx.sess().fileline_help(span, &*format!("candidate #{}: `{}`", i + 1, ty::item_path_str(fcx.tcx(), trait_info.def_id))) @@ -173,7 +192,7 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, #[derive(Copy)] pub struct TraitInfo { - def_id: ast::DefId, + pub def_id: ast::DefId, } impl TraitInfo { @@ -206,7 +225,7 @@ impl Ord for TraitInfo { } /// Retrieve all traits in this crate and any dependent crates. -fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> { +pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> { if ccx.all_traits.borrow().is_none() { use syntax::visit; @@ -268,7 +287,7 @@ fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> { } } -struct AllTraits<'a> { +pub struct AllTraits<'a> { borrow: cell::Ref<'a Option>, idx: usize } diff --git a/src/test/auxiliary/no_method_suggested_traits.rs b/src/test/auxiliary/no_method_suggested_traits.rs index fdeace00d4ca2..328561495eef0 100644 --- a/src/test/auxiliary/no_method_suggested_traits.rs +++ b/src/test/auxiliary/no_method_suggested_traits.rs @@ -12,8 +12,13 @@ pub use reexport::Reexported; pub mod foo { pub trait PubPub { - fn method(&self); + fn method(&self) {} + + fn method3(&self) {} } + + impl PubPub for u32 {} + impl PubPub for i32 {} } pub mod bar { trait PubPriv { diff --git a/src/test/compile-fail/no-method-suggested-traits.rs b/src/test/compile-fail/no-method-suggested-traits.rs index 2565d2b730267..277800778a87e 100644 --- a/src/test/compile-fail/no-method-suggested-traits.rs +++ b/src/test/compile-fail/no-method-suggested-traits.rs @@ -13,18 +13,50 @@ extern crate no_method_suggested_traits; mod foo { - trait Bar { //~ HELP `foo::Bar` - fn method(&self); + trait Bar { + fn method(&self) {} + + fn method2(&self) {} } + + impl Bar for u32 {} + + impl Bar for char {} } fn main() { 1u32.method(); //~^ ERROR does not implement + //~^^ HELP the following traits are implemented and define a method `method` + //~^^^ HELP `foo::Bar` + //~^^^^ HELP `no_method_suggested_traits::foo::PubPub` + + 'a'.method(); + //~^ ERROR does not implement + //~^^ HELP the following trait is implemented and defines a method `method` + //~^^^ HELP `foo::Bar` + + 1i32.method(); + //~^ ERROR does not implement + //~^^ HELP the following trait is implemented and defines a method `method` + //~^^^ HELP `no_method_suggested_traits::foo::PubPub` + + 1u64.method(); + //~^ ERROR does not implement //~^^ HELP the following traits define a method `method` + //~^^^ HELP `foo::Bar` + //~^^^^ HELP `no_method_suggested_traits::foo::PubPub` + //~^^^^^ HELP `no_method_suggested_traits::reexport::Reexported` + //~^^^^^^ HELP `no_method_suggested_traits::bar::PubPriv` + //~^^^^^^^ HELP `no_method_suggested_traits::qux::PrivPub` + //~^^^^^^^^ HELP `no_method_suggested_traits::quz::PrivPriv` + + 1u64.method2(); + //~^ ERROR does not implement + //~^^ HELP the following trait defines a method `method2` + //~^^^ HELP `foo::Bar` + 1u64.method3(); + //~^ ERROR does not implement + //~^^ HELP the following trait defines a method `method3` //~^^^ HELP `no_method_suggested_traits::foo::PubPub` - //~^^^^ HELP `no_method_suggested_traits::reexport::Reexported` - //~^^^^^ HELP `no_method_suggested_traits::bar::PubPriv` - //~^^^^^^ HELP `no_method_suggested_traits::qux::PrivPub` - //~^^^^^^^ HELP `no_method_suggested_traits::quz::PrivPriv` } From a9decbdc443f120d1ca2efb1798bfc20bad7c2d4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 15 Jan 2015 10:47:17 -0800 Subject: [PATCH 024/211] rustc: Move the privacy pass to its own crate --- mk/crates.mk | 12 +- src/librustc/middle/privacy.rs | 1562 +------------------------------ src/librustc_driver/driver.rs | 3 +- src/librustc_driver/lib.rs | 1 + src/librustc_privacy/lib.rs | 1598 ++++++++++++++++++++++++++++++++ 5 files changed, 1611 insertions(+), 1565 deletions(-) create mode 100644 src/librustc_privacy/lib.rs diff --git a/mk/crates.mk b/mk/crates.mk index 5b8772c4e0af5..d6cc3598bd51d 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -54,7 +54,7 @@ TARGET_CRATES := libc std flate arena term \ log regex graphviz core rbml alloc \ unicode RUSTC_CRATES := rustc rustc_typeck rustc_borrowck rustc_resolve rustc_driver \ - rustc_trans rustc_back rustc_llvm + rustc_trans rustc_back rustc_llvm rustc_privacy HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc fmt_macros CRATES := $(TARGET_CRATES) $(HOST_CRATES) TOOLS := compiletest rustdoc rustc rustbook @@ -68,12 +68,15 @@ DEPS_std := core libc rand alloc collections unicode \ DEPS_graphviz := std DEPS_syntax := std term serialize log fmt_macros arena libc DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \ - rustc_typeck rustc_resolve log syntax serialize rustc_llvm rustc_trans + rustc_typeck rustc_resolve log syntax serialize rustc_llvm \ + rustc_trans rustc_privacy + DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back \ log syntax serialize rustc_llvm DEPS_rustc_typeck := rustc syntax DEPS_rustc_borrowck := rustc log graphviz syntax DEPS_rustc_resolve := rustc log syntax +DEPS_rustc_privacy := rustc log syntax DEPS_rustc := syntax flate arena serialize getopts rbml \ log graphviz rustc_llvm rustc_back DEPS_rustc_llvm := native:rustllvm libc std @@ -122,12 +125,13 @@ DOC_CRATES := $(filter-out rustc, \ $(filter-out rustc_borrowck, \ $(filter-out rustc_resolve, \ $(filter-out rustc_driver, \ + $(filter-out rustc_privacy, \ $(filter-out log, \ $(filter-out regex, \ $(filter-out getopts, \ - $(filter-out syntax, $(CRATES))))))))))) + $(filter-out syntax, $(CRATES)))))))))))) COMPILER_DOC_CRATES := rustc rustc_trans rustc_borrowck rustc_resolve \ - rustc_typeck rustc_driver syntax + rustc_typeck rustc_driver syntax rustc_privacy # This macro creates some simple definitions for each crate being built, just # some munging of all of the parameters above. diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index b92870cfa42b0..74797dfd77a42 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -11,28 +11,14 @@ //! A pass that checks to make sure private fields and methods aren't used //! outside their scopes. This pass will also generate a set of exported items //! which are available for use externally when compiled as a library. + pub use self::PrivateDep::*; pub use self::ImportUse::*; pub use self::LastPrivate::*; -use self::PrivacyResult::*; -use self::FieldName::*; - -use std::mem::replace; -use metadata::csearch; -use middle::def; -use middle::ty::{self, Ty}; -use middle::ty::{MethodCall, MethodMap, MethodOrigin, MethodParam, MethodTypeParam}; -use middle::ty::{MethodStatic, MethodStaticUnboxedClosure, MethodObject, MethodTraitObject}; use util::nodemap::{DefIdSet, NodeMap, NodeSet}; -use syntax::{ast, ast_map}; -use syntax::ast_util::{is_local, local_def, PostExpansionMethod}; -use syntax::codemap::Span; -use syntax::parse::token; -use syntax::visit::{self, Visitor}; - -type Context<'a, 'tcx> = (&'a MethodMap<'tcx>, &'a def::ExportMap); +use syntax::ast; /// A set of AST nodes exported by the crate. pub type ExportedItems = NodeSet; @@ -84,1547 +70,3 @@ impl LastPrivate { } } } - -/// Result of a checking operation - None => no errors were found. Some => an -/// error and contains the span and message for reporting that error and -/// optionally the same for a note about the error. -type CheckResult = Option<(Span, String, Option<(Span, String)>)>; - -//////////////////////////////////////////////////////////////////////////////// -/// The parent visitor, used to determine what's the parent of what (node-wise) -//////////////////////////////////////////////////////////////////////////////// - -struct ParentVisitor { - parents: NodeMap, - curparent: ast::NodeId, -} - -impl<'v> Visitor<'v> for ParentVisitor { - fn visit_item(&mut self, item: &ast::Item) { - self.parents.insert(item.id, self.curparent); - - let prev = self.curparent; - match item.node { - ast::ItemMod(..) => { self.curparent = item.id; } - // Enum variants are parented to the enum definition itself because - // they inherit privacy - ast::ItemEnum(ref def, _) => { - for variant in def.variants.iter() { - // The parent is considered the enclosing enum because the - // enum will dictate the privacy visibility of this variant - // instead. - self.parents.insert(variant.node.id, item.id); - } - } - - // Trait methods are always considered "public", but if the trait is - // private then we need some private item in the chain from the - // method to the root. In this case, if the trait is private, then - // parent all the methods to the trait to indicate that they're - // private. - ast::ItemTrait(_, _, _, ref methods) if item.vis != ast::Public => { - for m in methods.iter() { - match *m { - ast::ProvidedMethod(ref m) => { - self.parents.insert(m.id, item.id); - } - ast::RequiredMethod(ref m) => { - self.parents.insert(m.id, item.id); - } - ast::TypeTraitItem(_) => {} - }; - } - } - - _ => {} - } - visit::walk_item(self, item); - self.curparent = prev; - } - - fn visit_foreign_item(&mut self, a: &ast::ForeignItem) { - self.parents.insert(a.id, self.curparent); - visit::walk_foreign_item(self, a); - } - - fn visit_fn(&mut self, a: visit::FnKind<'v>, b: &'v ast::FnDecl, - c: &'v ast::Block, d: Span, id: ast::NodeId) { - // We already took care of some trait methods above, otherwise things - // like impl methods and pub trait methods are parented to the - // containing module, not the containing trait. - if !self.parents.contains_key(&id) { - self.parents.insert(id, self.curparent); - } - visit::walk_fn(self, a, b, c, d); - } - - fn visit_struct_def(&mut self, s: &ast::StructDef, _: ast::Ident, - _: &'v ast::Generics, n: ast::NodeId) { - // Struct constructors are parented to their struct definitions because - // they essentially are the struct definitions. - match s.ctor_id { - Some(id) => { self.parents.insert(id, n); } - None => {} - } - - // While we have the id of the struct definition, go ahead and parent - // all the fields. - for field in s.fields.iter() { - self.parents.insert(field.node.id, self.curparent); - } - visit::walk_struct_def(self, s) - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// The embargo visitor, used to determine the exports of the ast -//////////////////////////////////////////////////////////////////////////////// - -struct EmbargoVisitor<'a, 'tcx: 'a> { - tcx: &'a ty::ctxt<'tcx>, - export_map: &'a def::ExportMap, - - // This flag is an indicator of whether the previous item in the - // hierarchical chain was exported or not. This is the indicator of whether - // children should be exported as well. Note that this can flip from false - // to true if a reexported module is entered (or an action similar). - prev_exported: bool, - - // This is a list of all exported items in the AST. An exported item is any - // function/method/item which is usable by external crates. This essentially - // means that the result is "public all the way down", but the "path down" - // may jump across private boundaries through reexport statements. - exported_items: ExportedItems, - - // This sets contains all the destination nodes which are publicly - // re-exported. This is *not* a set of all reexported nodes, only a set of - // all nodes which are reexported *and* reachable from external crates. This - // means that the destination of the reexport is exported, and hence the - // destination must also be exported. - reexports: NodeSet, - - // These two fields are closely related to one another in that they are only - // used for generation of the 'PublicItems' set, not for privacy checking at - // all - public_items: PublicItems, - prev_public: bool, -} - -impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> { - // There are checks inside of privacy which depend on knowing whether a - // trait should be exported or not. The two current consumers of this are: - // - // 1. Should default methods of a trait be exported? - // 2. Should the methods of an implementation of a trait be exported? - // - // The answer to both of these questions partly rely on whether the trait - // itself is exported or not. If the trait is somehow exported, then the - // answers to both questions must be yes. Right now this question involves - // more analysis than is currently done in rustc, so we conservatively - // answer "yes" so that all traits need to be exported. - fn exported_trait(&self, _id: ast::NodeId) -> bool { - true - } -} - -impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> { - fn visit_item(&mut self, item: &ast::Item) { - let orig_all_pub = self.prev_public; - self.prev_public = orig_all_pub && item.vis == ast::Public; - if self.prev_public { - self.public_items.insert(item.id); - } - - let orig_all_exported = self.prev_exported; - match item.node { - // impls/extern blocks do not break the "public chain" because they - // cannot have visibility qualifiers on them anyway - ast::ItemImpl(..) | ast::ItemForeignMod(..) => {} - - // Traits are a little special in that even if they themselves are - // not public they may still be exported. - ast::ItemTrait(..) => { - self.prev_exported = self.exported_trait(item.id); - } - - // Private by default, hence we only retain the "public chain" if - // `pub` is explicitly listed. - _ => { - self.prev_exported = - (orig_all_exported && item.vis == ast::Public) || - self.reexports.contains(&item.id); - } - } - - let public_first = self.prev_exported && - self.exported_items.insert(item.id); - - match item.node { - // Enum variants inherit from their parent, so if the enum is - // public all variants are public unless they're explicitly priv - ast::ItemEnum(ref def, _) if public_first => { - for variant in def.variants.iter() { - self.exported_items.insert(variant.node.id); - } - } - - // Implementations are a little tricky to determine what's exported - // out of them. Here's a few cases which are currently defined: - // - // * Impls for private types do not need to export their methods - // (either public or private methods) - // - // * Impls for public types only have public methods exported - // - // * Public trait impls for public types must have all methods - // exported. - // - // * Private trait impls for public types can be ignored - // - // * Public trait impls for private types have their methods - // exported. I'm not entirely certain that this is the correct - // thing to do, but I have seen use cases of where this will cause - // undefined symbols at linkage time if this case is not handled. - // - // * Private trait impls for private types can be completely ignored - ast::ItemImpl(_, _, _, _, ref ty, ref impl_items) => { - let public_ty = match ty.node { - ast::TyPath(_, id) => { - match self.tcx.def_map.borrow()[id].clone() { - def::DefPrimTy(..) => true, - def => { - let did = def.def_id(); - !is_local(did) || - self.exported_items.contains(&did.node) - } - } - } - _ => true, - }; - let tr = ty::impl_trait_ref(self.tcx, local_def(item.id)); - let public_trait = tr.clone().map_or(false, |tr| { - !is_local(tr.def_id) || - self.exported_items.contains(&tr.def_id.node) - }); - - if public_ty || public_trait { - for impl_item in impl_items.iter() { - match *impl_item { - ast::MethodImplItem(ref method) => { - let meth_public = - match method.pe_explicit_self().node { - ast::SelfStatic => public_ty, - _ => true, - } && method.pe_vis() == ast::Public; - if meth_public || tr.is_some() { - self.exported_items.insert(method.id); - } - } - ast::TypeImplItem(_) => {} - } - } - } - } - - // Default methods on traits are all public so long as the trait - // is public - ast::ItemTrait(_, _, _, ref methods) if public_first => { - for method in methods.iter() { - match *method { - ast::ProvidedMethod(ref m) => { - debug!("provided {}", m.id); - self.exported_items.insert(m.id); - } - ast::RequiredMethod(ref m) => { - debug!("required {}", m.id); - self.exported_items.insert(m.id); - } - ast::TypeTraitItem(ref t) => { - debug!("typedef {}", t.ty_param.id); - self.exported_items.insert(t.ty_param.id); - } - } - } - } - - // Struct constructors are public if the struct is all public. - ast::ItemStruct(ref def, _) if public_first => { - match def.ctor_id { - Some(id) => { self.exported_items.insert(id); } - None => {} - } - } - - ast::ItemTy(ref ty, _) if public_first => { - if let ast::TyPath(_, id) = ty.node { - match self.tcx.def_map.borrow()[id].clone() { - def::DefPrimTy(..) | def::DefTyParam(..) => {}, - def => { - let did = def.def_id(); - if is_local(did) { - self.exported_items.insert(did.node); - } - } - } - } - } - - _ => {} - } - - visit::walk_item(self, item); - - self.prev_exported = orig_all_exported; - self.prev_public = orig_all_pub; - } - - fn visit_foreign_item(&mut self, a: &ast::ForeignItem) { - if (self.prev_exported && a.vis == ast::Public) || self.reexports.contains(&a.id) { - self.exported_items.insert(a.id); - } - } - - fn visit_mod(&mut self, m: &ast::Mod, _sp: Span, id: ast::NodeId) { - // This code is here instead of in visit_item so that the - // crate module gets processed as well. - if self.prev_exported { - assert!(self.export_map.contains_key(&id), "wut {}", id); - for export in self.export_map[id].iter() { - if is_local(export.def_id) { - self.reexports.insert(export.def_id.node); - } - } - } - visit::walk_mod(self, m) - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// The privacy visitor, where privacy checks take place (violations reported) -//////////////////////////////////////////////////////////////////////////////// - -struct PrivacyVisitor<'a, 'tcx: 'a> { - tcx: &'a ty::ctxt<'tcx>, - curitem: ast::NodeId, - in_foreign: bool, - parents: NodeMap, - external_exports: ExternalExports, - last_private_map: LastPrivateMap, -} - -enum PrivacyResult { - Allowable, - ExternallyDenied, - DisallowedBy(ast::NodeId), -} - -enum FieldName { - UnnamedField(uint), // index - // FIXME #6993: change type (and name) from Ident to Name - NamedField(ast::Ident), -} - -impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> { - // used when debugging - fn nodestr(&self, id: ast::NodeId) -> String { - self.tcx.map.node_to_string(id).to_string() - } - - // Determines whether the given definition is public from the point of view - // of the current item. - fn def_privacy(&self, did: ast::DefId) -> PrivacyResult { - if !is_local(did) { - if self.external_exports.contains(&did) { - debug!("privacy - {:?} was externally exported", did); - return Allowable; - } - debug!("privacy - is {:?} a public method", did); - - return match self.tcx.impl_or_trait_items.borrow().get(&did) { - Some(&ty::MethodTraitItem(ref meth)) => { - debug!("privacy - well at least it's a method: {:?}", - *meth); - match meth.container { - ty::TraitContainer(id) => { - debug!("privacy - recursing on trait {:?}", id); - self.def_privacy(id) - } - ty::ImplContainer(id) => { - match ty::impl_trait_ref(self.tcx, id) { - Some(t) => { - debug!("privacy - impl of trait {:?}", id); - self.def_privacy(t.def_id) - } - None => { - debug!("privacy - found a method {:?}", - meth.vis); - if meth.vis == ast::Public { - Allowable - } else { - ExternallyDenied - } - } - } - } - } - } - Some(&ty::TypeTraitItem(ref typedef)) => { - match typedef.container { - ty::TraitContainer(id) => { - debug!("privacy - recursing on trait {:?}", id); - self.def_privacy(id) - } - ty::ImplContainer(id) => { - match ty::impl_trait_ref(self.tcx, id) { - Some(t) => { - debug!("privacy - impl of trait {:?}", id); - self.def_privacy(t.def_id) - } - None => { - debug!("privacy - found a typedef {:?}", - typedef.vis); - if typedef.vis == ast::Public { - Allowable - } else { - ExternallyDenied - } - } - } - } - } - } - None => { - debug!("privacy - nope, not even a method"); - ExternallyDenied - } - }; - } - - debug!("privacy - local {} not public all the way down", - self.tcx.map.node_to_string(did.node)); - // return quickly for things in the same module - if self.parents.get(&did.node) == self.parents.get(&self.curitem) { - debug!("privacy - same parent, we're done here"); - return Allowable; - } - - // We now know that there is at least one private member between the - // destination and the root. - let mut closest_private_id = did.node; - loop { - debug!("privacy - examining {}", self.nodestr(closest_private_id)); - let vis = match self.tcx.map.find(closest_private_id) { - // If this item is a method, then we know for sure that it's an - // actual method and not a static method. The reason for this is - // that these cases are only hit in the ExprMethodCall - // expression, and ExprCall will have its path checked later - // (the path of the trait/impl) if it's a static method. - // - // With this information, then we can completely ignore all - // trait methods. The privacy violation would be if the trait - // couldn't get imported, not if the method couldn't be used - // (all trait methods are public). - // - // However, if this is an impl method, then we dictate this - // decision solely based on the privacy of the method - // invocation. - // FIXME(#10573) is this the right behavior? Why not consider - // where the method was defined? - Some(ast_map::NodeImplItem(ii)) => { - match *ii { - ast::MethodImplItem(ref m) => { - let imp = self.tcx.map - .get_parent_did(closest_private_id); - match ty::impl_trait_ref(self.tcx, imp) { - Some(..) => return Allowable, - _ if m.pe_vis() == ast::Public => { - return Allowable - } - _ => m.pe_vis() - } - } - ast::TypeImplItem(_) => return Allowable, - } - } - Some(ast_map::NodeTraitItem(_)) => { - return Allowable; - } - - // This is not a method call, extract the visibility as one - // would normally look at it - Some(ast_map::NodeItem(it)) => it.vis, - Some(ast_map::NodeForeignItem(_)) => { - self.tcx.map.get_foreign_vis(closest_private_id) - } - Some(ast_map::NodeVariant(..)) => { - ast::Public // need to move up a level (to the enum) - } - _ => ast::Public, - }; - if vis != ast::Public { break } - // if we've reached the root, then everything was allowable and this - // access is public. - if closest_private_id == ast::CRATE_NODE_ID { return Allowable } - closest_private_id = self.parents[closest_private_id]; - - // If we reached the top, then we were public all the way down and - // we can allow this access. - if closest_private_id == ast::DUMMY_NODE_ID { return Allowable } - } - debug!("privacy - closest priv {}", self.nodestr(closest_private_id)); - if self.private_accessible(closest_private_id) { - Allowable - } else { - DisallowedBy(closest_private_id) - } - } - - /// For a local private node in the AST, this function will determine - /// whether the node is accessible by the current module that iteration is - /// inside. - fn private_accessible(&self, id: ast::NodeId) -> bool { - let parent = self.parents[id]; - debug!("privacy - accessible parent {}", self.nodestr(parent)); - - // After finding `did`'s closest private member, we roll ourselves back - // to see if this private member's parent is anywhere in our ancestry. - // By the privacy rules, we can access all of our ancestor's private - // members, so that's why we test the parent, and not the did itself. - let mut cur = self.curitem; - loop { - debug!("privacy - questioning {}, {}", self.nodestr(cur), cur); - match cur { - // If the relevant parent is in our history, then we're allowed - // to look inside any of our ancestor's immediate private items, - // so this access is valid. - x if x == parent => return true, - - // If we've reached the root, then we couldn't access this item - // in the first place - ast::DUMMY_NODE_ID => return false, - - // Keep going up - _ => {} - } - - cur = self.parents[cur]; - } - } - - fn report_error(&self, result: CheckResult) -> bool { - match result { - None => true, - Some((span, msg, note)) => { - self.tcx.sess.span_err(span, &msg[]); - match note { - Some((span, msg)) => { - self.tcx.sess.span_note(span, &msg[]) - } - None => {}, - } - false - }, - } - } - - /// Guarantee that a particular definition is public. Returns a CheckResult - /// which contains any errors found. These can be reported using `report_error`. - /// If the result is `None`, no errors were found. - fn ensure_public(&self, span: Span, to_check: ast::DefId, - source_did: Option, msg: &str) -> CheckResult { - let id = match self.def_privacy(to_check) { - ExternallyDenied => { - return Some((span, format!("{} is private", msg), None)) - } - Allowable => return None, - DisallowedBy(id) => id, - }; - - // If we're disallowed by a particular id, then we attempt to give a - // nice error message to say why it was disallowed. It was either - // because the item itself is private or because its parent is private - // and its parent isn't in our ancestry. - let (err_span, err_msg) = if id == source_did.unwrap_or(to_check).node { - return Some((span, format!("{} is private", msg), None)); - } else { - (span, format!("{} is inaccessible", msg)) - }; - let item = match self.tcx.map.find(id) { - Some(ast_map::NodeItem(item)) => { - match item.node { - // If an impl disallowed this item, then this is resolve's - // way of saying that a struct/enum's static method was - // invoked, and the struct/enum itself is private. Crawl - // back up the chains to find the relevant struct/enum that - // was private. - ast::ItemImpl(_, _, _, _, ref ty, _) => { - let id = match ty.node { - ast::TyPath(_, id) => id, - _ => return Some((err_span, err_msg, None)), - }; - let def = self.tcx.def_map.borrow()[id].clone(); - let did = def.def_id(); - assert!(is_local(did)); - match self.tcx.map.get(did.node) { - ast_map::NodeItem(item) => item, - _ => self.tcx.sess.span_bug(item.span, - "path is not an item") - } - } - _ => item - } - } - Some(..) | None => return Some((err_span, err_msg, None)), - }; - let desc = match item.node { - ast::ItemMod(..) => "module", - ast::ItemTrait(..) => "trait", - ast::ItemStruct(..) => "struct", - ast::ItemEnum(..) => "enum", - _ => return Some((err_span, err_msg, None)) - }; - let msg = format!("{} `{}` is private", desc, - token::get_ident(item.ident)); - Some((err_span, err_msg, Some((span, msg)))) - } - - // Checks that a field is in scope. - fn check_field(&mut self, - span: Span, - id: ast::DefId, - name: FieldName) { - let fields = ty::lookup_struct_fields(self.tcx, id); - let field = match name { - NamedField(ident) => { - debug!("privacy - check named field {} in struct {:?}", ident.name, id); - fields.iter().find(|f| f.name == ident.name).unwrap() - } - UnnamedField(idx) => &fields[idx] - }; - if field.vis == ast::Public || - (is_local(field.id) && self.private_accessible(field.id.node)) { - return - } - - let struct_type = ty::lookup_item_type(self.tcx, id).ty; - let struct_desc = match struct_type.sty { - ty::ty_struct(_, _) => - format!("struct `{}`", ty::item_path_str(self.tcx, id)), - // struct variant fields have inherited visibility - ty::ty_enum(..) => return, - _ => self.tcx.sess.span_bug(span, "can't find struct for field") - }; - let msg = match name { - NamedField(name) => format!("field `{}` of {} is private", - token::get_ident(name), struct_desc), - UnnamedField(idx) => format!("field #{} of {} is private", - idx + 1, struct_desc), - }; - self.tcx.sess.span_err(span, &msg[]); - } - - // Given the ID of a method, checks to ensure it's in scope. - fn check_static_method(&mut self, - span: Span, - method_id: ast::DefId, - name: ast::Ident) { - // If the method is a default method, we need to use the def_id of - // the default implementation. - let method_id = match ty::impl_or_trait_item(self.tcx, method_id) { - ty::MethodTraitItem(method_type) => { - method_type.provided_source.unwrap_or(method_id) - } - ty::TypeTraitItem(_) => method_id, - }; - - let string = token::get_ident(name); - self.report_error(self.ensure_public(span, - method_id, - None, - &format!("method `{}`", - string)[])); - } - - // Checks that a path is in scope. - fn check_path(&mut self, span: Span, path_id: ast::NodeId, path: &ast::Path) { - debug!("privacy - path {}", self.nodestr(path_id)); - let orig_def = self.tcx.def_map.borrow()[path_id].clone(); - let ck = |&: tyname: &str| { - let ck_public = |&: def: ast::DefId| { - let name = token::get_ident(path.segments.last().unwrap().identifier); - let origdid = orig_def.def_id(); - self.ensure_public(span, - def, - Some(origdid), - &format!("{} `{}`", tyname, name)[]) - }; - - match self.last_private_map[path_id] { - LastMod(AllPublic) => {}, - LastMod(DependsOn(def)) => { - self.report_error(ck_public(def)); - }, - LastImport { value_priv, - value_used: check_value, - type_priv, - type_used: check_type } => { - // This dance with found_error is because we don't want to report - // a privacy error twice for the same directive. - let found_error = match (type_priv, check_type) { - (Some(DependsOn(def)), Used) => { - !self.report_error(ck_public(def)) - }, - _ => false, - }; - if !found_error { - match (value_priv, check_value) { - (Some(DependsOn(def)), Used) => { - self.report_error(ck_public(def)); - }, - _ => {}, - } - } - // If an import is not used in either namespace, we still - // want to check that it could be legal. Therefore we check - // in both namespaces and only report an error if both would - // be illegal. We only report one error, even if it is - // illegal to import from both namespaces. - match (value_priv, check_value, type_priv, check_type) { - (Some(p), Unused, None, _) | - (None, _, Some(p), Unused) => { - let p = match p { - AllPublic => None, - DependsOn(def) => ck_public(def), - }; - if p.is_some() { - self.report_error(p); - } - }, - (Some(v), Unused, Some(t), Unused) => { - let v = match v { - AllPublic => None, - DependsOn(def) => ck_public(def), - }; - let t = match t { - AllPublic => None, - DependsOn(def) => ck_public(def), - }; - if let (Some(_), Some(t)) = (v, t) { - self.report_error(Some(t)); - } - }, - _ => {}, - } - }, - } - }; - // FIXME(#12334) Imports can refer to definitions in both the type and - // value namespaces. The privacy information is aware of this, but the - // def map is not. Therefore the names we work out below will not always - // be accurate and we can get slightly wonky error messages (but type - // checking is always correct). - match self.tcx.def_map.borrow()[path_id].clone() { - def::DefStaticMethod(..) => ck("static method"), - def::DefFn(..) => ck("function"), - def::DefStatic(..) => ck("static"), - def::DefConst(..) => ck("const"), - def::DefVariant(..) => ck("variant"), - def::DefTy(_, false) => ck("type"), - def::DefTy(_, true) => ck("enum"), - def::DefTrait(..) => ck("trait"), - def::DefStruct(..) => ck("struct"), - def::DefMethod(_, Some(..), _) => ck("trait method"), - def::DefMethod(..) => ck("method"), - def::DefMod(..) => ck("module"), - _ => {} - } - } - - // Checks that a method is in scope. - fn check_method(&mut self, span: Span, origin: &MethodOrigin, - ident: ast::Ident) { - match *origin { - MethodStatic(method_id) => { - self.check_static_method(span, method_id, ident) - } - MethodStaticUnboxedClosure(_) => {} - // Trait methods are always all public. The only controlling factor - // is whether the trait itself is accessible or not. - MethodTypeParam(MethodParam { ref trait_ref, .. }) | - MethodTraitObject(MethodObject { ref trait_ref, .. }) => { - self.report_error(self.ensure_public(span, trait_ref.def_id, - None, "source trait")); - } - } - } -} - -impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> { - fn visit_item(&mut self, item: &ast::Item) { - let orig_curitem = replace(&mut self.curitem, item.id); - visit::walk_item(self, item); - self.curitem = orig_curitem; - } - - fn visit_expr(&mut self, expr: &ast::Expr) { - match expr.node { - ast::ExprField(ref base, ident) => { - if let ty::ty_struct(id, _) = ty::expr_ty_adjusted(self.tcx, &**base).sty { - self.check_field(expr.span, id, NamedField(ident.node)); - } - } - ast::ExprTupField(ref base, idx) => { - if let ty::ty_struct(id, _) = ty::expr_ty_adjusted(self.tcx, &**base).sty { - self.check_field(expr.span, id, UnnamedField(idx.node)); - } - } - ast::ExprMethodCall(ident, _, _) => { - let method_call = MethodCall::expr(expr.id); - match self.tcx.method_map.borrow().get(&method_call) { - None => { - self.tcx.sess.span_bug(expr.span, - "method call not in \ - method map"); - } - Some(method) => { - debug!("(privacy checking) checking impl method"); - self.check_method(expr.span, &method.origin, ident.node); - } - } - } - ast::ExprStruct(_, ref fields, _) => { - match ty::expr_ty(self.tcx, expr).sty { - ty::ty_struct(id, _) => { - for field in (*fields).iter() { - self.check_field(expr.span, id, - NamedField(field.ident.node)); - } - } - ty::ty_enum(_, _) => { - match self.tcx.def_map.borrow()[expr.id].clone() { - def::DefVariant(_, variant_id, _) => { - for field in fields.iter() { - self.check_field(expr.span, variant_id, - NamedField(field.ident.node)); - } - } - _ => self.tcx.sess.span_bug(expr.span, - "resolve didn't \ - map enum struct \ - constructor to a \ - variant def"), - } - } - _ => self.tcx.sess.span_bug(expr.span, "struct expr \ - didn't have \ - struct type?!"), - } - } - ast::ExprPath(_) | ast::ExprQPath(_) => { - let guard = |&: did: ast::DefId| { - let fields = ty::lookup_struct_fields(self.tcx, did); - let any_priv = fields.iter().any(|f| { - f.vis != ast::Public && ( - !is_local(f.id) || - !self.private_accessible(f.id.node)) - }); - if any_priv { - self.tcx.sess.span_err(expr.span, - "cannot invoke tuple struct constructor \ - with private fields"); - } - }; - match self.tcx.def_map.borrow().get(&expr.id) { - Some(&def::DefStruct(did)) => { - guard(if is_local(did) { - local_def(self.tcx.map.get_parent(did.node)) - } else { - // "tuple structs" with zero fields (such as - // `pub struct Foo;`) don't have a ctor_id, hence - // the unwrap_or to the same struct id. - let maybe_did = - csearch::get_tuple_struct_definition_if_ctor( - &self.tcx.sess.cstore, did); - maybe_did.unwrap_or(did) - }) - } - _ => {} - } - } - _ => {} - } - - visit::walk_expr(self, expr); - } - - fn visit_view_item(&mut self, a: &ast::ViewItem) { - match a.node { - ast::ViewItemExternCrate(..) => {} - ast::ViewItemUse(ref vpath) => { - match vpath.node { - ast::ViewPathSimple(..) | ast::ViewPathGlob(..) => {} - ast::ViewPathList(ref prefix, ref list, _) => { - for pid in list.iter() { - match pid.node { - ast::PathListIdent { id, name } => { - debug!("privacy - ident item {}", id); - let seg = ast::PathSegment { - identifier: name, - parameters: ast::PathParameters::none(), - }; - let segs = vec![seg]; - let path = ast::Path { - global: false, - span: pid.span, - segments: segs, - }; - self.check_path(pid.span, id, &path); - } - ast::PathListMod { id } => { - debug!("privacy - mod item {}", id); - self.check_path(pid.span, id, prefix); - } - } - } - } - } - } - } - visit::walk_view_item(self, a); - } - - fn visit_pat(&mut self, pattern: &ast::Pat) { - // Foreign functions do not have their patterns mapped in the def_map, - // and there's nothing really relevant there anyway, so don't bother - // checking privacy. If you can name the type then you can pass it to an - // external C function anyway. - if self.in_foreign { return } - - match pattern.node { - ast::PatStruct(_, ref fields, _) => { - match ty::pat_ty(self.tcx, pattern).sty { - ty::ty_struct(id, _) => { - for field in fields.iter() { - self.check_field(pattern.span, id, - NamedField(field.node.ident)); - } - } - ty::ty_enum(_, _) => { - match self.tcx.def_map.borrow().get(&pattern.id) { - Some(&def::DefVariant(_, variant_id, _)) => { - for field in fields.iter() { - self.check_field(pattern.span, variant_id, - NamedField(field.node.ident)); - } - } - _ => self.tcx.sess.span_bug(pattern.span, - "resolve didn't \ - map enum struct \ - pattern to a \ - variant def"), - } - } - _ => self.tcx.sess.span_bug(pattern.span, - "struct pattern didn't have \ - struct type?!"), - } - } - - // Patterns which bind no fields are allowable (the path is check - // elsewhere). - ast::PatEnum(_, Some(ref fields)) => { - match ty::pat_ty(self.tcx, pattern).sty { - ty::ty_struct(id, _) => { - for (i, field) in fields.iter().enumerate() { - if let ast::PatWild(..) = field.node { - continue - } - self.check_field(field.span, id, UnnamedField(i)); - } - } - ty::ty_enum(..) => { - // enum fields have no privacy at this time - } - _ => {} - } - - } - _ => {} - } - - visit::walk_pat(self, pattern); - } - - fn visit_foreign_item(&mut self, fi: &ast::ForeignItem) { - self.in_foreign = true; - visit::walk_foreign_item(self, fi); - self.in_foreign = false; - } - - fn visit_path(&mut self, path: &ast::Path, id: ast::NodeId) { - self.check_path(path.span, id, path); - visit::walk_path(self, path); - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// The privacy sanity check visitor, ensures unnecessary visibility isn't here -//////////////////////////////////////////////////////////////////////////////// - -struct SanePrivacyVisitor<'a, 'tcx: 'a> { - tcx: &'a ty::ctxt<'tcx>, - in_fn: bool, -} - -impl<'a, 'tcx, 'v> Visitor<'v> for SanePrivacyVisitor<'a, 'tcx> { - fn visit_item(&mut self, item: &ast::Item) { - if self.in_fn { - self.check_all_inherited(item); - } else { - self.check_sane_privacy(item); - } - - let in_fn = self.in_fn; - let orig_in_fn = replace(&mut self.in_fn, match item.node { - ast::ItemMod(..) => false, // modules turn privacy back on - _ => in_fn, // otherwise we inherit - }); - visit::walk_item(self, item); - self.in_fn = orig_in_fn; - } - - fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl, - b: &'v ast::Block, s: Span, _: ast::NodeId) { - // This catches both functions and methods - let orig_in_fn = replace(&mut self.in_fn, true); - visit::walk_fn(self, fk, fd, b, s); - self.in_fn = orig_in_fn; - } - - fn visit_view_item(&mut self, i: &ast::ViewItem) { - match i.vis { - ast::Inherited => {} - ast::Public => { - if self.in_fn { - self.tcx.sess.span_err(i.span, "unnecessary `pub`, imports \ - in functions are never \ - reachable"); - } else if let ast::ViewItemExternCrate(..) = i.node { - self.tcx.sess.span_err(i.span, "`pub` visibility \ - is not allowed"); - } - } - } - visit::walk_view_item(self, i); - } -} - -impl<'a, 'tcx> SanePrivacyVisitor<'a, 'tcx> { - /// Validates all of the visibility qualifiers placed on the item given. This - /// ensures that there are no extraneous qualifiers that don't actually do - /// anything. In theory these qualifiers wouldn't parse, but that may happen - /// later on down the road... - fn check_sane_privacy(&self, item: &ast::Item) { - let tcx = self.tcx; - let check_inherited = |&: sp: Span, vis: ast::Visibility, note: &str| { - if vis != ast::Inherited { - tcx.sess.span_err(sp, "unnecessary visibility qualifier"); - if note.len() > 0 { - tcx.sess.span_note(sp, note); - } - } - }; - match item.node { - // implementations of traits don't need visibility qualifiers because - // that's controlled by having the trait in scope. - ast::ItemImpl(_, _, _, Some(..), _, ref impl_items) => { - check_inherited(item.span, item.vis, - "visibility qualifiers have no effect on trait \ - impls"); - for impl_item in impl_items.iter() { - match *impl_item { - ast::MethodImplItem(ref m) => { - check_inherited(m.span, m.pe_vis(), ""); - } - ast::TypeImplItem(_) => {} - } - } - } - - ast::ItemImpl(..) => { - check_inherited(item.span, item.vis, - "place qualifiers on individual methods instead"); - } - ast::ItemForeignMod(..) => { - check_inherited(item.span, item.vis, - "place qualifiers on individual functions \ - instead"); - } - - ast::ItemEnum(ref def, _) => { - for v in def.variants.iter() { - match v.node.vis { - ast::Public => { - if item.vis == ast::Public { - tcx.sess.span_err(v.span, "unnecessary `pub` \ - visibility"); - } - } - ast::Inherited => {} - } - } - } - - ast::ItemTrait(_, _, _, ref methods) => { - for m in methods.iter() { - match *m { - ast::ProvidedMethod(ref m) => { - check_inherited(m.span, m.pe_vis(), - "unnecessary visibility"); - } - ast::RequiredMethod(ref m) => { - check_inherited(m.span, m.vis, - "unnecessary visibility"); - } - ast::TypeTraitItem(_) => {} - } - } - } - - ast::ItemConst(..) | ast::ItemStatic(..) | ast::ItemStruct(..) | - ast::ItemFn(..) | ast::ItemMod(..) | ast::ItemTy(..) | - ast::ItemMac(..) => {} - } - } - - /// When inside of something like a function or a method, visibility has no - /// control over anything so this forbids any mention of any visibility - fn check_all_inherited(&self, item: &ast::Item) { - let tcx = self.tcx; - fn check_inherited(tcx: &ty::ctxt, sp: Span, vis: ast::Visibility) { - if vis != ast::Inherited { - tcx.sess.span_err(sp, "visibility has no effect inside functions"); - } - } - let check_struct = |&: def: &ast::StructDef| { - for f in def.fields.iter() { - match f.node.kind { - ast::NamedField(_, p) => check_inherited(tcx, f.span, p), - ast::UnnamedField(..) => {} - } - } - }; - check_inherited(tcx, item.span, item.vis); - match item.node { - ast::ItemImpl(_, _, _, _, _, ref impl_items) => { - for impl_item in impl_items.iter() { - match *impl_item { - ast::MethodImplItem(ref m) => { - check_inherited(tcx, m.span, m.pe_vis()); - } - ast::TypeImplItem(_) => {} - } - } - } - ast::ItemForeignMod(ref fm) => { - for i in fm.items.iter() { - check_inherited(tcx, i.span, i.vis); - } - } - ast::ItemEnum(ref def, _) => { - for v in def.variants.iter() { - check_inherited(tcx, v.span, v.node.vis); - } - } - - ast::ItemStruct(ref def, _) => check_struct(&**def), - - ast::ItemTrait(_, _, _, ref methods) => { - for m in methods.iter() { - match *m { - ast::RequiredMethod(..) => {} - ast::ProvidedMethod(ref m) => check_inherited(tcx, m.span, - m.pe_vis()), - ast::TypeTraitItem(_) => {} - } - } - } - - ast::ItemStatic(..) | ast::ItemConst(..) | - ast::ItemFn(..) | ast::ItemMod(..) | ast::ItemTy(..) | - ast::ItemMac(..) => {} - } - } -} - -struct VisiblePrivateTypesVisitor<'a, 'tcx: 'a> { - tcx: &'a ty::ctxt<'tcx>, - exported_items: &'a ExportedItems, - public_items: &'a PublicItems, - in_variant: bool, -} - -struct CheckTypeForPrivatenessVisitor<'a, 'b: 'a, 'tcx: 'b> { - inner: &'a VisiblePrivateTypesVisitor<'b, 'tcx>, - /// whether the type refers to private types. - contains_private: bool, - /// whether we've recurred at all (i.e. if we're pointing at the - /// first type on which visit_ty was called). - at_outer_type: bool, - // whether that first type is a public path. - outer_type_is_public_path: bool, -} - -impl<'a, 'tcx> VisiblePrivateTypesVisitor<'a, 'tcx> { - fn path_is_private_type(&self, path_id: ast::NodeId) -> bool { - let did = match self.tcx.def_map.borrow().get(&path_id).cloned() { - // `int` etc. (None doesn't seem to occur.) - None | Some(def::DefPrimTy(..)) => return false, - Some(def) => def.def_id() - }; - // A path can only be private if: - // it's in this crate... - if !is_local(did) { - return false - } - // .. and it corresponds to a private type in the AST (this returns - // None for type parameters) - match self.tcx.map.find(did.node) { - Some(ast_map::NodeItem(ref item)) => item.vis != ast::Public, - Some(_) | None => false, - } - } - - fn trait_is_public(&self, trait_id: ast::NodeId) -> bool { - // FIXME: this would preferably be using `exported_items`, but all - // traits are exported currently (see `EmbargoVisitor.exported_trait`) - self.public_items.contains(&trait_id) - } - - fn check_ty_param_bound(&self, - ty_param_bound: &ast::TyParamBound) { - if let ast::TraitTyParamBound(ref trait_ref, _) = *ty_param_bound { - if !self.tcx.sess.features.borrow().visible_private_types && - self.path_is_private_type(trait_ref.trait_ref.ref_id) { - let span = trait_ref.trait_ref.path.span; - self.tcx.sess.span_err(span, - "private trait in exported type \ - parameter bound"); - } - } - } -} - -impl<'a, 'b, 'tcx, 'v> Visitor<'v> for CheckTypeForPrivatenessVisitor<'a, 'b, 'tcx> { - fn visit_ty(&mut self, ty: &ast::Ty) { - if let ast::TyPath(_, path_id) = ty.node { - if self.inner.path_is_private_type(path_id) { - self.contains_private = true; - // found what we're looking for so let's stop - // working. - return - } else if self.at_outer_type { - self.outer_type_is_public_path = true; - } - } - self.at_outer_type = false; - visit::walk_ty(self, ty) - } - - // don't want to recurse into [, .. expr] - fn visit_expr(&mut self, _: &ast::Expr) {} -} - -impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> { - fn visit_item(&mut self, item: &ast::Item) { - match item.node { - // contents of a private mod can be reexported, so we need - // to check internals. - ast::ItemMod(_) => {} - - // An `extern {}` doesn't introduce a new privacy - // namespace (the contents have their own privacies). - ast::ItemForeignMod(_) => {} - - ast::ItemTrait(_, _, ref bounds, _) => { - if !self.trait_is_public(item.id) { - return - } - - for bound in bounds.iter() { - self.check_ty_param_bound(bound) - } - } - - // impls need some special handling to try to offer useful - // error messages without (too many) false positives - // (i.e. we could just return here to not check them at - // all, or some worse estimation of whether an impl is - // publicly visible. - ast::ItemImpl(_, _, ref g, ref trait_ref, ref self_, ref impl_items) => { - // `impl [... for] Private` is never visible. - let self_contains_private; - // impl [... for] Public<...>, but not `impl [... for] - // ~[Public]` or `(Public,)` etc. - let self_is_public_path; - - // check the properties of the Self type: - { - let mut visitor = CheckTypeForPrivatenessVisitor { - inner: self, - contains_private: false, - at_outer_type: true, - outer_type_is_public_path: false, - }; - visitor.visit_ty(&**self_); - self_contains_private = visitor.contains_private; - self_is_public_path = visitor.outer_type_is_public_path; - } - - // miscellaneous info about the impl - - // `true` iff this is `impl Private for ...`. - let not_private_trait = - trait_ref.as_ref().map_or(true, // no trait counts as public trait - |tr| { - let did = ty::trait_ref_to_def_id(self.tcx, tr); - - !is_local(did) || self.trait_is_public(did.node) - }); - - // `true` iff this is a trait impl or at least one method is public. - // - // `impl Public { $( fn ...() {} )* }` is not visible. - // - // This is required over just using the methods' privacy - // directly because we might have `impl> ...`, - // and we shouldn't warn about the generics if all the methods - // are private (because `T` won't be visible externally). - let trait_or_some_public_method = - trait_ref.is_some() || - impl_items.iter() - .any(|impl_item| { - match *impl_item { - ast::MethodImplItem(ref m) => { - self.exported_items.contains(&m.id) - } - ast::TypeImplItem(_) => false, - } - }); - - if !self_contains_private && - not_private_trait && - trait_or_some_public_method { - - visit::walk_generics(self, g); - - match *trait_ref { - None => { - for impl_item in impl_items.iter() { - match *impl_item { - ast::MethodImplItem(ref method) => { - visit::walk_method_helper(self, &**method) - } - ast::TypeImplItem(_) => {} - } - } - } - Some(ref tr) => { - // Any private types in a trait impl fall into two - // categories. - // 1. mentioned in the trait definition - // 2. mentioned in the type params/generics - // - // Those in 1. can only occur if the trait is in - // this crate and will've been warned about on the - // trait definition (there's no need to warn twice - // so we don't check the methods). - // - // Those in 2. are warned via walk_generics and this - // call here. - self.visit_trait_ref(tr) - } - } - } else if trait_ref.is_none() && self_is_public_path { - // impl Public { ... }. Any public static - // methods will be visible as `Public::foo`. - let mut found_pub_static = false; - for impl_item in impl_items.iter() { - match *impl_item { - ast::MethodImplItem(ref method) => { - if method.pe_explicit_self().node == - ast::SelfStatic && - self.exported_items - .contains(&method.id) { - found_pub_static = true; - visit::walk_method_helper(self, &**method); - } - } - ast::TypeImplItem(_) => {} - } - } - if found_pub_static { - visit::walk_generics(self, g) - } - } - return - } - - // `type ... = ...;` can contain private types, because - // we're introducing a new name. - ast::ItemTy(..) => return, - - // not at all public, so we don't care - _ if !self.exported_items.contains(&item.id) => return, - - _ => {} - } - - // we've carefully constructed it so that if we're here, then - // any `visit_ty`'s will be called on things that are in - // public signatures, i.e. things that we're interested in for - // this visitor. - visit::walk_item(self, item); - } - - fn visit_generics(&mut self, generics: &ast::Generics) { - for ty_param in generics.ty_params.iter() { - for bound in ty_param.bounds.iter() { - self.check_ty_param_bound(bound) - } - } - for predicate in generics.where_clause.predicates.iter() { - match predicate { - &ast::WherePredicate::BoundPredicate(ref bound_pred) => { - for bound in bound_pred.bounds.iter() { - self.check_ty_param_bound(bound) - } - } - &ast::WherePredicate::RegionPredicate(_) => {} - &ast::WherePredicate::EqPredicate(ref eq_pred) => { - self.visit_ty(&*eq_pred.ty); - } - } - } - } - - fn visit_foreign_item(&mut self, item: &ast::ForeignItem) { - if self.exported_items.contains(&item.id) { - visit::walk_foreign_item(self, item) - } - } - - fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl, - b: &'v ast::Block, s: Span, id: ast::NodeId) { - // needs special handling for methods. - if self.exported_items.contains(&id) { - visit::walk_fn(self, fk, fd, b, s); - } - } - - fn visit_ty(&mut self, t: &ast::Ty) { - if let ast::TyPath(ref p, path_id) = t.node { - if !self.tcx.sess.features.borrow().visible_private_types && - self.path_is_private_type(path_id) { - self.tcx.sess.span_err(p.span, - "private type in exported type signature"); - } - } - visit::walk_ty(self, t) - } - - fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics) { - if self.exported_items.contains(&v.node.id) { - self.in_variant = true; - visit::walk_variant(self, v, g); - self.in_variant = false; - } - } - - fn visit_struct_field(&mut self, s: &ast::StructField) { - match s.node.kind { - ast::NamedField(_, vis) if vis == ast::Public || self.in_variant => { - visit::walk_struct_field(self, s); - } - _ => {} - } - } - - - // we don't need to introspect into these at all: an - // expression/block context can't possibly contain exported - // things, and neither do view_items. (Making them no-ops stops us - // from traversing the whole AST without having to be super - // careful about our `walk_...` calls above.) - fn visit_view_item(&mut self, _: &ast::ViewItem) {} - fn visit_block(&mut self, _: &ast::Block) {} - fn visit_expr(&mut self, _: &ast::Expr) {} -} - -pub fn check_crate(tcx: &ty::ctxt, - export_map: &def::ExportMap, - external_exports: ExternalExports, - last_private_map: LastPrivateMap) - -> (ExportedItems, PublicItems) { - let krate = tcx.map.krate(); - - // Figure out who everyone's parent is - let mut visitor = ParentVisitor { - parents: NodeMap::new(), - curparent: ast::DUMMY_NODE_ID, - }; - visit::walk_crate(&mut visitor, krate); - - // Use the parent map to check the privacy of everything - let mut visitor = PrivacyVisitor { - curitem: ast::DUMMY_NODE_ID, - in_foreign: false, - tcx: tcx, - parents: visitor.parents, - external_exports: external_exports, - last_private_map: last_private_map, - }; - visit::walk_crate(&mut visitor, krate); - - // Sanity check to make sure that all privacy usage and controls are - // reasonable. - let mut visitor = SanePrivacyVisitor { - in_fn: false, - tcx: tcx, - }; - visit::walk_crate(&mut visitor, krate); - - tcx.sess.abort_if_errors(); - - // Build up a set of all exported items in the AST. This is a set of all - // items which are reachable from external crates based on visibility. - let mut visitor = EmbargoVisitor { - tcx: tcx, - exported_items: NodeSet::new(), - public_items: NodeSet::new(), - reexports: NodeSet::new(), - export_map: export_map, - prev_exported: true, - prev_public: true, - }; - loop { - let before = visitor.exported_items.len(); - visit::walk_crate(&mut visitor, krate); - if before == visitor.exported_items.len() { - break - } - } - - let EmbargoVisitor { exported_items, public_items, .. } = visitor; - - { - let mut visitor = VisiblePrivateTypesVisitor { - tcx: tcx, - exported_items: &exported_items, - public_items: &public_items, - in_variant: false, - }; - visit::walk_crate(&mut visitor, krate); - } - return (exported_items, public_items); -} diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index be620c72178bb..3fac5ba9674c3 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -26,6 +26,7 @@ use rustc_trans::back::link; use rustc_trans::back::write; use rustc_trans::trans; use rustc_typeck as typeck; +use rustc_privacy; use serialize::json; @@ -630,7 +631,7 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, let maps = (external_exports, last_private_map); let (exported_items, public_items) = time(time_passes, "privacy checking", maps, |(a, b)| - middle::privacy::check_crate(&ty_cx, &export_map, a, b)); + rustc_privacy::check_crate(&ty_cx, &export_map, a, b)); time(time_passes, "intrinsic checking", (), |_| middle::intrinsicck::check_crate(&ty_cx)); diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 40a3eb9fe5b1d..5b6c64c1beea6 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -38,6 +38,7 @@ extern crate libc; extern crate rustc; extern crate rustc_back; extern crate rustc_borrowck; +extern crate rustc_privacy; extern crate rustc_resolve; extern crate rustc_trans; extern crate rustc_typeck; diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs new file mode 100644 index 0000000000000..85ca3b05d12c1 --- /dev/null +++ b/src/librustc_privacy/lib.rs @@ -0,0 +1,1598 @@ +// 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. + +#![crate_name = "rustc_privacy"] +#![unstable] +#![staged_api] +#![crate_type = "dylib"] +#![crate_type = "rlib"] +#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "http://www.rust-lang.org/favicon.ico", + html_root_url = "http://doc.rust-lang.org/nightly/")] + +#![feature(rustc_diagnostic_macros)] +#![allow(unknown_features)] #![feature(int_uint)] + +#[macro_use] extern crate log; +#[macro_use] extern crate syntax; + +extern crate rustc; + +use self::PrivacyResult::*; +use self::FieldName::*; + +use std::mem::replace; + +use rustc::metadata::csearch; +use rustc::middle::def; +use rustc::middle::privacy::ImportUse::*; +use rustc::middle::privacy::LastPrivate::*; +use rustc::middle::privacy::PrivateDep::*; +use rustc::middle::privacy::{ExportedItems, PublicItems, LastPrivateMap}; +use rustc::middle::privacy::{ExternalExports}; +use rustc::middle::ty::{MethodTypeParam, MethodStatic}; +use rustc::middle::ty::{MethodCall, MethodMap, MethodOrigin, MethodParam}; +use rustc::middle::ty::{MethodStaticUnboxedClosure, MethodObject}; +use rustc::middle::ty::{MethodTraitObject}; +use rustc::middle::ty::{self, Ty}; +use rustc::util::nodemap::{NodeMap, NodeSet}; + +use syntax::{ast, ast_map}; +use syntax::ast_util::{is_local, local_def, PostExpansionMethod}; +use syntax::codemap::Span; +use syntax::parse::token; +use syntax::visit::{self, Visitor}; + +type Context<'a, 'tcx> = (&'a MethodMap<'tcx>, &'a def::ExportMap); + +/// Result of a checking operation - None => no errors were found. Some => an +/// error and contains the span and message for reporting that error and +/// optionally the same for a note about the error. +type CheckResult = Option<(Span, String, Option<(Span, String)>)>; + +//////////////////////////////////////////////////////////////////////////////// +/// The parent visitor, used to determine what's the parent of what (node-wise) +//////////////////////////////////////////////////////////////////////////////// + +struct ParentVisitor { + parents: NodeMap, + curparent: ast::NodeId, +} + +impl<'v> Visitor<'v> for ParentVisitor { + fn visit_item(&mut self, item: &ast::Item) { + self.parents.insert(item.id, self.curparent); + + let prev = self.curparent; + match item.node { + ast::ItemMod(..) => { self.curparent = item.id; } + // Enum variants are parented to the enum definition itself because + // they inherit privacy + ast::ItemEnum(ref def, _) => { + for variant in def.variants.iter() { + // The parent is considered the enclosing enum because the + // enum will dictate the privacy visibility of this variant + // instead. + self.parents.insert(variant.node.id, item.id); + } + } + + // Trait methods are always considered "public", but if the trait is + // private then we need some private item in the chain from the + // method to the root. In this case, if the trait is private, then + // parent all the methods to the trait to indicate that they're + // private. + ast::ItemTrait(_, _, _, ref methods) if item.vis != ast::Public => { + for m in methods.iter() { + match *m { + ast::ProvidedMethod(ref m) => { + self.parents.insert(m.id, item.id); + } + ast::RequiredMethod(ref m) => { + self.parents.insert(m.id, item.id); + } + ast::TypeTraitItem(_) => {} + }; + } + } + + _ => {} + } + visit::walk_item(self, item); + self.curparent = prev; + } + + fn visit_foreign_item(&mut self, a: &ast::ForeignItem) { + self.parents.insert(a.id, self.curparent); + visit::walk_foreign_item(self, a); + } + + fn visit_fn(&mut self, a: visit::FnKind<'v>, b: &'v ast::FnDecl, + c: &'v ast::Block, d: Span, id: ast::NodeId) { + // We already took care of some trait methods above, otherwise things + // like impl methods and pub trait methods are parented to the + // containing module, not the containing trait. + if !self.parents.contains_key(&id) { + self.parents.insert(id, self.curparent); + } + visit::walk_fn(self, a, b, c, d); + } + + fn visit_struct_def(&mut self, s: &ast::StructDef, _: ast::Ident, + _: &'v ast::Generics, n: ast::NodeId) { + // Struct constructors are parented to their struct definitions because + // they essentially are the struct definitions. + match s.ctor_id { + Some(id) => { self.parents.insert(id, n); } + None => {} + } + + // While we have the id of the struct definition, go ahead and parent + // all the fields. + for field in s.fields.iter() { + self.parents.insert(field.node.id, self.curparent); + } + visit::walk_struct_def(self, s) + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// The embargo visitor, used to determine the exports of the ast +//////////////////////////////////////////////////////////////////////////////// + +struct EmbargoVisitor<'a, 'tcx: 'a> { + tcx: &'a ty::ctxt<'tcx>, + export_map: &'a def::ExportMap, + + // This flag is an indicator of whether the previous item in the + // hierarchical chain was exported or not. This is the indicator of whether + // children should be exported as well. Note that this can flip from false + // to true if a reexported module is entered (or an action similar). + prev_exported: bool, + + // This is a list of all exported items in the AST. An exported item is any + // function/method/item which is usable by external crates. This essentially + // means that the result is "public all the way down", but the "path down" + // may jump across private boundaries through reexport statements. + exported_items: ExportedItems, + + // This sets contains all the destination nodes which are publicly + // re-exported. This is *not* a set of all reexported nodes, only a set of + // all nodes which are reexported *and* reachable from external crates. This + // means that the destination of the reexport is exported, and hence the + // destination must also be exported. + reexports: NodeSet, + + // These two fields are closely related to one another in that they are only + // used for generation of the 'PublicItems' set, not for privacy checking at + // all + public_items: PublicItems, + prev_public: bool, +} + +impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> { + // There are checks inside of privacy which depend on knowing whether a + // trait should be exported or not. The two current consumers of this are: + // + // 1. Should default methods of a trait be exported? + // 2. Should the methods of an implementation of a trait be exported? + // + // The answer to both of these questions partly rely on whether the trait + // itself is exported or not. If the trait is somehow exported, then the + // answers to both questions must be yes. Right now this question involves + // more analysis than is currently done in rustc, so we conservatively + // answer "yes" so that all traits need to be exported. + fn exported_trait(&self, _id: ast::NodeId) -> bool { + true + } +} + +impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> { + fn visit_item(&mut self, item: &ast::Item) { + let orig_all_pub = self.prev_public; + self.prev_public = orig_all_pub && item.vis == ast::Public; + if self.prev_public { + self.public_items.insert(item.id); + } + + let orig_all_exported = self.prev_exported; + match item.node { + // impls/extern blocks do not break the "public chain" because they + // cannot have visibility qualifiers on them anyway + ast::ItemImpl(..) | ast::ItemForeignMod(..) => {} + + // Traits are a little special in that even if they themselves are + // not public they may still be exported. + ast::ItemTrait(..) => { + self.prev_exported = self.exported_trait(item.id); + } + + // Private by default, hence we only retain the "public chain" if + // `pub` is explicitly listed. + _ => { + self.prev_exported = + (orig_all_exported && item.vis == ast::Public) || + self.reexports.contains(&item.id); + } + } + + let public_first = self.prev_exported && + self.exported_items.insert(item.id); + + match item.node { + // Enum variants inherit from their parent, so if the enum is + // public all variants are public unless they're explicitly priv + ast::ItemEnum(ref def, _) if public_first => { + for variant in def.variants.iter() { + self.exported_items.insert(variant.node.id); + } + } + + // Implementations are a little tricky to determine what's exported + // out of them. Here's a few cases which are currently defined: + // + // * Impls for private types do not need to export their methods + // (either public or private methods) + // + // * Impls for public types only have public methods exported + // + // * Public trait impls for public types must have all methods + // exported. + // + // * Private trait impls for public types can be ignored + // + // * Public trait impls for private types have their methods + // exported. I'm not entirely certain that this is the correct + // thing to do, but I have seen use cases of where this will cause + // undefined symbols at linkage time if this case is not handled. + // + // * Private trait impls for private types can be completely ignored + ast::ItemImpl(_, _, _, _, ref ty, ref impl_items) => { + let public_ty = match ty.node { + ast::TyPath(_, id) => { + match self.tcx.def_map.borrow()[id].clone() { + def::DefPrimTy(..) => true, + def => { + let did = def.def_id(); + !is_local(did) || + self.exported_items.contains(&did.node) + } + } + } + _ => true, + }; + let tr = ty::impl_trait_ref(self.tcx, local_def(item.id)); + let public_trait = tr.clone().map_or(false, |tr| { + !is_local(tr.def_id) || + self.exported_items.contains(&tr.def_id.node) + }); + + if public_ty || public_trait { + for impl_item in impl_items.iter() { + match *impl_item { + ast::MethodImplItem(ref method) => { + let meth_public = + match method.pe_explicit_self().node { + ast::SelfStatic => public_ty, + _ => true, + } && method.pe_vis() == ast::Public; + if meth_public || tr.is_some() { + self.exported_items.insert(method.id); + } + } + ast::TypeImplItem(_) => {} + } + } + } + } + + // Default methods on traits are all public so long as the trait + // is public + ast::ItemTrait(_, _, _, ref methods) if public_first => { + for method in methods.iter() { + match *method { + ast::ProvidedMethod(ref m) => { + debug!("provided {}", m.id); + self.exported_items.insert(m.id); + } + ast::RequiredMethod(ref m) => { + debug!("required {}", m.id); + self.exported_items.insert(m.id); + } + ast::TypeTraitItem(ref t) => { + debug!("typedef {}", t.ty_param.id); + self.exported_items.insert(t.ty_param.id); + } + } + } + } + + // Struct constructors are public if the struct is all public. + ast::ItemStruct(ref def, _) if public_first => { + match def.ctor_id { + Some(id) => { self.exported_items.insert(id); } + None => {} + } + } + + ast::ItemTy(ref ty, _) if public_first => { + if let ast::TyPath(_, id) = ty.node { + match self.tcx.def_map.borrow()[id].clone() { + def::DefPrimTy(..) | def::DefTyParam(..) => {}, + def => { + let did = def.def_id(); + if is_local(did) { + self.exported_items.insert(did.node); + } + } + } + } + } + + _ => {} + } + + visit::walk_item(self, item); + + self.prev_exported = orig_all_exported; + self.prev_public = orig_all_pub; + } + + fn visit_foreign_item(&mut self, a: &ast::ForeignItem) { + if (self.prev_exported && a.vis == ast::Public) || self.reexports.contains(&a.id) { + self.exported_items.insert(a.id); + } + } + + fn visit_mod(&mut self, m: &ast::Mod, _sp: Span, id: ast::NodeId) { + // This code is here instead of in visit_item so that the + // crate module gets processed as well. + if self.prev_exported { + assert!(self.export_map.contains_key(&id), "wut {}", id); + for export in self.export_map[id].iter() { + if is_local(export.def_id) { + self.reexports.insert(export.def_id.node); + } + } + } + visit::walk_mod(self, m) + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// The privacy visitor, where privacy checks take place (violations reported) +//////////////////////////////////////////////////////////////////////////////// + +struct PrivacyVisitor<'a, 'tcx: 'a> { + tcx: &'a ty::ctxt<'tcx>, + curitem: ast::NodeId, + in_foreign: bool, + parents: NodeMap, + external_exports: ExternalExports, + last_private_map: LastPrivateMap, +} + +enum PrivacyResult { + Allowable, + ExternallyDenied, + DisallowedBy(ast::NodeId), +} + +enum FieldName { + UnnamedField(uint), // index + // FIXME #6993: change type (and name) from Ident to Name + NamedField(ast::Ident), +} + +impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> { + // used when debugging + fn nodestr(&self, id: ast::NodeId) -> String { + self.tcx.map.node_to_string(id).to_string() + } + + // Determines whether the given definition is public from the point of view + // of the current item. + fn def_privacy(&self, did: ast::DefId) -> PrivacyResult { + if !is_local(did) { + if self.external_exports.contains(&did) { + debug!("privacy - {:?} was externally exported", did); + return Allowable; + } + debug!("privacy - is {:?} a public method", did); + + return match self.tcx.impl_or_trait_items.borrow().get(&did) { + Some(&ty::MethodTraitItem(ref meth)) => { + debug!("privacy - well at least it's a method: {:?}", + *meth); + match meth.container { + ty::TraitContainer(id) => { + debug!("privacy - recursing on trait {:?}", id); + self.def_privacy(id) + } + ty::ImplContainer(id) => { + match ty::impl_trait_ref(self.tcx, id) { + Some(t) => { + debug!("privacy - impl of trait {:?}", id); + self.def_privacy(t.def_id) + } + None => { + debug!("privacy - found a method {:?}", + meth.vis); + if meth.vis == ast::Public { + Allowable + } else { + ExternallyDenied + } + } + } + } + } + } + Some(&ty::TypeTraitItem(ref typedef)) => { + match typedef.container { + ty::TraitContainer(id) => { + debug!("privacy - recursing on trait {:?}", id); + self.def_privacy(id) + } + ty::ImplContainer(id) => { + match ty::impl_trait_ref(self.tcx, id) { + Some(t) => { + debug!("privacy - impl of trait {:?}", id); + self.def_privacy(t.def_id) + } + None => { + debug!("privacy - found a typedef {:?}", + typedef.vis); + if typedef.vis == ast::Public { + Allowable + } else { + ExternallyDenied + } + } + } + } + } + } + None => { + debug!("privacy - nope, not even a method"); + ExternallyDenied + } + }; + } + + debug!("privacy - local {} not public all the way down", + self.tcx.map.node_to_string(did.node)); + // return quickly for things in the same module + if self.parents.get(&did.node) == self.parents.get(&self.curitem) { + debug!("privacy - same parent, we're done here"); + return Allowable; + } + + // We now know that there is at least one private member between the + // destination and the root. + let mut closest_private_id = did.node; + loop { + debug!("privacy - examining {}", self.nodestr(closest_private_id)); + let vis = match self.tcx.map.find(closest_private_id) { + // If this item is a method, then we know for sure that it's an + // actual method and not a static method. The reason for this is + // that these cases are only hit in the ExprMethodCall + // expression, and ExprCall will have its path checked later + // (the path of the trait/impl) if it's a static method. + // + // With this information, then we can completely ignore all + // trait methods. The privacy violation would be if the trait + // couldn't get imported, not if the method couldn't be used + // (all trait methods are public). + // + // However, if this is an impl method, then we dictate this + // decision solely based on the privacy of the method + // invocation. + // FIXME(#10573) is this the right behavior? Why not consider + // where the method was defined? + Some(ast_map::NodeImplItem(ii)) => { + match *ii { + ast::MethodImplItem(ref m) => { + let imp = self.tcx.map + .get_parent_did(closest_private_id); + match ty::impl_trait_ref(self.tcx, imp) { + Some(..) => return Allowable, + _ if m.pe_vis() == ast::Public => { + return Allowable + } + _ => m.pe_vis() + } + } + ast::TypeImplItem(_) => return Allowable, + } + } + Some(ast_map::NodeTraitItem(_)) => { + return Allowable; + } + + // This is not a method call, extract the visibility as one + // would normally look at it + Some(ast_map::NodeItem(it)) => it.vis, + Some(ast_map::NodeForeignItem(_)) => { + self.tcx.map.get_foreign_vis(closest_private_id) + } + Some(ast_map::NodeVariant(..)) => { + ast::Public // need to move up a level (to the enum) + } + _ => ast::Public, + }; + if vis != ast::Public { break } + // if we've reached the root, then everything was allowable and this + // access is public. + if closest_private_id == ast::CRATE_NODE_ID { return Allowable } + closest_private_id = self.parents[closest_private_id]; + + // If we reached the top, then we were public all the way down and + // we can allow this access. + if closest_private_id == ast::DUMMY_NODE_ID { return Allowable } + } + debug!("privacy - closest priv {}", self.nodestr(closest_private_id)); + if self.private_accessible(closest_private_id) { + Allowable + } else { + DisallowedBy(closest_private_id) + } + } + + /// For a local private node in the AST, this function will determine + /// whether the node is accessible by the current module that iteration is + /// inside. + fn private_accessible(&self, id: ast::NodeId) -> bool { + let parent = self.parents[id]; + debug!("privacy - accessible parent {}", self.nodestr(parent)); + + // After finding `did`'s closest private member, we roll ourselves back + // to see if this private member's parent is anywhere in our ancestry. + // By the privacy rules, we can access all of our ancestor's private + // members, so that's why we test the parent, and not the did itself. + let mut cur = self.curitem; + loop { + debug!("privacy - questioning {}, {}", self.nodestr(cur), cur); + match cur { + // If the relevant parent is in our history, then we're allowed + // to look inside any of our ancestor's immediate private items, + // so this access is valid. + x if x == parent => return true, + + // If we've reached the root, then we couldn't access this item + // in the first place + ast::DUMMY_NODE_ID => return false, + + // Keep going up + _ => {} + } + + cur = self.parents[cur]; + } + } + + fn report_error(&self, result: CheckResult) -> bool { + match result { + None => true, + Some((span, msg, note)) => { + self.tcx.sess.span_err(span, &msg[]); + match note { + Some((span, msg)) => { + self.tcx.sess.span_note(span, &msg[]) + } + None => {}, + } + false + }, + } + } + + /// Guarantee that a particular definition is public. Returns a CheckResult + /// which contains any errors found. These can be reported using `report_error`. + /// If the result is `None`, no errors were found. + fn ensure_public(&self, span: Span, to_check: ast::DefId, + source_did: Option, msg: &str) -> CheckResult { + let id = match self.def_privacy(to_check) { + ExternallyDenied => { + return Some((span, format!("{} is private", msg), None)) + } + Allowable => return None, + DisallowedBy(id) => id, + }; + + // If we're disallowed by a particular id, then we attempt to give a + // nice error message to say why it was disallowed. It was either + // because the item itself is private or because its parent is private + // and its parent isn't in our ancestry. + let (err_span, err_msg) = if id == source_did.unwrap_or(to_check).node { + return Some((span, format!("{} is private", msg), None)); + } else { + (span, format!("{} is inaccessible", msg)) + }; + let item = match self.tcx.map.find(id) { + Some(ast_map::NodeItem(item)) => { + match item.node { + // If an impl disallowed this item, then this is resolve's + // way of saying that a struct/enum's static method was + // invoked, and the struct/enum itself is private. Crawl + // back up the chains to find the relevant struct/enum that + // was private. + ast::ItemImpl(_, _, _, _, ref ty, _) => { + let id = match ty.node { + ast::TyPath(_, id) => id, + _ => return Some((err_span, err_msg, None)), + }; + let def = self.tcx.def_map.borrow()[id].clone(); + let did = def.def_id(); + assert!(is_local(did)); + match self.tcx.map.get(did.node) { + ast_map::NodeItem(item) => item, + _ => self.tcx.sess.span_bug(item.span, + "path is not an item") + } + } + _ => item + } + } + Some(..) | None => return Some((err_span, err_msg, None)), + }; + let desc = match item.node { + ast::ItemMod(..) => "module", + ast::ItemTrait(..) => "trait", + ast::ItemStruct(..) => "struct", + ast::ItemEnum(..) => "enum", + _ => return Some((err_span, err_msg, None)) + }; + let msg = format!("{} `{}` is private", desc, + token::get_ident(item.ident)); + Some((err_span, err_msg, Some((span, msg)))) + } + + // Checks that a field is in scope. + fn check_field(&mut self, + span: Span, + id: ast::DefId, + name: FieldName) { + let fields = ty::lookup_struct_fields(self.tcx, id); + let field = match name { + NamedField(ident) => { + debug!("privacy - check named field {} in struct {:?}", ident.name, id); + fields.iter().find(|f| f.name == ident.name).unwrap() + } + UnnamedField(idx) => &fields[idx] + }; + if field.vis == ast::Public || + (is_local(field.id) && self.private_accessible(field.id.node)) { + return + } + + let struct_type = ty::lookup_item_type(self.tcx, id).ty; + let struct_desc = match struct_type.sty { + ty::ty_struct(_, _) => + format!("struct `{}`", ty::item_path_str(self.tcx, id)), + // struct variant fields have inherited visibility + ty::ty_enum(..) => return, + _ => self.tcx.sess.span_bug(span, "can't find struct for field") + }; + let msg = match name { + NamedField(name) => format!("field `{}` of {} is private", + token::get_ident(name), struct_desc), + UnnamedField(idx) => format!("field #{} of {} is private", + idx + 1, struct_desc), + }; + self.tcx.sess.span_err(span, &msg[]); + } + + // Given the ID of a method, checks to ensure it's in scope. + fn check_static_method(&mut self, + span: Span, + method_id: ast::DefId, + name: ast::Ident) { + // If the method is a default method, we need to use the def_id of + // the default implementation. + let method_id = match ty::impl_or_trait_item(self.tcx, method_id) { + ty::MethodTraitItem(method_type) => { + method_type.provided_source.unwrap_or(method_id) + } + ty::TypeTraitItem(_) => method_id, + }; + + let string = token::get_ident(name); + self.report_error(self.ensure_public(span, + method_id, + None, + &format!("method `{}`", + string)[])); + } + + // Checks that a path is in scope. + fn check_path(&mut self, span: Span, path_id: ast::NodeId, path: &ast::Path) { + debug!("privacy - path {}", self.nodestr(path_id)); + let orig_def = self.tcx.def_map.borrow()[path_id].clone(); + let ck = |&: tyname: &str| { + let ck_public = |&: def: ast::DefId| { + debug!("privacy - ck_public {:?}", def); + let name = token::get_ident(path.segments.last().unwrap().identifier); + let origdid = orig_def.def_id(); + self.ensure_public(span, + def, + Some(origdid), + &format!("{} `{}`", tyname, name)[]) + }; + + match self.last_private_map[path_id] { + LastMod(AllPublic) => {}, + LastMod(DependsOn(def)) => { + self.report_error(ck_public(def)); + }, + LastImport { value_priv, + value_used: check_value, + type_priv, + type_used: check_type } => { + // This dance with found_error is because we don't want to + // report a privacy error twice for the same directive. + let found_error = match (type_priv, check_type) { + (Some(DependsOn(def)), Used) => { + !self.report_error(ck_public(def)) + }, + _ => false, + }; + if !found_error { + match (value_priv, check_value) { + (Some(DependsOn(def)), Used) => { + self.report_error(ck_public(def)); + }, + _ => {}, + } + } + // If an import is not used in either namespace, we still + // want to check that it could be legal. Therefore we check + // in both namespaces and only report an error if both would + // be illegal. We only report one error, even if it is + // illegal to import from both namespaces. + match (value_priv, check_value, type_priv, check_type) { + (Some(p), Unused, None, _) | + (None, _, Some(p), Unused) => { + let p = match p { + AllPublic => None, + DependsOn(def) => ck_public(def), + }; + if p.is_some() { + self.report_error(p); + } + }, + (Some(v), Unused, Some(t), Unused) => { + let v = match v { + AllPublic => None, + DependsOn(def) => ck_public(def), + }; + let t = match t { + AllPublic => None, + DependsOn(def) => ck_public(def), + }; + if let (Some(_), Some(t)) = (v, t) { + self.report_error(Some(t)); + } + }, + _ => {}, + } + }, + } + }; + // FIXME(#12334) Imports can refer to definitions in both the type and + // value namespaces. The privacy information is aware of this, but the + // def map is not. Therefore the names we work out below will not always + // be accurate and we can get slightly wonky error messages (but type + // checking is always correct). + match self.tcx.def_map.borrow()[path_id].clone() { + def::DefStaticMethod(..) => ck("static method"), + def::DefFn(..) => ck("function"), + def::DefStatic(..) => ck("static"), + def::DefConst(..) => ck("const"), + def::DefVariant(..) => ck("variant"), + def::DefTy(_, false) => ck("type"), + def::DefTy(_, true) => ck("enum"), + def::DefTrait(..) => ck("trait"), + def::DefStruct(..) => ck("struct"), + def::DefMethod(_, Some(..), _) => ck("trait method"), + def::DefMethod(..) => ck("method"), + def::DefMod(..) => ck("module"), + _ => {} + } + } + + // Checks that a method is in scope. + fn check_method(&mut self, span: Span, origin: &MethodOrigin, + ident: ast::Ident) { + match *origin { + MethodStatic(method_id) => { + self.check_static_method(span, method_id, ident) + } + MethodStaticUnboxedClosure(_) => {} + // Trait methods are always all public. The only controlling factor + // is whether the trait itself is accessible or not. + MethodTypeParam(MethodParam { ref trait_ref, .. }) | + MethodTraitObject(MethodObject { ref trait_ref, .. }) => { + self.report_error(self.ensure_public(span, trait_ref.def_id, + None, "source trait")); + } + } + } +} + +impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> { + fn visit_item(&mut self, item: &ast::Item) { + let orig_curitem = replace(&mut self.curitem, item.id); + visit::walk_item(self, item); + self.curitem = orig_curitem; + } + + fn visit_expr(&mut self, expr: &ast::Expr) { + match expr.node { + ast::ExprField(ref base, ident) => { + if let ty::ty_struct(id, _) = ty::expr_ty_adjusted(self.tcx, &**base).sty { + self.check_field(expr.span, id, NamedField(ident.node)); + } + } + ast::ExprTupField(ref base, idx) => { + if let ty::ty_struct(id, _) = ty::expr_ty_adjusted(self.tcx, &**base).sty { + self.check_field(expr.span, id, UnnamedField(idx.node)); + } + } + ast::ExprMethodCall(ident, _, _) => { + let method_call = MethodCall::expr(expr.id); + match self.tcx.method_map.borrow().get(&method_call) { + None => { + self.tcx.sess.span_bug(expr.span, + "method call not in \ + method map"); + } + Some(method) => { + debug!("(privacy checking) checking impl method"); + self.check_method(expr.span, &method.origin, ident.node); + } + } + } + ast::ExprStruct(_, ref fields, _) => { + match ty::expr_ty(self.tcx, expr).sty { + ty::ty_struct(id, _) => { + for field in (*fields).iter() { + self.check_field(expr.span, id, + NamedField(field.ident.node)); + } + } + ty::ty_enum(_, _) => { + match self.tcx.def_map.borrow()[expr.id].clone() { + def::DefVariant(_, variant_id, _) => { + for field in fields.iter() { + self.check_field(expr.span, variant_id, + NamedField(field.ident.node)); + } + } + _ => self.tcx.sess.span_bug(expr.span, + "resolve didn't \ + map enum struct \ + constructor to a \ + variant def"), + } + } + _ => self.tcx.sess.span_bug(expr.span, "struct expr \ + didn't have \ + struct type?!"), + } + } + ast::ExprPath(..) => { + let guard = |&: did: ast::DefId| { + let fields = ty::lookup_struct_fields(self.tcx, did); + let any_priv = fields.iter().any(|f| { + f.vis != ast::Public && ( + !is_local(f.id) || + !self.private_accessible(f.id.node)) + }); + if any_priv { + self.tcx.sess.span_err(expr.span, + "cannot invoke tuple struct constructor \ + with private fields"); + } + }; + match self.tcx.def_map.borrow().get(&expr.id) { + Some(&def::DefStruct(did)) => { + guard(if is_local(did) { + local_def(self.tcx.map.get_parent(did.node)) + } else { + // "tuple structs" with zero fields (such as + // `pub struct Foo;`) don't have a ctor_id, hence + // the unwrap_or to the same struct id. + let maybe_did = + csearch::get_tuple_struct_definition_if_ctor( + &self.tcx.sess.cstore, did); + maybe_did.unwrap_or(did) + }) + } + _ => {} + } + } + _ => {} + } + + visit::walk_expr(self, expr); + } + + fn visit_view_item(&mut self, a: &ast::ViewItem) { + match a.node { + ast::ViewItemExternCrate(..) => {} + ast::ViewItemUse(ref vpath) => { + match vpath.node { + ast::ViewPathSimple(..) | ast::ViewPathGlob(..) => {} + ast::ViewPathList(ref prefix, ref list, _) => { + for pid in list.iter() { + match pid.node { + ast::PathListIdent { id, name } => { + debug!("privacy - ident item {}", id); + let seg = ast::PathSegment { + identifier: name, + parameters: ast::PathParameters::none(), + }; + let segs = vec![seg]; + let path = ast::Path { + global: false, + span: pid.span, + segments: segs, + }; + self.check_path(pid.span, id, &path); + } + ast::PathListMod { id } => { + debug!("privacy - mod item {}", id); + self.check_path(pid.span, id, prefix); + } + } + } + } + } + } + } + visit::walk_view_item(self, a); + } + + fn visit_pat(&mut self, pattern: &ast::Pat) { + // Foreign functions do not have their patterns mapped in the def_map, + // and there's nothing really relevant there anyway, so don't bother + // checking privacy. If you can name the type then you can pass it to an + // external C function anyway. + if self.in_foreign { return } + + match pattern.node { + ast::PatStruct(_, ref fields, _) => { + match ty::pat_ty(self.tcx, pattern).sty { + ty::ty_struct(id, _) => { + for field in fields.iter() { + self.check_field(pattern.span, id, + NamedField(field.node.ident)); + } + } + ty::ty_enum(_, _) => { + match self.tcx.def_map.borrow().get(&pattern.id) { + Some(&def::DefVariant(_, variant_id, _)) => { + for field in fields.iter() { + self.check_field(pattern.span, variant_id, + NamedField(field.node.ident)); + } + } + _ => self.tcx.sess.span_bug(pattern.span, + "resolve didn't \ + map enum struct \ + pattern to a \ + variant def"), + } + } + _ => self.tcx.sess.span_bug(pattern.span, + "struct pattern didn't have \ + struct type?!"), + } + } + + // Patterns which bind no fields are allowable (the path is check + // elsewhere). + ast::PatEnum(_, Some(ref fields)) => { + match ty::pat_ty(self.tcx, pattern).sty { + ty::ty_struct(id, _) => { + for (i, field) in fields.iter().enumerate() { + if let ast::PatWild(..) = field.node { + continue + } + self.check_field(field.span, id, UnnamedField(i)); + } + } + ty::ty_enum(..) => { + // enum fields have no privacy at this time + } + _ => {} + } + + } + _ => {} + } + + visit::walk_pat(self, pattern); + } + + fn visit_foreign_item(&mut self, fi: &ast::ForeignItem) { + self.in_foreign = true; + visit::walk_foreign_item(self, fi); + self.in_foreign = false; + } + + fn visit_path(&mut self, path: &ast::Path, id: ast::NodeId) { + self.check_path(path.span, id, path); + visit::walk_path(self, path); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// The privacy sanity check visitor, ensures unnecessary visibility isn't here +//////////////////////////////////////////////////////////////////////////////// + +struct SanePrivacyVisitor<'a, 'tcx: 'a> { + tcx: &'a ty::ctxt<'tcx>, + in_fn: bool, +} + +impl<'a, 'tcx, 'v> Visitor<'v> for SanePrivacyVisitor<'a, 'tcx> { + fn visit_item(&mut self, item: &ast::Item) { + if self.in_fn { + self.check_all_inherited(item); + } else { + self.check_sane_privacy(item); + } + + let in_fn = self.in_fn; + let orig_in_fn = replace(&mut self.in_fn, match item.node { + ast::ItemMod(..) => false, // modules turn privacy back on + _ => in_fn, // otherwise we inherit + }); + visit::walk_item(self, item); + self.in_fn = orig_in_fn; + } + + fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl, + b: &'v ast::Block, s: Span, _: ast::NodeId) { + // This catches both functions and methods + let orig_in_fn = replace(&mut self.in_fn, true); + visit::walk_fn(self, fk, fd, b, s); + self.in_fn = orig_in_fn; + } + + fn visit_view_item(&mut self, i: &ast::ViewItem) { + match i.vis { + ast::Inherited => {} + ast::Public => { + if self.in_fn { + self.tcx.sess.span_err(i.span, "unnecessary `pub`, imports \ + in functions are never \ + reachable"); + } else if let ast::ViewItemExternCrate(..) = i.node { + self.tcx.sess.span_err(i.span, "`pub` visibility \ + is not allowed"); + } + } + } + visit::walk_view_item(self, i); + } +} + +impl<'a, 'tcx> SanePrivacyVisitor<'a, 'tcx> { + /// Validates all of the visibility qualifiers placed on the item given. This + /// ensures that there are no extraneous qualifiers that don't actually do + /// anything. In theory these qualifiers wouldn't parse, but that may happen + /// later on down the road... + fn check_sane_privacy(&self, item: &ast::Item) { + let tcx = self.tcx; + let check_inherited = |&: sp: Span, vis: ast::Visibility, note: &str| { + if vis != ast::Inherited { + tcx.sess.span_err(sp, "unnecessary visibility qualifier"); + if note.len() > 0 { + tcx.sess.span_note(sp, note); + } + } + }; + match item.node { + // implementations of traits don't need visibility qualifiers because + // that's controlled by having the trait in scope. + ast::ItemImpl(_, _, _, Some(..), _, ref impl_items) => { + check_inherited(item.span, item.vis, + "visibility qualifiers have no effect on trait \ + impls"); + for impl_item in impl_items.iter() { + match *impl_item { + ast::MethodImplItem(ref m) => { + check_inherited(m.span, m.pe_vis(), ""); + } + ast::TypeImplItem(_) => {} + } + } + } + + ast::ItemImpl(..) => { + check_inherited(item.span, item.vis, + "place qualifiers on individual methods instead"); + } + ast::ItemForeignMod(..) => { + check_inherited(item.span, item.vis, + "place qualifiers on individual functions \ + instead"); + } + + ast::ItemEnum(ref def, _) => { + for v in def.variants.iter() { + match v.node.vis { + ast::Public => { + if item.vis == ast::Public { + tcx.sess.span_err(v.span, "unnecessary `pub` \ + visibility"); + } + } + ast::Inherited => {} + } + } + } + + ast::ItemTrait(_, _, _, ref methods) => { + for m in methods.iter() { + match *m { + ast::ProvidedMethod(ref m) => { + check_inherited(m.span, m.pe_vis(), + "unnecessary visibility"); + } + ast::RequiredMethod(ref m) => { + check_inherited(m.span, m.vis, + "unnecessary visibility"); + } + ast::TypeTraitItem(_) => {} + } + } + } + + ast::ItemConst(..) | ast::ItemStatic(..) | ast::ItemStruct(..) | + ast::ItemFn(..) | ast::ItemMod(..) | ast::ItemTy(..) | + ast::ItemMac(..) => {} + } + } + + /// When inside of something like a function or a method, visibility has no + /// control over anything so this forbids any mention of any visibility + fn check_all_inherited(&self, item: &ast::Item) { + let tcx = self.tcx; + fn check_inherited(tcx: &ty::ctxt, sp: Span, vis: ast::Visibility) { + if vis != ast::Inherited { + tcx.sess.span_err(sp, "visibility has no effect inside functions"); + } + } + let check_struct = |&: def: &ast::StructDef| { + for f in def.fields.iter() { + match f.node.kind { + ast::NamedField(_, p) => check_inherited(tcx, f.span, p), + ast::UnnamedField(..) => {} + } + } + }; + check_inherited(tcx, item.span, item.vis); + match item.node { + ast::ItemImpl(_, _, _, _, _, ref impl_items) => { + for impl_item in impl_items.iter() { + match *impl_item { + ast::MethodImplItem(ref m) => { + check_inherited(tcx, m.span, m.pe_vis()); + } + ast::TypeImplItem(_) => {} + } + } + } + ast::ItemForeignMod(ref fm) => { + for i in fm.items.iter() { + check_inherited(tcx, i.span, i.vis); + } + } + ast::ItemEnum(ref def, _) => { + for v in def.variants.iter() { + check_inherited(tcx, v.span, v.node.vis); + } + } + + ast::ItemStruct(ref def, _) => check_struct(&**def), + + ast::ItemTrait(_, _, _, ref methods) => { + for m in methods.iter() { + match *m { + ast::RequiredMethod(..) => {} + ast::ProvidedMethod(ref m) => check_inherited(tcx, m.span, + m.pe_vis()), + ast::TypeTraitItem(_) => {} + } + } + } + + ast::ItemStatic(..) | ast::ItemConst(..) | + ast::ItemFn(..) | ast::ItemMod(..) | ast::ItemTy(..) | + ast::ItemMac(..) => {} + } + } +} + +struct VisiblePrivateTypesVisitor<'a, 'tcx: 'a> { + tcx: &'a ty::ctxt<'tcx>, + exported_items: &'a ExportedItems, + public_items: &'a PublicItems, + in_variant: bool, +} + +struct CheckTypeForPrivatenessVisitor<'a, 'b: 'a, 'tcx: 'b> { + inner: &'a VisiblePrivateTypesVisitor<'b, 'tcx>, + /// whether the type refers to private types. + contains_private: bool, + /// whether we've recurred at all (i.e. if we're pointing at the + /// first type on which visit_ty was called). + at_outer_type: bool, + // whether that first type is a public path. + outer_type_is_public_path: bool, +} + +impl<'a, 'tcx> VisiblePrivateTypesVisitor<'a, 'tcx> { + fn path_is_private_type(&self, path_id: ast::NodeId) -> bool { + let did = match self.tcx.def_map.borrow().get(&path_id).cloned() { + // `int` etc. (None doesn't seem to occur.) + None | Some(def::DefPrimTy(..)) => return false, + Some(def) => def.def_id() + }; + // A path can only be private if: + // it's in this crate... + if !is_local(did) { + return false + } + // .. and it corresponds to a private type in the AST (this returns + // None for type parameters) + match self.tcx.map.find(did.node) { + Some(ast_map::NodeItem(ref item)) => item.vis != ast::Public, + Some(_) | None => false, + } + } + + fn trait_is_public(&self, trait_id: ast::NodeId) -> bool { + // FIXME: this would preferably be using `exported_items`, but all + // traits are exported currently (see `EmbargoVisitor.exported_trait`) + self.public_items.contains(&trait_id) + } + + fn check_ty_param_bound(&self, + ty_param_bound: &ast::TyParamBound) { + if let ast::TraitTyParamBound(ref trait_ref, _) = *ty_param_bound { + if !self.tcx.sess.features.borrow().visible_private_types && + self.path_is_private_type(trait_ref.trait_ref.ref_id) { + let span = trait_ref.trait_ref.path.span; + self.tcx.sess.span_err(span, + "private trait in exported type \ + parameter bound"); + } + } + } +} + +impl<'a, 'b, 'tcx, 'v> Visitor<'v> for CheckTypeForPrivatenessVisitor<'a, 'b, 'tcx> { + fn visit_ty(&mut self, ty: &ast::Ty) { + if let ast::TyPath(_, path_id) = ty.node { + if self.inner.path_is_private_type(path_id) { + self.contains_private = true; + // found what we're looking for so let's stop + // working. + return + } else if self.at_outer_type { + self.outer_type_is_public_path = true; + } + } + self.at_outer_type = false; + visit::walk_ty(self, ty) + } + + // don't want to recurse into [, .. expr] + fn visit_expr(&mut self, _: &ast::Expr) {} +} + +impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> { + fn visit_item(&mut self, item: &ast::Item) { + match item.node { + // contents of a private mod can be reexported, so we need + // to check internals. + ast::ItemMod(_) => {} + + // An `extern {}` doesn't introduce a new privacy + // namespace (the contents have their own privacies). + ast::ItemForeignMod(_) => {} + + ast::ItemTrait(_, _, ref bounds, _) => { + if !self.trait_is_public(item.id) { + return + } + + for bound in bounds.iter() { + self.check_ty_param_bound(bound) + } + } + + // impls need some special handling to try to offer useful + // error messages without (too many) false positives + // (i.e. we could just return here to not check them at + // all, or some worse estimation of whether an impl is + // publicly visible. + ast::ItemImpl(_, _, ref g, ref trait_ref, ref self_, ref impl_items) => { + // `impl [... for] Private` is never visible. + let self_contains_private; + // impl [... for] Public<...>, but not `impl [... for] + // ~[Public]` or `(Public,)` etc. + let self_is_public_path; + + // check the properties of the Self type: + { + let mut visitor = CheckTypeForPrivatenessVisitor { + inner: self, + contains_private: false, + at_outer_type: true, + outer_type_is_public_path: false, + }; + visitor.visit_ty(&**self_); + self_contains_private = visitor.contains_private; + self_is_public_path = visitor.outer_type_is_public_path; + } + + // miscellaneous info about the impl + + // `true` iff this is `impl Private for ...`. + let not_private_trait = + trait_ref.as_ref().map_or(true, // no trait counts as public trait + |tr| { + let did = ty::trait_ref_to_def_id(self.tcx, tr); + + !is_local(did) || self.trait_is_public(did.node) + }); + + // `true` iff this is a trait impl or at least one method is public. + // + // `impl Public { $( fn ...() {} )* }` is not visible. + // + // This is required over just using the methods' privacy + // directly because we might have `impl> ...`, + // and we shouldn't warn about the generics if all the methods + // are private (because `T` won't be visible externally). + let trait_or_some_public_method = + trait_ref.is_some() || + impl_items.iter() + .any(|impl_item| { + match *impl_item { + ast::MethodImplItem(ref m) => { + self.exported_items.contains(&m.id) + } + ast::TypeImplItem(_) => false, + } + }); + + if !self_contains_private && + not_private_trait && + trait_or_some_public_method { + + visit::walk_generics(self, g); + + match *trait_ref { + None => { + for impl_item in impl_items.iter() { + match *impl_item { + ast::MethodImplItem(ref method) => { + visit::walk_method_helper(self, &**method) + } + ast::TypeImplItem(_) => {} + } + } + } + Some(ref tr) => { + // Any private types in a trait impl fall into two + // categories. + // 1. mentioned in the trait definition + // 2. mentioned in the type params/generics + // + // Those in 1. can only occur if the trait is in + // this crate and will've been warned about on the + // trait definition (there's no need to warn twice + // so we don't check the methods). + // + // Those in 2. are warned via walk_generics and this + // call here. + self.visit_trait_ref(tr) + } + } + } else if trait_ref.is_none() && self_is_public_path { + // impl Public { ... }. Any public static + // methods will be visible as `Public::foo`. + let mut found_pub_static = false; + for impl_item in impl_items.iter() { + match *impl_item { + ast::MethodImplItem(ref method) => { + if method.pe_explicit_self().node == + ast::SelfStatic && + self.exported_items + .contains(&method.id) { + found_pub_static = true; + visit::walk_method_helper(self, &**method); + } + } + ast::TypeImplItem(_) => {} + } + } + if found_pub_static { + visit::walk_generics(self, g) + } + } + return + } + + // `type ... = ...;` can contain private types, because + // we're introducing a new name. + ast::ItemTy(..) => return, + + // not at all public, so we don't care + _ if !self.exported_items.contains(&item.id) => return, + + _ => {} + } + + // we've carefully constructed it so that if we're here, then + // any `visit_ty`'s will be called on things that are in + // public signatures, i.e. things that we're interested in for + // this visitor. + visit::walk_item(self, item); + } + + fn visit_generics(&mut self, generics: &ast::Generics) { + for ty_param in generics.ty_params.iter() { + for bound in ty_param.bounds.iter() { + self.check_ty_param_bound(bound) + } + } + for predicate in generics.where_clause.predicates.iter() { + match predicate { + &ast::WherePredicate::BoundPredicate(ref bound_pred) => { + for bound in bound_pred.bounds.iter() { + self.check_ty_param_bound(bound) + } + } + &ast::WherePredicate::RegionPredicate(_) => {} + &ast::WherePredicate::EqPredicate(ref eq_pred) => { + self.visit_ty(&*eq_pred.ty); + } + } + } + } + + fn visit_foreign_item(&mut self, item: &ast::ForeignItem) { + if self.exported_items.contains(&item.id) { + visit::walk_foreign_item(self, item) + } + } + + fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl, + b: &'v ast::Block, s: Span, id: ast::NodeId) { + // needs special handling for methods. + if self.exported_items.contains(&id) { + visit::walk_fn(self, fk, fd, b, s); + } + } + + fn visit_ty(&mut self, t: &ast::Ty) { + if let ast::TyPath(ref p, path_id) = t.node { + if !self.tcx.sess.features.borrow().visible_private_types && + self.path_is_private_type(path_id) { + self.tcx.sess.span_err(p.span, + "private type in exported type signature"); + } + } + visit::walk_ty(self, t) + } + + fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics) { + if self.exported_items.contains(&v.node.id) { + self.in_variant = true; + visit::walk_variant(self, v, g); + self.in_variant = false; + } + } + + fn visit_struct_field(&mut self, s: &ast::StructField) { + match s.node.kind { + ast::NamedField(_, vis) if vis == ast::Public || self.in_variant => { + visit::walk_struct_field(self, s); + } + _ => {} + } + } + + + // we don't need to introspect into these at all: an + // expression/block context can't possibly contain exported + // things, and neither do view_items. (Making them no-ops stops us + // from traversing the whole AST without having to be super + // careful about our `walk_...` calls above.) + fn visit_view_item(&mut self, _: &ast::ViewItem) {} + fn visit_block(&mut self, _: &ast::Block) {} + fn visit_expr(&mut self, _: &ast::Expr) {} +} + +pub fn check_crate(tcx: &ty::ctxt, + export_map: &def::ExportMap, + external_exports: ExternalExports, + last_private_map: LastPrivateMap) + -> (ExportedItems, PublicItems) { + let krate = tcx.map.krate(); + + // Figure out who everyone's parent is + let mut visitor = ParentVisitor { + parents: NodeMap::new(), + curparent: ast::DUMMY_NODE_ID, + }; + visit::walk_crate(&mut visitor, krate); + + // Use the parent map to check the privacy of everything + let mut visitor = PrivacyVisitor { + curitem: ast::DUMMY_NODE_ID, + in_foreign: false, + tcx: tcx, + parents: visitor.parents, + external_exports: external_exports, + last_private_map: last_private_map, + }; + visit::walk_crate(&mut visitor, krate); + + // Sanity check to make sure that all privacy usage and controls are + // reasonable. + let mut visitor = SanePrivacyVisitor { + in_fn: false, + tcx: tcx, + }; + visit::walk_crate(&mut visitor, krate); + + tcx.sess.abort_if_errors(); + + // Build up a set of all exported items in the AST. This is a set of all + // items which are reachable from external crates based on visibility. + let mut visitor = EmbargoVisitor { + tcx: tcx, + exported_items: NodeSet::new(), + public_items: NodeSet::new(), + reexports: NodeSet::new(), + export_map: export_map, + prev_exported: true, + prev_public: true, + }; + loop { + let before = visitor.exported_items.len(); + visit::walk_crate(&mut visitor, krate); + if before == visitor.exported_items.len() { + break + } + } + + let EmbargoVisitor { exported_items, public_items, .. } = visitor; + + { + let mut visitor = VisiblePrivateTypesVisitor { + tcx: tcx, + exported_items: &exported_items, + public_items: &public_items, + in_variant: false, + }; + visit::walk_crate(&mut visitor, krate); + } + return (exported_items, public_items); +} From 811522260760d5c1d6a4c8162ba816a471f37232 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 15 Jan 2015 10:49:47 -0800 Subject: [PATCH 025/211] rustc_resolve: Correctly record privacy of methods Loading methods from external crates was erroneously using the type's privacy for each method instead of each method's privacy. This commit fixes that. Closes #21202 --- src/librustc_privacy/lib.rs | 2 +- src/librustc_resolve/build_reduced_graph.rs | 6 ++--- src/test/auxiliary/issue-21202.rs | 16 +++++++++++++ src/test/compile-fail/issue-21202.rs | 25 +++++++++++++++++++++ 4 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 src/test/auxiliary/issue-21202.rs create mode 100644 src/test/compile-fail/issue-21202.rs diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 85ca3b05d12c1..8e5f7c576906c 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -888,7 +888,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> { struct type?!"), } } - ast::ExprPath(..) => { + ast::ExprPath(_) | ast::ExprQPath(_) => { let guard = |&: did: ast::DefId| { let fields = ty::lookup_struct_fields(self.tcx, did); let any_priv = fields.iter().any(|f| { diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 466bd608736ce..8d62c5e1ca04c 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -999,7 +999,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { root: &Rc, def_like: DefLike, name: Name, - visibility: Visibility) { + def_visibility: Visibility) { match def_like { DlDef(def) => { // Add the new child item, if necessary. @@ -1027,7 +1027,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { DUMMY_SP); self.handle_external_def(def, - visibility, + def_visibility, &*child_name_bindings, token::get_name(name).get(), name, @@ -1106,7 +1106,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { let def = DefFn(method_info.def_id, false); // NB: not IMPORTABLE - let modifiers = if visibility == ast::Public { + let modifiers = if method_info.vis == ast::Public { PUBLIC } else { DefModifiers::empty() diff --git a/src/test/auxiliary/issue-21202.rs b/src/test/auxiliary/issue-21202.rs new file mode 100644 index 0000000000000..afdbf78aa8292 --- /dev/null +++ b/src/test/auxiliary/issue-21202.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. + +pub mod A { + pub struct Foo; + impl Foo { + fn foo(&self) { } + } +} diff --git a/src/test/compile-fail/issue-21202.rs b/src/test/compile-fail/issue-21202.rs new file mode 100644 index 0000000000000..5c1de6dfc55b4 --- /dev/null +++ b/src/test/compile-fail/issue-21202.rs @@ -0,0 +1,25 @@ +// 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. + +// aux-build:issue-21202.rs + +extern crate "issue-21202" as crate1; + +use crate1::A; + +mod B { + use crate1::A::Foo; + fn bar(f: Foo) { + Foo::foo(&f); + //~^ ERROR: function `foo` is private + } +} + +fn main() { } From cbeb77ec7a42b988703fa0b0c857f26d464fdc2f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 6 Jan 2015 08:46:07 -0800 Subject: [PATCH 026/211] rustc: Fix a leak in dependency= paths With the addition of separate search paths to the compiler, it was intended that applications such as Cargo could require a `--extern` flag per `extern crate` directive in the source. The system can currently be subverted, however, due to the `existing_match()` logic in the crate loader. When loading crates we first attempt to match an `extern crate` directive against all previously loaded crates to avoid reading metadata twice. This "hit the cache if possible" step was erroneously leaking crates across the search path boundaries, however. For example: extern crate b; extern crate a; If `b` depends on `a`, then it will load crate `a` when the `extern crate b` directive is being processed. When the compiler reaches `extern crate a` it will use the previously loaded version no matter what. If the compiler was not invoked with `-L crate=path/to/a`, it will still succeed. This behavior is allowing `extern crate` declarations in Cargo without a corresponding declaration in the manifest of a dependency, which is considered a bug. This commit fixes this problem by keeping track of the origin search path for a crate. Crates loaded from the dependency search path are not candidates for crates which are loaded from the crate search path. As a result of this fix, this is a likely a breaking change for a number of Cargo packages. If the compiler starts informing that a crate can no longer be found, it likely means that the dependency was forgotten in your Cargo.toml. [breaking-change] --- src/librustc/metadata/creader.rs | 64 +++++++++++-------- src/librustc/metadata/cstore.rs | 11 ++-- src/librustc/metadata/filesearch.rs | 20 +++--- src/librustc/metadata/loader.rs | 47 +++++++------- src/librustc/session/search_paths.rs | 9 ++- src/librustc_trans/back/link.rs | 8 +-- .../run-make/compiler-lookup-paths-2/Makefile | 8 +++ .../run-make/compiler-lookup-paths-2/a.rs | 11 ++++ .../run-make/compiler-lookup-paths-2/b.rs | 12 ++++ .../run-make/compiler-lookup-paths-2/c.rs | 13 ++++ .../extern-flag-disambiguates/Makefile | 2 + .../run-make/extern-flag-disambiguates/a.rs | 4 +- .../run-make/extern-flag-disambiguates/b.rs | 6 +- .../run-make/extern-flag-disambiguates/c.rs | 6 +- .../run-make/extern-flag-disambiguates/d.rs | 2 +- 15 files changed, 145 insertions(+), 78 deletions(-) create mode 100644 src/test/run-make/compiler-lookup-paths-2/Makefile create mode 100644 src/test/run-make/compiler-lookup-paths-2/a.rs create mode 100644 src/test/run-make/compiler-lookup-paths-2/b.rs create mode 100644 src/test/run-make/compiler-lookup-paths-2/c.rs diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index a8326cc060ae7..5de683f8a4f07 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -58,8 +58,8 @@ fn dump_crates(cstore: &CStore) { debug!(" hash: {}", data.hash()); opt_source.map(|cs| { let CrateSource { dylib, rlib, cnum: _ } = cs; - dylib.map(|dl| debug!(" dylib: {}", dl.display())); - rlib.map(|rl| debug!(" rlib: {}", rl.display())); + dylib.map(|dl| debug!(" dylib: {}", dl.0.display())); + rlib.map(|rl| debug!(" rlib: {}", rl.0.display())); }); }) } @@ -305,8 +305,8 @@ impl<'a> CrateReader<'a> { } } - fn existing_match(&self, name: &str, - hash: Option<&Svh>) -> Option { + fn existing_match(&self, name: &str, hash: Option<&Svh>, kind: PathKind) + -> Option { let mut ret = None; self.sess.cstore.iter_crate_data(|cnum, data| { if data.name != name { return } @@ -317,27 +317,37 @@ impl<'a> CrateReader<'a> { None => {} } - // When the hash is None we're dealing with a top-level dependency in - // which case we may have a specification on the command line for this - // library. Even though an upstream library may have loaded something of - // the same name, we have to make sure it was loaded from the exact same - // location as well. + // When the hash is None we're dealing with a top-level dependency + // in which case we may have a specification on the command line for + // this library. Even though an upstream library may have loaded + // something of the same name, we have to make sure it was loaded + // from the exact same location as well. // // We're also sure to compare *paths*, not actual byte slices. The // `source` stores paths which are normalized which may be different // from the strings on the command line. let source = self.sess.cstore.get_used_crate_source(cnum).unwrap(); - match self.sess.opts.externs.get(name) { - Some(locs) => { - let found = locs.iter().any(|l| { - let l = fs::realpath(&Path::new(&l[])).ok(); - l == source.dylib || l == source.rlib - }); - if found { - ret = Some(cnum); - } + if let Some(locs) = self.sess.opts.externs.get(name) { + let found = locs.iter().any(|l| { + let l = fs::realpath(&Path::new(&l[])).ok(); + source.dylib.as_ref().map(|p| &p.0) == l.as_ref() || + source.rlib.as_ref().map(|p| &p.0) == l.as_ref() + }); + if found { + ret = Some(cnum); } - None => ret = Some(cnum), + } + + // Alright, so we've gotten this far which means that `data` has the + // right name, we don't have a hash, and we don't have a --extern + // pointing for ourselves. We're still not quite yet done because we + // have to make sure that this crate was found in the crate lookup + // path (this is a top-level dependency) as we don't want to + // implicitly load anything inside the dependency lookup path. + let prev_kind = source.dylib.as_ref().or(source.rlib.as_ref()) + .unwrap().1; + if ret.is_none() && (prev_kind == kind || prev_kind == PathKind::All) { + ret = Some(cnum); } }); return ret; @@ -359,8 +369,8 @@ impl<'a> CrateReader<'a> { let crate_paths = if root.is_none() { Some(CratePaths { ident: ident.to_string(), - dylib: lib.dylib.clone(), - rlib: lib.rlib.clone(), + dylib: lib.dylib.clone().map(|p| p.0), + rlib: lib.rlib.clone().map(|p| p.0), }) } else { None @@ -400,7 +410,7 @@ impl<'a> CrateReader<'a> { kind: PathKind) -> (ast::CrateNum, Rc, cstore::CrateSource) { - match self.existing_match(name, hash) { + match self.existing_match(name, hash, kind) { None => { let mut load_ctxt = loader::Context { sess: self.sess, @@ -483,8 +493,8 @@ impl<'a> CrateReader<'a> { let library = match load_ctxt.maybe_load_library_crate() { Some(l) => l, None if is_cross => { - // Try loading from target crates. This will abort later if we try to - // load a plugin registrar function, + // Try loading from target crates. This will abort later if we + // try to load a plugin registrar function, target_only = true; should_link = info.should_link; @@ -497,7 +507,9 @@ impl<'a> CrateReader<'a> { }; let dylib = library.dylib.clone(); - let register = should_link && self.existing_match(info.name.as_slice(), None).is_none(); + let register = should_link && self.existing_match(info.name.as_slice(), + None, + PathKind::Crate).is_none(); let metadata = if register { // Register crate now to avoid double-reading metadata let (_, cmd, _) = self.register_crate(&None, &info.ident[], @@ -511,7 +523,7 @@ impl<'a> CrateReader<'a> { PluginMetadata { sess: self.sess, metadata: metadata, - dylib: dylib, + dylib: dylib.map(|p| p.0), info: info, vi_span: span, target_only: target_only, diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs index ec0b80c3a5342..49e03376a8cdd 100644 --- a/src/librustc/metadata/cstore.rs +++ b/src/librustc/metadata/cstore.rs @@ -20,6 +20,7 @@ pub use self::NativeLibraryKind::*; use back::svh::Svh; use metadata::decoder; use metadata::loader; +use session::search_paths::PathKind; use util::nodemap::{FnvHashMap, NodeMap}; use std::cell::RefCell; @@ -65,8 +66,8 @@ pub enum NativeLibraryKind { // must be non-None. #[derive(PartialEq, Clone)] pub struct CrateSource { - pub dylib: Option, - pub rlib: Option, + pub dylib: Option<(Path, PathKind)>, + pub rlib: Option<(Path, PathKind)>, pub cnum: ast::CrateNum, } @@ -178,10 +179,10 @@ impl CStore { let mut libs = self.used_crate_sources.borrow() .iter() .map(|src| (src.cnum, match prefer { - RequireDynamic => src.dylib.clone(), - RequireStatic => src.rlib.clone(), + RequireDynamic => src.dylib.clone().map(|p| p.0), + RequireStatic => src.rlib.clone().map(|p| p.0), })) - .collect::)>>(); + .collect::>(); libs.sort_by(|&(a, _), &(b, _)| { ordering.position_elem(&a).cmp(&ordering.position_elem(&b)) }); diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs index 193b14174c7ab..55a23aa516eca 100644 --- a/src/librustc/metadata/filesearch.rs +++ b/src/librustc/metadata/filesearch.rs @@ -39,13 +39,13 @@ pub struct FileSearch<'a> { impl<'a> FileSearch<'a> { pub fn for_each_lib_search_path(&self, mut f: F) where - F: FnMut(&Path) -> FileMatch, + F: FnMut(&Path, PathKind) -> FileMatch, { let mut visited_dirs = HashSet::new(); let mut found = false; - for path in self.search_paths.iter(self.kind) { - match f(path) { + for (path, kind) in self.search_paths.iter(self.kind) { + match f(path, kind) { FileMatches => found = true, FileDoesntMatch => () } @@ -56,7 +56,7 @@ impl<'a> FileSearch<'a> { let tlib_path = make_target_lib_path(self.sysroot, self.triple); if !visited_dirs.contains(tlib_path.as_vec()) { - match f(&tlib_path) { + match f(&tlib_path, PathKind::All) { FileMatches => found = true, FileDoesntMatch => () } @@ -76,7 +76,7 @@ impl<'a> FileSearch<'a> { visited_dirs.insert(tlib_path.as_vec().to_vec()); // Don't keep searching the RUST_PATH if one match turns up -- // if we did, we'd get a "multiple matching crates" error - match f(&tlib_path) { + match f(&tlib_path, PathKind::All) { FileMatches => { break; } @@ -91,8 +91,10 @@ impl<'a> FileSearch<'a> { make_target_lib_path(self.sysroot, self.triple) } - pub fn search(&self, mut pick: F) where F: FnMut(&Path) -> FileMatch { - self.for_each_lib_search_path(|lib_search_path| { + pub fn search(&self, mut pick: F) + where F: FnMut(&Path, PathKind) -> FileMatch + { + self.for_each_lib_search_path(|lib_search_path, kind| { debug!("searching {}", lib_search_path.display()); match fs::readdir(lib_search_path) { Ok(files) => { @@ -108,7 +110,7 @@ impl<'a> FileSearch<'a> { let files2 = files.iter().filter(|p| !is_rlib(p)); for path in files1.chain(files2) { debug!("testing {}", path.display()); - let maybe_picked = pick(path); + let maybe_picked = pick(path, kind); match maybe_picked { FileMatches => { debug!("picked {}", path.display()); @@ -142,7 +144,7 @@ impl<'a> FileSearch<'a> { // Returns a list of directories where target-specific dylibs might be located. pub fn get_dylib_search_paths(&self) -> Vec { let mut paths = Vec::new(); - self.for_each_lib_search_path(|lib_search_path| { + self.for_each_lib_search_path(|lib_search_path, _| { paths.push(lib_search_path.clone()); FileDoesntMatch }); diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index f749721cca8c2..70b6ddf23fd8d 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -215,6 +215,7 @@ use back::archive::{METADATA_FILENAME}; use back::svh::Svh; use session::Session; +use session::search_paths::PathKind; use llvm; use llvm::{False, ObjectFile, mk_section_iter}; use llvm::archive_ro::ArchiveRO; @@ -229,7 +230,7 @@ use rustc_back::target::Target; use std::ffi::CString; use std::cmp; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::io::fs::PathExtensions; use std::io; use std::ptr; @@ -260,8 +261,8 @@ pub struct Context<'a> { } pub struct Library { - pub dylib: Option, - pub rlib: Option, + pub dylib: Option<(Path, PathKind)>, + pub rlib: Option<(Path, PathKind)>, pub metadata: MetadataBlob, } @@ -384,7 +385,7 @@ impl<'a> Context<'a> { // of the crate id (path/name/id). // // The goal of this step is to look at as little metadata as possible. - self.filesearch.search(|path| { + self.filesearch.search(|path, kind| { let file = match path.filename_str() { None => return FileDoesntMatch, Some(file) => file, @@ -404,12 +405,12 @@ impl<'a> Context<'a> { let hash_str = hash.to_string(); let slot = candidates.entry(hash_str).get().unwrap_or_else( - |vacant_entry| vacant_entry.insert((HashSet::new(), HashSet::new()))); + |vacant_entry| vacant_entry.insert((HashMap::new(), HashMap::new()))); let (ref mut rlibs, ref mut dylibs) = *slot; if rlib { - rlibs.insert(fs::realpath(path).unwrap()); + rlibs.insert(fs::realpath(path).unwrap(), kind); } else { - dylibs.insert(fs::realpath(path).unwrap()); + dylibs.insert(fs::realpath(path).unwrap(), kind); } FileMatches @@ -453,16 +454,16 @@ impl<'a> Context<'a> { self.sess.note("candidates:"); for lib in libraries.iter() { match lib.dylib { - Some(ref p) => { + Some((ref p, _)) => { self.sess.note(&format!("path: {}", p.display())[]); } None => {} } match lib.rlib { - Some(ref p) => { + Some((ref p, _)) => { self.sess.note(&format!("path: {}", - p.display())[]); + p.display())[]); } None => {} } @@ -483,9 +484,9 @@ impl<'a> Context<'a> { // read the metadata from it if `*slot` is `None`. If the metadata couldn't // be read, it is assumed that the file isn't a valid rust library (no // errors are emitted). - fn extract_one(&mut self, m: HashSet, flavor: &str, - slot: &mut Option) -> Option { - let mut ret = None::; + fn extract_one(&mut self, m: HashMap, flavor: &str, + slot: &mut Option) -> Option<(Path, PathKind)> { + let mut ret = None::<(Path, PathKind)>; let mut error = 0u; if slot.is_some() { @@ -500,7 +501,7 @@ impl<'a> Context<'a> { } } - for lib in m.into_iter() { + for (lib, kind) in m.into_iter() { info!("{} reading metadata from: {}", flavor, lib.display()); let metadata = match get_metadata_section(self.target.options.is_like_osx, &lib) { @@ -525,7 +526,7 @@ impl<'a> Context<'a> { self.crate_name)[]); self.sess.span_note(self.span, &format!(r"candidate #1: {}", - ret.as_ref().unwrap() + ret.as_ref().unwrap().0 .display())[]); error = 1; ret = None; @@ -538,7 +539,7 @@ impl<'a> Context<'a> { continue } *slot = Some(metadata); - ret = Some(lib); + ret = Some((lib, kind)); } return if error > 0 {None} else {ret} } @@ -606,8 +607,8 @@ impl<'a> Context<'a> { // rlibs/dylibs. let sess = self.sess; let dylibname = self.dylibname(); - let mut rlibs = HashSet::new(); - let mut dylibs = HashSet::new(); + let mut rlibs = HashMap::new(); + let mut dylibs = HashMap::new(); { let mut locs = locs.iter().map(|l| Path::new(&l[])).filter(|loc| { if !loc.exists() { @@ -637,13 +638,15 @@ impl<'a> Context<'a> { false }); - // Now that we have an iterator of good candidates, make sure there's at - // most one rlib and at most one dylib. + // Now that we have an iterator of good candidates, make sure + // there's at most one rlib and at most one dylib. for loc in locs { if loc.filename_str().unwrap().ends_with(".rlib") { - rlibs.insert(fs::realpath(&loc).unwrap()); + rlibs.insert(fs::realpath(&loc).unwrap(), + PathKind::ExternFlag); } else { - dylibs.insert(fs::realpath(&loc).unwrap()); + dylibs.insert(fs::realpath(&loc).unwrap(), + PathKind::ExternFlag); } } }; diff --git a/src/librustc/session/search_paths.rs b/src/librustc/session/search_paths.rs index 0cd501ca05fcd..0cf04fe0a006a 100644 --- a/src/librustc/session/search_paths.rs +++ b/src/librustc/session/search_paths.rs @@ -25,6 +25,7 @@ pub enum PathKind { Native, Crate, Dependency, + ExternFlag, All, } @@ -54,14 +55,16 @@ impl SearchPaths { } impl<'a> Iterator for Iter<'a> { - type Item = &'a Path; + type Item = (&'a Path, PathKind); - fn next(&mut self) -> Option<&'a Path> { + fn next(&mut self) -> Option<(&'a Path, PathKind)> { loop { match self.iter.next() { Some(&(kind, ref p)) if self.kind == PathKind::All || kind == PathKind::All || - kind == self.kind => return Some(p), + kind == self.kind => { + return Some((p, kind)) + } Some(..) => {} None => return None, } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 351be70cf5261..dacf620cbd1d0 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -507,7 +507,7 @@ fn link_binary_output(sess: &Session, fn archive_search_paths(sess: &Session) -> Vec { let mut search = Vec::new(); - sess.target_filesearch(PathKind::Native).for_each_lib_search_path(|path| { + sess.target_filesearch(PathKind::Native).for_each_lib_search_path(|path, _| { search.push(path.clone()); FileDoesntMatch }); @@ -1043,7 +1043,7 @@ fn link_args(cmd: &mut Command, // in the current crate. Upstream crates with native library dependencies // may have their native library pulled in above. fn add_local_native_libraries(cmd: &mut Command, sess: &Session) { - sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path| { + sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path, _| { cmd.arg("-L").arg(path); FileDoesntMatch }); @@ -1146,10 +1146,10 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, let src = sess.cstore.get_used_crate_source(cnum).unwrap(); match kind { cstore::RequireDynamic => { - add_dynamic_crate(cmd, sess, src.dylib.unwrap()) + add_dynamic_crate(cmd, sess, src.dylib.unwrap().0) } cstore::RequireStatic => { - add_static_crate(cmd, sess, tmpdir, src.rlib.unwrap()) + add_static_crate(cmd, sess, tmpdir, src.rlib.unwrap().0) } } diff --git a/src/test/run-make/compiler-lookup-paths-2/Makefile b/src/test/run-make/compiler-lookup-paths-2/Makefile new file mode 100644 index 0000000000000..bd7f62d5c2d89 --- /dev/null +++ b/src/test/run-make/compiler-lookup-paths-2/Makefile @@ -0,0 +1,8 @@ +-include ../tools.mk + +all: + mkdir -p $(TMPDIR)/a $(TMPDIR)/b + $(RUSTC) a.rs && mv $(TMPDIR)/liba.rlib $(TMPDIR)/a + $(RUSTC) b.rs -L $(TMPDIR)/a && mv $(TMPDIR)/libb.rlib $(TMPDIR)/b + $(RUSTC) c.rs -L crate=$(TMPDIR)/b -L dependency=$(TMPDIR)/a \ + && exit 1 || exit 0 diff --git a/src/test/run-make/compiler-lookup-paths-2/a.rs b/src/test/run-make/compiler-lookup-paths-2/a.rs new file mode 100644 index 0000000000000..e7572a5f61578 --- /dev/null +++ b/src/test/run-make/compiler-lookup-paths-2/a.rs @@ -0,0 +1,11 @@ +// 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. + +#![crate_type = "lib"] diff --git a/src/test/run-make/compiler-lookup-paths-2/b.rs b/src/test/run-make/compiler-lookup-paths-2/b.rs new file mode 100644 index 0000000000000..fee0da9b4c194 --- /dev/null +++ b/src/test/run-make/compiler-lookup-paths-2/b.rs @@ -0,0 +1,12 @@ +// 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. + +#![crate_type = "lib"] +extern crate a; diff --git a/src/test/run-make/compiler-lookup-paths-2/c.rs b/src/test/run-make/compiler-lookup-paths-2/c.rs new file mode 100644 index 0000000000000..66fe51d10991e --- /dev/null +++ b/src/test/run-make/compiler-lookup-paths-2/c.rs @@ -0,0 +1,13 @@ +// 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. + +#![crate_type = "lib"] +extern crate b; +extern crate a; diff --git a/src/test/run-make/extern-flag-disambiguates/Makefile b/src/test/run-make/extern-flag-disambiguates/Makefile index 9b86bf9754956..049b76c1b648a 100644 --- a/src/test/run-make/extern-flag-disambiguates/Makefile +++ b/src/test/run-make/extern-flag-disambiguates/Makefile @@ -17,8 +17,10 @@ all: $(RUSTC) -C metadata=2 -C extra-filename=-2 a.rs $(RUSTC) b.rs --extern a=$(TMPDIR)/liba-1.rlib $(RUSTC) c.rs --extern a=$(TMPDIR)/liba-2.rlib + @echo before $(RUSTC) --cfg before d.rs --extern a=$(TMPDIR)/liba-1.rlib $(call RUN,d) + @echo after $(RUSTC) --cfg after d.rs --extern a=$(TMPDIR)/liba-1.rlib $(call RUN,d) diff --git a/src/test/run-make/extern-flag-disambiguates/a.rs b/src/test/run-make/extern-flag-disambiguates/a.rs index 11b9ba6fce338..ac92aede7894f 100644 --- a/src/test/run-make/extern-flag-disambiguates/a.rs +++ b/src/test/run-make/extern-flag-disambiguates/a.rs @@ -11,6 +11,6 @@ #![crate_name = "a"] #![crate_type = "rlib"] -static FOO: uint = 3; +static FOO: usize = 3; -pub fn token() -> &'static uint { &FOO } +pub fn token() -> &'static usize { &FOO } diff --git a/src/test/run-make/extern-flag-disambiguates/b.rs b/src/test/run-make/extern-flag-disambiguates/b.rs index 3156cf0ba7275..8ae238f5a482c 100644 --- a/src/test/run-make/extern-flag-disambiguates/b.rs +++ b/src/test/run-make/extern-flag-disambiguates/b.rs @@ -13,7 +13,7 @@ extern crate a; -static FOO: uint = 3; +static FOO: usize = 3; -pub fn token() -> &'static uint { &FOO } -pub fn a_token() -> &'static uint { a::token() } +pub fn token() -> &'static usize { &FOO } +pub fn a_token() -> &'static usize { a::token() } diff --git a/src/test/run-make/extern-flag-disambiguates/c.rs b/src/test/run-make/extern-flag-disambiguates/c.rs index d3bbc762ef2f3..6eccdf7e5c857 100644 --- a/src/test/run-make/extern-flag-disambiguates/c.rs +++ b/src/test/run-make/extern-flag-disambiguates/c.rs @@ -13,7 +13,7 @@ extern crate a; -static FOO: uint = 3; +static FOO: usize = 3; -pub fn token() -> &'static uint { &FOO } -pub fn a_token() -> &'static uint { a::token() } +pub fn token() -> &'static usize { &FOO } +pub fn a_token() -> &'static usize { a::token() } diff --git a/src/test/run-make/extern-flag-disambiguates/d.rs b/src/test/run-make/extern-flag-disambiguates/d.rs index d850daffc3941..90e1330d4ae47 100644 --- a/src/test/run-make/extern-flag-disambiguates/d.rs +++ b/src/test/run-make/extern-flag-disambiguates/d.rs @@ -13,7 +13,7 @@ extern crate b; extern crate c; #[cfg(after)] extern crate a; -fn t(a: &'static uint) -> uint { a as *const _ as uint } +fn t(a: &'static usize) -> usize { a as *const _ as usize } fn main() { assert!(t(a::token()) == t(b::a_token())); From 948dcaa72b1d259068182d85fb4e13cea826bf64 Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Thu, 15 Jan 2015 00:31:22 -0800 Subject: [PATCH 027/211] Remove perl as a dependency --- README.md | 1 - configure | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index e858e91773b92..183c2fb6906f7 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ documentation. 1. Make sure you have installed the dependencies: * `g++` 4.7 or `clang++` 3.x * `python` 2.6 or later (but not 3.x) - * `perl` 5.0 or later * GNU `make` 3.81 or later * `curl` * `git` diff --git a/configure b/configure index 86ed88c8d0631..2c741bc972fb6 100755 --- a/configure +++ b/configure @@ -617,7 +617,6 @@ putvar CFG_BOOTSTRAP_KEY step_msg "looking for build programs" -probe_need CFG_PERL perl probe_need CFG_CURLORWGET curl wget probe_need CFG_PYTHON python2.7 python2.6 python2 python @@ -1375,7 +1374,7 @@ do done # Munge any paths that appear in config.mk back to posix-y -perl -i.bak -p -e 's@ ([a-zA-Z]):[/\\]@ /\1/@go;' config.tmp +sed -i.bak -e 's@ \([a-zA-Z]\):[/\\]@ /\1/@g;' config.tmp rm -f config.tmp.bak msg From 68372ae65597839c800c653d0c64152523c6b978 Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Thu, 15 Jan 2015 00:31:45 -0800 Subject: [PATCH 028/211] Remove unused script --- src/etc/check-links.pl | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100755 src/etc/check-links.pl diff --git a/src/etc/check-links.pl b/src/etc/check-links.pl deleted file mode 100755 index 3818c0f840139..0000000000000 --- a/src/etc/check-links.pl +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/perl -w -# 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. - -my $file = $ARGV[0]; - -my @lines = <>; - -my $anchors = {}; - -my $i = 0; -for $line (@lines) { - $i++; - if ($line =~ m/id="([^"]+)"/) { - $anchors->{$1} = $i; - } -} - -$i = 0; -for $line (@lines) { - $i++; - while ($line =~ m/href="#([^"]+)"/g) { - if (! exists($anchors->{$1})) { - print "$file:$i: $1 referenced\n"; - } - } -} From de3ea99ec55d5f544abef6df836d67e6b87cb299 Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Thu, 15 Jan 2015 00:23:46 -0800 Subject: [PATCH 029/211] Port indenter to python --- src/etc/indenter | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/etc/indenter b/src/etc/indenter index 1a3a446533572..b3eed6a144342 100755 --- a/src/etc/indenter +++ b/src/etc/indenter @@ -1,16 +1,19 @@ -#!/usr/bin/perl -use strict; -use warnings; +#!/usr/bin/env python +import re +import sys -my $indent = 0; -while (<>) { - if (/^rust: ~">>/) { - $indent += 1; - } +indent = 0 +more_re = re.compile(r"^rust: ~\">>") +less_re = re.compile(r"^rust: ~\"<<") +while True: + line = sys.stdin.readline() + if not line: + break - printf "%03d %s%s", $indent, (" " x $indent), $_; + if more_re.match(line): + indent += 1 - if (/^rust: ~"< Date: Tue, 13 Jan 2015 21:24:26 -0800 Subject: [PATCH 030/211] Rewrite Condvar::wait_timeout and make it public **The implementation is a direct adaptation of libcxx's condition_variable implementation.** pthread_cond_timedwait uses the non-monotonic system clock. It's possible to change the clock to a monotonic via pthread_cond_attr, but this is incompatible with static initialization. To deal with this, we calculate the timeout using the system clock, and maintain a separate record of the start and end times with a monotonic clock to be used for calculation of the return value. --- src/libstd/sync/condvar.rs | 123 +++++++++++++++++++++++++++++--- src/libstd/sync/poison.rs | 17 ++++- src/libstd/sys/unix/condvar.rs | 56 +++++++++------ src/libstd/sys/unix/mod.rs | 1 + src/libstd/sys/unix/time.rs | 124 +++++++++++++++++++++++++++++++++ src/libstd/sys/windows/mod.rs | 1 + src/libstd/sys/windows/time.rs | 50 +++++++++++++ src/libstd/time/mod.rs | 68 +----------------- 8 files changed, 343 insertions(+), 97 deletions(-) create mode 100644 src/libstd/sys/unix/time.rs create mode 100644 src/libstd/sys/windows/time.rs diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index bcd5f56a35379..b8b186f31e044 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -12,6 +12,7 @@ use prelude::v1::*; use sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; use sync::poison::{self, LockResult}; +use sys::time::SteadyTime; use sys_common::condvar as sys; use sys_common::mutex as sys_mutex; use time::Duration; @@ -153,13 +154,8 @@ impl Condvar { /// /// Like `wait`, the lock specified will be re-acquired when this function /// returns, regardless of whether the timeout elapsed or not. - // Note that this method is *not* public, and this is quite intentional - // because we're not quite sure about the semantics of relative vs absolute - // durations or how the timing guarantees play into what the system APIs - // provide. There are also additional concerns about the unix-specific - // implementation which may need to be addressed. - #[allow(dead_code)] - fn wait_timeout<'a, T>(&self, guard: MutexGuard<'a, T>, dur: Duration) + #[unstable] + pub fn wait_timeout<'a, T>(&self, guard: MutexGuard<'a, T>, dur: Duration) -> LockResult<(MutexGuard<'a, T>, bool)> { unsafe { let me: &'static Condvar = &*(self as *const _); @@ -167,6 +163,25 @@ impl Condvar { } } + /// Wait on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// The semantics of this function are equivalent to `wait_timeout` except + /// that the implementation will repeatedly wait while the duration has not + /// passed and the provided function returns `false`. + #[unstable] + pub fn wait_timeout_with<'a, T, F>(&self, + guard: MutexGuard<'a, T>, + dur: Duration, + f: F) + -> LockResult<(MutexGuard<'a, T>, bool)> + where F: FnMut(LockResult<&mut T>) -> bool { + unsafe { + let me: &'static Condvar = &*(self as *const _); + me.inner.wait_timeout_with(guard, dur, f) + } + } + /// Wake up one blocked thread on this condvar. /// /// If there is a blocked thread on this condition variable, then it will @@ -220,9 +235,9 @@ impl StaticCondvar { /// specified duration. /// /// See `Condvar::wait_timeout`. - #[allow(dead_code)] // may want to stabilize this later, see wait_timeout above - fn wait_timeout<'a, T>(&'static self, guard: MutexGuard<'a, T>, dur: Duration) - -> LockResult<(MutexGuard<'a, T>, bool)> { + #[unstable = "may be merged with Condvar in the future"] + pub fn wait_timeout<'a, T>(&'static self, guard: MutexGuard<'a, T>, dur: Duration) + -> LockResult<(MutexGuard<'a, T>, bool)> { let (poisoned, success) = unsafe { let lock = mutex::guard_lock(&guard); self.verify(lock); @@ -236,6 +251,50 @@ impl StaticCondvar { } } + /// Wait on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// The implementation will repeatedly wait while the duration has not + /// passed and the function returns `false`. + /// + /// See `Condvar::wait_timeout_with`. + #[unstable = "may be merged with Condvar in the future"] + pub fn wait_timeout_with<'a, T, F>(&'static self, + guard: MutexGuard<'a, T>, + dur: Duration, + mut f: F) + -> LockResult<(MutexGuard<'a, T>, bool)> + where F: FnMut(LockResult<&mut T>) -> bool { + // This could be made more efficient by pushing the implementation into sys::condvar + let start = SteadyTime::now(); + let mut guard_result: LockResult> = Ok(guard); + while !f(guard_result + .as_mut() + .map(|g| &mut **g) + .map_err(|e| poison::new_poison_error(&mut **e.get_mut()))) { + let now = SteadyTime::now(); + let consumed = &now - &start; + let guard = guard_result.unwrap_or_else(|e| e.into_inner()); + let (new_guard_result, no_timeout) = match self.wait_timeout(guard, dur - consumed) { + Ok((new_guard, no_timeout)) => (Ok(new_guard), no_timeout), + Err(err) => { + let (new_guard, no_timeout) = err.into_inner(); + (Err(poison::new_poison_error(new_guard)), no_timeout) + } + }; + guard_result = new_guard_result; + if !no_timeout { + let result = f(guard_result + .as_mut() + .map(|g| &mut **g) + .map_err(|e| poison::new_poison_error(&mut **e.get_mut()))); + return poison::map_result(guard_result, |g| (g, result)); + } + } + + poison::map_result(guard_result, |g| (g, true)) + } + /// Wake up one blocked thread on this condvar. /// /// See `Condvar::notify_one`. @@ -285,6 +344,7 @@ mod tests { use super::{StaticCondvar, CONDVAR_INIT}; use sync::mpsc::channel; use sync::{StaticMutex, MUTEX_INIT, Condvar, Mutex, Arc}; + use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use thread::Thread; use time::Duration; @@ -372,6 +432,49 @@ mod tests { unsafe { C.destroy(); M.destroy(); } } + #[test] + fn wait_timeout_with() { + static C: StaticCondvar = CONDVAR_INIT; + static M: StaticMutex = MUTEX_INIT; + static S: AtomicUsize = ATOMIC_USIZE_INIT; + + let g = M.lock().unwrap(); + let (g, success) = C.wait_timeout_with(g, Duration::nanoseconds(1000), |_| false).unwrap(); + assert!(!success); + + let (tx, rx) = channel(); + let _t = Thread::scoped(move || { + rx.recv().unwrap(); + let g = M.lock().unwrap(); + S.store(1, Ordering::SeqCst); + C.notify_one(); + drop(g); + + rx.recv().unwrap(); + let g = M.lock().unwrap(); + S.store(2, Ordering::SeqCst); + C.notify_one(); + drop(g); + + rx.recv().unwrap(); + let _g = M.lock().unwrap(); + S.store(3, Ordering::SeqCst); + C.notify_one(); + }); + + let mut state = 0; + let (_g, success) = C.wait_timeout_with(g, Duration::days(1), |_| { + assert_eq!(state, S.load(Ordering::SeqCst)); + tx.send(()).unwrap(); + state += 1; + match state { + 1|2 => false, + _ => true, + } + }).unwrap(); + assert!(success); + } + #[test] #[should_fail] fn two_mutexes() { diff --git a/src/libstd/sync/poison.rs b/src/libstd/sync/poison.rs index 385df45b400c4..cc8c331ef3997 100644 --- a/src/libstd/sync/poison.rs +++ b/src/libstd/sync/poison.rs @@ -99,8 +99,23 @@ impl fmt::Show for PoisonError { impl PoisonError { /// Consumes this error indicating that a lock is poisoned, returning the /// underlying guard to allow access regardless. - #[stable] + #[deprecated="renamed to into_inner"] pub fn into_guard(self) -> T { self.guard } + + /// Consumes this error indicating that a lock is poisoned, returning the + /// underlying guard to allow access regardless. + #[unstable] + pub fn into_inner(self) -> T { self.guard } + + /// Reaches into this error indicating that a lock is poisoned, returning a + /// reference to the underlying guard to allow access regardless. + #[unstable] + pub fn get_ref(&self) -> &T { &self.guard } + + /// Reaches into this error indicating that a lock is poisoned, returning a + /// mutable reference to the underlying guard to allow access regardless. + #[unstable] + pub fn get_mut(&mut self) -> &mut T { &mut self.guard } } impl FromError> for TryLockError { diff --git a/src/libstd/sys/unix/condvar.rs b/src/libstd/sys/unix/condvar.rs index 52dd261824fd4..85a65bbef508d 100644 --- a/src/libstd/sys/unix/condvar.rs +++ b/src/libstd/sys/unix/condvar.rs @@ -10,9 +10,12 @@ use cell::UnsafeCell; use libc; +use std::option::Option::{Some, None}; use sys::mutex::{self, Mutex}; +use sys::time; use sys::sync as ffi; use time::Duration; +use num::{Int, NumCast}; pub struct Condvar { inner: UnsafeCell } @@ -46,33 +49,46 @@ impl Condvar { debug_assert_eq!(r, 0); } + // This implementation is modeled after libcxx's condition_variable + // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46 + // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367 pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - assert!(dur >= Duration::nanoseconds(0)); + if dur <= Duration::zero() { + return false; + } - // First, figure out what time it currently is - let mut tv = libc::timeval { tv_sec: 0, tv_usec: 0 }; - let r = ffi::gettimeofday(&mut tv, 0 as *mut _); + // First, figure out what time it currently is, in both system and stable time. + // pthread_cond_timedwait uses system time, but we want to report timeout based on stable + // time. + let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 }; + let stable_now = time::SteadyTime::now(); + let r = ffi::gettimeofday(&mut sys_now, 0 as *mut _); debug_assert_eq!(r, 0); - // Offset that time with the specified duration - let abs = Duration::seconds(tv.tv_sec as i64) + - Duration::microseconds(tv.tv_usec as i64) + - dur; - let ns = abs.num_nanoseconds().unwrap() as u64; - let timeout = libc::timespec { - tv_sec: (ns / 1000000000) as libc::time_t, - tv_nsec: (ns % 1000000000) as libc::c_long, + let seconds = NumCast::from(dur.num_seconds()); + let timeout = match seconds.and_then(|s| sys_now.tv_sec.checked_add(s)) { + Some(sec) => { + libc::timespec { + tv_sec: sec, + tv_nsec: (dur - Duration::seconds(dur.num_seconds())) + .num_nanoseconds().unwrap() as libc::c_long, + } + } + None => { + libc::timespec { + tv_sec: Int::max_value(), + tv_nsec: 1_000_000_000 - 1, + } + } }; // And wait! - let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), - &timeout); - if r != 0 { - debug_assert_eq!(r as int, libc::ETIMEDOUT as int); - false - } else { - true - } + let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), &timeout); + debug_assert!(r == libc::ETIMEDOUT || r == 0); + + // ETIMEDOUT is not a totally reliable method of determining timeout due to clock shifts, + // so do the check ourselves + &time::SteadyTime::now() - &stable_now < dur } #[inline] diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 6a408aa60f0bf..bb98d1e052a1c 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -52,6 +52,7 @@ pub mod sync; pub mod tcp; pub mod thread; pub mod thread_local; +pub mod time; pub mod timer; pub mod tty; pub mod udp; diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs new file mode 100644 index 0000000000000..cc1e23fbca98a --- /dev/null +++ b/src/libstd/sys/unix/time.rs @@ -0,0 +1,124 @@ +// 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. + +pub use self::inner::SteadyTime; + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod inner { + use libc; + use time::Duration; + use ops::Sub; + use sync::{Once, ONCE_INIT}; + + pub struct SteadyTime { + t: u64 + } + + extern { + pub fn mach_absolute_time() -> u64; + pub fn mach_timebase_info(info: *mut libc::mach_timebase_info) -> libc::c_int; + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + SteadyTime { + t: unsafe { mach_absolute_time() }, + } + } + + pub fn ns(&self) -> u64 { + let info = info(); + self.t * info.numer as u64 / info.denom as u64 + } + } + + fn info() -> &'static libc::mach_timebase_info { + static mut INFO: libc::mach_timebase_info = libc::mach_timebase_info { + numer: 0, + denom: 0, + }; + static ONCE: Once = ONCE_INIT; + + unsafe { + ONCE.call_once(|| { + mach_timebase_info(&mut INFO); + }); + &INFO + } + } + + impl<'a> Sub for &'a SteadyTime { + type Output = Duration; + + fn sub(self, other: &SteadyTime) -> Duration { + unsafe { + let info = info(); + let diff = self.t as i64 - other.t as i64; + Duration::nanoseconds(diff * info.numer as i64 / info.denom as i64) + } + } + } +} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +mod inner { + use libc; + use time::Duration; + use ops::Sub; + + const NSEC_PER_SEC: i64 = 1_000_000_000; + + pub struct SteadyTime { + t: libc::timespec, + } + + // Apparently android provides this in some other library? + #[cfg(not(target_os = "android"))] + #[link(name = "rt")] + extern {} + + extern { + fn clock_gettime(clk_id: libc::c_int, tp: *mut libc::timespec) -> libc::c_int; + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + let mut t = SteadyTime { + t: libc::timespec { + tv_sec: 0, + tv_nsec: 0, + } + }; + unsafe { + assert_eq!(0, clock_gettime(libc::CLOCK_MONOTONIC, &mut t.t)); + } + t + } + + pub fn ns(&self) -> u64 { + self.t.tv_sec as u64 * NSEC_PER_SEC as u64 + self.t.tv_nsec as u64 + } + } + + impl<'a> Sub for &'a SteadyTime { + type Output = Duration; + + fn sub(self, other: &SteadyTime) -> Duration { + if self.t.tv_nsec >= other.t.tv_nsec { + Duration::seconds(self.t.tv_sec as i64 - other.t.tv_sec as i64) + + Duration::nanoseconds(self.t.tv_nsec as i64 - other.t.tv_nsec as i64) + } else { + Duration::seconds(self.t.tv_sec as i64 - 1 - other.t.tv_sec as i64) + + Duration::nanoseconds(self.t.tv_nsec as i64 + NSEC_PER_SEC - + other.t.tv_nsec as i64) + } + } + } +} diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 0e706c3cc6a57..72fc2f8700d6f 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -50,6 +50,7 @@ pub mod rwlock; pub mod sync; pub mod stack_overflow; pub mod tcp; +pub mod time; pub mod thread; pub mod thread_local; pub mod timer; diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs new file mode 100644 index 0000000000000..20ceff0aa6931 --- /dev/null +++ b/src/libstd/sys/windows/time.rs @@ -0,0 +1,50 @@ +// 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. +use libc; +use ops::Sub; +use time::Duration; +use sync::{Once, ONCE_INIT}; + +pub struct SteadyTime { + t: libc::LARGE_INTEGER, +} + +impl SteadyTime { + pub fn now() -> SteadyTime { + let mut t = SteadyTime { t: 0 }; + unsafe { libc::QueryPerformanceCounter(&mut t.t); } + t + } + + pub fn ns(&self) -> u64 { + self.t as u64 * 1_000_000_000 / frequency() as u64 + } +} + +fn frequency() -> libc::LARGE_INTEGER { + static mut FREQUENCY: libc::LARGE_INTEGER = 0; + static ONCE: Once = ONCE_INIT; + + unsafe { + ONCE.call_once(|| { + libc::QueryPerformanceFrequency(&mut FREQUENCY); + }); + FREQUENCY + } +} + +impl<'a> Sub for &'a SteadyTime { + type Output = Duration; + + fn sub(self, other: &SteadyTime) -> Duration { + let diff = self.t as i64 - other.t as i64; + Duration::microseconds(diff * 1_000_000 / frequency() as i64) + } +} diff --git a/src/libstd/time/mod.rs b/src/libstd/time/mod.rs index d6c94f27a8baf..f62571942a78d 100644 --- a/src/libstd/time/mod.rs +++ b/src/libstd/time/mod.rs @@ -10,7 +10,7 @@ //! Temporal quantification. -use libc; +use sys::time::SteadyTime; pub use self::duration::Duration; @@ -20,69 +20,5 @@ pub mod duration; /// in nanoseconds since an unspecified epoch. // NB: this is intentionally not public, this is not ready to stabilize its api. fn precise_time_ns() -> u64 { - return os_precise_time_ns(); - - #[cfg(windows)] - fn os_precise_time_ns() -> u64 { - let mut ticks_per_s = 0; - assert_eq!(unsafe { - libc::QueryPerformanceFrequency(&mut ticks_per_s) - }, 1); - let ticks_per_s = if ticks_per_s == 0 {1} else {ticks_per_s}; - let mut ticks = 0; - assert_eq!(unsafe { - libc::QueryPerformanceCounter(&mut ticks) - }, 1); - - return (ticks as u64 * 1000000000) / (ticks_per_s as u64); - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - fn os_precise_time_ns() -> u64 { - use sync; - - static mut TIMEBASE: libc::mach_timebase_info = libc::mach_timebase_info { numer: 0, - denom: 0 }; - static ONCE: sync::Once = sync::ONCE_INIT; - unsafe { - ONCE.call_once(|| { - imp::mach_timebase_info(&mut TIMEBASE); - }); - let time = imp::mach_absolute_time(); - time * TIMEBASE.numer as u64 / TIMEBASE.denom as u64 - } - } - - #[cfg(not(any(windows, target_os = "macos", target_os = "ios")))] - fn os_precise_time_ns() -> u64 { - let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 }; - unsafe { - imp::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts); - } - return (ts.tv_sec as u64) * 1000000000 + (ts.tv_nsec as u64) - } -} - -#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios")))] -mod imp { - use libc::{c_int, timespec}; - - // Apparently android provides this in some other library? - #[cfg(not(target_os = "android"))] - #[link(name = "rt")] - extern {} - - extern { - pub fn clock_gettime(clk_id: c_int, tp: *mut timespec) -> c_int; - } - -} -#[cfg(any(target_os = "macos", target_os = "ios"))] -mod imp { - use libc::{c_int, mach_timebase_info}; - - extern { - pub fn mach_absolute_time() -> u64; - pub fn mach_timebase_info(info: *mut mach_timebase_info) -> c_int; - } + SteadyTime::now().ns() } From 89de6926b59136c73bf667434a53b9e05146499d Mon Sep 17 00:00:00 2001 From: Daniil Smirnov Date: Sat, 17 Jan 2015 01:37:07 +0300 Subject: [PATCH 031/211] Fixes `join_multicast` on Windows #20381 --- src/liblibc/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index deab0cabfbe51..7be2178c9b2d0 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -2206,10 +2206,10 @@ pub mod consts { pub const IPPROTO_TCP: c_int = 6; pub const IPPROTO_IP: c_int = 0; pub const IPPROTO_IPV6: c_int = 41; - pub const IP_MULTICAST_TTL: c_int = 3; - pub const IP_MULTICAST_LOOP: c_int = 4; - pub const IP_ADD_MEMBERSHIP: c_int = 5; - pub const IP_DROP_MEMBERSHIP: c_int = 6; + pub const IP_MULTICAST_TTL: c_int = 10; + pub const IP_MULTICAST_LOOP: c_int = 11; + pub const IP_ADD_MEMBERSHIP: c_int = 12; + pub const IP_DROP_MEMBERSHIP: c_int = 13; pub const IPV6_ADD_MEMBERSHIP: c_int = 5; pub const IPV6_DROP_MEMBERSHIP: c_int = 6; pub const IP_TTL: c_int = 4; From 38cb91e66ca7fc7374909b31598dab09db37edaa Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 16 Jan 2015 10:55:24 -0800 Subject: [PATCH 032/211] syntax: Feature gate #[start] and #[main] These two attributes are used to change the entry point into a Rust program, but for now they're being put behind feature gates until we have a chance to think about them a little more. The #[start] attribute specifically may have its signature changed. This is a breaking change to due the usage of these attributes generating errors by default now. If your crate is using these attributes, add this to your crate root: #![feature(start)] // if you're using the #[start] attribute #![feature(main)] // if you're using the #[main] attribute cc #20064 --- src/doc/trpl/unsafe.md | 8 ++++---- src/libsyntax/feature_gate.rs | 14 ++++++++++++++ src/test/compile-fail/feature-gate-main.rs | 12 ++++++++++++ src/test/compile-fail/feature-gate-start.rs | 13 +++++++++++++ src/test/compile-fail/issue-9575.rs | 2 ++ src/test/compile-fail/lang-item-missing.rs | 1 + src/test/compile-fail/lint-dead-code-2.rs | 1 + src/test/compile-fail/multiple-main-2.rs | 2 ++ src/test/compile-fail/multiple-main-3.rs | 2 ++ src/test/compile-fail/privacy1.rs | 2 +- src/test/compile-fail/privacy2.rs | 1 + src/test/compile-fail/privacy3.rs | 1 + src/test/compile-fail/privacy4.rs | 2 +- src/test/run-pass/attr-main-2.rs | 1 + src/test/run-pass/attr-main.rs | 1 + src/test/run-pass/attr-start.rs | 1 + src/test/run-pass/attr.rs | 2 ++ src/test/run-pass/intrinsic-alignment.rs | 2 +- src/test/run-pass/issue-16597-empty.rs | 1 + src/test/run-pass/issue-20823.rs | 1 + src/test/run-pass/lang-item-public.rs | 2 +- src/test/run-pass/native-print-no-runtime.rs | 1 + src/test/run-pass/running-with-no-runtime.rs | 2 ++ src/test/run-pass/smallest-hello-world.rs | 2 +- src/test/run-pass/use.rs | 5 ++--- src/test/run-pass/vec-macro-no-std.rs | 2 +- 26 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 src/test/compile-fail/feature-gate-main.rs create mode 100644 src/test/compile-fail/feature-gate-start.rs diff --git a/src/doc/trpl/unsafe.md b/src/doc/trpl/unsafe.md index 075340660df15..aac0e29170d0d 100644 --- a/src/doc/trpl/unsafe.md +++ b/src/doc/trpl/unsafe.md @@ -447,7 +447,7 @@ in the same format as C: ``` #![no_std] -#![feature(lang_items)] +#![feature(lang_items, start)] // Pull in the system libc library for what crt0.o likely requires extern crate libc; @@ -475,7 +475,7 @@ compiler's name mangling too: ```ignore #![no_std] #![no_main] -#![feature(lang_items)] +#![feature(lang_items, start)] extern crate libc; @@ -529,7 +529,7 @@ vectors provided from C, using idiomatic Rust practices. ``` #![no_std] -#![feature(lang_items)] +#![feature(lang_items, start)] # extern crate libc; extern crate core; @@ -653,7 +653,7 @@ sugar for dynamic allocations via `malloc` and `free`: ``` #![no_std] -#![feature(lang_items, box_syntax)] +#![feature(lang_items, box_syntax, start)] extern crate libc; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 8929bbe0232ed..3a4cf184cdf2d 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -77,6 +77,8 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("while_let", Accepted), ("plugin", Active), + ("start", Active), + ("main", Active), // A temporary feature gate used to enable parser extensions needed // to bootstrap fix for #5723. @@ -276,6 +278,18 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { self.gate_feature("plugin_registrar", i.span, "compiler plugins are experimental and possibly buggy"); } + if attr::contains_name(&i.attrs[], "start") { + self.gate_feature("start", i.span, + "a #[start] function is an experimental \ + feature whose signature may change \ + over time"); + } + if attr::contains_name(&i.attrs[], "main") { + self.gate_feature("main", i.span, + "declaration of a nonstandard #[main] \ + function may change over time, for now \ + a top-level `fn main()` is required"); + } } ast::ItemStruct(..) => { diff --git a/src/test/compile-fail/feature-gate-main.rs b/src/test/compile-fail/feature-gate-main.rs new file mode 100644 index 0000000000000..db1c1a944172d --- /dev/null +++ b/src/test/compile-fail/feature-gate-main.rs @@ -0,0 +1,12 @@ +// 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. + +#[main] +fn foo() {} //~ ERROR: declaration of a nonstandard #[main] function may change over time diff --git a/src/test/compile-fail/feature-gate-start.rs b/src/test/compile-fail/feature-gate-start.rs new file mode 100644 index 0000000000000..6a9acf04290e4 --- /dev/null +++ b/src/test/compile-fail/feature-gate-start.rs @@ -0,0 +1,13 @@ +// 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. + +#[start] +fn foo() {} //~ ERROR: a #[start] function is an experimental feature + diff --git a/src/test/compile-fail/issue-9575.rs b/src/test/compile-fail/issue-9575.rs index b7537f2a93251..94dd787f08635 100644 --- a/src/test/compile-fail/issue-9575.rs +++ b/src/test/compile-fail/issue-9575.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(start)] + #[start] fn start(argc: isize, argv: *const *const u8, crate_map: *const u8) -> isize { //~^ ERROR incorrect number of function parameters diff --git a/src/test/compile-fail/lang-item-missing.rs b/src/test/compile-fail/lang-item-missing.rs index 793d9c77c3bc4..61d55fd0964e3 100644 --- a/src/test/compile-fail/lang-item-missing.rs +++ b/src/test/compile-fail/lang-item-missing.rs @@ -14,6 +14,7 @@ // error-pattern: requires `sized` lang_item #![no_std] +#![feature(start)] #[start] fn start(argc: isize, argv: *const *const u8) -> isize { diff --git a/src/test/compile-fail/lint-dead-code-2.rs b/src/test/compile-fail/lint-dead-code-2.rs index e8b85ffd69a44..4a0e4f4319e0a 100644 --- a/src/test/compile-fail/lint-dead-code-2.rs +++ b/src/test/compile-fail/lint-dead-code-2.rs @@ -10,6 +10,7 @@ #![allow(unused_variables)] #![deny(dead_code)] +#![feature(main, start)] struct Foo; diff --git a/src/test/compile-fail/multiple-main-2.rs b/src/test/compile-fail/multiple-main-2.rs index e3dbc67b7a9ef..d9c232d7dac37 100644 --- a/src/test/compile-fail/multiple-main-2.rs +++ b/src/test/compile-fail/multiple-main-2.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(main)] + #[main] fn bar() { } diff --git a/src/test/compile-fail/multiple-main-3.rs b/src/test/compile-fail/multiple-main-3.rs index 58cc148568ee6..866a59e7a4f62 100644 --- a/src/test/compile-fail/multiple-main-3.rs +++ b/src/test/compile-fail/multiple-main-3.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(main)] + #[main] fn main1() { } diff --git a/src/test/compile-fail/privacy1.rs b/src/test/compile-fail/privacy1.rs index 9dafae3d87dca..b2ee62ddabe55 100644 --- a/src/test/compile-fail/privacy1.rs +++ b/src/test/compile-fail/privacy1.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(lang_items)] +#![feature(lang_items, start)] #![no_std] // makes debugging this test *a lot* easier (during resolve) #[lang="sized"] diff --git a/src/test/compile-fail/privacy2.rs b/src/test/compile-fail/privacy2.rs index b59905776d38a..b3d7321edabd5 100644 --- a/src/test/compile-fail/privacy2.rs +++ b/src/test/compile-fail/privacy2.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(start)] #![no_std] // makes debugging this test *a lot* easier (during resolve) // Test to make sure that globs don't leak in regular `use` statements. diff --git a/src/test/compile-fail/privacy3.rs b/src/test/compile-fail/privacy3.rs index 80219b70e07b5..245a9c21a6b2e 100644 --- a/src/test/compile-fail/privacy3.rs +++ b/src/test/compile-fail/privacy3.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(start)] #![no_std] // makes debugging this test *a lot* easier (during resolve) // Test to make sure that private items imported through globs remain private diff --git a/src/test/compile-fail/privacy4.rs b/src/test/compile-fail/privacy4.rs index 3f17d463890be..e35089b8606c2 100644 --- a/src/test/compile-fail/privacy4.rs +++ b/src/test/compile-fail/privacy4.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(lang_items)] +#![feature(lang_items, start)] #![no_std] // makes debugging this test *a lot* easier (during resolve) #[lang = "sized"] pub trait Sized {} diff --git a/src/test/run-pass/attr-main-2.rs b/src/test/run-pass/attr-main-2.rs index 2f5e72491bef3..fd0ae0729af25 100644 --- a/src/test/run-pass/attr-main-2.rs +++ b/src/test/run-pass/attr-main-2.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(main)] pub fn main() { panic!() diff --git a/src/test/run-pass/attr-main.rs b/src/test/run-pass/attr-main.rs index cf8940b4d5b2d..29b504bed535d 100644 --- a/src/test/run-pass/attr-main.rs +++ b/src/test/run-pass/attr-main.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(main)] #[main] fn foo() { diff --git a/src/test/run-pass/attr-start.rs b/src/test/run-pass/attr-start.rs index 3bea7e84807ce..2bf09404200a5 100644 --- a/src/test/run-pass/attr-start.rs +++ b/src/test/run-pass/attr-start.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(start)] #[start] fn start(_argc: int, _argv: *const *const u8) -> int { diff --git a/src/test/run-pass/attr.rs b/src/test/run-pass/attr.rs index 2f30eb8154ff1..129d69b6e67cc 100644 --- a/src/test/run-pass/attr.rs +++ b/src/test/run-pass/attr.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(main)] + #[main] fn foo() { } diff --git a/src/test/run-pass/intrinsic-alignment.rs b/src/test/run-pass/intrinsic-alignment.rs index 52fcaf5c3ae8d..da07e9594c0e6 100644 --- a/src/test/run-pass/intrinsic-alignment.rs +++ b/src/test/run-pass/intrinsic-alignment.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(intrinsics)] +#![feature(intrinsics, main)] mod rusti { extern "rust-intrinsic" { diff --git a/src/test/run-pass/issue-16597-empty.rs b/src/test/run-pass/issue-16597-empty.rs index c51e33c01040b..a6a1c5f16b31c 100644 --- a/src/test/run-pass/issue-16597-empty.rs +++ b/src/test/run-pass/issue-16597-empty.rs @@ -9,6 +9,7 @@ // except according to those terms. // compile-flags:--test +// no-pretty-expanded // This verifies that the test generation doesn't crash when we have // no tests - for more information, see PR #16892. diff --git a/src/test/run-pass/issue-20823.rs b/src/test/run-pass/issue-20823.rs index 73b7fc264a529..561b61954768e 100644 --- a/src/test/run-pass/issue-20823.rs +++ b/src/test/run-pass/issue-20823.rs @@ -9,6 +9,7 @@ // except according to those terms. // compile-flags: --test +// no-pretty-expanded #![deny(unstable)] diff --git a/src/test/run-pass/lang-item-public.rs b/src/test/run-pass/lang-item-public.rs index 81774c73c392c..f94f648275932 100644 --- a/src/test/run-pass/lang-item-public.rs +++ b/src/test/run-pass/lang-item-public.rs @@ -13,7 +13,7 @@ // ignore-windows #13361 #![no_std] -#![feature(lang_items)] +#![feature(lang_items, start)] extern crate "lang-item-public" as lang_lib; diff --git a/src/test/run-pass/native-print-no-runtime.rs b/src/test/run-pass/native-print-no-runtime.rs index a7937efd66fce..b151eddb94e5d 100644 --- a/src/test/run-pass/native-print-no-runtime.rs +++ b/src/test/run-pass/native-print-no-runtime.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(start)] #[start] pub fn main(_: int, _: *const *const u8) -> int { diff --git a/src/test/run-pass/running-with-no-runtime.rs b/src/test/run-pass/running-with-no-runtime.rs index d6c25672cdbbc..7b809235e8d3d 100644 --- a/src/test/run-pass/running-with-no-runtime.rs +++ b/src/test/run-pass/running-with-no-runtime.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(start)] + use std::ffi; use std::io::process::{Command, ProcessOutput}; use std::os; diff --git a/src/test/run-pass/smallest-hello-world.rs b/src/test/run-pass/smallest-hello-world.rs index 2877aa6bd3be1..4913b34c4b311 100644 --- a/src/test/run-pass/smallest-hello-world.rs +++ b/src/test/run-pass/smallest-hello-world.rs @@ -13,7 +13,7 @@ // Smallest "hello world" with a libc runtime #![no_std] -#![feature(intrinsics, lang_items)] +#![feature(intrinsics, lang_items, start)] extern crate libc; diff --git a/src/test/run-pass/use.rs b/src/test/run-pass/use.rs index 67083f5362394..a2e1d8a567167 100644 --- a/src/test/run-pass/use.rs +++ b/src/test/run-pass/use.rs @@ -9,14 +9,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - #![allow(unused_imports)] - +#![feature(start)] #![no_std] + extern crate std; extern crate "std" as zed; - use std::str; use zed::str as x; mod baz { diff --git a/src/test/run-pass/vec-macro-no-std.rs b/src/test/run-pass/vec-macro-no-std.rs index 7b2c35aa08d30..c6c3701734970 100644 --- a/src/test/run-pass/vec-macro-no-std.rs +++ b/src/test/run-pass/vec-macro-no-std.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(lang_items)] +#![feature(lang_items, start)] #![no_std] extern crate "std" as other; From 785176b7ccc3f6acdf18496473ec5f2345897067 Mon Sep 17 00:00:00 2001 From: Michael Pankov Date: Thu, 15 Jan 2015 16:45:59 +0300 Subject: [PATCH 033/211] rustup: Don't attempt to download SHA if it exists This way installer can work fully in offline mode. Put pre-downloaded files (rust-nightly-x86_64-unknown-linux-gnu.tar.gz and rust-nightly-x86_64-unknown-linux-gnu.tar.gz.sha256) into the $HOME/.rustup/YYYY-MM-DD directory as it would be done by script itself. Specify --save and --date=YYYY-MM-DD when running. Files will be picked up and used to install in offline mode. --- src/etc/rustup.sh | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/etc/rustup.sh b/src/etc/rustup.sh index 749d9eaa173e8..5a34770b88ea0 100755 --- a/src/etc/rustup.sh +++ b/src/etc/rustup.sh @@ -453,16 +453,33 @@ then RUST_URL="${RUST_URL}/${CFG_DATE}" fi -verify_hash() { - remote_sha256="$1" - local_file="$2" - +download_hash() { msg "Downloading ${remote_sha256}" remote_sha256=`"${CFG_CURL}" -f "${remote_sha256}"` + if [ -n "${CFG_SAVE}" ]; then + echo "${remote_sha256}" > "${local_sha_file}" + fi if [ "$?" -ne 0 ]; then rm -Rf "${CFG_TMP_DIR}" err "Failed to download ${remote_url}" fi +} + +verify_hash() { + remote_sha256="$1" + local_file="$2" + local_sha_file="${local_file}.sha256" + + if [ -n "${CFG_SAVE}" ]; then + if [ -f "${local_sha_file}" ]; then + msg "Local ${local_sha_file} exists, treating as remote hash" + remote_sha256=`cat "${local_sha_file}"` + else + download_hash + fi + else + download_hash + fi msg "Verifying hash" local_sha256=$(calculate_hash "${local_file}") From 4f08de84c923f08535e9a6f754b2776286ebcd01 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 16 Jan 2015 09:23:18 +1100 Subject: [PATCH 034/211] Add comprehensive test for no-ICE behaviour of SIMD FFI. This just compiles a test using SIMD in FFI (mostly importing LLVM intrinsics) for almost all rustc's supported platforms, but not linking it or running it, so there's absolutely no guarantee that this is correct. --- src/test/run-make/simd-ffi/Makefile | 33 ++++++++++++ src/test/run-make/simd-ffi/simd.rs | 81 +++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 src/test/run-make/simd-ffi/Makefile create mode 100755 src/test/run-make/simd-ffi/simd.rs diff --git a/src/test/run-make/simd-ffi/Makefile b/src/test/run-make/simd-ffi/Makefile new file mode 100644 index 0000000000000..68a6a5fbfe864 --- /dev/null +++ b/src/test/run-make/simd-ffi/Makefile @@ -0,0 +1,33 @@ +-include ../tools.mk + +# construct a fairly exhaustive list of platforms that we +# support. These ones don't follow a pattern +TARGETS=arm-linux-androideabi arm-unknown-linux-gnueabihf arm-unknown-linux-gnueabi + +# these ones do, each OS lists the architectures it supports +LINUX=aarch64 i686 x86_64 mips mipsel +WINDOWS=i686 x86_64 +# fails with: failed to get iphonesimulator SDK path: no such file or directory +#IOS=i386 aarch64 armv7 +DARWIN=i686 x86_64 + +$(foreach arch,$(LINUX),$(eval TARGETS += $(arch)-unknown-linux-gnu)) +$(foreach arch,$(WINDOWS),$(eval TARGETS += $(arch)-pc-windows-gnu)) +#$(foreach arch,$(IOS),$(eval TARGETS += $(arch)-apple-ios)) +$(foreach arch,$(DARWIN),$(eval TARGETS += $(arch)-apple-darwin)) + +all: $(TARGETS) + +define MK_TARGETS +# compile the rust file to the given target, but only to asm and IR +# form, to avoid having to have an appropriate linker. +# +# we need some features because the integer SIMD instructions are not +# enabled by-default for i686 and ARM; these features will be invalid +# on some platforms, but LLVM just prints a warning so that's fine for +# now. +$(1): simd.rs + $$(RUSTC) --target=$(1) --emit=llvm-ir,asm simd.rs -C target-feature='+neon,+sse2' +endef + +$(foreach targetxxx,$(TARGETS),$(eval $(call MK_TARGETS,$(targetxxx)))) diff --git a/src/test/run-make/simd-ffi/simd.rs b/src/test/run-make/simd-ffi/simd.rs new file mode 100755 index 0000000000000..d5945db94791f --- /dev/null +++ b/src/test/run-make/simd-ffi/simd.rs @@ -0,0 +1,81 @@ +// 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. + +// ensures that public symbols are not removed completely +#![crate_type = "lib"] +// we can compile to a variety of platforms, because we don't need +// cross-compiled standard libraries. +#![no_std] + +#![feature(simd, link_llvm_intrinsics, lang_items)] + + +#[repr(C)] +#[derive(Copy)] +#[simd] +pub struct f32x4(f32, f32, f32, f32); + + +extern { + #[link_name = "llvm.sqrt.v4f32"] + fn vsqrt(x: f32x4) -> f32x4; +} + +pub fn foo(x: f32x4) -> f32x4 { + unsafe {vsqrt(x)} +} + +#[repr(C)] +#[derive(Copy)] +#[simd] +pub struct i32x4(i32, i32, i32, i32); + + +extern { + // _mm_sll_epi32 + #[cfg(any(target_arch = "x86", + target_arch = "x86-64"))] + #[link_name = "llvm.x86.sse2.psll.d"] + fn integer(a: i32x4, b: i32x4) -> i32x4; + + // vmaxq_s32 + #[cfg(any(target_arch = "arm"))] + #[link_name = "llvm.arm.neon.vmaxs.v4i32"] + fn integer(a: i32x4, b: i32x4) -> i32x4; + // vmaxq_s32 + #[cfg(any(target_arch = "aarch64"))] + #[link_name = "llvm.aarch64.neon.maxs.v4i32"] + fn integer(a: i32x4, b: i32x4) -> i32x4; + + // just some substitute foreign symbol, not an LLVM intrinsic; so + // we still get type checking, but not as detailed as (ab)using + // LLVM. + #[cfg(not(any(target_arch = "x86", + target_arch = "x86-64", + target_arch = "arm", + target_arch = "aarch64")))] + fn integer(a: i32x4, b: i32x4) -> i32x4; +} + +pub fn bar(a: i32x4, b: i32x4) -> i32x4 { + unsafe {integer(a, b)} +} + +#[lang = "sized"] +trait Sized {} + +#[lang = "copy"] +trait Copy {} + +mod std { + pub mod marker { + pub use Copy; + } +} From c8e0e9549d4327c54d9eb5fd0de5e23312c34fe9 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 16 Jan 2015 10:16:20 +1100 Subject: [PATCH 035/211] Feature gate SIMD in FFI, due to unknown ABIs. I don't know if this handling of SIMD types is correct for the C ABI on all platforms, so lets add an even finer feature gate than just the `simd` one. The `simd` one can be used with (relatively) little risk of complete nonsense, the reason for it is that it is likely that things will change. Using the types in FFI with an incorrect ABI will at best give absolute nonsense results, but possibly cause serious breakage too, so this is a step up in badness, hence a new feature gate. --- src/librustc_trans/trans/foreign.rs | 34 ++++++++++++++++++- src/libsyntax/feature_gate.rs | 4 +++ .../compile-fail/feature-gate-simd-ffi.rs | 26 ++++++++++++++ src/test/run-make/simd-ffi/simd.rs | 2 +- 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 src/test/compile-fail/feature-gate-simd-ffi.rs diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs index fb61bab6ade2c..abb961d87de96 100644 --- a/src/librustc_trans/trans/foreign.rs +++ b/src/librustc_trans/trans/foreign.rs @@ -36,6 +36,7 @@ use syntax::parse::token::{InternedString, special_idents}; use syntax::parse::token; use syntax::{ast}; use syntax::{attr, ast_map}; +use syntax::print::pprust; use util::ppaux::Repr; /////////////////////////////////////////////////////////////////////////// @@ -426,16 +427,47 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, return bcx; } +// feature gate SIMD types in FFI, since I (huonw) am not sure the +// ABIs are handled at all correctly. +fn gate_simd_ffi(tcx: &ty::ctxt, decl: &ast::FnDecl, ty: &ty::BareFnTy) { + if !tcx.sess.features.borrow().simd_ffi { + let check = |&: ast_ty: &ast::Ty, ty: ty::Ty| { + if ty::type_is_simd(tcx, ty) { + tcx.sess.span_err(ast_ty.span, + &format!("use of SIMD type `{}` in FFI is highly experimental and \ + may result in invalid code", + pprust::ty_to_string(ast_ty))[]); + tcx.sess.span_help(ast_ty.span, + "add #![feature(simd_ffi)] to the crate attributes to enable"); + } + }; + let sig = &ty.sig.0; + for (input, ty) in decl.inputs.iter().zip(sig.inputs.iter()) { + check(&*input.ty, *ty) + } + match decl.output { + ast::NoReturn(_) => {} + ast::Return(ref ty) => check(&**ty, sig.output.unwrap()) + } + } +} + pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &ast::ForeignMod) { let _icx = push_ctxt("foreign::trans_foreign_mod"); for foreign_item in foreign_mod.items.iter() { let lname = link_name(&**foreign_item); - if let ast::ForeignItemFn(..) = foreign_item.node { + if let ast::ForeignItemFn(ref decl, _) = foreign_item.node { match foreign_mod.abi { Rust | RustIntrinsic => {} abi => { let ty = ty::node_id_to_type(ccx.tcx(), foreign_item.id); + match ty.sty { + ty::ty_bare_fn(_, bft) => gate_simd_ffi(ccx.tcx(), &**decl, bft), + _ => ccx.tcx().sess.span_bug(foreign_item.span, + "foreign fn's sty isn't a bare_fn_ty?") + } + register_foreign_item_fn(ccx, abi, ty, &lname.get()[]); // Unlike for other items, we shouldn't call diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 8929bbe0232ed..9231d4ad65943 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -72,6 +72,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("slicing_syntax", Active), ("box_syntax", Active), ("on_unimplemented", Active), + ("simd_ffi", Active), ("if_let", Accepted), ("while_let", Accepted), @@ -128,6 +129,7 @@ pub struct Features { pub visible_private_types: bool, pub quote: bool, pub old_orphan_check: bool, + pub simd_ffi: bool, } impl Features { @@ -139,6 +141,7 @@ impl Features { visible_private_types: false, quote: false, old_orphan_check: false, + simd_ffi: false, } } } @@ -524,6 +527,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C visible_private_types: cx.has_feature("visible_private_types"), quote: cx.has_feature("quote"), old_orphan_check: cx.has_feature("old_orphan_check"), + simd_ffi: cx.has_feature("simd_ffi"), }, unknown_features) } diff --git a/src/test/compile-fail/feature-gate-simd-ffi.rs b/src/test/compile-fail/feature-gate-simd-ffi.rs new file mode 100644 index 0000000000000..409c85b719804 --- /dev/null +++ b/src/test/compile-fail/feature-gate-simd-ffi.rs @@ -0,0 +1,26 @@ +// 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. + +#![feature(simd)] +#![allow(dead_code)] + +use std::simd::f32x4; + +#[simd] #[derive(Copy)] #[repr(C)] struct LocalSimd(u8, u8); + +extern { + fn foo() -> f32x4; //~ ERROR use of SIMD type + fn bar(x: f32x4); //~ ERROR use of SIMD type + + fn baz() -> LocalSimd; //~ ERROR use of SIMD type + fn qux(x: LocalSimd); //~ ERROR use of SIMD type +} + +fn main() {} diff --git a/src/test/run-make/simd-ffi/simd.rs b/src/test/run-make/simd-ffi/simd.rs index d5945db94791f..76079ddb8bd91 100755 --- a/src/test/run-make/simd-ffi/simd.rs +++ b/src/test/run-make/simd-ffi/simd.rs @@ -14,7 +14,7 @@ // cross-compiled standard libraries. #![no_std] -#![feature(simd, link_llvm_intrinsics, lang_items)] +#![feature(simd, simd_ffi, link_llvm_intrinsics, lang_items)] #[repr(C)] From ada312ffbbfa03ac15c620f7a5702ef29b6e1321 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sat, 17 Jan 2015 12:29:26 +1100 Subject: [PATCH 036/211] Address nits in trait suggestions. --- src/librustc_typeck/check/method/probe.rs | 79 ++++++++----------- src/librustc_typeck/check/method/suggest.rs | 14 ++-- .../no-method-suggested-traits.rs | 14 ++-- 3 files changed, 50 insertions(+), 57 deletions(-) diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 9df8875152e3b..9bbc7b258e244 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -44,7 +44,6 @@ struct ProbeContext<'a, 'tcx:'a> { extension_candidates: Vec>, impl_dups: HashSet, static_candidates: Vec, - all_traits_search: bool, } struct CandidateStep<'tcx> { @@ -211,7 +210,6 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { steps: Rc::new(steps), opt_simplified_steps: opt_simplified_steps, static_candidates: Vec::new(), - all_traits_search: false, } } @@ -724,60 +722,53 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { // THE ACTUAL SEARCH fn pick(mut self) -> PickResult<'tcx> { - let steps = self.steps.clone(); - - for step in steps.iter() { - match self.pick_step(step) { - Some(r) => { - return r; - } - None => { } - } + match self.pick_core() { + Some(r) => return r, + None => {} } let static_candidates = mem::replace(&mut self.static_candidates, vec![]); - let out_of_scope_traits = if !self.all_traits_search { - // things failed, and we haven't yet looked through all - // traits, so lets do that now: - self.reset(); - self.all_traits_search = true; - - let span = self.span; - let tcx = self.tcx(); - - self.assemble_extension_candidates_for_all_traits(); - - match self.pick() { - Ok(p) => vec![p.method_ty.container.id()], - Err(Ambiguity(v)) => v.into_iter().map(|source| { - match source { - TraitSource(id) => id, - ImplSource(impl_id) => { - match ty::trait_id_of_impl(tcx, impl_id) { - Some(id) => id, - None => tcx.sess.span_bug(span, - "found inherent method when looking \ - at traits") - } + // things failed, so lets look at all traits, for diagnostic purposes now: + self.reset(); + + let span = self.span; + let tcx = self.tcx(); + + self.assemble_extension_candidates_for_all_traits(); + + let out_of_scope_traits = match self.pick_core() { + Some(Ok(p)) => vec![p.method_ty.container.id()], + Some(Err(Ambiguity(v))) => v.into_iter().map(|source| { + match source { + TraitSource(id) => id, + ImplSource(impl_id) => { + match ty::trait_id_of_impl(tcx, impl_id) { + Some(id) => id, + None => + tcx.sess.span_bug(span, + "found inherent method when looking at traits") } } - }).collect(), - // it'd be really weird for this assertion to trigger, - // given the `vec![]` in the else branch below - Err(NoMatch(_, others)) => { - assert!(others.is_empty()); - vec![] } + }).collect(), + Some(Err(NoMatch(_, others))) => { + assert!(others.is_empty()); + vec![] } - } else { - // we've just looked through all traits and didn't find - // anything at all. - vec![] + None => vec![], }; +; Err(NoMatch(static_candidates, out_of_scope_traits)) } + fn pick_core(&mut self) -> Option> { + let steps = self.steps.clone(); + + // find the first step that works + steps.iter().filter_map(|step| self.pick_step(step)).next() + } + fn pick_step(&mut self, step: &CandidateStep<'tcx>) -> Option> { debug!("pick_step: step={}", step.repr(self.tcx())); diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 013c6e2f953a0..2a89a1d28bfc6 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -147,16 +147,16 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, candidates.sort(); let msg = format!( "methods from traits can only be called if the trait is in scope; \ - the following {traits_are} implemented and {define} a method `{name}`:", + the following {traits_are} implemented but not in scope, \ + perhaps add a `use` for {one_of_them}:", traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"}, - define = if candidates.len() == 1 {"defines"} else {"define"}, - name = method_ustring); + one_of_them = if candidates.len() == 1 {"it"} else {"one of them"}); fcx.sess().fileline_help(span, &msg[]); for (i, trait_did) in candidates.iter().enumerate() { fcx.sess().fileline_help(span, - &*format!("candidate #{}: `{}`", + &*format!("candidate #{}: use `{}`", i + 1, ty::item_path_str(fcx.tcx(), *trait_did))) @@ -174,9 +174,11 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, candidates.sort_by(|a, b| a.cmp(b).reverse()); let msg = format!( - "methods from traits can only be called if the trait is implemented and \ - in scope; no such traits are but the following {traits_define} a method `{name}`:", + "methods from traits can only be called if the trait is implemented and in scope; \ + the following {traits_define} a method `{name}`, \ + perhaps you need to implement {one_of_them}:", traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"}, + one_of_them = if candidates.len() == 1 {"it"} else {"one of them"}, name = method_ustring); fcx.sess().fileline_help(span, &msg[]); diff --git a/src/test/compile-fail/no-method-suggested-traits.rs b/src/test/compile-fail/no-method-suggested-traits.rs index 277800778a87e..ba8121eb5cc35 100644 --- a/src/test/compile-fail/no-method-suggested-traits.rs +++ b/src/test/compile-fail/no-method-suggested-traits.rs @@ -26,24 +26,24 @@ mod foo { fn main() { 1u32.method(); - //~^ ERROR does not implement - //~^^ HELP the following traits are implemented and define a method `method` + //~^ HELP following traits are implemented but not in scope, perhaps add a `use` for one of them + //~^^ ERROR does not implement //~^^^ HELP `foo::Bar` //~^^^^ HELP `no_method_suggested_traits::foo::PubPub` 'a'.method(); //~^ ERROR does not implement - //~^^ HELP the following trait is implemented and defines a method `method` + //~^^ HELP the following trait is implemented but not in scope, perhaps add a `use` for it: //~^^^ HELP `foo::Bar` 1i32.method(); //~^ ERROR does not implement - //~^^ HELP the following trait is implemented and defines a method `method` + //~^^ HELP the following trait is implemented but not in scope, perhaps add a `use` for it: //~^^^ HELP `no_method_suggested_traits::foo::PubPub` 1u64.method(); //~^ ERROR does not implement - //~^^ HELP the following traits define a method `method` + //~^^ HELP following traits define a method `method`, perhaps you need to implement one of them //~^^^ HELP `foo::Bar` //~^^^^ HELP `no_method_suggested_traits::foo::PubPub` //~^^^^^ HELP `no_method_suggested_traits::reexport::Reexported` @@ -53,10 +53,10 @@ fn main() { 1u64.method2(); //~^ ERROR does not implement - //~^^ HELP the following trait defines a method `method2` + //~^^ HELP the following trait defines a method `method2`, perhaps you need to implement it //~^^^ HELP `foo::Bar` 1u64.method3(); //~^ ERROR does not implement - //~^^ HELP the following trait defines a method `method3` + //~^^ HELP the following trait defines a method `method3`, perhaps you need to implement it //~^^^ HELP `no_method_suggested_traits::foo::PubPub` } From fbcc34f48339f643676a3a06c6c31a9ea47a9b8d Mon Sep 17 00:00:00 2001 From: Tim Parenti Date: Fri, 16 Jan 2015 22:25:22 -0500 Subject: [PATCH 037/211] Grammar tweak to old guide stub documents. Removes extra "the" from the phrase "the the Rust Programming Language book", which isn't particularly grammatical. --- src/doc/guide-crates.md | 2 +- src/doc/guide-error-handling.md | 2 +- src/doc/guide-ffi.md | 2 +- src/doc/guide-macros.md | 2 +- src/doc/guide-ownership.md | 2 +- src/doc/guide-plugins.md | 2 +- src/doc/guide-pointers.md | 2 +- src/doc/guide-strings.md | 2 +- src/doc/guide-tasks.md | 2 +- src/doc/guide-testing.md | 2 +- src/doc/guide-unsafe.md | 2 +- src/doc/guide.md | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/doc/guide-crates.md b/src/doc/guide-crates.md index 8277988b7fe29..85badc11d64f0 100644 --- a/src/doc/guide-crates.md +++ b/src/doc/guide-crates.md @@ -1,4 +1,4 @@ % The (old) Rust Crates and Modules Guide -This content has moved into the +This content has moved into [the Rust Programming Language book](book/crates-and-modules.html). diff --git a/src/doc/guide-error-handling.md b/src/doc/guide-error-handling.md index 215fe6a441e0e..54fa529f3aa8e 100644 --- a/src/doc/guide-error-handling.md +++ b/src/doc/guide-error-handling.md @@ -1,4 +1,4 @@ % Error Handling in Rust -This content has moved into the +This content has moved into [the Rust Programming Language book](book/error-handling.html). diff --git a/src/doc/guide-ffi.md b/src/doc/guide-ffi.md index 4c818cacbfaf3..1130a10bd1c55 100644 --- a/src/doc/guide-ffi.md +++ b/src/doc/guide-ffi.md @@ -1,4 +1,4 @@ % The (old) Rust Foreign Function Interface Guide -This content has moved into the +This content has moved into [the Rust Programming Language book](book/ffi.html). diff --git a/src/doc/guide-macros.md b/src/doc/guide-macros.md index 534ae3504c3a2..228cb3c624f89 100644 --- a/src/doc/guide-macros.md +++ b/src/doc/guide-macros.md @@ -1,4 +1,4 @@ % The (old) Rust Macros Guide -This content has moved into the +This content has moved into [the Rust Programming Language book](book/macros.html). diff --git a/src/doc/guide-ownership.md b/src/doc/guide-ownership.md index 26e059aeb2680..884f14726ca87 100644 --- a/src/doc/guide-ownership.md +++ b/src/doc/guide-ownership.md @@ -1,4 +1,4 @@ % The (old) Rust Ownership Guide -This content has moved into the +This content has moved into [the Rust Programming Language book](book/ownership.html). diff --git a/src/doc/guide-plugins.md b/src/doc/guide-plugins.md index abfe7a44703a2..d6495d02e1189 100644 --- a/src/doc/guide-plugins.md +++ b/src/doc/guide-plugins.md @@ -1,4 +1,4 @@ % The (old) Rust Compiler Plugins Guide -This content has moved into the +This content has moved into [the Rust Programming Language book](book/plugins.html). diff --git a/src/doc/guide-pointers.md b/src/doc/guide-pointers.md index e72eaf62720b7..0374166405c62 100644 --- a/src/doc/guide-pointers.md +++ b/src/doc/guide-pointers.md @@ -1,4 +1,4 @@ % The (old) Rust Pointer Guide -This content has moved into the +This content has moved into [the Rust Programming Language book](book/pointers.html). diff --git a/src/doc/guide-strings.md b/src/doc/guide-strings.md index fd1420024c665..d030614489bcc 100644 --- a/src/doc/guide-strings.md +++ b/src/doc/guide-strings.md @@ -1,4 +1,4 @@ % The (old) Guide to Rust Strings -This content has moved into the +This content has moved into [the Rust Programming Language book](book/strings.html). diff --git a/src/doc/guide-tasks.md b/src/doc/guide-tasks.md index be8cb67098620..197559bef0408 100644 --- a/src/doc/guide-tasks.md +++ b/src/doc/guide-tasks.md @@ -1,4 +1,4 @@ % The (old) Rust Threads and Communication Guide -This content has moved into the +This content has moved into [the Rust Programming Language book](book/tasks.html). diff --git a/src/doc/guide-testing.md b/src/doc/guide-testing.md index 79721300d941d..67bcb0a5e546a 100644 --- a/src/doc/guide-testing.md +++ b/src/doc/guide-testing.md @@ -1,4 +1,4 @@ % The (old) Rust Testing Guide -This content has moved into the +This content has moved into [the Rust Programming Language book](book/testing.html). diff --git a/src/doc/guide-unsafe.md b/src/doc/guide-unsafe.md index fe8fcc4c19d84..3c1a82d017449 100644 --- a/src/doc/guide-unsafe.md +++ b/src/doc/guide-unsafe.md @@ -1,4 +1,4 @@ % Writing Safe Low-level and Unsafe Code in Rust -This content has moved into the +This content has moved into [the Rust Programming Language book](book/unsafe.html). diff --git a/src/doc/guide.md b/src/doc/guide.md index ba1e2590e7fa5..b9e70e7cfd7e0 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -1,4 +1,4 @@ % The (old) Rust Guide -This content has moved into the +This content has moved into [the Rust Programming Language book](book/README.html). From 812ce6c190d896cf1cc1bef9f22c00266e962c43 Mon Sep 17 00:00:00 2001 From: we Date: Sat, 17 Jan 2015 07:34:10 +0300 Subject: [PATCH 038/211] Remove unnecessary explicit conversions to *const T --- src/doc/trpl/unsafe.md | 2 +- src/liballoc/heap.rs | 2 +- src/libcollections/btree/node.rs | 6 +++--- src/libcollections/ring_buf.rs | 6 +++--- src/libcollections/slice.rs | 2 +- src/libcollections/vec.rs | 12 ++++++------ src/libcore/atomic.rs | 12 ++++++------ src/libcore/ptr.rs | 2 +- src/libcore/slice.rs | 4 ++-- src/librustc_trans/trans/builder.rs | 2 +- src/librustdoc/flock.rs | 4 ++-- src/libstd/collections/hash/table.rs | 23 ++++++++++------------- src/libstd/sys/unix/backtrace.rs | 2 +- src/libstd/thread_local/mod.rs | 2 +- 14 files changed, 39 insertions(+), 42 deletions(-) diff --git a/src/doc/trpl/unsafe.md b/src/doc/trpl/unsafe.md index 075340660df15..997dda98ef369 100644 --- a/src/doc/trpl/unsafe.md +++ b/src/doc/trpl/unsafe.md @@ -254,7 +254,7 @@ impl Drop for Unique { // Copy the object out from the pointer onto the stack, // where it is covered by normal Rust destructor semantics // and cleans itself up, if necessary - ptr::read(self.ptr as *const T); + ptr::read(self.ptr); // clean-up our allocation free(self.ptr as *mut c_void) diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index b7bc1b4764614..bd5b43b782e8d 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -298,7 +298,7 @@ mod imp { libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8 } else { let new_ptr = allocate(size, align); - ptr::copy_memory(new_ptr, ptr as *const u8, cmp::min(size, old_size)); + ptr::copy_memory(new_ptr, ptr, cmp::min(size, old_size)); deallocate(ptr, old_size, align); new_ptr } diff --git a/src/libcollections/btree/node.rs b/src/libcollections/btree/node.rs index 7d5422290e25a..19c48f88caa5c 100644 --- a/src/libcollections/btree/node.rs +++ b/src/libcollections/btree/node.rs @@ -326,11 +326,11 @@ impl Node { pub fn as_slices<'a>(&'a self) -> (&'a [K], &'a [V]) { unsafe {( mem::transmute(raw::Slice { - data: self.keys.0 as *const K, + data: self.keys.0, len: self.len() }), mem::transmute(raw::Slice { - data: self.vals.0 as *const V, + data: self.vals.0, len: self.len() }) )} @@ -349,7 +349,7 @@ impl Node { } else { unsafe { mem::transmute(raw::Slice { - data: self.edges.0 as *const Node, + data: self.edges.0, len: self.len() + 1 }) } diff --git a/src/libcollections/ring_buf.rs b/src/libcollections/ring_buf.rs index c3d2267586837..b9cb4be7c1891 100644 --- a/src/libcollections/ring_buf.rs +++ b/src/libcollections/ring_buf.rs @@ -88,19 +88,19 @@ impl RingBuf { /// Turn ptr into a slice #[inline] unsafe fn buffer_as_slice(&self) -> &[T] { - mem::transmute(RawSlice { data: self.ptr as *const T, len: self.cap }) + mem::transmute(RawSlice { data: self.ptr, len: self.cap }) } /// Turn ptr into a mut slice #[inline] unsafe fn buffer_as_mut_slice(&mut self) -> &mut [T] { - mem::transmute(RawSlice { data: self.ptr as *const T, len: self.cap }) + mem::transmute(RawSlice { data: self.ptr, len: self.cap }) } /// Moves an element out of the buffer #[inline] unsafe fn buffer_read(&mut self, off: uint) -> T { - ptr::read(self.ptr.offset(off as int) as *const T) + ptr::read(self.ptr.offset(off as int)) } /// Writes an element into the buffer, moving it. diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index 4812ecc2c0b75..988ec4c661faa 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -1222,7 +1222,7 @@ fn insertion_sort(v: &mut [T], mut compare: F) where F: FnMut(&T, &T) -> O &*buf_v.offset(j), (i - j) as uint); ptr::copy_nonoverlapping_memory(buf_v.offset(j), - &tmp as *const T, + &tmp, 1); mem::forget(tmp); } diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index 73afefc5a0331..adc7ce8f07214 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -426,7 +426,7 @@ impl Vec { pub fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T] { unsafe { mem::transmute(RawSlice { - data: *self.ptr as *const T, + data: *self.ptr, len: self.len, }) } @@ -574,7 +574,7 @@ impl Vec { let ptr = self.as_mut_ptr().offset(index as int); // copy it out, unsafely having a copy of the value on // the stack and in the vector at the same time. - ret = ptr::read(ptr as *const T); + ret = ptr::read(ptr); // Shift everything down to fill in that spot. ptr::copy_memory(ptr, &*ptr.offset(1), len - index - 1); @@ -842,7 +842,7 @@ impl Vec { // | | // end_u end_t - let t = ptr::read(pv.start_t as *const T); + let t = ptr::read(pv.start_t); // start_u start_t // | | // +-+-+-+-+-+-+-+-+-+ @@ -1414,7 +1414,7 @@ impl AsSlice for Vec { fn as_slice<'a>(&'a self) -> &'a [T] { unsafe { mem::transmute(RawSlice { - data: *self.ptr as *const T, + data: *self.ptr, len: self.len }) } @@ -1777,11 +1777,11 @@ impl Drop for PartialVecNonZeroSized { // We have instances of `U`s and `T`s in `vec`. Destruct them. while self.start_u != self.end_u { - let _ = ptr::read(self.start_u as *const U); // Run a `U` destructor. + let _ = ptr::read(self.start_u); // Run a `U` destructor. self.start_u = self.start_u.offset(1); } while self.start_t != self.end_t { - let _ = ptr::read(self.start_t as *const T); // Run a `T` destructor. + let _ = ptr::read(self.start_t); // Run a `T` destructor. self.start_t = self.start_t.offset(1); } // After this destructor ran, the destructor of `vec` will run, diff --git a/src/libcore/atomic.rs b/src/libcore/atomic.rs index aa93d9ed83792..18f7fff9053ce 100644 --- a/src/libcore/atomic.rs +++ b/src/libcore/atomic.rs @@ -199,7 +199,7 @@ impl AtomicBool { #[inline] #[stable] pub fn load(&self, order: Ordering) -> bool { - unsafe { atomic_load(self.v.get() as *const usize, order) > 0 } + unsafe { atomic_load(self.v.get(), order) > 0 } } /// Stores a value into the bool. @@ -438,7 +438,7 @@ impl AtomicIsize { /// ``` #[inline] pub fn load(&self, order: Ordering) -> isize { - unsafe { atomic_load(self.v.get() as *const isize, order) } + unsafe { atomic_load(self.v.get(), order) } } /// Stores a value into the isize. @@ -615,7 +615,7 @@ impl AtomicUsize { /// ``` #[inline] pub fn load(&self, order: Ordering) -> usize { - unsafe { atomic_load(self.v.get() as *const usize, order) } + unsafe { atomic_load(self.v.get(), order) } } /// Stores a value into the usize. @@ -796,7 +796,7 @@ impl AtomicPtr { #[stable] pub fn load(&self, order: Ordering) -> *mut T { unsafe { - atomic_load(self.p.get() as *const *mut T, order) as *mut T + atomic_load(self.p.get(), order) as *mut T } } @@ -1070,7 +1070,7 @@ impl AtomicInt { #[inline] pub fn load(&self, order: Ordering) -> int { - unsafe { atomic_load(self.v.get() as *const int, order) } + unsafe { atomic_load(self.v.get(), order) } } #[inline] @@ -1123,7 +1123,7 @@ impl AtomicUint { #[inline] pub fn load(&self, order: Ordering) -> uint { - unsafe { atomic_load(self.v.get() as *const uint, order) } + unsafe { atomic_load(self.v.get(), order) } } #[inline] diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index baf998d0828a2..0b89467d63b83 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -329,7 +329,7 @@ impl PtrExt for *mut T { #[inline] #[stable] unsafe fn offset(self, count: int) -> *mut T { - intrinsics::offset(self as *const T, count) as *mut T + intrinsics::offset(self, count) as *mut T } #[inline] diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index 22da168911daa..50cbb7a61dce3 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -741,7 +741,7 @@ macro_rules! make_slice { diff / mem::size_of::<$t>() }; unsafe { - transmute::<_, $result>(RawSlice { data: $start as *const T, len: len }) + transmute::<_, $result>(RawSlice { data: $start, len: len }) } }} } @@ -1409,7 +1409,7 @@ pub unsafe fn from_raw_buf<'a, T>(p: &'a *const T, len: uint) -> &'a [T] { #[inline] #[unstable = "should be renamed to from_raw_parts_mut"] pub unsafe fn from_raw_mut_buf<'a, T>(p: &'a *mut T, len: uint) -> &'a mut [T] { - transmute(RawSlice { data: *p as *const T, len: len }) + transmute(RawSlice { data: *p, len: len }) } // diff --git a/src/librustc_trans/trans/builder.rs b/src/librustc_trans/trans/builder.rs index 75194e3d21fcb..2ad4211034240 100644 --- a/src/librustc_trans/trans/builder.rs +++ b/src/librustc_trans/trans/builder.rs @@ -33,7 +33,7 @@ pub struct Builder<'a, 'tcx: 'a> { // lot more efficient) than doing str::as_c_str("", ...) every time. pub fn noname() -> *const c_char { static CNULL: c_char = 0; - &CNULL as *const c_char + &CNULL } impl<'a, 'tcx> Builder<'a, 'tcx> { diff --git a/src/librustdoc/flock.rs b/src/librustdoc/flock.rs index dcc90117d2660..b682723631d3f 100644 --- a/src/librustdoc/flock.rs +++ b/src/librustdoc/flock.rs @@ -126,7 +126,7 @@ mod imp { l_sysid: 0, }; let ret = unsafe { - libc::fcntl(fd, os::F_SETLKW, &flock as *const os::flock) + libc::fcntl(fd, os::F_SETLKW, &flock) }; if ret == -1 { unsafe { libc::close(fd); } @@ -147,7 +147,7 @@ mod imp { l_sysid: 0, }; unsafe { - libc::fcntl(self.fd, os::F_SETLK, &flock as *const os::flock); + libc::fcntl(self.fd, os::F_SETLK, &flock); libc::close(self.fd); } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index f28b95dbe95c4..d810460a7d497 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -395,9 +395,6 @@ impl> + DerefMut> FullBucket { /// This works similarly to `put`, building an `EmptyBucket` out of the /// taken bucket. pub fn take(mut self) -> (EmptyBucket, K, V) { - let key = self.raw.key as *const K; - let val = self.raw.val as *const V; - self.table.size -= 1; unsafe { @@ -408,8 +405,8 @@ impl> + DerefMut> FullBucket { idx: self.idx, table: self.table }, - ptr::read(key), - ptr::read(val) + ptr::read(self.raw.key), + ptr::read(self.raw.val) ) } } @@ -477,8 +474,8 @@ impl>> GapThenFull { pub fn shift(mut self) -> Option> { unsafe { *self.gap.raw.hash = mem::replace(&mut *self.full.raw.hash, EMPTY_BUCKET); - copy_nonoverlapping_memory(self.gap.raw.key, self.full.raw.key as *const K, 1); - copy_nonoverlapping_memory(self.gap.raw.val, self.full.raw.val as *const V, 1); + copy_nonoverlapping_memory(self.gap.raw.key, self.full.raw.key, 1); + copy_nonoverlapping_memory(self.gap.raw.val, self.full.raw.val, 1); } let FullBucket { raw: prev_raw, idx: prev_idx, .. } = self.full; @@ -781,8 +778,8 @@ impl<'a, K, V> Iterator for RevMoveBuckets<'a, K, V> { if *self.raw.hash != EMPTY_BUCKET { self.elems_left -= 1; return Some(( - ptr::read(self.raw.key as *const K), - ptr::read(self.raw.val as *const V) + ptr::read(self.raw.key), + ptr::read(self.raw.val) )); } } @@ -878,8 +875,8 @@ impl Iterator for IntoIter { SafeHash { hash: *bucket.hash, }, - ptr::read(bucket.key as *const K), - ptr::read(bucket.val as *const V) + ptr::read(bucket.key), + ptr::read(bucket.val) ) } }) @@ -906,8 +903,8 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> { SafeHash { hash: ptr::replace(bucket.hash, EMPTY_BUCKET), }, - ptr::read(bucket.key as *const K), - ptr::read(bucket.val as *const V) + ptr::read(bucket.key), + ptr::read(bucket.val) ) } }) diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs index 7164931c55acd..70b9c012b008a 100644 --- a/src/libstd/sys/unix/backtrace.rs +++ b/src/libstd/sys/unix/backtrace.rs @@ -229,7 +229,7 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { } let mut info: Dl_info = unsafe { intrinsics::init() }; - if unsafe { dladdr(addr as *const libc::c_void, &mut info) == 0 } { + if unsafe { dladdr(addr, &mut info) == 0 } { output(w, idx,addr, None) } else { output(w, idx, addr, Some(unsafe { diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs index e7c4e4ccdfb88..4c99cff34da65 100644 --- a/src/libstd/thread_local/mod.rs +++ b/src/libstd/thread_local/mod.rs @@ -449,7 +449,7 @@ mod imp { // destructor as running for this thread so calls to `get` will return // `None`. *(*ptr).dtor_running.get() = true; - ptr::read((*ptr).inner.get() as *const T); + ptr::read((*ptr).inner.get()); } } From 868669f420df66c6acd866391a855200efa4a5d6 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 12 Jan 2015 13:13:01 -0500 Subject: [PATCH 039/211] Add note about libc::exit's unsafety. Fixes #19245. --- src/liblibc/lib.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index deab0cabfbe51..7fb609b4c9450 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -4168,6 +4168,27 @@ pub mod funcs { pub fn malloc(size: size_t) -> *mut c_void; pub fn realloc(p: *mut c_void, size: size_t) -> *mut c_void; pub fn free(p: *mut c_void); + + /// Exits the running program in a possibly dangerous manner. + /// + /// # Unsafety + /// + /// While this forces your program to exit, it does so in a way that has + /// consequences. This will skip all unwinding code, which means that anything + /// relying on unwinding for cleanup (such as flushing and closing a buffer to a + /// file) may act in an unexpected way. + /// + /// # Examples + /// + /// ```no_run + /// extern crate libc; + /// + /// fn main() { + /// unsafe { + /// libc::exit(1); + /// } + /// } + /// ``` pub fn exit(status: c_int) -> !; pub fn _exit(status: c_int) -> !; pub fn atexit(cb: extern fn()) -> c_int; From a2e277edf49cb05e79c0346de9a145e4ce6754cb Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 12 Jan 2015 16:07:44 -0500 Subject: [PATCH 040/211] Add enum discriminats to the reference. Fixes #15755 --- src/doc/reference.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/doc/reference.md b/src/doc/reference.md index c8e31f27b3507..a27d6c6e268a9 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -1413,6 +1413,27 @@ a = Animal::Cat { name: "Spotty".to_string(), weight: 2.7 }; In this example, `Cat` is a _struct-like enum variant_, whereas `Dog` is simply called an enum variant. +Enums have a discriminant. You can assign them explicitly: + +``` +enum Foo { + Bar = 123, +} +``` + +If a discriminant isn't assigned, they start at zero, and add one for each +variant, in order. + +You can cast an enum to get this value: + +``` +# enum Foo { Bar = 123 } +let x = Foo::Bar as u32; // x is now 123u32 +``` + +This only works as long as none of the variants have data attached. If +it were `Bar(i32)`, this is disallowed. + ### Constant items ```{.ebnf .gram} From 873ae555e9a42fd12b0a92134112ec000d749ab2 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Thu, 15 Jan 2015 22:56:53 +0200 Subject: [PATCH 041/211] Revert stability for Bitv and BitvSet The collections were promoted to stable by mistake and do not match RFC 509. This reverts the stability back to unstable. [breaking-change] since previously stable API became unstable. Fixes #21193 --- src/libcollections/bit.rs | 4 ++-- src/libcollections/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libcollections/bit.rs b/src/libcollections/bit.rs index 82e5af67ae316..efd056b0d66a5 100644 --- a/src/libcollections/bit.rs +++ b/src/libcollections/bit.rs @@ -156,7 +156,7 @@ static FALSE: bool = false; /// println!("{:?}", bv); /// println!("total bits set to true: {}", bv.iter().filter(|x| *x).count()); /// ``` -#[stable] +#[unstable = "RFC 509"] pub struct Bitv { /// Internal representation of the bit vector storage: Vec, @@ -1107,7 +1107,7 @@ impl<'a> RandomAccessIterator for Iter<'a> { /// assert!(bv[3]); /// ``` #[derive(Clone)] -#[stable] +#[unstable = "RFC 509"] pub struct BitvSet { bitv: Bitv, } diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index 54ab26c4f7763..e222373ff5980 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -70,12 +70,12 @@ pub mod string; pub mod vec; pub mod vec_map; -#[stable] +#[unstable = "RFC 509"] pub mod bitv { pub use bit::{Bitv, Iter}; } -#[stable] +#[unstable = "RFC 509"] pub mod bitv_set { pub use bit::{BitvSet, Union, Intersection, Difference, SymmetricDifference}; pub use bit::SetIter as Iter; From 9ed27df180be7b006bbbd364812767280087b123 Mon Sep 17 00:00:00 2001 From: Andrew Barchuk Date: Tue, 13 Jan 2015 23:22:32 +0200 Subject: [PATCH 042/211] Fix intro concurrency examples compilation warns * Use range notation instead of deprecated `range()` * Remove deprecated `u` integer suffixes used in ranges * Replace deprecated `i` integer suffixes with `is` for vector numbers `Thread::spawn()` still gives "use of unstable item" warning which I hadn't found a way to fix. --- src/doc/intro.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/intro.md b/src/doc/intro.md index d93b680ae6de7..b5e5fab7469c0 100644 --- a/src/doc/intro.md +++ b/src/doc/intro.md @@ -424,11 +424,11 @@ Let's see an example. This Rust code will not compile: use std::thread::Thread; fn main() { - let mut numbers = vec![1i, 2i, 3i]; + let mut numbers = vec![1is, 2is, 3is]; - for i in range(0u, 3u) { + for i in 0..3 { Thread::spawn(move || { - for j in range(0, 3) { numbers[j] += 1 } + for j in 0..3 { numbers[j] += 1 } }); } } @@ -478,9 +478,9 @@ use std::thread::Thread; use std::sync::{Arc,Mutex}; fn main() { - let numbers = Arc::new(Mutex::new(vec![1i, 2i, 3i])); + let numbers = Arc::new(Mutex::new(vec![1is, 2is, 3is])); - for i in range(0u, 3u) { + for i in 0..3 { let number = numbers.clone(); Thread::spawn(move || { let mut array = number.lock().unwrap(); From f77208972a8603f6cc63aa4c91a0e277c0075e48 Mon Sep 17 00:00:00 2001 From: Andrew Barchuk Date: Wed, 14 Jan 2015 21:59:46 +0200 Subject: [PATCH 043/211] Remove Thread::detach() call from intro example The mentioned method are no longer part of Thread. Spawned threads are detached by default as of now. --- src/doc/intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/intro.md b/src/doc/intro.md index b5e5fab7469c0..98ce6e1466daa 100644 --- a/src/doc/intro.md +++ b/src/doc/intro.md @@ -546,7 +546,7 @@ fn main() { for i in range(0u, 3) { Thread::spawn(move || { println!("{}", vec[i]); - }).detach(); + }); } } ``` From 5f1ba93ad2793a3c8377c98edf3b753618cc2e77 Mon Sep 17 00:00:00 2001 From: Andrew Barchuk Date: Wed, 14 Jan 2015 22:28:48 +0200 Subject: [PATCH 044/211] Replace obsolete constructions in into examples Replace deprecated integer suffixes. Remove integer type notations altogether where possible. Replace uses of deprecated `range()` function with range notation. --- src/doc/intro.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/doc/intro.md b/src/doc/intro.md index 98ce6e1466daa..3487738467fbc 100644 --- a/src/doc/intro.md +++ b/src/doc/intro.md @@ -424,7 +424,7 @@ Let's see an example. This Rust code will not compile: use std::thread::Thread; fn main() { - let mut numbers = vec![1is, 2is, 3is]; + let mut numbers = vec![1is, 2, 3]; for i in 0..3 { Thread::spawn(move || { @@ -438,15 +438,15 @@ It gives us this error: ```text 6:71 error: capture of moved value: `numbers` - for j in range(0, 3) { numbers[j] += 1 } - ^~~~~~~ + for j in 0..3 { numbers[j] += 1 } + ^~~~~~~ 7:50 note: `numbers` moved into closure environment here spawn(move || { - for j in range(0, 3) { numbers[j] += 1 } + for j in 0..3 { numbers[j] += 1 } }); 6:79 error: cannot assign to immutable dereference (dereference is implicit, due to indexing) - for j in range(0, 3) { numbers[j] += 1 } - ^~~~~~~~~~~~~~~ + for j in 0..3 { numbers[j] += 1 } + ^~~~~~~~~~~~~~~ ``` It mentions that "numbers moved into closure environment". Because we @@ -478,7 +478,7 @@ use std::thread::Thread; use std::sync::{Arc,Mutex}; fn main() { - let numbers = Arc::new(Mutex::new(vec![1is, 2is, 3is])); + let numbers = Arc::new(Mutex::new(vec![1is, 2, 3])); for i in 0..3 { let number = numbers.clone(); @@ -541,9 +541,9 @@ safety check that makes this an error about moved values: use std::thread::Thread; fn main() { - let vec = vec![1i, 2, 3]; + let vec = vec![1is, 2, 3]; - for i in range(0u, 3) { + for i in 0us..3 { Thread::spawn(move || { println!("{}", vec[i]); }); @@ -557,9 +557,9 @@ you can remove it. As an example, this is a poor way to iterate through a vector: ```{rust} -let vec = vec![1i, 2, 3]; +let vec = vec![1, 2, 3]; -for i in range(0u, vec.len()) { +for i in 0..vec.len() { println!("{}", vec[i]); } ``` @@ -569,7 +569,7 @@ that we don't try to access an invalid index. However, we can remove this while retaining safety. The answer is iterators: ```{rust} -let vec = vec![1i, 2, 3]; +let vec = vec![1, 2, 3]; for x in vec.iter() { println!("{}", x); From a03701defaf00f323aeaff033d84d93545448b77 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 12 Jan 2015 13:41:37 -0500 Subject: [PATCH 045/211] Remove segmented stack info from the FFI chapter of the book. Fixes #20071. --- src/doc/trpl/ffi.md | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/doc/trpl/ffi.md b/src/doc/trpl/ffi.md index 940d2c968be67..32b60f0a81d0b 100644 --- a/src/doc/trpl/ffi.md +++ b/src/doc/trpl/ffi.md @@ -164,29 +164,6 @@ pub fn uncompress(src: &[u8]) -> Option> { For reference, the examples used here are also available as an [library on GitHub](https://github.com/thestinger/rust-snappy). -# Stack management - -Rust threads by default run on a *large stack*. This is actually implemented as a -reserving a large segment of the address space and then lazily mapping in pages -as they are needed. When calling an external C function, the code is invoked on -the same stack as the rust stack. This means that there is no extra -stack-switching mechanism in place because it is assumed that the large stack -for the rust thread is plenty for the C function to have. - -A planned future improvement (not yet implemented at the time of this writing) -is to have a guard page at the end of every rust stack. No rust function will -hit this guard page (due to Rust's usage of LLVM's `__morestack`). The intention -for this unmapped page is to prevent infinite recursion in C from overflowing -onto other rust stacks. If the guard page is hit, then the process will be -terminated with a message saying that the guard page was hit. - -For normal external function usage, this all means that there shouldn't be any -need for any extra effort on a user's perspective. The C stack naturally -interleaves with the rust stack, and it's "large enough" for both to -interoperate. If, however, it is determined that a larger stack is necessary, -there are appropriate functions in the thread spawning API to control the size of -the stack of the thread which is spawned. - # Destructors Foreign libraries often hand off ownership of resources to the calling code. From 078bd498b9fa6eab40df147ce6015ab9aae62b40 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 12 Jan 2015 16:59:34 -0500 Subject: [PATCH 046/211] Evaluate # fn in docs I searched for times when we were hiding functions with # in the documentation, and fixed them to not use it unless neccesary. I also made random improvements whenever I changed something. For example, I changed Example to Examples, for consistency. Fixes #13423 --- src/doc/trpl/threads.md | 82 ++++++++++++++++++++++----------------- src/libcore/cell.rs | 1 - src/libcore/finally.rs | 2 - src/libcore/macros.rs | 17 +++++--- src/libcore/marker.rs | 3 +- src/libcore/prelude.rs | 4 -- src/libcore/result.rs | 2 - src/libstd/fmt.rs | 11 +----- src/libstd/io/mod.rs | 27 ++++++------- src/libstd/io/net/pipe.rs | 2 - src/libstd/io/net/tcp.rs | 6 +-- src/libstd/io/timer.rs | 4 +- src/libstd/macros.rs | 20 +++++----- src/libstd/sync/future.rs | 16 +++++--- 14 files changed, 95 insertions(+), 102 deletions(-) diff --git a/src/doc/trpl/threads.md b/src/doc/trpl/threads.md index 4c6a7f1323fb1..df94e91067c15 100644 --- a/src/doc/trpl/threads.md +++ b/src/doc/trpl/threads.md @@ -51,13 +51,15 @@ closure is limited to capturing `Send`-able data from its environment ensures that `spawn` can safely move the entire closure and all its associated state into an entirely different thread for execution. -```{rust,ignore} -# use std::thread::spawn; -# fn generate_thread_number() -> int { 0 } +```rust +use std::thread::Thread; + +fn generate_thread_number() -> i32 { 4 } // a very simple generation + // Generate some state locally let child_thread_number = generate_thread_number(); -spawn(move || { +Thread::spawn(move || { // Capture it in the remote thread. The `move` keyword indicates // that this closure should move `child_thread_number` into its // environment, rather than capturing a reference into the @@ -77,20 +79,22 @@ The simplest way to create a channel is to use the `channel` function to create of a channel, and a *receiver* is the receiving endpoint. Consider the following example of calculating two results concurrently: -```{rust,ignore} -# use std::thread::spawn; +```rust +use std::thread::Thread; +use std::sync::mpsc; -let (tx, rx): (Sender, Receiver) = channel(); +let (tx, rx): (mpsc::Sender, mpsc::Receiver) = mpsc::channel(); -spawn(move || { +Thread::spawn(move || { let result = some_expensive_computation(); tx.send(result); }); some_other_expensive_computation(); let result = rx.recv(); -# fn some_expensive_computation() -> int { 42 } -# fn some_other_expensive_computation() {} + +fn some_expensive_computation() -> u32 { 42 } // very expensive ;) +fn some_other_expensive_computation() {} // even more so ``` Let's examine this example in detail. First, the `let` statement creates a @@ -98,19 +102,21 @@ stream for sending and receiving integers (the left-hand side of the `let`, `(tx, rx)`, is an example of a destructuring let: the pattern separates a tuple into its component parts). -```{rust,ignore} -let (tx, rx): (Sender, Receiver) = channel(); +```rust +# use std::sync::mpsc; +let (tx, rx): (mpsc::Sender, mpsc::Receiver) = mpsc::channel(); ``` The child thread will use the sender to send data to the parent thread, which will wait to receive the data on the receiver. The next statement spawns the child thread. -```{rust,ignore} -# use std::thread::spawn; -# fn some_expensive_computation() -> int { 42 } -# let (tx, rx) = channel(); -spawn(move || { +```rust +# use std::thread::Thread; +# use std::sync::mpsc; +# fn some_expensive_computation() -> u32 { 42 } +# let (tx, rx) = mpsc::channel(); +Thread::spawn(move || { let result = some_expensive_computation(); tx.send(result); }); @@ -125,9 +131,10 @@ computation, then sends the result over the captured channel. Finally, the parent continues with some other expensive computation, then waits for the child's result to arrive on the receiver: -```{rust,ignore} +```rust +# use std::sync::mpsc; # fn some_other_expensive_computation() {} -# let (tx, rx) = channel::(); +# let (tx, rx) = mpsc::channel::(); # tx.send(0); some_other_expensive_computation(); let result = rx.recv(); @@ -140,8 +147,9 @@ single `Receiver` value. What if our example needed to compute multiple results across a number of threads? The following program is ill-typed: ```{rust,ignore} -# fn some_expensive_computation() -> int { 42 } -let (tx, rx) = channel(); +# use std::sync::mpsc; +# fn some_expensive_computation() -> u32 { 42 } +let (tx, rx) = mpsc::channel(); spawn(move || { tx.send(some_expensive_computation()); @@ -156,19 +164,22 @@ spawn(move || { Instead we can clone the `tx`, which allows for multiple senders. -```{rust,ignore} -let (tx, rx) = channel(); +```rust +use std::thread::Thread; +use std::sync::mpsc; + +let (tx, rx) = mpsc::channel(); -for init_val in range(0u, 3) { +for init_val in 0 .. 3 { // Create a new channel handle to distribute to the child thread let child_tx = tx.clone(); - spawn(move || { + Thread::spawn(move || { child_tx.send(some_expensive_computation(init_val)); }); } -let result = rx.recv() + rx.recv() + rx.recv(); -# fn some_expensive_computation(_i: uint) -> int { 42 } +let result = rx.recv().unwrap() + rx.recv().unwrap() + rx.recv().unwrap(); +# fn some_expensive_computation(_i: u32) -> u32 { 42 } ``` Cloning a `Sender` produces a new handle to the same channel, allowing multiple @@ -181,21 +192,22 @@ Note that the above cloning example is somewhat contrived since you could also simply use three `Sender` pairs, but it serves to illustrate the point. For reference, written with multiple streams, it might look like the example below. -```{rust,ignore} -# use std::thread::spawn; +```rust +use std::thread::Thread; +use std::sync::mpsc; // Create a vector of ports, one for each child thread -let rxs = Vec::from_fn(3, |init_val| { - let (tx, rx) = channel(); - spawn(move || { +let rxs = (0 .. 3).map(|&:init_val| { + let (tx, rx) = mpsc::channel(); + Thread::spawn(move || { tx.send(some_expensive_computation(init_val)); }); rx -}); +}).collect::>(); // Wait on each port, accumulating the results -let result = rxs.iter().fold(0, |accum, rx| accum + rx.recv() ); -# fn some_expensive_computation(_i: uint) -> int { 42 } +let result = rxs.iter().fold(0, |&:accum, rx| accum + rx.recv().unwrap() ); +# fn some_expensive_computation(_i: u32) -> u32 { 42 } ``` ## Backgrounding computations: Futures diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index ec4007c4c6dd4..963cb48db0701 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -115,7 +115,6 @@ //! } //! # fn calc_span_tree(&self) -> Vec<(uint, uint)> { vec![] } //! } -//! # fn main() { } //! ``` //! //! ## Mutating implementations of `clone` diff --git a/src/libcore/finally.rs b/src/libcore/finally.rs index 4c2a2ff108603..ed3612bded05e 100644 --- a/src/libcore/finally.rs +++ b/src/libcore/finally.rs @@ -23,13 +23,11 @@ //! //! use std::finally::Finally; //! -//! # fn main() { //! (|&mut:| { //! // ... //! }).finally(|| { //! // this code is always run //! }) -//! # } //! ``` #![deprecated = "It is unclear if this module is more robust than implementing \ diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index 3d3b9f8cf6585..1c37126e8e902 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -39,13 +39,16 @@ macro_rules! panic { /// // the panic message for these assertions is the stringified value of the /// // expression given. /// assert!(true); -/// # fn some_computation() -> bool { true } +/// +/// fn some_computation() -> bool { true } // a very simple function +/// /// assert!(some_computation()); /// /// // assert with a custom message -/// # let x = true; +/// let x = true; /// assert!(x, "x wasn't true!"); -/// # let a = 3i; let b = 27i; +/// +/// let a = 3i; let b = 27i; /// assert!(a + b == 30, "a = {}, b = {}", a, b); /// ``` #[macro_export] @@ -108,13 +111,15 @@ macro_rules! assert_eq { /// // the panic message for these assertions is the stringified value of the /// // expression given. /// debug_assert!(true); -/// # fn some_expensive_computation() -> bool { true } +/// +/// fn some_expensive_computation() -> bool { true } // a very simple function /// debug_assert!(some_expensive_computation()); /// /// // assert with a custom message -/// # let x = true; +/// let x = true; /// debug_assert!(x, "x wasn't true!"); -/// # let a = 3i; let b = 27i; +/// +/// let a = 3; let b = 27; /// debug_assert!(a + b == 30, "a = {}, b = {}", a, b); /// ``` #[macro_export] diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 715a79abe8540..6d272f91698e7 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -210,8 +210,7 @@ impl Clone for ContravariantType { /// "interior" mutability: /// /// ``` -/// pub struct Cell { value: T } -/// # fn main() {} +/// struct Cell { value: T } /// ``` /// /// The type system would infer that `value` is only read here and diff --git a/src/libcore/prelude.rs b/src/libcore/prelude.rs index c3bb9c91557f3..da3f180d7e127 100644 --- a/src/libcore/prelude.rs +++ b/src/libcore/prelude.rs @@ -21,11 +21,7 @@ //! # Example //! //! ```ignore -//! # fn main() { -//! #![feature(globs)] -//! //! use core::prelude::*; -//! # } //! ``` // Reexported core operators diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 110bce5c12472..1ab810f937de9 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -217,11 +217,9 @@ //! makes it clear: //! //! ``` -//! # #![feature(macro_rules)] //! macro_rules! try { //! ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(e) }) //! } -//! # fn main() { } //! ``` //! //! `try!` is imported by the prelude, and is available everywhere. diff --git a/src/libstd/fmt.rs b/src/libstd/fmt.rs index 907925e93d399..36afa0956d2de 100644 --- a/src/libstd/fmt.rs +++ b/src/libstd/fmt.rs @@ -26,15 +26,13 @@ //! //! Some examples of the `format!` extension are: //! -//! ```rust -//! # fn main() { +//! ``` //! format!("Hello"); // => "Hello" //! format!("Hello, {}!", "world"); // => "Hello, world!" //! format!("The number is {}", 1i); // => "The number is 1" //! format!("{:?}", (3i, 4i)); // => "(3i, 4i)" //! format!("{value}", value=4i); // => "4" //! format!("{} {}", 1i, 2u); // => "1 2" -//! # } //! ``` //! //! From these, you can see that the first argument is a format string. It is @@ -83,12 +81,10 @@ //! //! For example, the following `format!` expressions all use named argument: //! -//! ```rust -//! # fn main() { +//! ``` //! format!("{argument}", argument = "test"); // => "test" //! format!("{name} {}", 1i, name = 2i); // => "2 1" //! format!("{a} {c} {b}", a="a", b='b', c=3i); // => "a 3 b" -//! # } //! ``` //! //! It is illegal to put positional parameters (those without names) after @@ -288,8 +284,6 @@ //! use std::fmt; //! use std::io; //! -//! # #[allow(unused_must_use)] -//! # fn main() { //! fmt::format(format_args!("this returns {}", "String")); //! //! let some_writer: &mut io::Writer = &mut io::stdout(); @@ -299,7 +293,6 @@ //! write!(&mut io::stdout(), "{}", args); //! } //! my_fmt_fn(format_args!("or a {} too", "function")); -//! # } //! ``` //! //! The result of the `format_args!` macro is a value of type `fmt::Arguments`. diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index bab4dafd090bd..e2b71cd43af25 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -934,16 +934,15 @@ unsafe fn slice_vec_capacity<'a, T>(v: &'a mut Vec, start: uint, end: uint) - /// A `RefReader` is a struct implementing `Reader` which contains a reference /// to another reader. This is often useful when composing streams. /// -/// # Example +/// # Examples /// /// ``` -/// # fn main() {} -/// # fn process_input(r: R) {} -/// # fn foo() { /// use std::io; /// use std::io::ByRefReader; /// use std::io::util::LimitReader; /// +/// fn process_input(r: R) {} +/// /// let mut stream = io::stdin(); /// /// // Only allow the function to process at most one kilobyte of input @@ -953,8 +952,6 @@ unsafe fn slice_vec_capacity<'a, T>(v: &'a mut Vec, start: uint, end: uint) - /// } /// /// // 'stream' is still available for use here -/// -/// # } /// ``` pub struct RefReader<'a, R:'a> { /// The underlying reader which this is referencing @@ -1269,12 +1266,11 @@ impl<'a> Writer for &'a mut (Writer+'a) { /// # Example /// /// ``` -/// # fn main() {} -/// # fn process_input(r: R) {} -/// # fn foo () { /// use std::io::util::TeeReader; /// use std::io::{stdin, ByRefWriter}; /// +/// fn process_input(r: R) {} +/// /// let mut output = Vec::new(); /// /// { @@ -1285,7 +1281,6 @@ impl<'a> Writer for &'a mut (Writer+'a) { /// } /// /// println!("input processed: {:?}", output); -/// # } /// ``` pub struct RefWriter<'a, W:'a> { /// The underlying writer which this is referencing @@ -1705,19 +1700,19 @@ pub enum FileType { /// A structure used to describe metadata information about a file. This /// structure is created through the `stat` method on a `Path`. /// -/// # Example +/// # Examples +/// +/// ```no_run +/// # #![allow(unstable)] +/// +/// use std::io::fs::PathExtensions; /// -/// ``` -/// # use std::io::fs::PathExtensions; -/// # fn main() {} -/// # fn foo() { /// let info = match Path::new("foo.txt").stat() { /// Ok(stat) => stat, /// Err(e) => panic!("couldn't read foo.txt: {}", e), /// }; /// /// println!("byte size: {}", info.size); -/// # } /// ``` #[derive(Copy, Hash)] pub struct FileStat { diff --git a/src/libstd/io/net/pipe.rs b/src/libstd/io/net/pipe.rs index 42d9fff6d151d..61d164d21e320 100644 --- a/src/libstd/io/net/pipe.rs +++ b/src/libstd/io/net/pipe.rs @@ -168,9 +168,7 @@ impl UnixListener { /// # Example /// /// ``` - /// # fn main() {} /// # fn foo() { - /// # #![allow(unused_must_use)] /// use std::io::net::pipe::UnixListener; /// use std::io::{Listener, Acceptor}; /// diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index 6a3f5fcb2c699..4978085fa4fbe 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -272,12 +272,10 @@ impl sys_common::AsInner for TcpStream { /// A structure representing a socket server. This listener is used to create a /// `TcpAcceptor` which can be used to accept sockets on a local port. /// -/// # Example +/// # Examples /// -/// ```rust -/// # fn main() { } +/// ``` /// # fn foo() { -/// # #![allow(dead_code)] /// use std::io::{TcpListener, TcpStream}; /// use std::io::{Acceptor, Listener}; /// use std::thread::Thread; diff --git a/src/libstd/io/timer.rs b/src/libstd/io/timer.rs index 8a0445be47135..844a97dea2d50 100644 --- a/src/libstd/io/timer.rs +++ b/src/libstd/io/timer.rs @@ -27,10 +27,9 @@ use sys::timer::Timer as TimerImp; /// period of time. Handles to this timer can also be created in the form of /// receivers which will receive notifications over time. /// -/// # Example +/// # Examples /// /// ``` -/// # fn main() {} /// # fn foo() { /// use std::io::Timer; /// use std::time::Duration; @@ -54,7 +53,6 @@ use sys::timer::Timer as TimerImp; /// the `io::timer` module. /// /// ``` -/// # fn main() {} /// # fn foo() { /// use std::io::timer; /// use std::time::Duration; diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index a420c841d25e3..5795b4c38c64f 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -122,16 +122,18 @@ macro_rules! try { /// receivers. It places no restrictions on the types of receivers given to /// this macro, this can be viewed as a heterogeneous select. /// -/// # Example +/// # Examples /// /// ``` /// use std::thread::Thread; -/// use std::sync::mpsc::channel; +/// use std::sync::mpsc; +/// +/// // two placeholder functions for now +/// fn long_running_task() {} +/// fn calculate_the_answer() -> u32 { 42 } /// -/// let (tx1, rx1) = channel(); -/// let (tx2, rx2) = channel(); -/// # fn long_running_task() {} -/// # fn calculate_the_answer() -> int { 42i } +/// let (tx1, rx1) = mpsc::channel(); +/// let (tx2, rx2) = mpsc::channel(); /// /// Thread::spawn(move|| { long_running_task(); tx1.send(()).unwrap(); }); /// Thread::spawn(move|| { tx2.send(calculate_the_answer()).unwrap(); }); @@ -251,17 +253,15 @@ pub mod builtin { /// statement or expression position, meaning this macro may be difficult to /// use in some situations. /// - /// # Example + /// # Examples /// /// ``` /// #![feature(concat_idents)] /// - /// # fn main() { - /// fn foobar() -> int { 23 } + /// fn foobar() -> u32 { 23 } /// /// let f = concat_idents!(foo, bar); /// println!("{}", f()); - /// # } /// ``` #[macro_export] macro_rules! concat_idents { diff --git a/src/libstd/sync/future.rs b/src/libstd/sync/future.rs index 568c24446e7a3..36bbc5ff5b423 100644 --- a/src/libstd/sync/future.rs +++ b/src/libstd/sync/future.rs @@ -11,14 +11,18 @@ //! A type representing values that may be computed concurrently and operations //! for working with them. //! -//! # Example +//! # Examples //! -//! ```rust +//! ``` //! use std::sync::Future; -//! # fn fib(n: uint) -> uint {42}; -//! # fn make_a_sandwich() {}; -//! let mut delayed_fib = Future::spawn(move|| { fib(5000) }); -//! make_a_sandwich(); +//! +//! // a fake, for now +//! fn fib(n: u32) -> u32 { 42 }; +//! +//! let mut delayed_fib = Future::spawn(move || fib(5000)); +//! +//! // do stuff... +//! //! println!("fib(5000) = {}", delayed_fib.get()) //! ``` From 899ffcf62adde4cef2af0d543fc3fa627396a586 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 13 Jan 2015 10:40:18 -0500 Subject: [PATCH 047/211] Intpocalypse, book edition. Fix all usage of int/uint/i/u in the book. --- src/doc/trpl/ffi.md | 8 +-- src/doc/trpl/generics.md | 20 ++++---- src/doc/trpl/iterators.md | 74 +++++++++++++-------------- src/doc/trpl/looping.md | 6 +-- src/doc/trpl/macros.md | 34 ++++++------- src/doc/trpl/ownership.md | 4 +- src/doc/trpl/patterns.md | 48 +++++++++--------- src/doc/trpl/plugins.md | 6 +-- src/doc/trpl/pointers.md | 104 +++++++++++++++++++------------------- src/doc/trpl/testing.md | 4 +- src/doc/trpl/threads.md | 18 +++---- src/doc/trpl/traits.md | 12 ++--- src/doc/trpl/unsafe.md | 34 ++++++------- 13 files changed, 187 insertions(+), 185 deletions(-) diff --git a/src/doc/trpl/ffi.md b/src/doc/trpl/ffi.md index 32b60f0a81d0b..ab112280a69a5 100644 --- a/src/doc/trpl/ffi.md +++ b/src/doc/trpl/ffi.md @@ -116,11 +116,11 @@ pub fn compress(src: &[u8]) -> Vec { let psrc = src.as_ptr(); let mut dstlen = snappy_max_compressed_length(srclen); - let mut dst = Vec::with_capacity(dstlen as uint); + let mut dst = Vec::with_capacity(dstlen as usize); let pdst = dst.as_mut_ptr(); snappy_compress(psrc, srclen, pdst, &mut dstlen); - dst.set_len(dstlen as uint); + dst.set_len(dstlen as usize); dst } } @@ -148,11 +148,11 @@ pub fn uncompress(src: &[u8]) -> Option> { let mut dstlen: size_t = 0; snappy_uncompressed_length(psrc, srclen, &mut dstlen); - let mut dst = Vec::with_capacity(dstlen as uint); + let mut dst = Vec::with_capacity(dstlen as usize); let pdst = dst.as_mut_ptr(); if snappy_uncompress(psrc, srclen, pdst, &mut dstlen) == 0 { - dst.set_len(dstlen as uint); + dst.set_len(dstlen as usize); Some(dst) } else { None // SNAPPY_INVALID_INPUT diff --git a/src/doc/trpl/generics.md b/src/doc/trpl/generics.md index 023143ae64e26..74cb4530935df 100644 --- a/src/doc/trpl/generics.md +++ b/src/doc/trpl/generics.md @@ -5,7 +5,7 @@ multiple types of arguments. For example, remember our `OptionalInt` type? ```{rust} enum OptionalInt { - Value(int), + Value(i32), Missing, } ``` @@ -40,26 +40,26 @@ we substitute that type for the same type used in the generic. Here's an example of using `Option`, with some extra type annotations: ```{rust} -let x: Option = Some(5i); +let x: Option = Some(5); ``` -In the type declaration, we say `Option`. Note how similar this looks to -`Option`. So, in this particular `Option`, `T` has the value of `int`. On -the right-hand side of the binding, we do make a `Some(T)`, where `T` is `5i`. -Since that's an `int`, the two sides match, and Rust is happy. If they didn't +In the type declaration, we say `Option`. Note how similar this looks to +`Option`. So, in this particular `Option`, `T` has the value of `i32`. On +the right-hand side of the binding, we do make a `Some(T)`, where `T` is `5`. +Since that's an `i32`, the two sides match, and Rust is happy. If they didn't match, we'd get an error: ```{rust,ignore} -let x: Option = Some(5i); -// error: mismatched types: expected `core::option::Option` -// but found `core::option::Option` (expected f64 but found int) +let x: Option = Some(5); +// error: mismatched types: expected `core::option::Option`, +// found `core::option::Option<_>` (expected f64 but found integral variable) ``` That doesn't mean we can't make `Option`s that hold an `f64`! They just have to match up: ```{rust} -let x: Option = Some(5i); +let x: Option = Some(5); let y: Option = Some(5.0f64); ``` diff --git a/src/doc/trpl/iterators.md b/src/doc/trpl/iterators.md index 8312f762c113a..62cc1d5f62aec 100644 --- a/src/doc/trpl/iterators.md +++ b/src/doc/trpl/iterators.md @@ -5,7 +5,7 @@ Let's talk about loops. Remember Rust's `for` loop? Here's an example: ```{rust} -for x in range(0i, 10i) { +for x in range(0, 10) { println!("{}", x); } ``` @@ -17,7 +17,7 @@ call the `.next()` method on repeatedly, and it gives us a sequence of things. Like this: ```{rust} -let mut range = range(0i, 10i); +let mut range = range(0, 10); loop { match range.next() { @@ -32,8 +32,8 @@ loop { We make a mutable binding to the return value of `range`, which is our iterator. We then `loop`, with an inner `match`. This `match` is used on the result of `range.next()`, which gives us a reference to the next value of the iterator. -`next` returns an `Option`, in this case, which will be `Some(int)` when -we have a value and `None` once we run out. If we get `Some(int)`, we print it +`next` returns an `Option`, in this case, which will be `Some(i32)` when +we have a value and `None` once we run out. If we get `Some(i32)`, we print it out, and if we get `None`, we `break` out of the loop. This code sample is basically the same as our `for` loop version. The `for` @@ -50,9 +50,9 @@ primitive. For example, if you needed to iterate over the contents of a vector, you may be tempted to write this: ```{rust} -let nums = vec![1i, 2i, 3i]; +let nums = vec![1, 2, 3]; -for i in range(0u, nums.len()) { +for i in range(0, nums.len()) { println!("{}", nums[i]); } ``` @@ -62,7 +62,7 @@ vectors returns an iterator which iterates through a reference to each element of the vector in turn. So write this: ```{rust} -let nums = vec![1i, 2i, 3i]; +let nums = vec![1, 2, 3]; for num in nums.iter() { println!("{}", num); @@ -79,12 +79,12 @@ very common with iterators: we can ignore unnecessary bounds checks, but still know that we're safe. There's another detail here that's not 100% clear because of how `println!` -works. `num` is actually of type `&int`. That is, it's a reference to an `int`, -not an `int` itself. `println!` handles the dereferencing for us, so we don't +works. `num` is actually of type `&i32`. That is, it's a reference to an `i32`, +not an `i32` itself. `println!` handles the dereferencing for us, so we don't see it. This code works fine too: ```{rust} -let nums = vec![1i, 2i, 3i]; +let nums = vec![1, 2, 3]; for num in nums.iter() { println!("{}", *num); @@ -118,7 +118,7 @@ The most common consumer is `collect()`. This code doesn't quite compile, but it shows the intention: ```{rust,ignore} -let one_to_one_hundred = range(1i, 101i).collect(); +let one_to_one_hundred = range(1, 101).collect(); ``` As you can see, we call `collect()` on our iterator. `collect()` takes @@ -128,7 +128,7 @@ type of things you want to collect, and so you need to let it know. Here's the version that does compile: ```{rust} -let one_to_one_hundred = range(1i, 101i).collect::>(); +let one_to_one_hundred = range(1, 101).collect::>(); ``` If you remember, the `::<>` syntax allows us to give a type hint, @@ -138,7 +138,7 @@ and so we tell it that we want a vector of integers. is one: ```{rust} -let greater_than_forty_two = range(0i, 100i) +let greater_than_forty_two = range(0, 100) .find(|x| *x > 42); match greater_than_forty_two { @@ -155,8 +155,8 @@ element, `find` returns an `Option` rather than the element itself. Another important consumer is `fold`. Here's what it looks like: ```{rust} -let sum = range(1i, 4i) - .fold(0i, |sum, x| sum + x); +let sum = range(1, 4) + .fold(0, |sum, x| sum + x); ``` `fold()` is a consumer that looks like this: @@ -172,24 +172,24 @@ in this iterator: | base | accumulator | element | closure result | |------|-------------|---------|----------------| -| 0i | 0i | 1i | 1i | -| 0i | 1i | 2i | 3i | -| 0i | 3i | 3i | 6i | +| 0 | 0 | 1 | 1 | +| 0 | 1 | 2 | 3 | +| 0 | 3 | 3 | 6 | We called `fold()` with these arguments: ```{rust} -# range(1i, 4i) -.fold(0i, |sum, x| sum + x); +# range(1, 4) +.fold(0, |sum, x| sum + x); ``` -So, `0i` is our base, `sum` is our accumulator, and `x` is our element. On the -first iteration, we set `sum` to `0i`, and `x` is the first element of `nums`, -`1i`. We then add `sum` and `x`, which gives us `0i + 1i = 1i`. On the second +So, `0` is our base, `sum` is our accumulator, and `x` is our element. On the +first iteration, we set `sum` to `0`, and `x` is the first element of `nums`, +`1`. We then add `sum` and `x`, which gives us `0 + 1 = 1`. On the second iteration, that value becomes our accumulator, `sum`, and the element is -the second element of the array, `2i`. `1i + 2i = 3i`, and so that becomes +the second element of the array, `2`. `1 + 2 = 3`, and so that becomes the value of the accumulator for the last iteration. On that iteration, -`x` is the last element, `3i`, and `3i + 3i = 6i`, which is our final +`x` is the last element, `3`, and `3 + 3 = 6`, which is our final result for our sum. `1 + 2 + 3 = 6`, and that's the result we got. Whew. `fold` can be a bit strange the first few times you see it, but once it @@ -210,14 +210,14 @@ This code, for example, does not actually generate the numbers `1-100`, and just creates a value that represents the sequence: ```{rust} -let nums = range(1i, 100i); +let nums = range(1, 100); ``` Since we didn't do anything with the range, it didn't generate the sequence. Let's add the consumer: ```{rust} -let nums = range(1i, 100i).collect::>(); +let nums = range(1, 100).collect::>(); ``` Now, `collect()` will require that `range()` give it some numbers, and so @@ -228,7 +228,7 @@ which you've used before. `iter()` can turn a vector into a simple iterator that gives you each element in turn: ```{rust} -let nums = [1i, 2i, 3i]; +let nums = [1, 2, 3]; for num in nums.iter() { println!("{}", num); @@ -239,12 +239,12 @@ These two basic iterators should serve you well. There are some more advanced iterators, including ones that are infinite. Like `count`: ```{rust} -std::iter::count(1i, 5i); +std::iter::count(1, 5); ``` This iterator counts up from one, adding five each time. It will give you a new integer every time, forever (well, technically, until it reaches the -maximum number representable by an `int`). But since iterators are lazy, +maximum number representable by an `i32`). But since iterators are lazy, that's okay! You probably don't want to use `collect()` on it, though... That's enough about iterators. Iterator adapters are the last concept @@ -256,7 +256,7 @@ we need to talk about with regards to iterators. Let's get to it! a new iterator. The simplest one is called `map`: ```{rust,ignore} -range(1i, 100i).map(|x| x + 1i); +range(1, 100).map(|x| x + 1); ``` `map` is called upon another iterator, and produces a new iterator where each @@ -267,7 +267,7 @@ compile the example, you'll get a warning: ```{notrust,ignore} warning: unused result which must be used: iterator adaptors are lazy and do nothing unless consumed, #[warn(unused_must_use)] on by default - range(1i, 100i).map(|x| x + 1i); + range(1, 100).map(|x| x + 1); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` @@ -275,7 +275,7 @@ Laziness strikes again! That closure will never execute. This example doesn't print any numbers: ```{rust,ignore} -range(1i, 100i).map(|x| println!("{}", x)); +range(1, 100).map(|x| println!("{}", x)); ``` If you are trying to execute a closure on an iterator for its side effects, @@ -287,7 +287,7 @@ has no side effect on the original iterator. Let's try it out with our infinite iterator from before, `count()`: ```{rust} -for i in std::iter::count(1i, 5i).take(5) { +for i in std::iter::count(1, 5).take(5) { println!("{}", i); } ``` @@ -307,7 +307,7 @@ returns `true` or `false`. The new iterator `filter()` produces only the elements that that closure returns `true` for: ```{rust} -for i in range(1i, 100i).filter(|&x| x % 2 == 0) { +for i in range(1, 100).filter(|&x| x % 2 == 0) { println!("{}", i); } ``` @@ -322,11 +322,11 @@ You can chain all three things together: start with an iterator, adapt it a few times, and then consume the result. Check it out: ```{rust} -range(1i, 1000i) +range(1, 1000) .filter(|&x| x % 2 == 0) .filter(|&x| x % 3 == 0) .take(5) - .collect::>(); + .collect::>(); ``` This will give you a vector containing `6`, `12`, `18`, `24`, and `30`. diff --git a/src/doc/trpl/looping.md b/src/doc/trpl/looping.md index 2985477085e94..c01df64ef8f65 100644 --- a/src/doc/trpl/looping.md +++ b/src/doc/trpl/looping.md @@ -54,7 +54,7 @@ The other kind of looping construct in Rust is the `while` loop. It looks like this: ```{rust} -let mut x = 5u32; // mut x: u32 +let mut x = 5; // mut x: u32 let mut done = false; // mut done: bool while !done { @@ -91,7 +91,7 @@ can do with safety and code generation, so you should always prefer Let's take a look at that `while` loop we had earlier: ```{rust} -let mut x = 5u32; +let mut x = 5; let mut done = false; while !done { @@ -108,7 +108,7 @@ modifying iteration: `break` and `continue`. In this case, we can write the loop in a better way with `break`: ```{rust} -let mut x = 5u32; +let mut x = 5; loop { x += x - 3; diff --git a/src/doc/trpl/macros.md b/src/doc/trpl/macros.md index 8f4db3eee5ae7..46a4af0d25135 100644 --- a/src/doc/trpl/macros.md +++ b/src/doc/trpl/macros.md @@ -11,8 +11,8 @@ which both pattern-match on their input and both return early in one case, doing nothing otherwise: ~~~~ -# enum T { SpecialA(uint), SpecialB(uint) } -# fn f() -> uint { +# enum T { SpecialA(u32), SpecialB(u32) } +# fn f() -> u32 { # let input_1 = T::SpecialA(0); # let input_2 = T::SpecialA(0); match input_1 { @@ -24,7 +24,7 @@ match input_2 { T::SpecialB(x) => { return x; } _ => {} } -# return 0u; +# return 0; # } ~~~~ @@ -37,8 +37,8 @@ lightweight custom syntax extensions, themselves defined using the the pattern in the above code: ~~~~ -# enum T { SpecialA(uint), SpecialB(uint) } -# fn f() -> uint { +# enum T { SpecialA(u32), SpecialB(u32) } +# fn f() -> u32 { # let input_1 = T::SpecialA(0); # let input_2 = T::SpecialA(0); macro_rules! early_return { @@ -165,8 +165,8 @@ separator token (a comma-separated list could be written `$(...),*`), and `+` instead of `*` to mean "at least one". ~~~~ -# enum T { SpecialA(uint),SpecialB(uint),SpecialC(uint),SpecialD(uint)} -# fn f() -> uint { +# enum T { SpecialA(u32), SpecialB(u32), SpecialC(u32), SpecialD(u32) } +# fn f() -> u32 { # let input_1 = T::SpecialA(0); # let input_2 = T::SpecialA(0); macro_rules! early_return { @@ -226,10 +226,10 @@ solves the problem. Now consider code like the following: ~~~~ -# enum T1 { Good1(T2, uint), Bad1} +# enum T1 { Good1(T2, u32), Bad1} # struct T2 { body: T3 } -# enum T3 { Good2(uint), Bad2} -# fn f(x: T1) -> uint { +# enum T3 { Good2(u32), Bad2} +# fn f(x: T1) -> u32 { match x { T1::Good1(g1, val) => { match g1.body { @@ -273,10 +273,10 @@ macro_rules! biased_match { ) } -# enum T1 { Good1(T2, uint), Bad1} +# enum T1 { Good1(T2, u32), Bad1} # struct T2 { body: T3 } -# enum T3 { Good2(uint), Bad2} -# fn f(x: T1) -> uint { +# enum T3 { Good2(u32), Bad2} +# fn f(x: T1) -> u32 { biased_match!((x) -> (T1::Good1(g1, val)) else { return 0 }; binds g1, val ); biased_match!((g1.body) -> (T3::Good2(result) ) @@ -383,10 +383,10 @@ macro_rules! biased_match { } -# enum T1 { Good1(T2, uint), Bad1} +# enum T1 { Good1(T2, u32), Bad1} # struct T2 { body: T3 } -# enum T3 { Good2(uint), Bad2} -# fn f(x: T1) -> uint { +# enum T3 { Good2(u32), Bad2} +# fn f(x: T1) -> u32 { biased_match!( (x) -> (T1::Good1(g1, val)) else { return 0 }; (g1.body) -> (T3::Good2(result) ) else { panic!("Didn't get Good2") }; @@ -528,7 +528,7 @@ A further difficulty occurs when a macro is used in multiple crates. Say that `mylib` defines ```rust -pub fn increment(x: uint) -> uint { +pub fn increment(x: u32) -> u32 { x + 1 } diff --git a/src/doc/trpl/ownership.md b/src/doc/trpl/ownership.md index 7a397ce535470..6e125e218eaac 100644 --- a/src/doc/trpl/ownership.md +++ b/src/doc/trpl/ownership.md @@ -418,7 +418,7 @@ struct Wheel { fn main() { let car = Car { name: "DeLorean".to_string() }; - for _ in range(0u, 4) { + for _ in range(0, 4) { Wheel { size: 360, owner: car }; } } @@ -456,7 +456,7 @@ fn main() { let car_owner = Rc::new(car); - for _ in range(0u, 4) { + for _ in range(0, 4) { Wheel { size: 360, owner: car_owner.clone() }; } } diff --git a/src/doc/trpl/patterns.md b/src/doc/trpl/patterns.md index c54d502b4edde..4992c49c991d7 100644 --- a/src/doc/trpl/patterns.md +++ b/src/doc/trpl/patterns.md @@ -8,7 +8,7 @@ A quick refresher: you can match against literals directly, and `_` acts as an *any* case: ```{rust} -let x = 1i; +let x = 1; match x { 1 => println!("one"), @@ -21,7 +21,7 @@ match x { You can match multiple patterns with `|`: ```{rust} -let x = 1i; +let x = 1; match x { 1 | 2 => println!("one or two"), @@ -33,7 +33,7 @@ match x { You can match a range of values with `...`: ```{rust} -let x = 1i; +let x = 1; match x { 1 ... 5 => println!("one through five"), @@ -47,7 +47,7 @@ If you're matching multiple things, via a `|` or a `...`, you can bind the value to a name with `@`: ```{rust} -let x = 1i; +let x = 1; match x { e @ 1 ... 5 => println!("got a range element {}", e), @@ -60,11 +60,11 @@ ignore the value and type in the variant: ```{rust} enum OptionalInt { - Value(int), + Value(i32), Missing, } -let x = OptionalInt::Value(5i); +let x = OptionalInt::Value(5); match x { OptionalInt::Value(..) => println!("Got an int!"), @@ -76,11 +76,11 @@ You can introduce *match guards* with `if`: ```{rust} enum OptionalInt { - Value(int), + Value(i32), Missing, } -let x = OptionalInt::Value(5i); +let x = OptionalInt::Value(5); match x { OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"), @@ -93,33 +93,33 @@ If you're matching on a pointer, you can use the same syntax as you declared it with. First, `&`: ```{rust} -let x = &5i; +let x = &5; match x { &val => println!("Got a value: {}", val), } ``` -Here, the `val` inside the `match` has type `int`. In other words, the left-hand -side of the pattern destructures the value. If we have `&5i`, then in `&val`, `val` -would be `5i`. +Here, the `val` inside the `match` has type `i32`. In other words, the left-hand +side of the pattern destructures the value. If we have `&5`, then in `&val`, `val` +would be `5`. If you want to get a reference, use the `ref` keyword: ```{rust} -let x = 5i; +let x = 5; match x { ref r => println!("Got a reference to {}", r), } ``` -Here, the `r` inside the `match` has the type `&int`. In other words, the `ref` +Here, the `r` inside the `match` has the type `&i32`. In other words, the `ref` keyword _creates_ a reference, for use in the pattern. If you need a mutable reference, `ref mut` will work in the same way: ```{rust} -let mut x = 5i; +let mut x = 5; match x { ref mut mr => println!("Got a mutable reference to {}", mr), @@ -131,11 +131,11 @@ If you have a struct, you can destructure it inside of a pattern: ```{rust} # #![allow(non_shorthand_field_patterns)] struct Point { - x: int, - y: int, + x: i32, + y: i32, } -let origin = Point { x: 0i, y: 0i }; +let origin = Point { x: 0, y: 0 }; match origin { Point { x: x, y: y } => println!("({},{})", x, y), @@ -147,11 +147,11 @@ If we only care about some of the values, we don't have to give them all names: ```{rust} # #![allow(non_shorthand_field_patterns)] struct Point { - x: int, - y: int, + x: i32, + y: i32, } -let origin = Point { x: 0i, y: 0i }; +let origin = Point { x: 0, y: 0 }; match origin { Point { x: x, .. } => println!("x is {}", x), @@ -163,11 +163,11 @@ You can do this kind of match on any member, not just the first: ```{rust} # #![allow(non_shorthand_field_patterns)] struct Point { - x: int, - y: int, + x: i32, + y: i32, } -let origin = Point { x: 0i, y: 0i }; +let origin = Point { x: 0, y: 0 }; match origin { Point { y: y, .. } => println!("y is {}", y), diff --git a/src/doc/trpl/plugins.md b/src/doc/trpl/plugins.md index 4cd39d407a243..d710c2fe4e756 100644 --- a/src/doc/trpl/plugins.md +++ b/src/doc/trpl/plugins.md @@ -68,7 +68,7 @@ use rustc::plugin::Registry; fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) -> Box { - static NUMERALS: &'static [(&'static str, uint)] = &[ + static NUMERALS: &'static [(&'static str, u32)] = &[ ("M", 1000), ("CM", 900), ("D", 500), ("CD", 400), ("C", 100), ("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9), ("V", 5), ("IV", 4), @@ -83,7 +83,7 @@ fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) }; let mut text = text.as_slice(); - let mut total = 0u; + let mut total = 0; while !text.is_empty() { match NUMERALS.iter().find(|&&(rn, _)| text.starts_with(rn)) { Some(&(rn, val)) => { @@ -118,7 +118,7 @@ fn main() { } ``` -The advantages over a simple `fn(&str) -> uint` are: +The advantages over a simple `fn(&str) -> u32` are: * The (arbitrarily complex) conversion is done at compile time. * Input validation is also performed at compile time. diff --git a/src/doc/trpl/pointers.md b/src/doc/trpl/pointers.md index 6832d75245e5c..c918a80a86f01 100644 --- a/src/doc/trpl/pointers.md +++ b/src/doc/trpl/pointers.md @@ -28,9 +28,10 @@ question](http://stackoverflow.com/questions/79923/what-and-where-are-the-stack- as the rest of this guide assumes you know the difference.) Like this: ```{rust} -let x = 5i; -let y = 8i; +let x = 5; +let y = 8; ``` + | location | value | |----------|-------| | 0xd3e030 | 5 | @@ -46,10 +47,11 @@ Let's introduce a pointer. In some languages, there is just one type of *reference*, which is the simplest kind of pointer. ```{rust} -let x = 5i; -let y = 8i; +let x = 5; +let y = 8; let z = &y; ``` + |location | value | |-------- |----------| |0xd3e030 | 5 | @@ -58,12 +60,12 @@ let z = &y; See the difference? Rather than contain a value, the value of a pointer is a location in memory. In this case, the location of `y`. `x` and `y` have the -type `int`, but `z` has the type `&int`. We can print this location using the +type `i32`, but `z` has the type `&i32`. We can print this location using the `{:p}` format string: ```{rust} -let x = 5i; -let y = 8i; +let x = 5; +let y = 8; let z = &y; println!("{:p}", z); @@ -71,12 +73,12 @@ println!("{:p}", z); This would print `0xd3e028`, with our fictional memory addresses. -Because `int` and `&int` are different types, we can't, for example, add them +Because `i32` and `&i32` are different types, we can't, for example, add them together: ```{rust,ignore} -let x = 5i; -let y = 8i; +let x = 5; +let y = 8; let z = &y; println!("{}", x + z); @@ -85,7 +87,7 @@ println!("{}", x + z); This gives us an error: ```text -hello.rs:6:24: 6:25 error: mismatched types: expected `int` but found `&int` (expected int but found &-ptr) +hello.rs:6:24: 6:25 error: mismatched types: expected `i32` but found `&i32` (expected i32 but found &-ptr) hello.rs:6 println!("{}", x + z); ^ ``` @@ -95,8 +97,8 @@ pointer means accessing the value at the location stored in the pointer. This will work: ```{rust} -let x = 5i; -let y = 8i; +let x = 5; +let y = 8; let z = &y; println!("{}", x + *z); @@ -153,7 +155,7 @@ So what do pointers have to do with this? Well, since pointers point to a location in memory... ```text -func foo(&int x) { +func foo(&i32 x) { *x = 5 } @@ -252,7 +254,7 @@ The most basic type of pointer that Rust has is called a *reference*. Rust references look like this: ```{rust} -let x = 5i; +let x = 5; let y = &x; println!("{}", *y); @@ -269,18 +271,18 @@ referent, because `println!` will automatically dereference it for us. Here's a function that takes a reference: ```{rust} -fn succ(x: &int) -> int { *x + 1 } +fn succ(x: &i32) -> i32 { *x + 1 } ``` You can also use `&` as an operator to create a reference, so we can call this function in two different ways: ```{rust} -fn succ(x: &int) -> int { *x + 1 } +fn succ(x: &i32) -> i32 { *x + 1 } fn main() { - let x = 5i; + let x = 5; let y = &x; println!("{}", succ(y)); @@ -294,13 +296,13 @@ Of course, if this were real code, we wouldn't bother with the reference, and just write: ```{rust} -fn succ(x: int) -> int { x + 1 } +fn succ(x: i32) -> i32 { x + 1 } ``` References are immutable by default: ```{rust,ignore} -let x = 5i; +let x = 5; let y = &x; *y = 5; // error: cannot assign to immutable dereference of `&`-pointer `*y` @@ -310,21 +312,21 @@ They can be made mutable with `mut`, but only if its referent is also mutable. This works: ```{rust} -let mut x = 5i; +let mut x = 5; let y = &mut x; ``` This does not: ```{rust,ignore} -let x = 5i; +let x = 5; let y = &mut x; // error: cannot borrow immutable local variable `x` as mutable ``` Immutable pointers are allowed to alias: ```{rust} -let x = 5i; +let x = 5; let y = &x; let z = &x; ``` @@ -332,7 +334,7 @@ let z = &x; Mutable ones, however, are not: ```{rust,ignore} -let mut x = 5i; +let mut x = 5; let y = &mut x; let z = &mut x; // error: cannot borrow `x` as mutable more than once at a time ``` @@ -359,7 +361,7 @@ duration a *lifetime*. Let's try a more complex example: ```{rust} fn main() { - let x = &mut 5i; + let x = &mut 5; if *x < 10 { let y = &x; @@ -380,7 +382,7 @@ mutated, and therefore, lets us pass. This wouldn't work: ```{rust,ignore} fn main() { - let x = &mut 5i; + let x = &mut 5; if *x < 10 { let y = &x; @@ -425,13 +427,13 @@ References just borrow ownership, which is more polite if you don't need the ownership. In other words, prefer: ```{rust} -fn succ(x: &int) -> int { *x + 1 } +fn succ(x: &i32) -> i32 { *x + 1 } ``` to ```{rust} -fn succ(x: Box) -> int { *x + 1 } +fn succ(x: Box) -> i32 { *x + 1 } ``` As a corollary to that rule, references allow you to accept a wide variety of @@ -439,7 +441,7 @@ other pointers, and so are useful so that you don't have to write a number of variants per pointer. In other words, prefer: ```{rust} -fn succ(x: &int) -> int { *x + 1 } +fn succ(x: &i32) -> i32 { *x + 1 } ``` to @@ -447,9 +449,9 @@ to ```{rust} use std::rc::Rc; -fn box_succ(x: Box) -> int { *x + 1 } +fn box_succ(x: Box) -> i32 { *x + 1 } -fn rc_succ(x: Rc) -> int { *x + 1 } +fn rc_succ(x: Rc) -> i32 { *x + 1 } ``` Note that the caller of your function will have to modify their calls slightly: @@ -457,11 +459,11 @@ Note that the caller of your function will have to modify their calls slightly: ```{rust} use std::rc::Rc; -fn succ(x: &int) -> int { *x + 1 } +fn succ(x: &i32) -> i32 { *x + 1 } -let ref_x = &5i; -let box_x = Box::new(5i); -let rc_x = Rc::new(5i); +let ref_x = &5; +let box_x = Box::new(5); +let rc_x = Rc::new(5); succ(ref_x); succ(&*box_x); @@ -477,7 +479,7 @@ those contents. heap allocation in Rust. Creating a box looks like this: ```{rust} -let x = Box::new(5i); +let x = Box::new(5); ``` Boxes are heap allocated and they are deallocated automatically by Rust when @@ -485,7 +487,7 @@ they go out of scope: ```{rust} { - let x = Box::new(5i); + let x = Box::new(5); // stuff happens @@ -505,7 +507,7 @@ boxes, though. As a rough approximation, you can treat this Rust code: ```{rust} { - let x = Box::new(5i); + let x = Box::new(5); // stuff happens } @@ -544,12 +546,12 @@ for more detail on how lifetimes work. Using boxes and references together is very common. For example: ```{rust} -fn add_one(x: &int) -> int { +fn add_one(x: &i32) -> i32 { *x + 1 } fn main() { - let x = Box::new(5i); + let x = Box::new(5); println!("{}", add_one(&*x)); } @@ -561,12 +563,12 @@ function, and since it's only reading the value, allows it. We can borrow `x` multiple times, as long as it's not simultaneous: ```{rust} -fn add_one(x: &int) -> int { +fn add_one(x: &i32) -> i32 { *x + 1 } fn main() { - let x = Box::new(5i); + let x = Box::new(5); println!("{}", add_one(&*x)); println!("{}", add_one(&*x)); @@ -577,12 +579,12 @@ fn main() { Or as long as it's not a mutable borrow. This will error: ```{rust,ignore} -fn add_one(x: &mut int) -> int { +fn add_one(x: &mut i32) -> i32 { *x + 1 } fn main() { - let x = Box::new(5i); + let x = Box::new(5); println!("{}", add_one(&*x)); // error: cannot borrow immutable dereference // of `&`-pointer as mutable @@ -610,7 +612,7 @@ enum List { } fn main() { - let list: List = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Cons(3, Box::new(List::Nil)))))); + let list: List = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Cons(3, Box::new(List::Nil)))))); println!("{:?}", list); } ``` @@ -659,10 +661,10 @@ so as to avoid copying a large data structure. For example: ```{rust} struct BigStruct { - one: int, - two: int, + one: i32, + two: i32, // etc - one_hundred: int, + one_hundred: i32, } fn foo(x: Box) -> Box { @@ -687,10 +689,10 @@ This is an antipattern in Rust. Instead, write this: ```{rust} struct BigStruct { - one: int, - two: int, + one: i32, + two: i32, // etc - one_hundred: int, + one_hundred: i32, } fn foo(x: Box) -> BigStruct { diff --git a/src/doc/trpl/testing.md b/src/doc/trpl/testing.md index aefc7d7aa3d3c..fa40b7e84908e 100644 --- a/src/doc/trpl/testing.md +++ b/src/doc/trpl/testing.md @@ -512,7 +512,7 @@ use test::Bencher; #[bench] fn bench_xor_1000_ints(b: &mut Bencher) { b.iter(|| { - range(0u, 1000).fold(0, |old, new| old ^ new); + range(0, 1000).fold(0, |old, new| old ^ new); }); } ``` @@ -537,7 +537,7 @@ computation entirely. This could be done for the example above by adjusting the # impl X { fn iter(&self, _: F) where F: FnMut() -> T {} } let b = X; b.iter(|| { // note lack of `;` (could also use an explicit `return`). - range(0u, 1000).fold(0, |old, new| old ^ new) + range(0, 1000).fold(0, |old, new| old ^ new) }); ``` diff --git a/src/doc/trpl/threads.md b/src/doc/trpl/threads.md index df94e91067c15..1bad09b4b6e55 100644 --- a/src/doc/trpl/threads.md +++ b/src/doc/trpl/threads.md @@ -224,7 +224,7 @@ use std::sync::Future; # fn main() { # fn make_a_sandwich() {}; fn fib(n: u64) -> u64 { - // lengthy computation returning an uint + // lengthy computation returning an 64 12586269025 } @@ -249,7 +249,7 @@ computations. The workload will be distributed on the available cores. # #![allow(deprecated)] # use std::num::Float; # use std::sync::Future; -fn partial_sum(start: uint) -> f64 { +fn partial_sum(start: u64) -> f64 { let mut local_sum = 0f64; for num in range(start*100000, (start+1)*100000) { local_sum += (num as f64 + 1.0).powf(-2.0); @@ -289,7 +289,7 @@ use std::num::Float; use std::rand; use std::sync::Arc; -fn pnorm(nums: &[f64], p: uint) -> f64 { +fn pnorm(nums: &[f64], p: u64) -> f64 { nums.iter().fold(0.0, |a, b| a + b.powf(p as f64)).powf(1.0 / (p as f64)) } @@ -297,7 +297,7 @@ fn main() { let numbers = Vec::from_fn(1000000, |_| rand::random::()); let numbers_arc = Arc::new(numbers); - for num in range(1u, 10) { + for num in range(1, 10) { let thread_numbers = numbers_arc.clone(); spawn(move || { @@ -328,7 +328,7 @@ if it were local. ```{rust,ignore} # use std::rand; # use std::sync::Arc; -# fn pnorm(nums: &[f64], p: uint) -> f64 { 4.0 } +# fn pnorm(nums: &[f64], p: u64) -> f64 { 4.0 } # fn main() { # let numbers=Vec::from_fn(1000000, |_| rand::random::()); # let numbers_arc = Arc::new(numbers); @@ -357,16 +357,16 @@ each other if they panic. The simplest way of handling a panic is with the `try` function, which is similar to `spawn`, but immediately blocks and waits for the child thread to finish. `try` returns a value of type `Result>`. `Result` is an `enum` type with two variants: -`Ok` and `Err`. In this case, because the type arguments to `Result` are `int` +`Ok` and `Err`. In this case, because the type arguments to `Result` are `i32` and `()`, callers can pattern-match on a result to check whether it's an `Ok` -result with an `int` field (representing a successful result) or an `Err` result +result with an `i32` field (representing a successful result) or an `Err` result (representing termination with an error). ```{rust,ignore} # use std::thread::Thread; # fn some_condition() -> bool { false } -# fn calculate_result() -> int { 0 } -let result: Result> = Thread::spawn(move || { +# fn calculate_result() -> i32 { 0 } +let result: Result> = Thread::spawn(move || { if some_condition() { calculate_result() } else { diff --git a/src/doc/trpl/traits.md b/src/doc/trpl/traits.md index 96322296407f3..d12480d7dd9fa 100644 --- a/src/doc/trpl/traits.md +++ b/src/doc/trpl/traits.md @@ -145,7 +145,7 @@ As you can see, `print_area` is now generic, but also ensures that we have passed in the correct types. If we pass in an incorrect type: ```{rust,ignore} -print_area(5i); +print_area(5); ``` We get a compile-time error: @@ -156,14 +156,14 @@ error: failed to find an implementation of trait main::HasArea for int So far, we've only added trait implementations to structs, but you can implement a trait for any type. So technically, we _could_ implement -`HasArea` for `int`: +`HasArea` for `i32`: ```{rust} trait HasArea { fn area(&self) -> f64; } -impl HasArea for int { +impl HasArea for i32 { fn area(&self) -> f64 { println!("this is silly"); @@ -171,7 +171,7 @@ impl HasArea for int { } } -5i.area(); +5.area(); ``` It is considered poor style to implement methods on such primitive types, even @@ -264,8 +264,8 @@ it won't affect you, unless you `use` that trait. There's one more restriction on implementing traits. Either the trait or the type you're writing the `impl` for must be inside your crate. So, we could -implement the `HasArea` type for `int`, because `HasArea` is in our crate. But -if we tried to implement `Float`, a trait provided by Rust, for `int`, we could +implement the `HasArea` type for `i32`, because `HasArea` is in our crate. But +if we tried to implement `Float`, a trait provided by Rust, for `i32`, we could not, because both the trait and the type aren't in our crate. One last thing about traits: generic functions with a trait bound use diff --git a/src/doc/trpl/unsafe.md b/src/doc/trpl/unsafe.md index 075340660df15..ae5df30ff5612 100644 --- a/src/doc/trpl/unsafe.md +++ b/src/doc/trpl/unsafe.md @@ -95,7 +95,7 @@ offered by the Rust language and libraries. For example, they use-after-free; - are considered sendable (if their contents is considered sendable), so the compiler offers no assistance with ensuring their use is - thread-safe; for example, one can concurrently access a `*mut int` + thread-safe; for example, one can concurrently access a `*mut i32` from two threads without synchronization. - lack any form of lifetimes, unlike `&`, and so the compiler cannot reason about dangling pointers; and @@ -265,12 +265,12 @@ impl Drop for Unique { // A comparison between the built-in `Box` and this reimplementation fn main() { { - let mut x = Box::new(5i); + let mut x = Box::new(5); *x = 10; } // `x` is freed here { - let mut y = Unique::new(5i); + let mut y = Unique::new(5); *y.borrow_mut() = 10; } // `y` is freed here } @@ -367,7 +367,7 @@ expressions must be mutable lvalues: ``` # #![feature(asm)] # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn add(a: int, b: int) -> int { +fn add(a: i32, b: i32) -> i32 { let mut c = 0; unsafe { asm!("add $2, $0" @@ -378,7 +378,7 @@ fn add(a: int, b: int) -> int { c } # #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -# fn add(a: int, b: int) -> int { a + b } +# fn add(a: i32, b: i32) -> i32 { a + b } fn main() { assert_eq!(add(3, 14159), 14162) @@ -454,7 +454,7 @@ extern crate libc; // Entry point for this program #[start] -fn start(_argc: int, _argv: *const *const u8) -> int { +fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } @@ -480,7 +480,7 @@ compiler's name mangling too: extern crate libc; #[no_mangle] // ensure that this symbol is called `main` in the output -pub extern fn main(argc: int, argv: *const *const u8) -> int { +pub extern fn main(argc: i32, argv: *const *const u8) -> i32 { 0 } @@ -552,8 +552,8 @@ pub extern fn dot_product(a: *const u32, a_len: u32, // cannot tell the pointers are valid. let (a_slice, b_slice): (&[u32], &[u32]) = unsafe { mem::transmute(( - Slice { data: a, len: a_len as uint }, - Slice { data: b, len: b_len as uint }, + Slice { data: a, len: a_len as usize }, + Slice { data: b, len: b_len as usize }, )) }; @@ -568,13 +568,13 @@ pub extern fn dot_product(a: *const u32, a_len: u32, #[lang = "panic_fmt"] extern fn panic_fmt(args: &core::fmt::Arguments, file: &str, - line: uint) -> ! { + line: u32) -> ! { loop {} } #[lang = "stack_exhausted"] extern fn stack_exhausted() {} #[lang = "eh_personality"] extern fn eh_personality() {} -# #[start] fn start(argc: int, argv: *const *const u8) -> int { 0 } +# #[start] fn start(argc: isize, argv: *const *const u8) -> isize { 0 } # fn main() {} ``` @@ -628,7 +628,7 @@ via a declaration like extern "rust-intrinsic" { fn transmute(x: T) -> U; - fn offset(dst: *const T, offset: int) -> *const T; + fn offset(dst: *const T, offset: isize) -> *const T; } ``` @@ -665,24 +665,24 @@ extern { pub struct Box(*mut T); #[lang="exchange_malloc"] -unsafe fn allocate(size: uint, _align: uint) -> *mut u8 { +unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { let p = libc::malloc(size as libc::size_t) as *mut u8; // malloc failed - if p as uint == 0 { + if p as usize == 0 { abort(); } p } #[lang="exchange_free"] -unsafe fn deallocate(ptr: *mut u8, _size: uint, _align: uint) { +unsafe fn deallocate(ptr: *mut u8, _size: usize, _align: usize) { libc::free(ptr as *mut libc::c_void) } #[start] -fn main(argc: int, argv: *const *const u8) -> int { - let x = box 1i; +fn main(argc: isize, argv: *const *const u8) -> isize { + let x = box 1; 0 } From f979f91ae20b2da9b2414033460985d5e023fbc5 Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Fri, 16 Jan 2015 12:09:49 -0500 Subject: [PATCH 048/211] Bump LICENSE copyright year --- LICENSE-MIT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE-MIT b/LICENSE-MIT index 39d4bdb5acd31..e69282e381bc0 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2014 The Rust Project Developers +Copyright (c) 2015 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated From 0109ceaf827461d525ada1a71dcba909071d7cdc Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Fri, 16 Jan 2015 19:08:08 +0100 Subject: [PATCH 049/211] Fix typo. --- src/doc/trpl/functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/trpl/functions.md b/src/doc/trpl/functions.md index eae7fc1989506..d0ecb6067955d 100644 --- a/src/doc/trpl/functions.md +++ b/src/doc/trpl/functions.md @@ -143,7 +143,7 @@ fn foo(x: i32) -> i32 { ``` The previous definition without `return` may look a bit strange if you haven't -worked in an expression-based language before, but it becomes intutive over +worked in an expression-based language before, but it becomes intuitive over time. If this were production code, we wouldn't write it in that way anyway, we'd write this: From f12e60a5c8bacd8fb43b503000c19187aa3b09c2 Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Fri, 16 Jan 2015 21:29:43 +0100 Subject: [PATCH 050/211] Make Index trait example clearer The example of the `Index` and `IndexMut` trait contained too much `Foo`. It now contains a bit more `Bar` to make things clearer which parts are defining the type of the index. --- src/libcore/ops.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index e7eb307689fbe..7d41c3fc5a56f 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -830,28 +830,27 @@ shr_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize } /// /// # Example /// -/// A trivial implementation of `Index`. When `Foo[Foo]` happens, it ends up +/// A trivial implementation of `Index`. When `Foo[Bar]` happens, it ends up /// calling `index`, and therefore, `main` prints `Indexing!`. /// /// ``` -/// #![feature(associated_types)] -/// /// use std::ops::Index; /// /// #[derive(Copy)] /// struct Foo; +/// struct Bar; /// -/// impl Index for Foo { +/// impl Index for Foo { /// type Output = Foo; /// -/// fn index<'a>(&'a self, _index: &Foo) -> &'a Foo { +/// fn index<'a>(&'a self, _index: &Bar) -> &'a Foo { /// println!("Indexing!"); /// self /// } /// } /// /// fn main() { -/// Foo[Foo]; +/// Foo[Bar]; /// } /// ``` #[lang="index"] @@ -867,28 +866,27 @@ pub trait Index { /// /// # Example /// -/// A trivial implementation of `IndexMut`. When `Foo[Foo]` happens, it ends up +/// A trivial implementation of `IndexMut`. When `Foo[Bar]` happens, it ends up /// calling `index_mut`, and therefore, `main` prints `Indexing!`. /// /// ``` -/// #![feature(associated_types)] -/// /// use std::ops::IndexMut; /// /// #[derive(Copy)] /// struct Foo; +/// struct Bar; /// -/// impl IndexMut for Foo { +/// impl IndexMut for Foo { /// type Output = Foo; /// -/// fn index_mut<'a>(&'a mut self, _index: &Foo) -> &'a mut Foo { +/// fn index_mut<'a>(&'a mut self, _index: &Bar) -> &'a mut Foo { /// println!("Indexing!"); /// self /// } /// } /// /// fn main() { -/// &mut Foo[Foo]; +/// &mut Foo[Bar]; /// } /// ``` #[lang="index_mut"] From d5091c9cc9d1527824e11a3b9c5d9e5fea0e0c20 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 16 Jan 2015 15:30:27 -0500 Subject: [PATCH 051/211] Fix up titles of TRPL chapters --- src/doc/trpl/crates-and-modules.md | 2 +- src/doc/trpl/error-handling.md | 2 +- src/doc/trpl/ffi.md | 2 +- src/doc/trpl/macros.md | 2 +- src/doc/trpl/ownership.md | 2 +- src/doc/trpl/plugins.md | 2 +- src/doc/trpl/pointers.md | 2 +- src/doc/trpl/testing.md | 2 +- src/doc/trpl/unsafe.md | 2 +- src/doc/trpl/variable-bindings.md | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/doc/trpl/crates-and-modules.md b/src/doc/trpl/crates-and-modules.md index c12090e2a614c..6c5c14fe3111d 100644 --- a/src/doc/trpl/crates-and-modules.md +++ b/src/doc/trpl/crates-and-modules.md @@ -1,4 +1,4 @@ -% The Rust Crates and Modules Guide +% Crates and Modules When a project starts getting large, it's considered a good software engineering practice to split it up into a bunch of smaller pieces, and then diff --git a/src/doc/trpl/error-handling.md b/src/doc/trpl/error-handling.md index 4b1c92239aed3..5754a93d09783 100644 --- a/src/doc/trpl/error-handling.md +++ b/src/doc/trpl/error-handling.md @@ -1,4 +1,4 @@ -% Error Handling in Rust +% Error Handling > The best-laid plans of mice and men > Often go awry diff --git a/src/doc/trpl/ffi.md b/src/doc/trpl/ffi.md index ab112280a69a5..e1350a670b1c6 100644 --- a/src/doc/trpl/ffi.md +++ b/src/doc/trpl/ffi.md @@ -1,4 +1,4 @@ -% The Rust Foreign Function Interface Guide +% Foreign Function Interface # Introduction diff --git a/src/doc/trpl/macros.md b/src/doc/trpl/macros.md index 46a4af0d25135..e0bccb1b86f32 100644 --- a/src/doc/trpl/macros.md +++ b/src/doc/trpl/macros.md @@ -1,4 +1,4 @@ -% The Rust Macros Guide +% Macros # Introduction diff --git a/src/doc/trpl/ownership.md b/src/doc/trpl/ownership.md index 6e125e218eaac..b9db99d258ef2 100644 --- a/src/doc/trpl/ownership.md +++ b/src/doc/trpl/ownership.md @@ -1,4 +1,4 @@ -% The Rust Ownership Guide +% Ownership This guide presents Rust's ownership system. This is one of Rust's most unique and compelling features, with which Rust developers should become quite diff --git a/src/doc/trpl/plugins.md b/src/doc/trpl/plugins.md index d710c2fe4e756..6e8e2c7ffe292 100644 --- a/src/doc/trpl/plugins.md +++ b/src/doc/trpl/plugins.md @@ -1,4 +1,4 @@ -% The Rust Compiler Plugins Guide +% Compiler Plugins
diff --git a/src/doc/trpl/pointers.md b/src/doc/trpl/pointers.md index c918a80a86f01..387bdac18ac05 100644 --- a/src/doc/trpl/pointers.md +++ b/src/doc/trpl/pointers.md @@ -1,4 +1,4 @@ -% The Rust Pointer Guide +% Pointers Rust's pointers are one of its more unique and compelling features. Pointers are also one of the more confusing topics for newcomers to Rust. They can also diff --git a/src/doc/trpl/testing.md b/src/doc/trpl/testing.md index fa40b7e84908e..1c93fd351b6e4 100644 --- a/src/doc/trpl/testing.md +++ b/src/doc/trpl/testing.md @@ -1,4 +1,4 @@ -% The Rust Testing Guide +% Testing > Program testing can be a very effective way to show the presence of bugs, but > it is hopelessly inadequate for showing their absence. diff --git a/src/doc/trpl/unsafe.md b/src/doc/trpl/unsafe.md index ae5df30ff5612..406af336653dd 100644 --- a/src/doc/trpl/unsafe.md +++ b/src/doc/trpl/unsafe.md @@ -1,4 +1,4 @@ -% Writing Unsafe and Low-Level Code in Rust +% Unsafe and Low-Level Code # Introduction diff --git a/src/doc/trpl/variable-bindings.md b/src/doc/trpl/variable-bindings.md index e57fc7a120653..41c0e9de9b505 100644 --- a/src/doc/trpl/variable-bindings.md +++ b/src/doc/trpl/variable-bindings.md @@ -1,4 +1,4 @@ -% Variable bindings +% Variable Bindings The first thing we'll learn about are *variable bindings*. They look like this: From d1ab3799bdb9ac2bad8125a603ad1a4f62a6f544 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 16 Jan 2015 16:42:53 -0500 Subject: [PATCH 052/211] Update syntax of ignored test. --- src/test/compile-fail/unsupported-cast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/compile-fail/unsupported-cast.rs b/src/test/compile-fail/unsupported-cast.rs index 205c912f5a098..ca17c898ec37c 100644 --- a/src/test/compile-fail/unsupported-cast.rs +++ b/src/test/compile-fail/unsupported-cast.rs @@ -14,5 +14,5 @@ extern crate libc; fn main() { - println!("{}", 1.0 as *libc::FILE); // Can't cast float to foreign. + println!("{:?}", 1.0 as *const libc::FILE); // Can't cast float to foreign. } From 433ea0bd555977ae4defee7b6aeb65a05be747f2 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 16 Jan 2015 15:27:44 -0500 Subject: [PATCH 053/211] Add C -> Rust example to FFI chapter of the book. Fixes #10489. --- src/doc/trpl/ffi.md | 74 ++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/src/doc/trpl/ffi.md b/src/doc/trpl/ffi.md index e1350a670b1c6..b4c181b7e72d9 100644 --- a/src/doc/trpl/ffi.md +++ b/src/doc/trpl/ffi.md @@ -11,7 +11,7 @@ snappy includes a C interface (documented in The following is a minimal example of calling a foreign function which will compile if snappy is installed: -~~~~no_run +```no_run extern crate libc; use libc::size_t; @@ -24,7 +24,7 @@ fn main() { let x = unsafe { snappy_max_compressed_length(100) }; println!("max compressed length of a 100 byte buffer: {}", x); } -~~~~ +``` The `extern` block is a list of function signatures in a foreign library, in this case with the platform's C ABI. The `#[link(...)]` attribute is used to @@ -44,7 +44,7 @@ keeping the binding correct at runtime. The `extern` block can be extended to cover the entire snappy API: -~~~~no_run +```no_run extern crate libc; use libc::{c_int, size_t}; @@ -66,7 +66,7 @@ extern { compressed_length: size_t) -> c_int; } # fn main() {} -~~~~ +``` # Creating a safe interface @@ -79,7 +79,7 @@ vectors as pointers to memory. Rust's vectors are guaranteed to be a contiguous length is number of elements currently contained, and the capacity is the total size in elements of the allocated memory. The length is less than or equal to the capacity. -~~~~ +``` # extern crate libc; # use libc::{c_int, size_t}; # unsafe fn snappy_validate_compressed_buffer(_: *const u8, _: size_t) -> c_int { 0 } @@ -89,7 +89,7 @@ pub fn validate_compressed_buffer(src: &[u8]) -> bool { snappy_validate_compressed_buffer(src.as_ptr(), src.len() as size_t) == 0 } } -~~~~ +``` The `validate_compressed_buffer` wrapper above makes use of an `unsafe` block, but it makes the guarantee that calling it is safe for all inputs by leaving off `unsafe` from the function @@ -103,7 +103,7 @@ required capacity to hold the compressed output. The vector can then be passed t `snappy_compress` function as an output parameter. An output parameter is also passed to retrieve the true length after compression for setting the length. -~~~~ +``` # extern crate libc; # use libc::{size_t, c_int}; # unsafe fn snappy_compress(a: *const u8, b: size_t, c: *mut u8, @@ -124,12 +124,12 @@ pub fn compress(src: &[u8]) -> Vec { dst } } -~~~~ +``` Decompression is similar, because snappy stores the uncompressed size as part of the compression format and `snappy_uncompressed_length` will retrieve the exact buffer size required. -~~~~ +``` # extern crate libc; # use libc::{size_t, c_int}; # unsafe fn snappy_uncompress(compressed: *const u8, @@ -159,7 +159,7 @@ pub fn uncompress(src: &[u8]) -> Option> { } } } -~~~~ +``` For reference, the examples used here are also available as an [library on GitHub](https://github.com/thestinger/rust-snappy). @@ -185,7 +185,7 @@ A basic example is: Rust code: -~~~~no_run +```no_run extern fn callback(a: i32) { println!("I'm called from C with value {0}", a); } @@ -202,11 +202,11 @@ fn main() { trigger_callback(); // Triggers the callback } } -~~~~ +``` C code: -~~~~c +```c typedef void (*rust_callback)(int32_t); rust_callback cb; @@ -218,7 +218,7 @@ int32_t register_callback(rust_callback callback) { void trigger_callback() { cb(7); // Will call callback(7) in Rust } -~~~~ +``` In this example Rust's `main()` will call `trigger_callback()` in C, which would, in turn, call back to `callback()` in Rust. @@ -238,7 +238,7 @@ referenced Rust object. Rust code: -~~~~no_run +```no_run #[repr(C)] struct RustObject { a: i32, @@ -269,11 +269,11 @@ fn main() { trigger_callback(); } } -~~~~ +``` C code: -~~~~c +```c typedef void (*rust_callback)(void*, int32_t); void* cb_target; rust_callback cb; @@ -287,7 +287,7 @@ int32_t register_callback(void* callback_target, rust_callback callback) { void trigger_callback() { cb(cb_target, 7); // Will call callback(&rustObject, 7) in Rust } -~~~~ +``` ## Asynchronous callbacks @@ -366,13 +366,13 @@ the `link_args` attribute. This attribute is applied to `extern` blocks and specifies raw flags which need to get passed to the linker when producing an artifact. An example usage would be: -~~~ no_run +``` no_run #![feature(link_args)] #[link_args = "-foo -bar -baz"] extern {} # fn main() {} -~~~ +``` Note that this feature is currently hidden behind the `feature(link_args)` gate because this is not a sanctioned way of performing linking. Right now rustc @@ -393,9 +393,9 @@ the compiler that the unsafety does not leak out of the block. Unsafe functions, on the other hand, advertise it to the world. An unsafe function is written like this: -~~~~ +``` unsafe fn kaboom(ptr: *const int) -> int { *ptr } -~~~~ +``` This function can only be called from an `unsafe` block or another `unsafe` function. @@ -405,7 +405,7 @@ Foreign APIs often export a global variable which could do something like track global state. In order to access these variables, you declare them in `extern` blocks with the `static` keyword: -~~~no_run +```no_run extern crate libc; #[link(name = "readline")] @@ -417,13 +417,13 @@ fn main() { println!("You have readline version {} installed.", rl_readline_version as int); } -~~~ +``` Alternatively, you may need to alter global state provided by a foreign interface. To do this, statics can be declared with `mut` so rust can mutate them. -~~~no_run +```no_run extern crate libc; use std::ffi::CString; @@ -440,7 +440,7 @@ fn main() { // get a line, process it unsafe { rl_prompt = ptr::null(); } } -~~~ +``` # Foreign calling conventions @@ -448,7 +448,7 @@ Most foreign code exposes a C ABI, and Rust uses the platform's C calling conven calling foreign functions. Some foreign functions, most notably the Windows API, use other calling conventions. Rust provides a way to tell the compiler which convention to use: -~~~~ +``` extern crate libc; #[cfg(all(target_os = "win32", target_arch = "x86"))] @@ -458,7 +458,7 @@ extern "stdcall" { fn SetEnvironmentVariableA(n: *const u8, v: *const u8) -> libc::c_int; } # fn main() { } -~~~~ +``` This applies to the entire `extern` block. The list of supported ABI constraints are: @@ -518,3 +518,21 @@ with one of the non-nullable types, it is represented as a single pointer, and the non-data variant is represented as the null pointer. So `Option c_int>` is how one represents a nullable function pointer using the C ABI. + +# Calling Rust code from C + +You may wish to compile Rust code in a way so that it can be called from C. This is +fairly easy, but requires a few things: + +``` +#[no_mangle] +pub extern fn hello_rust() -> *const u8 { + "Hello, world!\0".as_ptr() +} +``` + +The `extern` makes this function adhere to the C calling convention, as +discussed above in "[Foreign Calling +Conventions](guide-ffi.html#foreign-calling-conventions)". The `no_mangle` +attribute turns off Rust's name mangling, so that it is easier to link to. + From 02968389dcbc32c757ff5d8a3e7bf96c5d9d4225 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 16 Jan 2015 16:21:06 -0500 Subject: [PATCH 054/211] Beef up Copy documentation Fixes #21249 Fixes #11540 --- src/libcore/marker.rs | 98 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 6d272f91698e7..99ba9666cd296 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -42,6 +42,104 @@ pub trait Sized { } /// Types that can be copied by simply copying bits (i.e. `memcpy`). +/// +/// By default, variable bindings have 'move semantics.' In other +/// words: +/// +/// ``` +/// #[derive(Show)] +/// struct Foo; +/// +/// let x = Foo; +/// +/// let y = x; +/// +/// // `x` has moved into `y`, and so cannot be used +/// +/// // println!("{:?}", x); // error: use of moved value +/// ``` +/// +/// However, if a type implements `Copy`, it instead has 'copy semantics': +/// +/// ``` +/// // we can just derive a `Copy` implementation +/// #[derive(Show, Copy)] +/// struct Foo; +/// +/// let x = Foo; +/// +/// let y = x; +/// +/// // `y` is a copy of `x` +/// +/// println!("{:?}", x); // A-OK! +/// ``` +/// +/// It's important to note that in these two examples, the only difference is if you are allowed to +/// access `x` after the assignment: a move is also a bitwise copy under the hood. +/// +/// ## When can my type be `Copy`? +/// +/// A type can implement `Copy` if all of its components implement `Copy`. For example, this +/// `struct` can be `Copy`: +/// +/// ``` +/// struct Point { +/// x: i32, +/// y: i32, +/// } +/// ``` +/// +/// A `struct` can be `Copy`, and `i32` is `Copy`, so therefore, `Point` is eligible to be `Copy`. +/// +/// ``` +/// # struct Point; +/// struct PointList { +/// points: Vec, +/// } +/// ``` +/// +/// The `PointList` `struct` cannot implement `Copy`, because `Vec` is not `Copy`. If we +/// attempt to derive a `Copy` implementation, we'll get an error. +/// +/// ```text +/// error: the trait `Copy` may not be implemented for this type; field `points` does not implement +/// `Copy` +/// ``` +/// +/// ## How can I implement `Copy`? +/// +/// There are two ways to implement `Copy` on your type: +/// +/// ``` +/// #[derive(Copy)] +/// struct MyStruct; +/// ``` +/// +/// and +/// +/// ``` +/// struct MyStruct; +/// impl Copy for MyStruct {} +/// ``` +/// +/// There is a small difference between the two: the `derive` strategy will also place a `Copy` +/// bound on type parameters, which isn't always desired. +/// +/// ## When can my type _not_ be `Copy`? +/// +/// Some types can't be copied safely. For example, copying `&mut T` would create an aliased +/// mutable reference, and copying `String` would result in two attempts to free the same buffer. +/// +/// Generalizing the latter case, any type implementing `Drop` can't be `Copy`, because it's +/// managing some resource besides its own `size_of::()` bytes. +/// +/// ## When should my type be `Copy`? +/// +/// Generally speaking, if your type _can_ implement `Copy`, it should. There's one important thing +/// to consider though: if you think your type may _not_ be able to implement `Copy` in the future, +/// then it might be prudent to not implement `Copy`. This is because removing `Copy` is a breaking +/// change: that second example would fail to compile if we made `Foo` non-`Copy`. #[stable] #[lang="copy"] pub trait Copy { From 34fa70fba5425cbbb96bce783e9fd5c23dd9b471 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 16 Jan 2015 12:20:03 -0800 Subject: [PATCH 055/211] std: Move the bitflags! macro to a gated crate In accordance with [collections reform part 2][rfc] this macro has been moved to an external [bitflags crate][crate] which is [available though crates.io][cratesio]. Inside the standard distribution the macro has been moved to a crate called `rustc_bitflags` for current users to continue using. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0509-collections-reform-part-2.md [crate]: https://github.com/rust-lang/bitflags [cratesio]: http://crates.io/crates/bitflags The major user of `bitflags!` in terms of a public-facing possibly-stable API today is the `FilePermissions` structure inside of `std::io`. This user, however, will likely no longer use `bitflags!` after I/O reform has landed. To prevent breaking APIs today, this structure remains as-is. Current users of the `bitflags!` macro should add this to their `Cargo.toml`: bitflags = "0.1" and this to their crate root: #[macro_use] extern crate bitflags; Due to the removal of a public macro, this is a: [breaking-change] --- mk/crates.mk | 7 +++++-- src/librustc/lib.rs | 1 + .../bitflags.rs => librustc_bitflags/lib.rs} | 14 ++++++++++++-- src/librustc_llvm/lib.rs | 1 + src/librustc_resolve/lib.rs | 1 + src/libstd/lib.rs | 7 +++---- src/libsyntax/lib.rs | 1 + 7 files changed, 24 insertions(+), 8 deletions(-) rename src/{libstd/bitflags.rs => librustc_bitflags/lib.rs} (97%) diff --git a/mk/crates.mk b/mk/crates.mk index d6cc3598bd51d..5957405f0f9ec 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -52,7 +52,7 @@ TARGET_CRATES := libc std flate arena term \ serialize getopts collections test rand \ log regex graphviz core rbml alloc \ - unicode + unicode rustc_bitflags RUSTC_CRATES := rustc rustc_typeck rustc_borrowck rustc_resolve rustc_driver \ rustc_trans rustc_back rustc_llvm rustc_privacy HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc fmt_macros @@ -64,7 +64,8 @@ DEPS_libc := core DEPS_unicode := core DEPS_alloc := core libc native:jemalloc DEPS_std := core libc rand alloc collections unicode \ - native:rust_builtin native:backtrace native:rustrt_native + native:rust_builtin native:backtrace native:rustrt_native \ + rustc_bitflags DEPS_graphviz := std DEPS_syntax := std term serialize log fmt_macros arena libc DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \ @@ -83,6 +84,7 @@ DEPS_rustc_llvm := native:rustllvm libc std DEPS_rustc_back := std syntax rustc_llvm flate log libc DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \ test +DEPS_rustc_bitflags := core DEPS_flate := std native:miniz DEPS_arena := std DEPS_graphviz := std @@ -114,6 +116,7 @@ ONLY_RLIB_alloc := 1 ONLY_RLIB_rand := 1 ONLY_RLIB_collections := 1 ONLY_RLIB_unicode := 1 +ONLY_RLIB_rustc_bitflags := 1 ################################################################################ # You should not need to edit below this line diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index b79f19413ef92..a283fdf36a96b 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -44,6 +44,7 @@ extern crate rbml; extern crate collections; #[macro_use] extern crate log; #[macro_use] extern crate syntax; +#[macro_use] #[no_link] extern crate rustc_bitflags; extern crate "serialize" as rustc_serialize; // used by deriving diff --git a/src/libstd/bitflags.rs b/src/librustc_bitflags/lib.rs similarity index 97% rename from src/libstd/bitflags.rs rename to src/librustc_bitflags/lib.rs index 3a059766fef8e..c47a525552ded 100644 --- a/src/libstd/bitflags.rs +++ b/src/librustc_bitflags/lib.rs @@ -8,10 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![crate_name = "rustc_bitflags"] #![unstable] +#![staged_api] +#![crate_type = "rlib"] +#![no_std] //! A typesafe bitmask flag generator. +#[cfg(test)] #[macro_use] extern crate std; + /// The `bitflags!` macro generates a `struct` that holds a set of C-style /// bitmask flags. It is useful for creating typesafe wrappers for C APIs. /// @@ -21,6 +27,8 @@ /// # Example /// /// ```{.rust} +/// #[macro_use] extern crate rustc_bitflags; +/// /// bitflags! { /// flags Flags: u32 { /// const FLAG_A = 0b00000001, @@ -45,6 +53,8 @@ /// The generated `struct`s can also be extended with type and trait implementations: /// /// ```{.rust} +/// #[macro_use] extern crate rustc_bitflags; +/// /// use std::fmt; /// /// bitflags! { @@ -273,8 +283,8 @@ macro_rules! bitflags { #[cfg(test)] #[allow(non_upper_case_globals)] mod tests { - use hash::{self, SipHasher}; - use option::Option::{Some, None}; + use std::hash::{self, SipHasher}; + use std::option::Option::{Some, None}; bitflags! { #[doc = "> The first principle is that you must not fool yourself — and"] diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 0d1836e014435..59676fa3504a9 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -28,6 +28,7 @@ #![allow(unknown_features)] #![feature(int_uint)] extern crate libc; +#[macro_use] #[no_link] extern crate rustc_bitflags; pub use self::OtherAttribute::*; pub use self::SpecialAttribute::*; diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 7743a437858d2..22064a35058ec 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -23,6 +23,7 @@ #[macro_use] extern crate log; #[macro_use] extern crate syntax; +#[macro_use] #[no_link] extern crate rustc_bitflags; extern crate rustc; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 182344452a484..2553bbdf52310 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -111,7 +111,7 @@ #![feature(box_syntax)] #![feature(old_impl_check)] #![feature(optin_builtin_traits)] -#![allow(unknown_features)] #![feature(int_uint)] +#![feature(int_uint)] // Don't link to std. We are std. #![no_std] @@ -136,6 +136,8 @@ extern crate alloc; extern crate unicode; extern crate libc; +#[macro_use] #[no_link] extern crate rustc_bitflags; + // Make std testable by not duplicating lang items. See #2912 #[cfg(test)] extern crate "std" as realstd; #[cfg(test)] pub use realstd::marker; @@ -181,9 +183,6 @@ pub use unicode::char; #[macro_use] mod macros; -#[macro_use] -pub mod bitflags; - mod rtdeps; /* The Prelude. */ diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 9c947f422e9a9..096e96b003bb0 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -35,6 +35,7 @@ extern crate serialize; extern crate term; extern crate libc; #[macro_use] extern crate log; +#[macro_use] #[no_link] extern crate rustc_bitflags; extern crate "serialize" as rustc_serialize; // used by deriving From 0c26524134fe32ae1e9f07a128bee729278ac964 Mon Sep 17 00:00:00 2001 From: Nick Howell Date: Sun, 4 Jan 2015 12:31:02 -0500 Subject: [PATCH 056/211] doc: Remove extra whitespace in the middle of lines to provide alignment "Idiomatic code should not use extra whitespace in the middle of a line to provide alignment." http://aturon.github.io/style/whitespace.html I realize the linked page still needs an RFC, but the docs should be written in accordance with the guidelines nevertheless. --- src/doc/trpl/arrays-vectors-and-slices.md | 8 ++-- src/doc/trpl/compound-data-types.md | 4 +- src/doc/trpl/error-handling.md | 12 +++--- src/doc/trpl/guessing-game.md | 52 +++++++++++------------ src/doc/trpl/iterators.md | 2 +- src/doc/trpl/looping.md | 2 +- src/doc/trpl/match.md | 12 +++--- src/doc/trpl/ownership.md | 26 ++++++------ src/doc/trpl/patterns.md | 4 +- src/doc/trpl/pointers.md | 2 +- src/doc/trpl/standard-input.md | 12 +++--- src/doc/trpl/unsafe.md | 4 +- 12 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/doc/trpl/arrays-vectors-and-slices.md b/src/doc/trpl/arrays-vectors-and-slices.md index e7ac55bfbd30d..2df769b3c2c67 100644 --- a/src/doc/trpl/arrays-vectors-and-slices.md +++ b/src/doc/trpl/arrays-vectors-and-slices.md @@ -5,7 +5,7 @@ things. The most basic is the *array*, a fixed-size list of elements of the same type. By default, arrays are immutable. ```{rust} -let a = [1, 2, 3]; // a: [i32; 3] +let a = [1, 2, 3]; // a: [i32; 3] let mut m = [1, 2, 3]; // mut m: [i32; 3] ``` @@ -68,7 +68,7 @@ let mut nums = vec![1, 2, 3]; // mut nums: Vec nums.push(4); -println!("The length of nums is now {}", nums.len()); // Prints 4 +println!("The length of nums is now {}", nums.len()); // Prints 4 ``` Vectors have many more useful methods. @@ -82,10 +82,10 @@ arrays: ```{rust} let a = [0, 1, 2, 3, 4]; -let middle = &a[1..4]; // A slice of a: just the elements 1, 2, and 3 +let middle = &a[1..4]; // A slice of a: just the elements 1, 2, and 3 for e in middle.iter() { - println!("{}", e); // Prints 1, 2, 3 + println!("{}", e); // Prints 1, 2, 3 } ``` diff --git a/src/doc/trpl/compound-data-types.md b/src/doc/trpl/compound-data-types.md index 5ad9fcd41f554..f584fe8d7a323 100644 --- a/src/doc/trpl/compound-data-types.md +++ b/src/doc/trpl/compound-data-types.md @@ -51,7 +51,7 @@ arity and contained types. ```rust let mut x = (1, 2); // x: (i32, i32) -let y = (2, 3); // y: (i32, i32) +let y = (2, 3); // y: (i32, i32) x = y; ``` @@ -156,7 +156,7 @@ These two will not be equal, even if they have the same values: ```{rust} # struct Color(i32, i32, i32); # struct Point(i32, i32, i32); -let black = Color(0, 0, 0); +let black = Color(0, 0, 0); let origin = Point(0, 0, 0); ``` diff --git a/src/doc/trpl/error-handling.md b/src/doc/trpl/error-handling.md index 5754a93d09783..d66142edf3fc9 100644 --- a/src/doc/trpl/error-handling.md +++ b/src/doc/trpl/error-handling.md @@ -60,12 +60,12 @@ fn probability(_: &Event) -> f64 { fn descriptive_probability(event: Event) -> &'static str { match probability(&event) { - 1.00 => "certain", - 0.00 => "impossible", + 1.00 => "certain", + 0.00 => "impossible", 0.00 ... 0.25 => "very unlikely", 0.25 ... 0.50 => "unlikely", 0.50 ... 0.75 => "likely", - 0.75 ... 1.00 => "very likely", + 0.75 ... 1.00 => "very likely", } } @@ -97,12 +97,12 @@ fn probability(_: &Event) -> f64 { fn descriptive_probability(event: Event) -> &'static str { match probability(&event) { - 1.00 => "certain", - 0.00 => "impossible", + 1.00 => "certain", + 0.00 => "impossible", 0.00 ... 0.25 => "very unlikely", 0.25 ... 0.50 => "unlikely", 0.50 ... 0.75 => "likely", - 0.75 ... 1.00 => "very likely", + 0.75 ... 1.00 => "very likely", _ => unreachable!() } } diff --git a/src/doc/trpl/guessing-game.md b/src/doc/trpl/guessing-game.md index 474e7db6942e1..6f67c88f2c0cc 100644 --- a/src/doc/trpl/guessing-game.md +++ b/src/doc/trpl/guessing-game.md @@ -297,9 +297,9 @@ fn main() { println!("You guessed: {}", input); match cmp(input, secret_number) { - Ordering::Less => println!("Too small!"), + Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), - Ordering::Equal => println!("You win!"), + Ordering::Equal => println!("You win!"), } } @@ -352,9 +352,9 @@ fn main() { println!("You guessed: {}", input); match cmp(input, secret_number) { - Ordering::Less => println!("Too small!"), + Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), - Ordering::Equal => println!("You win!"), + Ordering::Equal => println!("You win!"), } } @@ -422,8 +422,8 @@ In this case, we say `x` is a `u32` explicitly, so Rust is able to properly tell `random()` what to generate. In a similar fashion, both of these work: ```{rust,ignore} -let input_num = "5".parse::(); // input_num: Option -let input_num: Option = "5".parse(); // input_num: Option +let input_num = "5".parse::(); // input_num: Option +let input_num: Option = "5".parse(); // input_num: Option ``` Anyway, with us now converting our input to a number, our code looks like this: @@ -450,9 +450,9 @@ fn main() { println!("You guessed: {}", input_num); match cmp(input_num, secret_number) { - Ordering::Less => println!("Too small!"), + Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), - Ordering::Equal => println!("You win!"), + Ordering::Equal => println!("You win!"), } } @@ -499,7 +499,7 @@ fn main() { let num = match input_num { Some(num) => num, - None => { + None => { println!("Please input a number!"); return; } @@ -509,9 +509,9 @@ fn main() { println!("You guessed: {}", num); match cmp(num, secret_number) { - Ordering::Less => println!("Too small!"), + Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), - Ordering::Equal => println!("You win!"), + Ordering::Equal => println!("You win!"), } } @@ -566,7 +566,7 @@ fn main() { let num = match input_num { Some(num) => num, - None => { + None => { println!("Please input a number!"); return; } @@ -576,9 +576,9 @@ fn main() { println!("You guessed: {}", num); match cmp(num, secret_number) { - Ordering::Less => println!("Too small!"), + Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), - Ordering::Equal => println!("You win!"), + Ordering::Equal => println!("You win!"), } } @@ -642,7 +642,7 @@ fn main() { let num = match input_num { Some(num) => num, - None => { + None => { println!("Please input a number!"); return; } @@ -652,9 +652,9 @@ fn main() { println!("You guessed: {}", num); match cmp(num, secret_number) { - Ordering::Less => println!("Too small!"), + Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), - Ordering::Equal => println!("You win!"), + Ordering::Equal => println!("You win!"), } } } @@ -718,7 +718,7 @@ fn main() { let num = match input_num { Some(num) => num, - None => { + None => { println!("Please input a number!"); return; } @@ -728,9 +728,9 @@ fn main() { println!("You guessed: {}", num); match cmp(num, secret_number) { - Ordering::Less => println!("Too small!"), + Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), - Ordering::Equal => { + Ordering::Equal => { println!("You win!"); return; }, @@ -774,7 +774,7 @@ fn main() { let num = match input_num { Some(num) => num, - None => { + None => { println!("Please input a number!"); continue; } @@ -784,9 +784,9 @@ fn main() { println!("You guessed: {}", num); match cmp(num, secret_number) { - Ordering::Less => println!("Too small!"), + Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), - Ordering::Equal => { + Ordering::Equal => { println!("You win!"); return; }, @@ -851,7 +851,7 @@ fn main() { let num = match input_num { Some(num) => num, - None => { + None => { println!("Please input a number!"); continue; } @@ -861,9 +861,9 @@ fn main() { println!("You guessed: {}", num); match cmp(num, secret_number) { - Ordering::Less => println!("Too small!"), + Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), - Ordering::Equal => { + Ordering::Equal => { println!("You win!"); return; }, diff --git a/src/doc/trpl/iterators.md b/src/doc/trpl/iterators.md index 62cc1d5f62aec..75b3f8b06fc3b 100644 --- a/src/doc/trpl/iterators.md +++ b/src/doc/trpl/iterators.md @@ -143,7 +143,7 @@ let greater_than_forty_two = range(0, 100) match greater_than_forty_two { Some(_) => println!("We got some numbers!"), - None => println!("No numbers found :("), + None => println!("No numbers found :("), } ``` diff --git a/src/doc/trpl/looping.md b/src/doc/trpl/looping.md index c01df64ef8f65..28f02b1ffe152 100644 --- a/src/doc/trpl/looping.md +++ b/src/doc/trpl/looping.md @@ -54,7 +54,7 @@ The other kind of looping construct in Rust is the `while` loop. It looks like this: ```{rust} -let mut x = 5; // mut x: u32 +let mut x = 5; // mut x: u32 let mut done = false; // mut done: bool while !done { diff --git a/src/doc/trpl/match.md b/src/doc/trpl/match.md index 1833b05591be9..73bc775a1b290 100644 --- a/src/doc/trpl/match.md +++ b/src/doc/trpl/match.md @@ -84,9 +84,9 @@ fn main() { let y = 10; match cmp(x, y) { - Ordering::Less => println!("less"), + Ordering::Less => println!("less"), Ordering::Greater => println!("greater"), - Ordering::Equal => println!("equal"), + Ordering::Equal => println!("equal"), } } ``` @@ -112,12 +112,12 @@ fn main() { match x { OptionalInt::Value(n) => println!("x is {}", n), - OptionalInt::Missing => println!("x is missing!"), + OptionalInt::Missing => println!("x is missing!"), } match y { OptionalInt::Value(n) => println!("y is {}", n), - OptionalInt::Missing => println!("y is missing!"), + OptionalInt::Missing => println!("y is missing!"), } } ``` @@ -146,9 +146,9 @@ fn main() { let y = 10; println!("{}", match cmp(x, y) { - Ordering::Less => "less", + Ordering::Less => "less", Ordering::Greater => "greater", - Ordering::Equal => "equal", + Ordering::Equal => "equal", }); } ``` diff --git a/src/doc/trpl/ownership.md b/src/doc/trpl/ownership.md index b9db99d258ef2..9ced5bb656c42 100644 --- a/src/doc/trpl/ownership.md +++ b/src/doc/trpl/ownership.md @@ -517,31 +517,31 @@ Here are some examples of functions with elided lifetimes, and the version of what the elided lifetimes are expand to: ```{rust,ignore} -fn print(s: &str); // elided -fn print<'a>(s: &'a str); // expanded +fn print(s: &str); // elided +fn print<'a>(s: &'a str); // expanded -fn debug(lvl: u32, s: &str); // elided -fn debug<'a>(lvl: u32, s: &'a str); // expanded +fn debug(lvl: u32, s: &str); // elided +fn debug<'a>(lvl: u32, s: &'a str); // expanded // In the preceeding example, `lvl` doesn't need a lifetime because it's not a // reference (`&`). Only things relating to references (such as a `struct` // which contains a reference) need lifetimes. -fn substr(s: &str, until: u32) -> &str; // elided -fn substr<'a>(s: &'a str, until: u32) -> &'a str; // expanded +fn substr(s: &str, until: u32) -> &str; // elided +fn substr<'a>(s: &'a str, until: u32) -> &'a str; // expanded -fn get_str() -> &str; // ILLEGAL, no inputs +fn get_str() -> &str; // ILLEGAL, no inputs -fn frob(s: &str, t: &str) -> &str; // ILLEGAL, two inputs +fn frob(s: &str, t: &str) -> &str; // ILLEGAL, two inputs -fn get_mut(&mut self) -> &mut T; // elided -fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded +fn get_mut(&mut self) -> &mut T; // elided +fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded -fn args(&mut self, args: &[T]) -> &mut Command // elided +fn args(&mut self, args: &[T]) -> &mut Command // elided fn args<'a, 'b, T:ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command // expanded -fn new(buf: &mut [u8]) -> BufWriter; // elided -fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> // expanded +fn new(buf: &mut [u8]) -> BufWriter; // elided +fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> // expanded ``` # Related Resources diff --git a/src/doc/trpl/patterns.md b/src/doc/trpl/patterns.md index 4992c49c991d7..5c7b406a6fc43 100644 --- a/src/doc/trpl/patterns.md +++ b/src/doc/trpl/patterns.md @@ -68,7 +68,7 @@ let x = OptionalInt::Value(5); match x { OptionalInt::Value(..) => println!("Got an int!"), - OptionalInt::Missing => println!("No such luck."), + OptionalInt::Missing => println!("No such luck."), } ``` @@ -85,7 +85,7 @@ let x = OptionalInt::Value(5); match x { OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"), OptionalInt::Value(..) => println!("Got an int!"), - OptionalInt::Missing => println!("No such luck."), + OptionalInt::Missing => println!("No such luck."), } ``` diff --git a/src/doc/trpl/pointers.md b/src/doc/trpl/pointers.md index 387bdac18ac05..d74c10b814507 100644 --- a/src/doc/trpl/pointers.md +++ b/src/doc/trpl/pointers.md @@ -463,7 +463,7 @@ fn succ(x: &i32) -> i32 { *x + 1 } let ref_x = &5; let box_x = Box::new(5); -let rc_x = Rc::new(5); +let rc_x = Rc::new(5); succ(ref_x); succ(&*box_x); diff --git a/src/doc/trpl/standard-input.md b/src/doc/trpl/standard-input.md index c4d171bb3a900..7145139bba57e 100644 --- a/src/doc/trpl/standard-input.md +++ b/src/doc/trpl/standard-input.md @@ -83,12 +83,12 @@ fn main() { match x { OptionalInt::Value(n) => println!("x is {}", n), - OptionalInt::Missing => println!("x is missing!"), + OptionalInt::Missing => println!("x is missing!"), } match y { OptionalInt::Value(n) => println!("y is {}", n), - OptionalInt::Missing => println!("y is missing!"), + OptionalInt::Missing => println!("y is missing!"), } } ``` @@ -141,11 +141,11 @@ use std::io; fn main() { println!("Type something!"); - // here, we'll show the types at each step + // here, we'll show the types at each step - let input = io::stdin() // std::io::stdio::StdinReader - .read_line() // IoResult - .ok() // Option + let input = io::stdin() // std::io::stdio::StdinReader + .read_line() // IoResult + .ok() // Option .expect("Failed to read line"); // String println!("{}", input); diff --git a/src/doc/trpl/unsafe.md b/src/doc/trpl/unsafe.md index 406af336653dd..de6d311be57ed 100644 --- a/src/doc/trpl/unsafe.md +++ b/src/doc/trpl/unsafe.md @@ -567,8 +567,8 @@ pub extern fn dot_product(a: *const u32, a_len: u32, #[lang = "panic_fmt"] extern fn panic_fmt(args: &core::fmt::Arguments, - file: &str, - line: u32) -> ! { + file: &str, + line: u32) -> ! { loop {} } From 2c64023e3e03ca05cb10cecec32e5c684c7436ba Mon Sep 17 00:00:00 2001 From: Alfie John Date: Sat, 17 Jan 2015 13:19:21 +0000 Subject: [PATCH 057/211] docs: grammar fix --- src/doc/trpl/compound-data-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/trpl/compound-data-types.md b/src/doc/trpl/compound-data-types.md index f584fe8d7a323..0616f094e376e 100644 --- a/src/doc/trpl/compound-data-types.md +++ b/src/doc/trpl/compound-data-types.md @@ -297,7 +297,7 @@ enum StringResult { } ``` Where a `StringResult` is either a `StringResult::StringOK`, with the result of -a computation, or an `StringResult::ErrorReason` with a `String` explaining +a computation, or a `StringResult::ErrorReason` with a `String` explaining what caused the computation to fail. These kinds of `enum`s are actually very useful and are even part of the standard library. From 35d46fabaf63c0b1b607bd91cd5680eeaa2f9a14 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 17 Jan 2015 11:29:24 -0500 Subject: [PATCH 058/211] remove test_find_equiv, since find_equiv doesn't exist anymore --- src/libstd/collections/hash/map.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 80ae3076df37a..d3ac632617dc7 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2112,23 +2112,6 @@ mod test_map { assert_eq!(m.remove(&0), Some(0)); } - #[test] - fn test_find_equiv() { - let mut m = HashMap::new(); - - let (foo, bar, baz) = (1i,2i,3i); - m.insert("foo".to_string(), foo); - m.insert("bar".to_string(), bar); - m.insert("baz".to_string(), baz); - - - assert_eq!(m.get("foo"), Some(&foo)); - assert_eq!(m.get("bar"), Some(&bar)); - assert_eq!(m.get("baz"), Some(&baz)); - - assert_eq!(m.get("qux"), None); - } - #[test] fn test_from_iter() { let xs = [(1i, 1i), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; From de6f520192d76151bf99f2250afac75da3f040d5 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Sun, 18 Jan 2015 02:24:04 +0900 Subject: [PATCH 059/211] tests: Add htmldocck.py script for the use of Rustdoc tests. The script is intended as a tool for doing every sort of verifications amenable to Rustdoc's HTML output. For example, link checkers would go to this script. It already parses HTML into a document tree form (with a slight caveat), so future tests can make use of it. As an example, relevant `rustdoc-*` run-make tests have been updated to use `htmldocck.py` and got their `verify.sh` removed. In the future they may go to a dedicated directory with htmldocck running by default. The detailed explanation of test scripts is provided as a docstring of htmldocck. cc #19723 --- mk/tests.mk | 3 +- src/etc/htmldocck.py | 316 ++++++++++++++++++ src/etc/maketest.py | 2 + .../run-make/rustdoc-hidden-line/Makefile | 3 +- src/test/run-make/rustdoc-hidden-line/foo.rs | 4 + .../run-make/rustdoc-hidden-line/verify.sh | 8 - .../run-make/rustdoc-search-index/Makefile | 4 +- .../run-make/rustdoc-search-index/index.rs | 11 +- .../run-make/rustdoc-search-index/verify.sh | 33 -- src/test/run-make/rustdoc-smoke/Makefile | 3 +- src/test/run-make/rustdoc-smoke/foo.rs | 7 + src/test/run-make/rustdoc-smoke/verify.sh | 17 - src/test/run-make/rustdoc-where/Makefile | 6 +- src/test/run-make/rustdoc-where/foo.rs | 8 + src/test/run-make/rustdoc-where/verify.sh | 23 -- src/test/run-make/tools.mk | 1 + 16 files changed, 350 insertions(+), 99 deletions(-) create mode 100644 src/etc/htmldocck.py delete mode 100755 src/test/run-make/rustdoc-hidden-line/verify.sh delete mode 100755 src/test/run-make/rustdoc-search-index/verify.sh delete mode 100755 src/test/run-make/rustdoc-smoke/verify.sh delete mode 100755 src/test/run-make/rustdoc-where/verify.sh diff --git a/mk/tests.mk b/mk/tests.mk index c8c4beb11531b..10452d9127501 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -1011,7 +1011,8 @@ $(3)/test/run-make/%-$(1)-T-$(2)-H-$(3).ok: \ $$(LD_LIBRARY_PATH_ENV_NAME$(1)_T_$(2)_H_$(3)) \ "$$(LD_LIBRARY_PATH_ENV_HOSTDIR$(1)_T_$(2)_H_$(3))" \ "$$(LD_LIBRARY_PATH_ENV_TARGETDIR$(1)_T_$(2)_H_$(3))" \ - $(1) + $(1) \ + $$(S) @touch $$@ else # FIXME #11094 - The above rule doesn't work right for multiple targets diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py new file mode 100644 index 0000000000000..9693129db3b61 --- /dev/null +++ b/src/etc/htmldocck.py @@ -0,0 +1,316 @@ +# 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. + +r""" +htmldocck.py is a custom checker script for Rustdoc HTML outputs. + +# How and why? + +The principle is simple: This script receives a path to generated HTML +documentation and a "template" script, which has a series of check +commands like `@has` or `@matches`. Each command can be used to check if +some pattern is present or not present in the particular file or in +the particular node of HTML tree. In many cases, the template script +happens to be a source code given to rustdoc. + +While it indeed is possible to test in smaller portions, it has been +hard to construct tests in this fashion and major rendering errors were +discovered much later. This script is designed for making the black-box +and regression testing of Rustdoc easy. This does not preclude the needs +for unit testing, but can be used to complement related tests by quickly +showing the expected renderings. + +In order to avoid one-off dependencies for this task, this script uses +a reasonably working HTML parser and the existing XPath implementation +from Python 2's standard library. Hopefully we won't render +non-well-formed HTML. + +# Commands + +Commands start with an `@` followed by a command name (letters and +hyphens), and zero or more arguments separated by one or more whitespace +and optionally delimited with single or double quotes. The `@` mark +cannot be preceded by a non-whitespace character. Other lines (including +every text up to the first `@`) are ignored, but it is recommended to +avoid the use of `@` in the template file. + +There are a number of supported commands: + +* `@has PATH` checks for the existence of given file. + + `PATH` is relative to the output directory. It can be given as `-` + which repeats the most recently used `PATH`. + +* `@has PATH PATTERN` and `@matches PATH PATTERN` checks for + the occurrence of given `PATTERN` in the given file. Only one + occurrence of given pattern is enough. + + For `@has`, `PATTERN` is a whitespace-normalized (every consecutive + whitespace being replaced by one single space character) string. + The entire file is also whitespace-normalized including newlines. + + For `@matches`, `PATTERN` is a Python-supported regular expression. + The file remains intact but the regexp is matched with no `MULTILINE` + and `IGNORECASE` option. You can still use a prefix `(?m)` or `(?i)` + to override them, and `\A` and `\Z` for definitely matching + the beginning and end of the file. + + (The same distinction goes to other variants of these commands.) + +* `@has PATH XPATH PATTERN` and `@matches PATH XPATH PATTERN` checks for + the presence of given `XPATH` in the given HTML file, and also + the occurrence of given `PATTERN` in the matching node or attribute. + Only one occurrence of given pattern in the match is enough. + + `PATH` should be a valid and well-formed HTML file. It does *not* + accept arbitrary HTML5; it should have matching open and close tags + and correct entity references at least. + + `XPATH` is an XPath expression to match. This is fairly limited: + `tag`, `*`, `.`, `//`, `..`, `[@attr]`, `[@attr='value']`, `[tag]`, + `[POS]` (element located in given `POS`), `[last()-POS]`, `text()` + and `@attr` (both as the last segment) are supported. Some examples: + + - `//pre` or `.//pre` matches any element with a name `pre`. + - `//a[@href]` matches any element with an `href` attribute. + - `//*[@class="impl"]//code` matches any element with a name `code`, + which is an ancestor of some element which `class` attr is `impl`. + - `//h1[@class="fqn"]/span[1]/a[last()]/@class` matches a value of + `class` attribute in the last `a` element (can be followed by more + elements that are not `a`) inside the first `span` in the `h1` with + a class of `fqn`. Note that there cannot be no additional elements + between them due to the use of `/` instead of `//`. + + Do not try to use non-absolute paths, it won't work due to the flawed + ElementTree implementation. The script rejects them. + + For the text matches (i.e. paths not ending with `@attr`), any + subelements are flattened into one string; this is handy for ignoring + highlights for example. If you want to simply check the presence of + given node or attribute, use an empty string (`""`) as a `PATTERN`. + +All conditions can be negated with `!`. `@!has foo/type.NoSuch.html` +checks if the given file does not exist, for example. + +""" + +import sys +import os.path +import re +import shlex +from collections import namedtuple +from HTMLParser import HTMLParser +from xml.etree import cElementTree as ET + +# ⇤/⇥ are not in HTML 4 but are in HTML 5 +from htmlentitydefs import entitydefs +entitydefs['larrb'] = u'\u21e4' +entitydefs['rarrb'] = u'\u21e5' + +# "void elements" (no closing tag) from the HTML Standard section 12.1.2 +VOID_ELEMENTS = set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', + 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr']) + +# simplified HTML parser. +# this is possible because we are dealing with very regular HTML from rustdoc; +# we only have to deal with i) void elements and ii) empty attributes. +class CustomHTMLParser(HTMLParser): + def __init__(self, target=None): + HTMLParser.__init__(self) + self.__builder = target or ET.TreeBuilder() + def handle_starttag(self, tag, attrs): + attrs = dict((k, v or '') for k, v in attrs) + self.__builder.start(tag, attrs) + if tag in VOID_ELEMENTS: self.__builder.end(tag) + def handle_endtag(self, tag): + self.__builder.end(tag) + def handle_startendtag(self, tag, attrs): + attrs = dict((k, v or '') for k, v in attrs) + self.__builder.start(tag, attrs) + self.__builder.end(tag) + def handle_data(self, data): + self.__builder.data(data) + def handle_entityref(self, name): + self.__builder.data(entitydefs[name]) + def handle_charref(self, name): + code = int(name[1:], 16) if name.startswith(('x', 'X')) else int(name, 10) + self.__builder.data(unichr(code).encode('utf-8')) + def close(self): + HTMLParser.close(self) + return self.__builder.close() + +Command = namedtuple('Command', 'negated cmd args lineno') + +LINE_PATTERN = re.compile(r'(?<=(?!?)(?P[A-Za-z]+(?:-[A-Za-z]+)*)(?P.*)$') +def get_commands(template): + with open(template, 'rUb') as f: + for lineno, line in enumerate(f): + m = LINE_PATTERN.search(line.rstrip('\r\n')) + if not m: continue + + negated = (m.group('negated') == '!') + cmd = m.group('cmd') + args = m.group('args') + if args and not args[:1].isspace(): + raise RuntimeError('Invalid template syntax at line {}'.format(lineno+1)) + args = shlex.split(args) + yield Command(negated=negated, cmd=cmd, args=args, lineno=lineno+1) + +def _flatten(node, acc): + if node.text: acc.append(node.text) + for e in node: + _flatten(e, acc) + if e.tail: acc.append(e.tail) + +def flatten(node): + acc = [] + _flatten(node, acc) + return ''.join(acc) + +def normalize_xpath(path): + if path.startswith('//'): + return '.' + path # avoid warnings + elif path.startswith('.//'): + return path + else: + raise RuntimeError('Non-absolute XPath is not supported due to \ + the implementation issue.') + +class CachedFiles(object): + def __init__(self, root): + self.root = root + self.files = {} + self.trees = {} + self.last_path = None + + def resolve_path(self, path): + if path != '-': + path = os.path.normpath(path) + self.last_path = path + return path + elif self.last_path is None: + raise RuntimeError('Tried to use the previous path in the first command') + else: + return self.last_path + + def get_file(self, path): + path = self.resolve_path(path) + try: + return self.files[path] + except KeyError: + try: + with open(os.path.join(self.root, path)) as f: + data = f.read() + except Exception as e: + raise RuntimeError('Cannot open file {!r}: {}'.format(path, e)) + else: + self.files[path] = data + return data + + def get_tree(self, path): + path = self.resolve_path(path) + try: + return self.trees[path] + except KeyError: + try: + f = open(os.path.join(self.root, path)) + except Exception as e: + raise RuntimeError('Cannot open file {!r}: {}'.format(path, e)) + try: + with f: + tree = ET.parse(f, CustomHTMLParser()) + except Exception as e: + raise RuntimeError('Cannot parse an HTML file {!r}: {}'.format(path, e)) + else: + self.trees[path] = tree + return self.trees[path] + +def check_string(data, pat, regexp): + if not pat: + return True # special case a presence testing + elif regexp: + return re.search(pat, data) is not None + else: + data = ' '.join(data.split()) + pat = ' '.join(pat.split()) + return pat in data + +def check_tree_attr(tree, path, attr, pat, regexp): + path = normalize_xpath(path) + ret = False + for e in tree.findall(path): + try: + value = e.attrib[attr] + except KeyError: + continue + else: + ret = check_string(value, pat, regexp) + if ret: break + return ret + +def check_tree_text(tree, path, pat, regexp): + path = normalize_xpath(path) + ret = False + for e in tree.findall(path): + try: + value = flatten(e) + except KeyError: + continue + else: + ret = check_string(value, pat, regexp) + if ret: break + return ret + +def check(target, commands): + cache = CachedFiles(target) + for c in commands: + if c.cmd == 'has' or c.cmd == 'matches': # string test + regexp = (c.cmd == 'matches') + if len(c.args) == 1 and not regexp: # @has = file existence + try: + cache.get_file(c.args[0]) + ret = True + except RuntimeError: + ret = False + elif len(c.args) == 2: # @has/matches = string test + ret = check_string(cache.get_file(c.args[0]), c.args[1], regexp) + elif len(c.args) == 3: # @has/matches = XML tree test + tree = cache.get_tree(c.args[0]) + pat, sep, attr = c.args[1].partition('/@') + if sep: # attribute + ret = check_tree_attr(cache.get_tree(c.args[0]), pat, attr, c.args[2], regexp) + else: # normalized text + pat = c.args[1] + if pat.endswith('/text()'): pat = pat[:-7] + ret = check_tree_text(cache.get_tree(c.args[0]), pat, c.args[2], regexp) + else: + raise RuntimeError('Invalid number of @{} arguments \ + at line {}'.format(c.cmd, c.lineno)) + + elif c.cmd == 'valid-html': + raise RuntimeError('Unimplemented @valid-html at line {}'.format(c.lineno)) + + elif c.cmd == 'valid-links': + raise RuntimeError('Unimplemented @valid-links at line {}'.format(c.lineno)) + + else: + raise RuntimeError('Unrecognized @{} at line {}'.format(c.cmd, c.lineno)) + + if ret == c.negated: + raise RuntimeError('@{}{} check failed at line {}'.format('!' if c.negated else '', + c.cmd, c.lineno)) + +if __name__ == '__main__': + if len(sys.argv) < 3: + print >>sys.stderr, 'Usage: {}