コード例 #1
0
ファイル: name-resolver.cpp プロジェクト: LittleKu/sourcepawn
VarDecl *
NameResolver::HandleVarDecl(NameToken name, TypeSpecifier &spec, Expression *init)
{
  Scope *scope = getOrCreateScope();

  // :TODO: set variadic info
  VarDecl *var = new (pool_) VarDecl(name, init);

  // Note: the parser has already bound |var->init()| at this point, meaning
  // that aside from globals it should be impossible to self-initialize like:
  //    int x = x;
  //
  // :TODO: do check this for globals.
  VariableSymbol *sym = new (pool_) VariableSymbol(var, scope, var->name());
  registerSymbol(sym);
  var->setSymbol(sym);

  // Set this before we evaluate the type, since it determines whether or not
  // a const on a parameter is meaningless.
  if (spec.isByRef()) {
    assert(scope->kind() == Scope::Argument && sym->isArgument());
    sym->storage_flags() |= StorageFlags::byref;
  }

  // See the comment in TypeResolver::visitVarDecl for why we do not want to
  // infer sizes from literals for arguments.
  if (init &&
      !scope->isArgument() &&
      ((init->isArrayLiteral() && init->asArrayLiteral()->isFixedArrayLiteral()) ||
       (init->isStringLiteral())))
  {
    // Wait until the type resolution pass to figure this out. We still have
    // to precompute the base though.
    if (Type *type = resolveBase(spec))
      spec.setResolvedBaseType(type);
    var->te() = TypeExpr(new (pool_) TypeSpecifier(spec));
  } else {
    VarDeclSpecHelper helper(var, nullptr);
    var->te() = resolve(spec, &helper);
  }

  if (var->te().resolved()) {
    sym->setType(var->te().resolved());

    // We need to check this both here and in lazy resolution, which is gross,
    // but I don't see any obvious way to simplify it yet.
    if (spec.isByRef() && sym->type()->passesByReference()) {
      cc_.report(spec.byRefLoc(), rmsg::type_cannot_be_ref)
        << sym->type();
    }
  }

  // Even if we were able to resolve the type, if we have to resolve a constant
  // value, we'll have to add it to the resolver queue.
  if (!var->te().resolved() || sym->canUseInConstExpr())
    tr_.addPending(var);

  return var;
}
コード例 #2
0
bool
VarDeclSpecHelper::receiveConstQualifier(CompileContext &cc, const SourceLocation &constLoc, Type *type)
{
  VariableSymbol *sym = decl_->sym();
  if (sym->isArgument()) {
    if (!!(sym->storage_flags() & StorageFlags::byref)) {
      cc.report(constLoc, rmsg::const_ref_has_no_meaning) << type;
      return true;
    }
    if (!type->passesByReference()) {
      cc.report(constLoc, rmsg::const_has_no_meaning) << type;
      return true;
    }
  } else if (TypeSupportsCompileTimeInterning(type)) {
    sym->storage_flags() |= StorageFlags::constval;
  }

  sym->storage_flags() |= StorageFlags::readonly;
  return true;
}
コード例 #3
0
void
TypeResolver::visitVarDecl(VarDecl *node)
{
  VariableSymbol *sym = node->sym();

  assert(!sym->type());

  Type* type;
  if (TypeSpecifier *spec = node->te().spec()) {
    // We always infer sizes for postdims in variable scope. In argument
    // scope, we don't want something like:
    //
    //    f(x[] = {}), or
    //
    // To infer as int[0]. However, this should be illegal:
    //
    //    f(int x[] = {})
    //
    // So we simply never infer dimensions for arguments.
    //
    // Note: we should not be able to recurse from inside this block. If it
    // could, we'd have to mark spec as resolving earlier.
    Vector<int> literal_dims;
    if (Expression *init = node->initialization()) {
      if (spec->hasPostDims() && !sym->isArgument()) {
        // Compute the dimensions of initializers in case the declaration type
        // requires inference.
        if (ArrayLiteral *lit = init->asArrayLiteral()) {
          literal_dims = fixedArrayLiteralDimensions(spec, lit);
        } else if (StringLiteral *lit = init->asStringLiteral()) {
          literal_dims.append(lit->arrayLength());
        }
      }
    }

    VarDeclSpecHelper helper(node, &literal_dims);
    type = resolveType(node->te(), &helper);
  } else {
    type = node->te().resolved();
  }

  if (!assignTypeToSymbol(sym, type))
    return;

  if (sym->isConstExpr() || !sym->canUseInConstExpr())
    return;

  // If we're currently trying to resolve this variable's constant
  // expression, report an error.
  if (sym->isResolvingConstExpr()) {
    cc_.report(node->loc(), rmsg::recursive_constexpr)
      << sym->name();

    // Pawn requires that const variables have constexprs, so we just set a
    // default one to quell as many other errors as we can. In the future we
    // may want to lax this restriction.
    sym->setConstExpr(DefaultValueForPlainType(sym->type()));
    return;
  }

  // We got a constexpr with no initialization. Just assume it's 0, but
  // report an error as SP1 does.
  if (!node->initialization()) {
    cc_.report(node->loc(), rmsg::constant_var_needs_constexpr)
      << sym->name();
    sym->setConstExpr(DefaultValueForPlainType(sym->type()));
    return;
  }

  sym->setResolvingConstExpr();

  // In Pawn, a const var *must* be a constexpr. We only care about this for
  // ints/floats since constexprs aren't really relevant yet otherwise.
  BoxedValue box;
  ConstantEvaluator ceval(cc_, this, ConstantEvaluator::Required);
  switch (ceval.Evaluate(node->initialization(), &box)) {
    case ConstantEvaluator::Ok:
      break;
    case ConstantEvaluator::NotConstant:
      cc_.report(node->loc(), rmsg::constant_var_needs_constexpr)
        << sym->name();
      // FALLTHROUGH.
    case ConstantEvaluator::TypeError:
      // Error has already been reported.
      box = DefaultValueForPlainType(sym->type());
      break;
    default:
      assert(false);
  }

  // :TODO: type check box

  sym->setConstExpr(box);
}