visitAssign()

in lib/semantic.js [3146:3194]


  visitAssign(ast, env) {
    assert.equal(ast.type, 'assign');
    if (ast.left.type === 'virtualVariable') {
      this.checkVid(ast.left.vid, env);
    } else if (ast.left.type === 'variable') {
      this.checkId(ast.left.id, env);
    } else if (ast.left.type === 'property') {
      this.checkProperty(ast.left, env);
    } else if (ast.left.type === 'map_access') {
      let mainType;
      if (ast.left.propertyPath) {
        this.checkProperty(ast.left, env);
        mainType = this.calculatePropertyType(ast.left, env);
      } else {
        this.checkId(ast.left.id, env);
        mainType = this.getVariableType(ast.left.id, env);
      }

      if (mainType.type === 'map') {
        if (!this.isStringType(ast.left.accessKey, env)) {
          this.error(`The key expr type must be string type`, ast.left.accessKey);
        }
      } else if (mainType.type === 'array') {
        ast.left.type = 'array_access';
        if (!this.isNumberType(ast.left.accessKey, env)) {
          this.error(`The key expr type must be number type`, ast.left.accessKey);
        }
      } else {
        this.error(`the [] form only support map or array type`, ast.left.accessKey);
      }
    } else {
      throw new Error('unimplemented');
    }
    
    const expected = this.getExprType(ast.left, env);
    ast.left.inferred = expected;
    this.visitExpr(ast.expr, env);
    const actual = this.getExprType(ast.expr, env);
    if (!isAssignable(expected, actual, ast.expr)) {
      this.error(`can't assign ${display(actual)} to ${display(expected)}`, ast.expr);
    }

    if (expected.type === 'basic' && expected.name === 'readable') {
      if (actual.type === 'basic' && actual.name === 'bytes' ||
        actual.type === 'basic' && actual.name === 'string') {
        ast.expr.needToReadable = true;
      }
    }
  }