示例#1
0
TCResult ast::IfStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer)
{
	fs->pushLoc(this);
	defer(fs->popLoc());

	using Case = sst::IfStmt::Case;
	auto ret = util::pool<sst::IfStmt>(this->loc);

	fs->pushAnonymousTree();
	defer(fs->popTree());

	for(auto c : this->cases)
	{
		auto inits = util::map(c.inits, [fs](Stmt* s) -> auto { return s->typecheck(fs).stmt(); });
		auto cs = Case(c.cond->typecheck(fs).expr(), dcast(sst::Block, c.body->typecheck(fs).stmt()), inits);

		if(!cs.cond->type->isBoolType() && !cs.cond->type->isPointerType())
			error(cs.cond, "non-boolean expression with type '%s' cannot be used as a conditional", cs.cond->type);

		ret->cases.push_back(cs);

		iceAssert(ret->cases.back().body);
	}

	if(this->elseCase)
	{
		ret->elseCase = dcast(sst::Block, this->elseCase->typecheck(fs).stmt());
		iceAssert(ret->elseCase);
	}

	return TCResult(ret);
}
示例#2
0
void ConvType::parse() {
    // read in scope
    ConvScope scope( &cc->main_scope );
    cc->file_stack << ast_class->sourcefile;
    ast_class->block->parse_in( scope );

    base_size = scope.base_size;
    base_alig = scope.base_alig;
    for( ConvScope::NamedVar e : scope.variables ) {
        if ( e.stat )
            continue;
        if ( Ast_Assign *aa = dcast( e.expr.data ) ) {
            if ( aa->type ) {
                Past val = aa->val->parse_in( scope );
                ConvType *t = val->make_type();
                attributes << Attribute{ e.name, t, Past() };
            } else {
                TODO;
            }
            // aa->val
        } else
            TODO;
        PRINT( e.name );
        PRINT( e.expr );
    }

    cc->file_stack.pop_back();
}
示例#3
0
文件: errors.cpp 项目: flax-lang/flax
void OverloadError::post()
{
	// first, post the original error.
	this->top->wordsBeforeContext = "(call site)";
	this->top->post();

	// sort the candidates by line number
	// (idk maybe it's a windows thing but it feels like the order changes every so often)
	auto cds = std::vector<std::pair<Locatable*, ErrorMsg*>>(this->cands.begin(), this->cands.end());
	std::sort(cds.begin(), cds.end(), [](auto a, auto b) -> bool { return a.first->loc.line < b.first->loc.line; });

	// go through each candidate.
	int cand_counter = 1;
	for(auto [ loc, emg ] : cds)
	{
		if(emg->kind != ErrKind::Span)
		{
			emg->post();
		}
		else
		{
			auto spe = dcast(SpanError, emg);
			iceAssert(spe);

			spe->top = SimpleError::make(MsgType::Note, loc->loc, "candidate %d was defined here:", cand_counter++);
			spe->highlightActual = false;
			spe->post();
		}
	}

	for(auto sub : this->subs)
		sub->post();
}
示例#4
0
文件: enums.cpp 项目: flax-lang/flax
TCResult ast::EnumDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, const TypeParamMap_t& gmaps)
{
	fs->pushLoc(this);
	defer(fs->popLoc());

	auto tcr = this->generateDeclaration(fs, infer, gmaps);
	if(tcr.isParametric())  return tcr;
	else if(tcr.isError())  error(this, "failed to generate declaration for enum '%s'", this->name);

	auto defn = dcast(sst::EnumDefn, tcr.defn());
	iceAssert(defn);

	auto oldscope = fs->getCurrentScope();
	fs->teleportToScope(defn->id.scope);
	fs->pushTree(defn->id.name);

	if(this->memberType)	defn->memberType = fs->convertParserTypeToFIR(this->memberType);
	else					defn->memberType = fir::Type::getInt64();

	auto ety = fir::EnumType::get(defn->id, defn->memberType);

	size_t index = 0;
	for(auto cs : this->cases)
	{
		sst::Expr* val = 0;
		if(cs.value)
		{
			iceAssert(defn->memberType);
			val = cs.value->typecheck(fs, defn->memberType).expr();

			if(val->type != defn->memberType)
				error(cs.value, "mismatched type in enum case value; expected type '%s', but found type '%s'", defn->memberType, val->type);
		}

		auto ecd = util::pool<sst::EnumCaseDefn>(cs.loc);
		ecd->id = Identifier(cs.name, IdKind::Name);
		ecd->id.scope = fs->getCurrentScope();
		ecd->type = ety;
		ecd->parentEnum = defn;
		ecd->val = val;
		ecd->index = index++;

		defn->cases[cs.name] = ecd;
		fs->stree->addDefinition(cs.name, ecd);
	}

	defn->type = ety;

	fs->popTree();
	fs->teleportToScope(oldscope);

	return TCResult(defn);
}
示例#5
0
TCResult ast::DecompVarDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer)
{
	fs->pushLoc(this);
	defer(fs->popLoc());


	auto ret = util::pool<sst::DecompDefn>(this->loc);

	ret->immutable = this->immut;
	if(auto splat = dcast(ast::SplatOp, this->initialiser))
	{
		if(this->bindings.array)
		{
			SpanError::make(SimpleError::make(this->loc, "value splats can only be assigned to tuple decompositions"))
				->add(util::ESpan(this->initialiser->loc, ""))
				->postAndQuit();
		}

		bool isnest = false;
		for(const auto& b : this->bindings.inner)
		{
			if(b.name.empty())
			{
				isnest = true;
				break;
			}
		}

		if(isnest)
		{
			SpanError::make(SimpleError::make(this->loc, "cannot assign value splats to nested tuple decomposition"))
				->add(util::ESpan(this->initialiser->loc, ""))
				->postAndQuit();
		}

		// ok, at this point we should be fine.
		this->initialiser = util::pool<ast::LitTuple>(splat->loc, std::vector<ast::Expr*>(this->bindings.inner.size(), splat->expr));
	}

	ret->init = this->initialiser->typecheck(fs).expr();
	ret->bindings = fs->typecheckDecompositions(this->bindings, ret->init->type, this->immut, false);

	return TCResult(ret);
}
示例#6
0
static bool checkBlockPathsReturn(sst::TypecheckState* fs, sst::Block* block, fir::Type* retty, std::vector<sst::Block*>* faulty)
{
	// return value is whether or not the block had a return value;
	// true if all paths explicitly returned, false if not
	// this return value is used to determine whether we need to insert a
	// 'return void' thing.

	bool ret = false;
	for(size_t i = 0; i < block->statements.size(); i++)
	{
		auto& s = block->statements[i];
		if(auto retstmt = dcast(sst::ReturnStmt, s))
		{
			// ok...
			ret = true;
			auto t = retstmt->expectedType;
			iceAssert(t);

			if(fir::getCastDistance(t, retty) < 0)
			{
				if(retstmt->expectedType->isVoidType())
				{
					error(retstmt, "expected value after 'return'; function return type is '%s'", retty);
				}
				else
				{
					std::string msg;
					if(block->isSingleExpr) msg = "invalid single-expression with type '%s' in function returning '%s'";
					else                    msg = "mismatched type in return statement; function returns '%s', value has type '%s'";

					SpanError::make(SimpleError::make(retstmt->loc, msg.c_str(), retty, retstmt->expectedType))
						->add(util::ESpan(retstmt->value->loc, strprintf("type '%s'", retstmt->expectedType)))
						->append(SimpleError::make(MsgType::Note, fs->getCurrentFunction()->loc, "function definition is here:"))
						->postAndQuit();
				}
			}

			if(i != block->statements.size() - 1)
			{
				SimpleError::make(block->statements[i + 1]->loc, "unreachable code after return statement")
					->append(SimpleError::make(MsgType::Note, retstmt->loc, "return statement was here:"))
					->postAndQuit();;

				doTheExit();
			}
		}
		else /* if(i == block->statements.size() - 1) */
		{
			//* it's our duty to check the internals of these things regardless of their exhaustiveness
			//* so that we can check for the elision of the merge block.
			//? eg: if 's' itself does not have an else case, but say one of its branches is exhaustive (all arms return),
			//? then we can elide the merge block for that branch, even though we can't for 's' itself.
			//* this isn't strictly necessary (the program is still correct without it), but we generate nicer IR this way.

			bool exhausted = false;
			if(auto ifstmt = dcast(sst::IfStmt, s); ifstmt)
			{
				bool all = true;
				for(const auto& c: ifstmt->cases)
					all = all && checkBlockPathsReturn(fs, c.body, retty, faulty);

				exhausted = all && ifstmt->elseCase && checkBlockPathsReturn(fs, ifstmt->elseCase, retty, faulty);
				ifstmt->elideMergeBlock = exhausted;
			}
			else if(auto whileloop = dcast(sst::WhileLoop, s); whileloop)
			{
				exhausted = checkBlockPathsReturn(fs, whileloop->body, retty, faulty) && whileloop->isDoVariant;
				whileloop->elideMergeBlock = exhausted;
			}
			else if(auto feloop = dcast(sst::ForeachLoop, s); feloop)
			{
				// we don't set 'exhausted' here beacuse for loops cannot be guaranteed to be exhaustive.
				// but we still want to check the block inside for elision.
				feloop->elideMergeBlock = checkBlockPathsReturn(fs, feloop->body, retty, faulty);
			}

			ret = exhausted;
		}
示例#7
0
文件: driver.cpp 项目: flax-lang/flax
	TCResult resolveConstructorCall(TypecheckState* fs, TypeDefn* typedf, const std::vector<FnCallArgument>& arguments, const PolyArgMapping_t& pams)
	{
		//! ACHTUNG: DO NOT REARRANGE !
		//* NOTE: ClassDefn inherits from StructDefn *

		if(auto cls = dcast(ClassDefn, typedf))
		{
			// class initialisers must be called with named arguments only.
			for(const auto& arg : arguments)
			{
				if(arg.name.empty())
				{
					return TCResult(SimpleError::make(arg.loc, "arguments to class initialisers (for class '%s' here) must be named", cls->id.name));
				}
			}

			auto copy = arguments;

			//! SELF HANDLING (INSERTION) (CONSTRUCTOR)
			copy.push_back(FnCallArgument::make(cls->loc, "self", cls->type->getMutablePointerTo()));
			auto copy1 = copy;

			auto cand = resolveFunctionCallFromCandidates(fs, util::map(cls->initialisers, [](auto e) -> auto {
				return dcast(sst::Defn, e);
			}), &copy, pams, true);

			// TODO: support re-eval of constructor args!
			// TODO: support re-eval of constructor args!
			// TODO: support re-eval of constructor args!

			if(copy1 != copy)
				error(fs->loc(), "args changed for constructor call -- fixme!!!");

			if(cand.isError())
			{
				cand.error()->prepend(SimpleError::make(fs->loc(), "failed to find matching initialiser for class '%s':", cls->id.name));
				return TCResult(cand.error());
			}

			return TCResult(cand);
		}
		else if(auto str = dcast(StructDefn, typedf))
		{
			std::set<std::string> fieldNames;
			for(auto f : str->fields)
				fieldNames.insert(f->id.name);

			auto err = resolver::verifyStructConstructorArguments(fs->loc(), str->id.name, fieldNames, arguments);

			if(err.second != nullptr)
				return TCResult(err.second);

			// in actual fact we just return the thing here. sigh.
			return TCResult(str);
		}
		else if(auto uvd = dcast(sst::UnionVariantDefn, typedf))
		{
			// TODO: support re-eval of constructor args!
			// TODO: support re-eval of constructor args!
			// TODO: support re-eval of constructor args!

			auto copy = arguments;

			auto ret = resolver::resolveAndInstantiatePolymorphicUnion(fs, uvd, &copy, /* type_infer: */ nullptr, /* isFnCall: */ true);

			if(copy != arguments)
				error(fs->loc(), "args changed for constructor call -- fixme!!!");

			return ret;
		}
		else
		{
			return TCResult(
				SimpleError::make(fs->loc(), "unsupported constructor call on type '%s'", typedf->id.name)
			    ->append(SimpleError::make(typedf->loc, "type was defined here:"))
			);
		}
	}