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; }
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); }