@@ -179,12 +179,13 @@ func (c *CFGs) buildDecl(fn *types.Func, di *declInfo) {
179179 }
180180 di .started = true
181181
182- noreturn := isIntrinsicNoReturn (fn )
183-
184- if di .decl .Body != nil {
185- di .cfg = cfg .New (di .decl .Body , c .callMayReturn )
186- if cfginternal .IsNoReturn (di .cfg ) {
187- noreturn = true
182+ noreturn , known := knownIntrinsic (fn )
183+ if ! known {
184+ if di .decl .Body != nil {
185+ di .cfg = cfg .New (di .decl .Body , c .callMayReturn )
186+ if cfginternal .IsNoReturn (di .cfg ) {
187+ noreturn = true
188+ }
188189 }
189190 }
190191 if noreturn {
@@ -233,11 +234,36 @@ func (c *CFGs) callMayReturn(call *ast.CallExpr) (r bool) {
233234
234235var panicBuiltin = types .Universe .Lookup ("panic" ).(* types.Builtin )
235236
236- // isIntrinsicNoReturn reports whether a function intrinsically never
237- // returns because it stops execution of the calling thread.
237+ // knownIntrinsic reports whether a function intrinsically never
238+ // returns because it stops execution of the calling thread, or does
239+ // in fact return, contrary to its apparent body, because it is
240+ // handled specially by the compiler.
241+ //
238242// It is the base case in the recursion.
239- func isIntrinsicNoReturn (fn * types.Func ) bool {
243+ func knownIntrinsic (fn * types.Func ) ( noreturn , known bool ) {
240244 // Add functions here as the need arises, but don't allocate memory.
241- return typesinternal .IsFunctionNamed (fn , "syscall" , "Exit" , "ExitProcess" , "ExitThread" ) ||
242- typesinternal .IsFunctionNamed (fn , "runtime" , "Goexit" )
245+
246+ // Functions known intrinsically never to return.
247+ if typesinternal .IsFunctionNamed (fn , "syscall" , "Exit" , "ExitProcess" , "ExitThread" ) ||
248+ typesinternal .IsFunctionNamed (fn , "runtime" , "Goexit" ) {
249+ return true , true
250+ }
251+
252+ // Compiler intrinsics known to return, contrary to
253+ // what analysis of the function body would conclude.
254+ //
255+ // Not all such intrinsics must be listed here: ctrlflow
256+ // considers any function called for its value--such as
257+ // crypto/internal/constanttime.bool2Uint8--to potentially
258+ // return; only functions called as a statement, for effects,
259+ // are no-return candidates.
260+ //
261+ // Unfortunately this does sometimes mean peering into internals.
262+ // Where possible, use the nearest enclosing public API function.
263+ if typesinternal .IsFunctionNamed (fn , "internal/abi" , "EscapeNonString" ) ||
264+ typesinternal .IsFunctionNamed (fn , "hash/maphash" , "Comparable" ) {
265+ return false , true
266+ }
267+
268+ return // unknown
243269}
0 commit comments