ok, I had some inspiration after posting to SO (and examining the excellent answers I received, thanks guys!). I think this solution is easy to understand and is usable in any situation like this. It should also be quite efficient because it only needs to do one lookup for each student.
Basically, the whole problem is that you can't return multiple columns in a column based subquery... Well, I can just jam all the columns I want into one JSON column and then extract them later:
select
topScore.name,
topScore.topScoreJson ->> 'score' as topScore,
topScore.topScoreJson ->> 'day' as topScoreDay
from (
select
student.name,
(
select
json_object(
'score', testScore.score,
'day', testScore.day
)
from
testScore
where
testScore.studentId = student.id
order by
score desc
limit 1
) as topScoreJson
from
student
) as topScore
order by
topScore desc;