Check missing uncoditionally loaded missing modules at runtime

A potentially inherited (via dynamically calculated path) module may in turn
unconditionally load a module that does no exist in a source tree -- it is
not an error if this potentially inherited module is actually never inherited
because its dynamically calculated path will never reference it. Instead of
emitting an uncoditional `load` for a non-existent file (which is going to fail
in the Starlark parser), emit conditional load and a runtime check.

Fixes: 213922819
Test: internal
Change-Id: I92177878e199a1f00e5f0c4045c0c0daeddd6bdb
diff --git a/mk2rbc/node.go b/mk2rbc/node.go
index ebc57b2..333a8da 100644
--- a/mk2rbc/node.go
+++ b/mk2rbc/node.go
@@ -47,6 +47,7 @@
 	originalPath    string // Makefile file path
 	moduleLocalName string
 	optional        bool
+	missing         bool // a module may not exist if a module that depends on it is loaded dynamically
 }
 
 func (im moduleInfo) entryName() string {
@@ -57,7 +58,8 @@
 	name() string
 	entryName() string
 	emitSelect(gctx *generationContext)
-	shouldExist() bool
+	pathExpr() starlarkExpr
+	needsLoadCheck() bool
 }
 
 type inheritedStaticModule struct {
@@ -72,8 +74,12 @@
 func (im inheritedStaticModule) emitSelect(_ *generationContext) {
 }
 
-func (im inheritedStaticModule) shouldExist() bool {
-	return im.loadAlways
+func (im inheritedStaticModule) pathExpr() starlarkExpr {
+	return &stringLiteralExpr{im.path}
+}
+
+func (im inheritedStaticModule) needsLoadCheck() bool {
+	return im.missing
 }
 
 type inheritedDynamicModule struct {
@@ -105,20 +111,14 @@
 	gctx.write(")")
 	gctx.newLine()
 	gctx.writef("(%s, %s) = _entry if _entry else (None, None)", i.name(), i.entryName())
-	if i.loadAlways {
-		gctx.newLine()
-		gctx.writef("if not %s:", i.entryName())
-		gctx.indentLevel++
-		gctx.newLine()
-		gctx.write(`rblf.mkerror("`, gctx.starScript.mkFile, `", "Cannot find %s" % (`)
-		i.path.emit(gctx)
-		gctx.write("))")
-		gctx.indentLevel--
-	}
 }
 
-func (i inheritedDynamicModule) shouldExist() bool {
-	return i.loadAlways
+func (i inheritedDynamicModule) pathExpr() starlarkExpr {
+	return &i.path
+}
+
+func (i inheritedDynamicModule) needsLoadCheck() bool {
+	return true
 }
 
 type inheritNode struct {
@@ -128,20 +128,22 @@
 
 func (inn *inheritNode) emit(gctx *generationContext) {
 	// Unconditional case:
+	//    maybe check that loaded
 	//    rblf.inherit(handle, <module>, module_init)
 	// Conditional case:
 	//    if <module>_init != None:
 	//      same as above
 	inn.module.emitSelect(gctx)
-
 	name := inn.module.name()
 	entry := inn.module.entryName()
-	gctx.newLine()
 	if inn.loadAlways {
+		gctx.emitLoadCheck(inn.module)
+		gctx.newLine()
 		gctx.writef("%s(handle, %s, %s)", cfnInherit, name, entry)
 		return
 	}
 
+	gctx.newLine()
 	gctx.writef("if %s:", entry)
 	gctx.indentLevel++
 	gctx.newLine()
@@ -157,12 +159,14 @@
 func (inn *includeNode) emit(gctx *generationContext) {
 	inn.module.emitSelect(gctx)
 	entry := inn.module.entryName()
-	gctx.newLine()
 	if inn.loadAlways {
+		gctx.emitLoadCheck(inn.module)
+		gctx.newLine()
 		gctx.writef("%s(g, handle)", entry)
 		return
 	}
 
+	gctx.newLine()
 	gctx.writef("if %s != None:", entry)
 	gctx.indentLevel++
 	gctx.newLine()