From: Robert Griesemer Date: Thu, 6 Dec 2012 17:17:20 +0000 (-0800) Subject: spec: type assertions and type switches must be valid X-Git-Tag: go1.1rc2~1701 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=485673188dc6c3ee3113990ed1e96ca8f8f0df51;p=gostls13.git spec: type assertions and type switches must be valid The spec didn't preclude invalid type assertions and type switches, i.e., cases where a concrete type doesn't implement the interface type in the assertion in the first place. Both, the gc and gccgo compiler exclude these cases. This is documenting the status quo. Also: - minor clean up of respective examples - added sentence about default case in select statements Fixes #4472. R=rsc, iant, r, ken CC=golang-dev https://golang.org/cl/6869050 --- diff --git a/doc/go_spec.html b/doc/go_spec.html index 4061c780fb..f58c323216 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -2668,8 +2668,11 @@ The notation x.(T) is called a type assertion. More precisely, if T is not an interface type, x.(T) asserts that the dynamic type of x is identical to the type T. +In this case, T must implement the (interface) type of x; +otherwise the type assertion is invalid since it is not possible for x +to store a value of type T. If T is an interface type, x.(T) asserts that the dynamic type -of x implements the interface T (§Interface types). +of x implements the interface T.

If the type assertion holds, the value of the expression is the value @@ -2679,8 +2682,19 @@ In other words, even though the dynamic type of x is known only at run time, the type of x.(T) is known to be T in a correct program.

+ +
+var x interface{} = 7  // x has dynamic type int and value 7
+i := x.(int)           // i has type int and value 7
+
+type I interface { m() }
+var y I
+s := y.(string)        // illegal: string does not implement I (missing method m)
+r := y.(io.Reader)     // r has type io.Reader and y must implement both I and io.Reader
+
+

-If a type assertion is used in an assignment or initialization of the form +If a type assertion is used in an assignment or initialization of the form

@@ -2696,7 +2710,7 @@ otherwise, the expression returns (Z, false) where Z
 is the zero value for type T.
 No run-time panic occurs in this case.
 The type assertion in this construct thus acts like a function call
-returning a value and a boolean indicating success.  (§Assignments)
+returning a value and a boolean indicating success.
 

@@ -4159,9 +4173,20 @@ case x == 4: f3() A type switch compares types rather than values. It is otherwise similar to an expression switch. It is marked by a special switch expression that has the form of a type assertion -using the reserved word type rather than an actual type. -Cases then match literal types against the dynamic type of the expression -in the type assertion. +using the reserved word type rather than an actual type: +

+ +
+switch x.(type) {
+// cases
+}
+
+ +

+Cases then match actual types T against the dynamic type of the +expression x. As with type assertions, x must be of +interface type, and each non-interface type +T listed in a case must implement the type of x.

@@ -4197,17 +4222,17 @@ the following type switch:
 
 switch i := x.(type) {
 case nil:
-	printString("x is nil")
+	printString("x is nil")                // type of i is type of x (interface{})
 case int:
-	printInt(i)  // i is an int
+	printInt(i)                            // type of i is int
 case float64:
-	printFloat64(i)  // i is a float64
+	printFloat64(i)                        // type of i is float64
 case func(int) float64:
-	printFunction(i)  // i is a function
+	printFunction(i)                       // type of i is func(int) float64
 case bool, string:
-	printString("type is bool or string")  // i is an interface{}
+	printString("type is bool or string")  // type of i is type of x (interface{})
 default:
-	printString("don't know the type")
+	printString("don't know the type")     // type of i is type of x (interface{})
 }
 
@@ -4218,22 +4243,23 @@ could be rewritten:
 v := x  // x is evaluated exactly once
 if v == nil {
+	i := v                                 // type of i is type of x (interface{})
 	printString("x is nil")
 } else if i, isInt := v.(int); isInt {
-	printInt(i)  // i is an int
+	printInt(i)                            // type of i is int
 } else if i, isFloat64 := v.(float64); isFloat64 {
-	printFloat64(i)  // i is a float64
+	printFloat64(i)                        // type of i is float64
 } else if i, isFunc := v.(func(int) float64); isFunc {
-	printFunction(i)  // i is a function
+	printFunction(i)                       // type of i is func(int) float64
 } else {
-	i1, isBool := v.(bool)
-	i2, isString := v.(string)
+	_, isBool := v.(bool)
+	_, isString := v.(string)
 	if isBool || isString {
-		i := v
-		printString("type is bool or string")  // i is an interface{}
+		i := v                         // type of i is type of x (interface{})
+		printString("type is bool or string")
 	} else {
-		i := v
-		printString("don't know the type")  // i is an interface{}
+		i := v                         // type of i is type of x (interface{})
+		printString("don't know the type")
 	}
 }
 
@@ -4501,7 +4527,8 @@ If any of the resulting operations can proceed, one of those is chosen and the corresponding communication and statements are evaluated. Otherwise, if there is a default case, that executes; if there is no default case, the statement blocks until one of the communications can -complete. +complete. There can be at most one default case and it may appear anywhere in the +"select" statement. If there are no cases with non-nil channels, the statement blocks forever. Even if the statement blocks,