function infer()

in pxtblocks/blocklycompiler.ts [404:590]


    function infer(allBlocks: Blockly.Block[], e: Environment, w: Blockly.Workspace) {
        if (allBlocks) allBlocks.filter(b => b.isEnabled()).forEach((b: Blockly.Block) => {
            try {
                switch (b.type) {
                    case "math_op2":
                        unionParam(e, b, "x", ground(pNumber.type));
                        unionParam(e, b, "y", ground(pNumber.type));
                        break;

                    case "math_op3":
                        unionParam(e, b, "x", ground(pNumber.type));
                        break;

                    case "math_arithmetic":
                    case "logic_compare":
                        switch (b.getFieldValue("OP")) {
                            case "ADD": case "MINUS": case "MULTIPLY": case "DIVIDE":
                            case "LT": case "LTE": case "GT": case "GTE": case "POWER":
                                unionParam(e, b, "A", ground(pNumber.type));
                                unionParam(e, b, "B", ground(pNumber.type));
                                break;
                            case "AND": case "OR":
                                attachPlaceholderIf(e, b, "A", pBoolean.type);
                                attachPlaceholderIf(e, b, "B", pBoolean.type);
                                break;
                            case "EQ": case "NEQ":
                                attachPlaceholderIf(e, b, "A");
                                attachPlaceholderIf(e, b, "B");
                                let p1 = returnType(e, getInputTargetBlock(b, "A"));
                                let p2 = returnType(e, getInputTargetBlock(b, "B"));
                                try {
                                    union(p1, p2);
                                } catch (e) {
                                    // TypeScript should catch this error and bubble it up
                                }
                                break;
                        }
                        break;

                    case "logic_operation":
                        attachPlaceholderIf(e, b, "A", pBoolean.type);
                        attachPlaceholderIf(e, b, "B", pBoolean.type);
                        break;

                    case "logic_negate":
                        attachPlaceholderIf(e, b, "BOOL", pBoolean.type);
                        break;

                    case "controls_if":
                        for (let i = 0; i <= (<Blockly.IfBlock>b).elseifCount_; ++i)
                            attachPlaceholderIf(e, b, "IF" + i, pBoolean.type);
                        break;

                    case "pxt_controls_for":
                    case "controls_simple_for":
                        unionParam(e, b, "TO", ground(pNumber.type));
                        break;
                    case "pxt_controls_for_of":
                    case "controls_for_of":
                        const listTp = returnType(e, getInputTargetBlock(b, "LIST"));
                        const elementTp = lookup(e, b, getLoopVariableField(b).getField("VAR").getText()).type;
                        genericLink(listTp, elementTp);
                        break;
                    case "variables_set":
                    case "variables_change":
                        let p1 = lookup(e, b, b.getField("VAR").getText()).type;
                        attachPlaceholderIf(e, b, "VALUE");
                        let rhs = getInputTargetBlock(b, "VALUE");
                        if (rhs) {
                            // Get the inheritance chain for this type and check to see if the existing
                            // type shows up in it somewhere
                            let tr = returnTypeWithInheritance(e, rhs);
                            const t1 = find(p1);
                            if (t1.type && tr.slice(1).some(p => p.type === t1.type)) {
                                // If it does, we want to take the most narrow type (which will always be in 0)
                                p1.link = find(tr[0]);
                            }
                            else {
                                try {
                                    union(p1, tr[0]);
                                } catch (e) {
                                    // TypeScript should catch this error and bubble it up
                                }
                            }
                        }
                        break;
                    case "controls_repeat_ext":
                        unionParam(e, b, "TIMES", ground(pNumber.type));
                        break;

                    case "device_while":
                        attachPlaceholderIf(e, b, "COND", pBoolean.type);
                        break;
                    case "lists_index_get":
                        unionParam(e, b, "LIST", ground("Array"));
                        unionParam(e, b, "INDEX", ground(pNumber.type));
                        const listType = returnType(e, getInputTargetBlock(b, "LIST"));
                        const ret = returnType(e, b);
                        genericLink(listType, ret);
                        break;
                    case "lists_index_set":
                        unionParam(e, b, "LIST", ground("Array"));
                        attachPlaceholderIf(e, b, "VALUE");
                        handleGenericType(b, "LIST");
                        unionParam(e, b, "INDEX", ground(pNumber.type));
                        break;
                    case 'function_definition':
                        getReturnTypeOfFunction(e, b.getField("function_name",).getText());
                        break;
                    case 'function_call':
                    case 'function_call_output':
                        (b as Blockly.FunctionCallBlock).getArguments().forEach(arg => {
                            unionParam(e, b, arg.id, ground(arg.type));
                        });
                        break;
                    case pxtc.TS_RETURN_STATEMENT_TYPE:
                        attachPlaceholderIf(e, b, "RETURN_VALUE");
                        break;
                    case pxtc.PAUSE_UNTIL_TYPE:
                        unionParam(e, b, "PREDICATE", pBoolean);
                        break;
                    default:
                        if (b.type in e.stdCallTable) {
                            const call = e.stdCallTable[b.type];
                            if (call.attrs.shim === "ENUM_GET" || call.attrs.shim === "KIND_GET") return;
                            visibleParams(call, countOptionals(b)).forEach((p, i) => {
                                const isInstance = call.isExtensionMethod && i === 0;
                                if (p.definitionName && !b.getFieldValue(p.definitionName)) {
                                    let i = b.inputList.find((i: Blockly.Input) => i.name == p.definitionName);
                                    if (i && i.connection && i.connection.check_) {
                                        if (isInstance && connectionCheck(i) === "Array") {
                                            let gen = handleGenericType(b, p.definitionName);
                                            if (gen) {
                                                return;
                                            }
                                        }

                                        // All of our injected blocks have single output checks, but the builtin
                                        // blockly ones like string.length and array.length might have multiple
                                        for (let j = 0; j < i.connection.check_.length; j++) {
                                            try {
                                                let t = i.connection.check_[j];
                                                unionParam(e, b, p.definitionName, ground(t));
                                                break;
                                            }
                                            catch (e) {
                                                // Ignore type checking errors in the blocks...
                                            }
                                        }
                                    }
                                }
                            });
                        }
                }
            } catch (err) {
                const be = ((<any>err).block as Blockly.Block) || b;
                be.setWarningText(err + "");
                e.errors.push(be);
            }
        });

        // Last pass: if some variable has no type (because it was never used or
        // assigned to), just unify it with int...
        e.allVariables.forEach((v: VarInfo) => {
            if (getConcreteType(v.type).type == null)
                union(v.type, ground(v.type.isArrayType ? "number[]" : pNumber.type));
        });

        function connectionCheck(i: Blockly.Input) {
            return i.name ? i.connection && i.connection.check_ && i.connection.check_.length ? i.connection.check_[0] : "T" : undefined;
        }

        function handleGenericType(b: Blockly.Block, name: string) {
            let genericArgs = b.inputList.filter((input: Blockly.Input) => connectionCheck(input) === "T");
            if (genericArgs.length) {
                const gen = getInputTargetBlock(b, genericArgs[0].name);
                if (gen) {
                    const arg = returnType(e, gen);
                    const arrayType = arg.type ? ground(returnType(e, gen).type + "[]") : ground(null);
                    genericLink(arrayType, arg);
                    unionParam(e, b, name, arrayType);
                    return true;
                }
            }
            return false;
        }
    }