Does SELECT INTO Reserve the #Object Name in TempDB Prior to Runtime?
This has nothing to do with object name reservations in TempDB or anything to do with runtime. This is simply the parser not able to follow logic or code paths that assures that your code couldn't possibly try to create that table twice. Notice that you get the exact same (non-runtime!) error if you just click the Parse button (Ctrl+F5). Basically, if you have this:

IF 1=1 
  CREATE TABLE #foo(id1 INT);
ELSE
  CREATE TABLE #foo(id2 INT);


The parser sees this:

  CREATE TABLE #foo(id1 INT);
  CREATE TABLE #foo(id2 INT);


Why does it not work this way for actual tables, including actual user tables created in TempDB (note that it isn't database-specific, either)? The only answer I can suggest is that the parser has a different set of rules for #temp tables (there are a lot of other differences, too). If you want more specific reasons, you'll need to open a case with Microsoft and see if they'll give you any further details. My guess is you will be told: "this is the way it works."

Some more info in these answers:


Is it &quot;legal&quot; to CREATE and DROP #SomeTable more than once?
Can I recreate a temp table after dropping it?