]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/gc: suspend safemode during typecheck of inlined bodies.
authorLuuk van Dijk <lvd@golang.org>
Fri, 10 Feb 2012 21:50:55 +0000 (22:50 +0100)
committerLuuk van Dijk <lvd@golang.org>
Fri, 10 Feb 2012 21:50:55 +0000 (22:50 +0100)
Should be obviously correct.  Includes minimal test case.
A future CL should clear up the logic around typecheckok and importpkg != nil someday.

R=rsc, dsymonds, rsc
CC=golang-dev
https://golang.org/cl/5652057

src/cmd/gc/inl.c
test/golden.out
test/run
test/safe/main.go [new file with mode: 0644]
test/safe/nousesafe.go [new file with mode: 0644]
test/safe/pkg.go [new file with mode: 0644]
test/safe/usesafe.go [new file with mode: 0644]

index ed7a7eb959bd398a9c58150f47547c2ac5b9cb8a..96080cbfaf950ac60d8c1efc8e6c5bc1d54d271d 100644 (file)
@@ -53,22 +53,62 @@ static Node *inlfn;         // function currently being inlined
 static Node *inlretlabel;      // target of the goto substituted in place of a return
 static NodeList *inlretvars;   // temp out variables
 
-// Lazy typechecking of imported bodies.
-// TODO avoid redoing local functions (imporpkg would be wrong)
+// Get the function's package.  For ordinary functions it's on the ->sym, but for imported methods
+// the ->sym can be re-used in the local package, so peel it off the receiver's type.
+static Pkg*
+fnpkg(Node *fn)
+{
+       Type *rcvr;
+       
+       if(fn->type->thistuple) {
+               // method
+               rcvr = getthisx(fn->type)->type->type;
+               if(isptr[rcvr->etype])
+                       rcvr = rcvr->type;
+               if(!rcvr->sym)
+                       fatal("receiver with no sym: [%S] %lN  (%T)", fn->sym, fn, rcvr);
+               return rcvr->sym->pkg;
+       }
+       // non-method
+       return fn->sym->pkg;
+}
+
+// Lazy typechecking of imported bodies.  For local functions, caninl will set ->typecheck
+// because they're a copy of an already checked body. 
 void
 typecheckinl(Node *fn)
 {
        Node *savefn;
+       Pkg *pkg;
+       int save_safemode, lno;
+
+       if(fn->typecheck)
+               return;
+
+       lno = setlineno(fn);
 
        if (debug['m']>2)
                print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl);
 
+       // typecheckinl is only used for imported functions;
+       // their bodies may refer to unsafe as long as the package
+       // was marked safe during import (which was checked then).
+       pkg = fnpkg(fn);
+       if (pkg == localpkg || pkg == nil)
+               fatal("typecheckinl on local function %lN", fn);
+
+       save_safemode = safemode;
+       safemode = 0;
+
        savefn = curfn;
        curfn = fn;
-       importpkg = fn->sym->pkg;
        typechecklist(fn->inl, Etop);
-       importpkg = nil;
+       fn->typecheck = 1;
        curfn = savefn;
+
+       safemode = save_safemode;
+
+       lineno = lno;
 }
 
 // Caninl determines whether fn is inlineable. Currently that means:
@@ -105,6 +145,8 @@ caninl(Node *fn)
 
        fn->nname->inl = fn->nbody;
        fn->nbody = inlcopylist(fn->nname->inl);
+       // nbody will have been typechecked, so we can set this:
+       fn->typecheck = 1;
 
        // hack, TODO, check for better way to link method nodes back to the thing with the ->inl
        // this is so export can find the body of a method
@@ -444,12 +486,30 @@ inlnode(Node **np)
        lineno = lno;
 }
 
+static void    mkinlcall1(Node **np, Node *fn);
+
+static void
+mkinlcall(Node **np, Node *fn)
+{
+       int save_safemode;
+       Pkg *pkg;
+
+       save_safemode = safemode;
+
+       // imported functions may refer to unsafe as long as the
+       // package was marked safe during import (already checked).
+       pkg = fnpkg(fn);
+       if(pkg != localpkg && pkg != nil)
+               safemode = 0;
+       mkinlcall1(np, fn);
+       safemode = save_safemode;
+}
 // if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL.
 // On return ninit has the parameter assignments, the nbody is the
 // inlined function body and list, rlist contain the input, output
 // parameters.
 static void
-mkinlcall(Node **np, Node *fn)
+mkinlcall1(Node **np, Node *fn)
 {
        int i;
        Node *n, *call, *saveinlfn, *as, *m;
@@ -598,7 +658,7 @@ mkinlcall(Node **np, Node *fn)
        *np = call;
 
        inlfn = saveinlfn;
-       
+
        // transitive inlining
        // TODO do this pre-expansion on fn->inl directly.  requires
        // either supporting exporting statemetns with complex ninits
index e0b4cf6e2a791e8350e6dd70624835df6810bab2..764f561969dd6b19531478f40309fe2eac036b6d 100644 (file)
@@ -11,6 +11,8 @@
 
 == dwarf/
 
+== safe/
+
 == fixedbugs/
 
 == bugs/
index 714520aee379246534b883c000ff6378dcbaa8da..4b0481caa8bde0607500d56b41a26bc20845f911 100755 (executable)
--- a/test/run
+++ b/test/run
@@ -56,7 +56,7 @@ filterout() {
        grep '^'"$2"'$' $1 >/dev/null
 }
 
-for dir in . ken chan interface syntax dwarf fixedbugs bugs
+for dir in . ken chan interface syntax dwarf safe fixedbugs bugs
 do
        echo
        echo '==' $dir'/'
diff --git a/test/safe/main.go b/test/safe/main.go
new file mode 100644 (file)
index 0000000..d173ed9
--- /dev/null
@@ -0,0 +1,14 @@
+// true
+
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// can't use local path with -u, use -I. instead
+import "pkg"  // ERROR "import unsafe package"
+
+func main() {
+       print(pkg.Float32bits(1.0))
+}
diff --git a/test/safe/nousesafe.go b/test/safe/nousesafe.go
new file mode 100644 (file)
index 0000000..f61e7fe
--- /dev/null
@@ -0,0 +1,8 @@
+// $G $D/pkg.go && pack grc pkg.a pkg.$A 2> /dev/null && rm pkg.$A && errchk $G -I. -u $D/main.go
+// rm -f pkg.a
+
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ignored
diff --git a/test/safe/pkg.go b/test/safe/pkg.go
new file mode 100644 (file)
index 0000000..bebc43a
--- /dev/null
@@ -0,0 +1,16 @@
+// true
+
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// a package that uses unsafe on the inside but not in it's api
+
+package pkg
+
+import "unsafe"
+
+// this should be inlinable
+func Float32bits(f float32) uint32 {
+       return *(*uint32)(unsafe.Pointer(&f))
+}
\ No newline at end of file
diff --git a/test/safe/usesafe.go b/test/safe/usesafe.go
new file mode 100644 (file)
index 0000000..07c13c1
--- /dev/null
@@ -0,0 +1,8 @@
+// $G $D/pkg.go && pack grcS pkg.a pkg.$A 2> /dev/null && rm pkg.$A && $G -I. -u $D/main.go
+// rm -f pkg.a
+
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ignored