<commit_before>#include "Raycaster.hpp"
#include "helpers/Random.hpp"
#include "helpers/System.hpp"
#include <queue>
#include <stack>

Raycaster::Ray::Ray(const glm::vec3 & origin, const glm::vec3 & direction) : pos(origin), dir(glm::normalize(direction)){
}

Raycaster::RayHit::RayHit() : hit(false), dist(std::numeric_limits<float>::max()), u(0.0f), v(0.0f), localId(0), meshId(0) {
}

Raycaster::RayHit::RayHit(float distance, float uu, float vv, unsigned long lid, unsigned long mid) :
	hit(true), dist(distance), u(uu), v(vv), w(1.0f - uu - vv), localId(lid), meshId(mid) {
}

Raycaster::Raycaster(){

}

void Raycaster::addMesh(const Mesh & mesh, const glm::mat4 & model){
	const unsigned long indexOffset = (unsigned long)(_vertices.size());

	// Start by copying all vertices.
	const size_t startIndex = _vertices.size();
	_vertices.insert(_vertices.end(), mesh.positions.begin(), mesh.positions.end());
	const size_t endIndex = _vertices.size();

	if(model != glm::mat4(1.0f)){
		for(size_t vid = startIndex; vid < endIndex; ++vid){
			_vertices[vid] = glm::vec3(model * glm::vec4(_vertices[vid], 1.0f));
		}
	}

	const size_t trianglesCount = mesh.indices.size()/3;
	for(size_t tid = 0; tid < trianglesCount; ++tid){
		const size_t localId = 3 * tid;
		TriangleInfos triInfos;
		triInfos.v0 = indexOffset + mesh.indices[localId + 0];
		triInfos.v1 = indexOffset + mesh.indices[localId + 1];
		triInfos.v2 = indexOffset + mesh.indices[localId + 2];
		triInfos.localId = (unsigned long)(localId);
		triInfos.meshId = _meshCount;
		triInfos.box = BoundingBox(_vertices[triInfos.v0], _vertices[triInfos.v1], _vertices[triInfos.v2]);
		_triangles.push_back(triInfos);
	}

	Log::Info() << "[Raycaster]" << " Mesh " << _meshCount << " added, " << trianglesCount << " triangles, " << _vertices.size() - indexOffset << " vertices." << std::endl;

	++_meshCount;
}

void Raycaster::updateHierarchy(){

	Log::Info() << "[Raycaster] Building hierarchy... " << std::flush;

	struct SetInfos {
		size_t begin;
		size_t count;
		long parent;
		bool right;
	};

	std::stack<SetInfos> remainingSets;
	remainingSets.push({0, _triangles.size(), -1, false});

	while(!remainingSets.empty()){
		// Get the next node to process on the stack.
		const SetInfos current(remainingSets.top());
		remainingSets.pop();
		const size_t begin = current.begin;
		const size_t count = current.count;

		// Compute the global bounding box.
		BoundingBox global(_triangles[begin].box);
		for(size_t tid = 1; tid < count; ++tid){
			global.merge(_triangles[begin+tid].box);
		}

		// Create the node.
		_hierarchy.emplace_back();
		const size_t nodeId = _hierarchy.size()-1;
		Node currentNode;
		currentNode.box = global;
		// If the triangles count is low enough, we have a leaf.
		if(count < 3){
			currentNode.leaf = true;
			currentNode.left = begin;
			currentNode.right = count;
		} else {
			currentNode.leaf = false;

			// Pick the dimension along which the global bounding box is the largest.
			const glm::vec3 boxSize = global.getSize();
			const int axis = (boxSize.x >= boxSize.y && boxSize.x >= boxSize.z) ? 0 : (boxSize.y >= boxSize.z ? 1 : 2);
			// Compute the midpoint of all triangles centroids along the picked axis.
			float abscisse = 0.0f;
			for(size_t tid = 0; tid < count; ++tid){
				abscisse += _triangles[begin+tid].box.getCentroid()[axis];
			}
			abscisse /= float(count);

			// Split in two subnodes.
			// Main criterion: split at the midpoint along the chosen axis.
			const auto split = std::partition(_triangles.begin()+begin, _triangles.begin()+begin+count, [abscisse, axis](const TriangleInfos & t0){
				return t0.box.getCentroid()[axis] < abscisse;
			});
			size_t splitCount = std::distance(_triangles.begin()+begin, split);

			// Fallback criterion: split in two equal size subsets.
			// This can happen in case the primitive boxes overlap a lot,
			// or in case of equal coordinates along the chosen axis.
			if(splitCount == 0 || splitCount == count){
				splitCount = count/2;
			}

			// Create the two sub-nodes.
			remainingSets.push({begin, splitCount, long(nodeId), false});
			remainingSets.push({begin+splitCount, count-splitCount, long(nodeId), true});
		}
		_hierarchy[nodeId] = currentNode;

		// Update the parent node with infos.
		if(current.parent >= 0){
			if(current.right){
				_hierarchy[current.parent].right = nodeId;
			} else {
				_hierarchy[current.parent].left = nodeId;
			}
		}

	}
	Log::Info() << "Done." << std::endl;
}

void Raycaster::createBVHMeshes(std::vector<Mesh> &meshes) const {
	meshes.clear();
	// We want a mesh where the nodes are in increasing order of depth.
	struct NodeLocation {
		size_t node;
		size_t depth;
	};

	std::vector<NodeLocation> sortedNodes;
	sortedNodes.reserve(_hierarchy.size());

	// Breadth-first tree exploration.
	std::queue<NodeLocation> nodesToVisit;
	nodesToVisit.push({0, 0});
	size_t maxDepth = 0;
	while(!nodesToVisit.empty()){
		const NodeLocation & location = nodesToVisit.front();
		sortedNodes.push_back(location);
		// If this is not a leaf, enqueue the two children nodes.
		const Node & node = _hierarchy[location.node];
		if(!node.leaf){
			nodesToVisit.push({ node.left , location.depth+1 });
			nodesToVisit.push({ node.right, location.depth+1 });
		}
		// Find the max depth.
		maxDepth = std::max(maxDepth, location.depth);
		// Remove the current node from the visit queue.
		nodesToVisit.pop();
	}

	meshes.resize(maxDepth+1);

	// For each node, generate a wireframe bounding box.
	for(const auto & location : sortedNodes){
		const Node & node = _hierarchy[location.node];

		// Compute relative depth for colorisation.
		float depth = float(location.depth) / float(maxDepth);
		// We have fewer boxes at low depth, skew the hue scale.
		depth *= depth;
		// Decrease luminosity as we go deeper.
		const float lum = 0.5*(1.0f - depth);
		const glm::vec3 color = System::hslToRgb(glm::vec3(300.0f*depth, 0.9f, lum));

		// Setup vertices.
		Mesh & mesh = meshes[location.depth];
		const size_t firstIndex = mesh.positions.size();
		const auto corners = node.box.getCorners();
		for(const auto & corner : corners){
			mesh.positions.push_back(corner);
			mesh.colors.push_back(color);
		}
		// Setup degenerate triangles for each line.
		const std::vector<unsigned int> indices = {
			0,1,0, 0,2,0, 1,3,1, 2,3,2, 4,5,4, 4,6,4, 5,7,5, 6,7,6, 1,5,1, 0,4,0, 2,6,2, 3,7,3
		};
		for(const int iid : indices){
			mesh.indices.push_back(firstIndex + iid);
		}
	}
}

const Raycaster::RayHit Raycaster::intersects(const glm::vec3 & origin, const glm::vec3 & direction, float mini, float maxi) const {
	const Ray ray(origin, direction);

	std::stack<size_t> nodesToTest;
	nodesToTest.push(0);

	RayHit bestHit;
	while(!nodesToTest.empty()){
		const Node & node = _hierarchy[nodesToTest.top()];
		nodesToTest.pop();

		// If the ray doesn't intersect the bounding box, move to the next node.
		if(!Raycaster::intersects(ray, node.box, mini, maxi)){
			continue;
		}
		// If the node is a leaf, test all included triangles.
		if(node.leaf){
			for(size_t tid = 0; tid < node.right; ++tid){
				const auto & tri = _triangles[node.left + tid];
				const RayHit hit = intersects(ray, tri, mini, maxi);
				// We found a valid hit.
				if(hit.hit && hit.dist < bestHit.dist){
					bestHit = hit;
					maxi = bestHit.dist;
				}
			}
			// Move to the next node.
			continue;
		}
		// Else, intersect both child nodes.
		nodesToTest.push(node.left);
		nodesToTest.push(node.right);
	}
	return bestHit;
}

bool Raycaster::intersectsAny(const glm::vec3 & origin, const glm::vec3 & direction, float mini, float maxi) const {
	const Ray ray(origin, direction);

	std::stack<size_t> nodesToTest;
	nodesToTest.push(0);

	while(!nodesToTest.empty()){
		const Node & node = _hierarchy[nodesToTest.top()];
		nodesToTest.pop();
		// If the ray doesn't intersect the bounding box, move to the next node.
		if(!Raycaster::intersects(ray, node.box, mini, maxi)){
			continue;
		}
		// If the node is a leaf, test all included triangles.
		if(node.leaf){
			RayHit finalHit;
			for(size_t tid = 0; tid < node.right; ++tid){
				const auto & tri = _triangles[node.left + tid];
				if(intersects(ray, tri, mini, maxi).hit){
					return true;
				}
			}
			// No intersection move to the next node.
			continue;
		}
		// Check if any of the children is hit.
		nodesToTest.push(node.left);
		nodesToTest.push(node.right);
	}
	return false;
}

bool Raycaster::visible(const glm::vec3 & p0, const glm::vec3 & p1) const {
	const glm::vec3 direction = p1 - p0;
	const float maxi = glm::length(direction);
	return !intersectsAny(p0, direction, 0.0001f, maxi);
}

const Raycaster::RayHit Raycaster::intersects(const Raycaster::Ray & ray, const TriangleInfos & tri, float mini, float maxi) const {
	// Implement Moller-Trumbore intersection test.
	const glm::vec3 & v0 = _vertices[tri.v0];
	const glm::vec3 v01 = _vertices[tri.v1] - v0;
	const glm::vec3 v02 = _vertices[tri.v2] - v0;
	const glm::vec3 p = glm::cross(ray.dir, v02);
	const float det = glm::dot(v01, p);

	if(std::abs(det) < 0.00001f){
		return RayHit();
	}

	const float invDet = 1.0f / det;
	const glm::vec3 q = ray.pos - v0;
	const float u = invDet * glm::dot(q, p);
	if(u < 0.0f || u > 1.0f){
		return RayHit();
	}

	const glm::vec3 r = glm::cross(q, v01);
	const float v = invDet * glm::dot(ray.dir, r);
	if(v < 0.0f || (u+v) > 1.0f){
		return RayHit();
	}

	const float t = invDet * glm::dot(v02, r);
	if(t > mini && t < maxi){
		return RayHit(t, u, v, tri.localId, tri.meshId);
	}
	return RayHit();
}

bool Raycaster::intersects(const Raycaster::Ray & ray, const BoundingBox & box, float mini, float maxi){
	const glm::vec3 minRatio = (box.minis - ray.pos) / ray.dir;
	const glm::vec3 maxRatio = (box.maxis - ray.pos) / ray.dir;
	const glm::vec3 minFinal = glm::min(minRatio, maxRatio);
	const glm::vec3 maxFinal = glm::max(minRatio, maxRatio);

	const float closest  = std::max(minFinal[0], std::max(minFinal[1], minFinal[2]));
	const float furthest = std::min(maxFinal[0], std::min(maxFinal[1], maxFinal[2]));

	return std::max(closest, mini) <= std::min(furthest, maxi);
}

glm::vec3 Raycaster::interpolatePosition(const RayHit & hit, const Mesh & geometry){
	const unsigned long triId = hit.localId;
	const unsigned long i0 = geometry.indices[triId  ];
	const unsigned long i1 = geometry.indices[triId+1];
	const unsigned long i2 = geometry.indices[triId+2];
	return hit.w * geometry.positions[i0] + hit.u * geometry.positions[i1] + hit.v * geometry.positions[i2];
}

glm::vec3 Raycaster::interpolateNormal(const RayHit & hit, const Mesh & geometry){
	const unsigned long triId = hit.localId;
	const unsigned long i0 = geometry.indices[triId  ];
	const unsigned long i1 = geometry.indices[triId+1];
	const unsigned long i2 = geometry.indices[triId+2];
	const glm::vec3 n = hit.w * geometry.normals[i0] + hit.u * geometry.normals[i1] + hit.v * geometry.normals[i2];
	return glm::normalize(n);
}

glm::vec2 Raycaster::interpolateUV(const RayHit & hit, const Mesh & geometry){
	const unsigned long triId = hit.localId;
	const unsigned long i0 = geometry.indices[triId  ];
	const unsigned long i1 = geometry.indices[triId+1];
	const unsigned long i2 = geometry.indices[triId+2];
	return hit.w * geometry.texcoords[i0] + hit.u * geometry.texcoords[i1] + hit.v * geometry.texcoords[i2];
}
<commit_msg>Raycaster: fix precision issue in ray-triangle intersection test.<commit_after>#include "Raycaster.hpp"
#include "helpers/Random.hpp"
#include "helpers/System.hpp"
#include <queue>
#include <stack>

Raycaster::Ray::Ray(const glm::vec3 & origin, const glm::vec3 & direction) : pos(origin), dir(glm::normalize(direction)){
}

Raycaster::RayHit::RayHit() : hit(false), dist(std::numeric_limits<float>::max()), u(0.0f), v(0.0f), localId(0), meshId(0) {
}

Raycaster::RayHit::RayHit(float distance, float uu, float vv, unsigned long lid, unsigned long mid) :
	hit(true), dist(distance), u(uu), v(vv), w(1.0f - uu - vv), localId(lid), meshId(mid) {
}

Raycaster::Raycaster(){

}

void Raycaster::addMesh(const Mesh & mesh, const glm::mat4 & model){
	const unsigned long indexOffset = (unsigned long)(_vertices.size());

	// Start by copying all vertices.
	const size_t startIndex = _vertices.size();
	_vertices.insert(_vertices.end(), mesh.positions.begin(), mesh.positions.end());
	const size_t endIndex = _vertices.size();

	if(model != glm::mat4(1.0f)){
		for(size_t vid = startIndex; vid < endIndex; ++vid){
			_vertices[vid] = glm::vec3(model * glm::vec4(_vertices[vid], 1.0f));
		}
	}

	const size_t trianglesCount = mesh.indices.size()/3;
	for(size_t tid = 0; tid < trianglesCount; ++tid){
		const size_t localId = 3 * tid;
		TriangleInfos triInfos;
		triInfos.v0 = indexOffset + mesh.indices[localId + 0];
		triInfos.v1 = indexOffset + mesh.indices[localId + 1];
		triInfos.v2 = indexOffset + mesh.indices[localId + 2];
		triInfos.localId = (unsigned long)(localId);
		triInfos.meshId = _meshCount;
		triInfos.box = BoundingBox(_vertices[triInfos.v0], _vertices[triInfos.v1], _vertices[triInfos.v2]);
		_triangles.push_back(triInfos);
	}

	Log::Info() << "[Raycaster]" << " Mesh " << _meshCount << " added, " << trianglesCount << " triangles, " << _vertices.size() - indexOffset << " vertices." << std::endl;

	++_meshCount;
}

void Raycaster::updateHierarchy(){

	Log::Info() << "[Raycaster] Building hierarchy... " << std::flush;

	struct SetInfos {
		size_t begin;
		size_t count;
		long parent;
		bool right;
	};

	std::stack<SetInfos> remainingSets;
	remainingSets.push({0, _triangles.size(), -1, false});

	while(!remainingSets.empty()){
		// Get the next node to process on the stack.
		const SetInfos current(remainingSets.top());
		remainingSets.pop();
		const size_t begin = current.begin;
		const size_t count = current.count;

		// Compute the global bounding box.
		BoundingBox global(_triangles[begin].box);
		for(size_t tid = 1; tid < count; ++tid){
			global.merge(_triangles[begin+tid].box);
		}

		// Create the node.
		_hierarchy.emplace_back();
		const size_t nodeId = _hierarchy.size()-1;
		Node currentNode;
		currentNode.box = global;
		// If the triangles count is low enough, we have a leaf.
		if(count < 3){
			currentNode.leaf = true;
			currentNode.left = begin;
			currentNode.right = count;
		} else {
			currentNode.leaf = false;

			// Pick the dimension along which the global bounding box is the largest.
			const glm::vec3 boxSize = global.getSize();
			const int axis = (boxSize.x >= boxSize.y && boxSize.x >= boxSize.z) ? 0 : (boxSize.y >= boxSize.z ? 1 : 2);
			// Compute the midpoint of all triangles centroids along the picked axis.
			float abscisse = 0.0f;
			for(size_t tid = 0; tid < count; ++tid){
				abscisse += _triangles[begin+tid].box.getCentroid()[axis];
			}
			abscisse /= float(count);

			// Split in two subnodes.
			// Main criterion: split at the midpoint along the chosen axis.
			const auto split = std::partition(_triangles.begin()+begin, _triangles.begin()+begin+count, [abscisse, axis](const TriangleInfos & t0){
				return t0.box.getCentroid()[axis] < abscisse;
			});
			size_t splitCount = std::distance(_triangles.begin()+begin, split);

			// Fallback criterion: split in two equal size subsets.
			// This can happen in case the primitive boxes overlap a lot,
			// or in case of equal coordinates along the chosen axis.
			if(splitCount == 0 || splitCount == count){
				splitCount = count/2;
			}

			// Create the two sub-nodes.
			remainingSets.push({begin, splitCount, long(nodeId), false});
			remainingSets.push({begin+splitCount, count-splitCount, long(nodeId), true});
		}
		_hierarchy[nodeId] = currentNode;

		// Update the parent node with infos.
		if(current.parent >= 0){
			if(current.right){
				_hierarchy[current.parent].right = nodeId;
			} else {
				_hierarchy[current.parent].left = nodeId;
			}
		}

	}
	Log::Info() << "Done." << std::endl;
}

void Raycaster::createBVHMeshes(std::vector<Mesh> &meshes) const {
	meshes.clear();
	// We want a mesh where the nodes are in increasing order of depth.
	struct NodeLocation {
		size_t node;
		size_t depth;
	};

	std::vector<NodeLocation> sortedNodes;
	sortedNodes.reserve(_hierarchy.size());

	// Breadth-first tree exploration.
	std::queue<NodeLocation> nodesToVisit;
	nodesToVisit.push({0, 0});
	size_t maxDepth = 0;
	while(!nodesToVisit.empty()){
		const NodeLocation & location = nodesToVisit.front();
		sortedNodes.push_back(location);
		// If this is not a leaf, enqueue the two children nodes.
		const Node & node = _hierarchy[location.node];
		if(!node.leaf){
			nodesToVisit.push({ node.left , location.depth+1 });
			nodesToVisit.push({ node.right, location.depth+1 });
		}
		// Find the max depth.
		maxDepth = std::max(maxDepth, location.depth);
		// Remove the current node from the visit queue.
		nodesToVisit.pop();
	}

	meshes.resize(maxDepth+1);

	// For each node, generate a wireframe bounding box.
	for(const auto & location : sortedNodes){
		const Node & node = _hierarchy[location.node];

		// Compute relative depth for colorisation.
		float depth = float(location.depth) / float(maxDepth);
		// We have fewer boxes at low depth, skew the hue scale.
		depth *= depth;
		// Decrease luminosity as we go deeper.
		const float lum = 0.5*(1.0f - depth);
		const glm::vec3 color = System::hslToRgb(glm::vec3(300.0f*depth, 0.9f, lum));

		// Setup vertices.
		Mesh & mesh = meshes[location.depth];
		const size_t firstIndex = mesh.positions.size();
		const auto corners = node.box.getCorners();
		for(const auto & corner : corners){
			mesh.positions.push_back(corner);
			mesh.colors.push_back(color);
		}
		// Setup degenerate triangles for each line.
		const std::vector<unsigned int> indices = {
			0,1,0, 0,2,0, 1,3,1, 2,3,2, 4,5,4, 4,6,4, 5,7,5, 6,7,6, 1,5,1, 0,4,0, 2,6,2, 3,7,3
		};
		for(const int iid : indices){
			mesh.indices.push_back(firstIndex + iid);
		}
	}
}

const Raycaster::RayHit Raycaster::intersects(const glm::vec3 & origin, const glm::vec3 & direction, float mini, float maxi) const {
	const Ray ray(origin, direction);

	std::stack<size_t> nodesToTest;
	nodesToTest.push(0);

	RayHit bestHit;
	while(!nodesToTest.empty()){
		const Node & node = _hierarchy[nodesToTest.top()];
		nodesToTest.pop();

		// If the ray doesn't intersect the bounding box, move to the next node.
		if(!Raycaster::intersects(ray, node.box, mini, maxi)){
			continue;
		}
		// If the node is a leaf, test all included triangles.
		if(node.leaf){
			for(size_t tid = 0; tid < node.right; ++tid){
				const auto & tri = _triangles[node.left + tid];
				const RayHit hit = intersects(ray, tri, mini, maxi);
				// We found a valid hit.
				if(hit.hit && hit.dist < bestHit.dist){
					bestHit = hit;
					maxi = bestHit.dist;
				}
			}
			// Move to the next node.
			continue;
		}
		// Else, intersect both child nodes.
		nodesToTest.push(node.left);
		nodesToTest.push(node.right);
	}
	return bestHit;
}

bool Raycaster::intersectsAny(const glm::vec3 & origin, const glm::vec3 & direction, float mini, float maxi) const {
	const Ray ray(origin, direction);

	std::stack<size_t> nodesToTest;
	nodesToTest.push(0);

	while(!nodesToTest.empty()){
		const Node & node = _hierarchy[nodesToTest.top()];
		nodesToTest.pop();
		// If the ray doesn't intersect the bounding box, move to the next node.
		if(!Raycaster::intersects(ray, node.box, mini, maxi)){
			continue;
		}
		// If the node is a leaf, test all included triangles.
		if(node.leaf){
			RayHit finalHit;
			for(size_t tid = 0; tid < node.right; ++tid){
				const auto & tri = _triangles[node.left + tid];
				if(intersects(ray, tri, mini, maxi).hit){
					return true;
				}
			}
			// No intersection move to the next node.
			continue;
		}
		// Check if any of the children is hit.
		nodesToTest.push(node.left);
		nodesToTest.push(node.right);
	}
	return false;
}

bool Raycaster::visible(const glm::vec3 & p0, const glm::vec3 & p1) const {
	const glm::vec3 direction = p1 - p0;
	const float maxi = glm::length(direction);
	return !intersectsAny(p0, direction, 0.0001f, maxi);
}

const Raycaster::RayHit Raycaster::intersects(const Raycaster::Ray & ray, const TriangleInfos & tri, float mini, float maxi) const {
	// Implement Moller-Trumbore intersection test.
	const glm::vec3 & v0 = _vertices[tri.v0];
	const glm::vec3 v01 = _vertices[tri.v1] - v0;
	const glm::vec3 v02 = _vertices[tri.v2] - v0;
	const glm::vec3 p = glm::cross(ray.dir, v02);
	const float det = glm::dot(v01, p);

	if(std::abs(det) < std::numeric_limits<float>::epsilon()){
		return RayHit();
	}

	const float invDet = 1.0f / det;
	const glm::vec3 q = ray.pos - v0;
	const float u = invDet * glm::dot(q, p);
	if(u < 0.0f || u > 1.0f){
		return RayHit();
	}

	const glm::vec3 r = glm::cross(q, v01);
	const float v = invDet * glm::dot(ray.dir, r);
	if(v < 0.0f || (u+v) > 1.0f){
		return RayHit();
	}

	const float t = invDet * glm::dot(v02, r);
	if(t > mini && t < maxi){
		return RayHit(t, u, v, tri.localId, tri.meshId);
	}
	return RayHit();
}

bool Raycaster::intersects(const Raycaster::Ray & ray, const BoundingBox & box, float mini, float maxi){
	const glm::vec3 minRatio = (box.minis - ray.pos) / ray.dir;
	const glm::vec3 maxRatio = (box.maxis - ray.pos) / ray.dir;
	const glm::vec3 minFinal = glm::min(minRatio, maxRatio);
	const glm::vec3 maxFinal = glm::max(minRatio, maxRatio);

	const float closest  = std::max(minFinal[0], std::max(minFinal[1], minFinal[2]));
	const float furthest = std::min(maxFinal[0], std::min(maxFinal[1], maxFinal[2]));

	return std::max(closest, mini) <= std::min(furthest, maxi);
}

glm::vec3 Raycaster::interpolatePosition(const RayHit & hit, const Mesh & geometry){
	const unsigned long triId = hit.localId;
	const unsigned long i0 = geometry.indices[triId  ];
	const unsigned long i1 = geometry.indices[triId+1];
	const unsigned long i2 = geometry.indices[triId+2];
	return hit.w * geometry.positions[i0] + hit.u * geometry.positions[i1] + hit.v * geometry.positions[i2];
}

glm::vec3 Raycaster::interpolateNormal(const RayHit & hit, const Mesh & geometry){
	const unsigned long triId = hit.localId;
	const unsigned long i0 = geometry.indices[triId  ];
	const unsigned long i1 = geometry.indices[triId+1];
	const unsigned long i2 = geometry.indices[triId+2];
	const glm::vec3 n = hit.w * geometry.normals[i0] + hit.u * geometry.normals[i1] + hit.v * geometry.normals[i2];
	return glm::normalize(n);
}

glm::vec2 Raycaster::interpolateUV(const RayHit & hit, const Mesh & geometry){
	const unsigned long triId = hit.localId;
	const unsigned long i0 = geometry.indices[triId  ];
	const unsigned long i1 = geometry.indices[triId+1];
	const unsigned long i2 = geometry.indices[triId+2];
	return hit.w * geometry.texcoords[i0] + hit.u * geometry.texcoords[i1] + hit.v * geometry.texcoords[i2];
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include "util/flet.h"
#include "util/scoped_map.h"
#include "util/interrupt.h"
#include "kernel/environment.h"
#include "kernel/normalizer.h"
#include "kernel/builtin.h"
#include "kernel/kernel_exception.h"
#include "kernel/type_checker_justification.h"
#include "kernel/instantiate.h"
#include "kernel/free_vars.h"
#include "kernel/metavar.h"
#include "library/kernel_bindings.h"
#include "library/type_inferer.h"

namespace lean {
static name g_x_name("x");
class type_inferer::imp {
    typedef scoped_map<expr, expr, expr_hash_alloc, expr_eqp> cache;
    typedef buffer<unification_constraint> unification_constraints;

    ro_environment            m_env;
    context                   m_ctx;
    cached_metavar_env        m_menv;
    unification_constraints * m_uc;
    normalizer                m_normalizer;
    cache                     m_cache;

    expr normalize(expr const & e, context const & ctx) {
        return m_normalizer(e, ctx);
    }

    expr check_type(expr const & e, expr const & s, context const & ctx) {
        if (is_type(e))
            return e;
        if (is_bool(e))
            return Type();
        expr u = normalize(e, ctx);
        if (is_type(u))
            return u;
        if (is_bool(u))
            return Type();
        if (has_metavar(u) && m_menv && m_uc) {
            justification jst = mk_type_expected_justification(ctx, s);
            m_uc->push_back(mk_convertible_constraint(ctx, u, TypeU, jst));
            return u;
        }
        throw type_expected_exception(m_env, ctx, s);
    }

    expr get_range(expr t, expr const & e, context const & ctx) {
        unsigned num = num_args(e) - 1;
        while (num > 0) {
            --num;
            if (is_pi(t)) {
                t = abst_body(t);
            } else {
                t = m_normalizer(t, ctx);
                if (is_pi(t)) {
                    t = abst_body(t);
                } else if (has_metavar(t) && m_menv && m_uc) {
                    // Create two fresh variables A and B,
                    // and assign r == (Pi(x : A), B)
                    expr A   = m_menv->mk_metavar(ctx);
                    expr B   = m_menv->mk_metavar(extend(ctx, g_x_name, A));
                    expr p   = mk_pi(g_x_name, A, B);
                    justification jst = mk_function_expected_justification(ctx, e);
                    m_uc->push_back(mk_eq_constraint(ctx, t, p, jst));
                    t        = abst_body(p);
                } else {
                    throw function_expected_exception(m_env, ctx, e);
                }
            }
        }
        if (closed(t))
            return t;
        else
            return instantiate(t, num_args(e)-1, &arg(e, 1));
    }

    expr infer_type(expr const & e, context const & ctx) {
        // cheap cases, we do not cache results
        switch (e.kind()) {
        case expr_kind::MetaVar:
            if (m_menv) {
                if (m_menv->is_assigned(e))
                    return infer_type(*(m_menv->get_subst(e)), ctx);
                else
                    return m_menv->get_type(e);
            } else {
                throw unexpected_metavar_occurrence(m_env, e);
            }
        case expr_kind::Constant: {
            if (const_type(e)) {
                return *const_type(e);
            } else {
                object const & obj = m_env->get_object(const_name(e));
                if (obj.has_type())
                    return obj.get_type();
                else
                    throw has_no_type_exception(m_env, e);
            }
            break;
        }
        case expr_kind::Var: {
            auto p = lookup_ext(ctx, var_idx(e));
            context_entry const & ce = p.first;
            if (ce.get_domain()) {
                context const & ce_ctx   = p.second;
                return lift_free_vars(*(ce.get_domain()), ctx.size() - ce_ctx.size());
            }
            // Remark: the case where ce.get_domain() is not
            // available is not considered cheap.
            break;
        }
        case expr_kind::Eq:
            return mk_bool_type();
        case expr_kind::Value:
            return to_value(e).get_type();
        case expr_kind::Type:
            return mk_type(ty_level(e) + 1);
        case expr_kind::App: case expr_kind::Lambda:
        case expr_kind::Pi:  case expr_kind::Let:
            break; // expensive cases
        }

        check_system("type inference");
        bool shared = false;
        if (is_shared(e)) {
            shared = true;
            auto it = m_cache.find(e);
            if (it != m_cache.end())
                return it->second;
        }

        expr r;
        switch (e.kind()) {
        case expr_kind::Constant: case expr_kind::Eq:
        case expr_kind::Value:    case expr_kind::Type:
        case expr_kind::MetaVar:
            lean_unreachable(); // LCOV_EXCL_LINE
        case expr_kind::Var: {
            auto p = lookup_ext(ctx, var_idx(e));
            context_entry const & ce = p.first;
            context const & ce_ctx   = p.second;
            lean_assert(!ce.get_domain());
            r = lift_free_vars(infer_type(*(ce.get_body()), ce_ctx), ctx.size() - ce_ctx.size());
            break;
        }
        case expr_kind::App: {
            expr const & f = arg(e, 0);
            expr f_t = infer_type(f, ctx);
            r = get_range(f_t, e, ctx);
            break;
        }
        case expr_kind::Lambda: {
            cache::mk_scope sc(m_cache);
            r = mk_pi(abst_name(e), abst_domain(e), infer_type(abst_body(e), extend(ctx, abst_name(e), abst_domain(e))));
            break;
        }
        case expr_kind::Pi: {
            expr t1  = check_type(infer_type(abst_domain(e), ctx), abst_domain(e), ctx);
            expr t2;
            context new_ctx = extend(ctx, abst_name(e), abst_domain(e));
            {
                cache::mk_scope sc(m_cache);
                t2 = check_type(infer_type(abst_body(e), new_ctx), abst_body(e), new_ctx);
            }
            if (is_type(t1) && is_type(t2)) {
                r = mk_type(max(ty_level(t1), ty_level(t2)));
            } else {
                lean_assert(m_uc);
                justification jst = mk_max_type_justification(ctx, e);
                r = m_menv->mk_metavar(ctx);
                m_uc->push_back(mk_max_constraint(new_ctx, lift_free_vars(t1, 0, 1), t2, r, jst));
            }
            break;
        }
        case expr_kind::Let: {
            cache::mk_scope sc(m_cache);
            r = infer_type(let_body(e), extend(ctx, let_name(e), let_type(e), let_value(e)));
            break;
        }}

        if (shared) {
            m_cache.insert(e, r);
        }
        return r;
    }

    void set_ctx(context const & ctx) {
        if (!is_eqp(m_ctx, ctx)) {
            clear();
            m_ctx = ctx;
        }
    }

public:
    imp(ro_environment const & env):
        m_env(env),
        m_normalizer(env) {
        m_uc = nullptr;
    }

    expr operator()(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc) {
        set_ctx(ctx);
        if (m_menv.update(menv))
            clear_cache();
        flet<unification_constraints*> set(m_uc, uc);
        return infer_type(e, ctx);
    }

    void clear_cache() {
        m_cache.clear();
        m_normalizer.clear();
    }

    void clear() {
        clear_cache();
        m_menv.clear();
        m_ctx = context();
    }

    bool is_proposition(expr const & e, context const & ctx, optional<metavar_env> const & menv) {
        // Catch easy cases
        switch (e.kind()) {
        case expr_kind::Lambda: case expr_kind::Pi: case expr_kind::Type: return false;
        case expr_kind::Eq: return true;
        default: break;
        }
        expr t = operator()(e, ctx, menv, nullptr);
        if (is_bool(t))
            return true;
        else
            return is_bool(normalize(t, ctx));
    }
};
type_inferer::type_inferer(ro_environment const & env):m_ptr(new imp(env)) {}
type_inferer::~type_inferer() {}
expr type_inferer::operator()(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc) {
    return m_ptr->operator()(e, ctx, menv, uc);
}
expr type_inferer::operator()(expr const & e, context const & ctx, metavar_env const & menv, buffer<unification_constraint> & uc) {
    return m_ptr->operator()(e, ctx, some_menv(menv), &uc);
}
expr type_inferer::operator()(expr const & e, context const & ctx) {
    return operator()(e, ctx, none_menv(), nullptr);
}
bool type_inferer::is_proposition(expr const & e, context const & ctx, optional<metavar_env> const & menv) {
    return m_ptr->is_proposition(e, ctx, menv);
}
bool type_inferer::is_proposition(expr const & e, context const & ctx) {
    return is_proposition(e, ctx, none_menv());
}
bool type_inferer::is_proposition(expr const & e, context const & ctx, metavar_env const & menv) {
    return is_proposition(e, ctx, some_menv(menv));
}
void type_inferer::clear() { m_ptr->clear(); }

constexpr char const * type_inferer_mt = "type_inferer";
type_inferer & to_type_inferer(lua_State * L, int i) { return *static_cast<type_inferer*>(luaL_checkudata(L, i, type_inferer_mt)); }
DECL_PRED(type_inferer)
DECL_GC(type_inferer)

static int type_inferer_call(lua_State * L) {
    int nargs = lua_gettop(L);
    type_inferer & inferer = to_type_inferer(L, 1);
    if (nargs == 2)
        return push_expr(L, inferer(to_expr(L, 2)));
    else
        return push_expr(L, inferer(to_expr(L, 2), to_context(L, 3)));
}

static int type_inferer_clear(lua_State * L) {
    to_type_inferer(L, 1).clear();
    return 0;
}

static int mk_type_inferer(lua_State * L) {
    void * mem = lua_newuserdata(L, sizeof(type_inferer));
    new (mem) type_inferer(to_environment(L, 1));
    luaL_getmetatable(L, type_inferer_mt);
    lua_setmetatable(L, -2);
    return 1;
}

static const struct luaL_Reg type_inferer_m[] = {
    {"__gc",            type_inferer_gc}, // never throws
    {"__call",          safe_function<type_inferer_call>},
    {"clear",           safe_function<type_inferer_clear>},
    {0, 0}
};

void open_type_inferer(lua_State * L) {
    luaL_newmetatable(L, type_inferer_mt);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    setfuncs(L, type_inferer_m, 0);

    SET_GLOBAL_FUN(mk_type_inferer,          "type_inferer");
    SET_GLOBAL_FUN(type_inferer_pred,        "is_type_inferer");
}
}
<commit_msg>feat(library/type_inferer): provide the metavar_env to instantiate and lift_free_vars in the type_inferer, it will minimize the number of local_entries needed<commit_after>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include "util/flet.h"
#include "util/scoped_map.h"
#include "util/interrupt.h"
#include "kernel/environment.h"
#include "kernel/normalizer.h"
#include "kernel/builtin.h"
#include "kernel/kernel_exception.h"
#include "kernel/type_checker_justification.h"
#include "kernel/instantiate.h"
#include "kernel/free_vars.h"
#include "kernel/metavar.h"
#include "library/kernel_bindings.h"
#include "library/type_inferer.h"

namespace lean {
static name g_x_name("x");
class type_inferer::imp {
    typedef scoped_map<expr, expr, expr_hash_alloc, expr_eqp> cache;
    typedef buffer<unification_constraint> unification_constraints;

    ro_environment            m_env;
    context                   m_ctx;
    cached_metavar_env        m_menv;
    unification_constraints * m_uc;
    normalizer                m_normalizer;
    cache                     m_cache;

    expr normalize(expr const & e, context const & ctx) {
        return m_normalizer(e, ctx, m_menv.to_some_menv());
    }
    expr lift_free_vars(expr const & e, unsigned s, unsigned d) {
        return ::lean::lift_free_vars(e, s, d, m_menv.to_some_menv());
    }

    expr lift_free_vars(expr const & e, unsigned d) {
        return ::lean::lift_free_vars(e, d, m_menv.to_some_menv());
    }

    expr instantiate(expr const & e, unsigned n, expr const * s) {
        return ::lean::instantiate(e, n, s, m_menv.to_some_menv());
    }

    expr check_type(expr const & e, expr const & s, context const & ctx) {
        if (is_type(e))
            return e;
        if (is_bool(e))
            return Type();
        expr u = normalize(e, ctx);
        if (is_type(u))
            return u;
        if (is_bool(u))
            return Type();
        if (has_metavar(u) && m_menv && m_uc) {
            justification jst = mk_type_expected_justification(ctx, s);
            m_uc->push_back(mk_convertible_constraint(ctx, u, TypeU, jst));
            return u;
        }
        throw type_expected_exception(m_env, ctx, s);
    }

    expr get_range(expr t, expr const & e, context const & ctx) {
        unsigned num = num_args(e) - 1;
        while (num > 0) {
            --num;
            if (is_pi(t)) {
                t = abst_body(t);
            } else {
                t = m_normalizer(t, ctx);
                if (is_pi(t)) {
                    t = abst_body(t);
                } else if (has_metavar(t) && m_menv && m_uc) {
                    // Create two fresh variables A and B,
                    // and assign r == (Pi(x : A), B)
                    expr A   = m_menv->mk_metavar(ctx);
                    expr B   = m_menv->mk_metavar(extend(ctx, g_x_name, A));
                    expr p   = mk_pi(g_x_name, A, B);
                    justification jst = mk_function_expected_justification(ctx, e);
                    m_uc->push_back(mk_eq_constraint(ctx, t, p, jst));
                    t        = abst_body(p);
                } else {
                    throw function_expected_exception(m_env, ctx, e);
                }
            }
        }
        if (closed(t))
            return t;
        else
            return instantiate(t, num_args(e)-1, &arg(e, 1));
    }

    expr infer_type(expr const & e, context const & ctx) {
        // cheap cases, we do not cache results
        switch (e.kind()) {
        case expr_kind::MetaVar:
            if (m_menv) {
                if (m_menv->is_assigned(e))
                    return infer_type(*(m_menv->get_subst(e)), ctx);
                else
                    return m_menv->get_type(e);
            } else {
                throw unexpected_metavar_occurrence(m_env, e);
            }
        case expr_kind::Constant: {
            if (const_type(e)) {
                return *const_type(e);
            } else {
                object const & obj = m_env->get_object(const_name(e));
                if (obj.has_type())
                    return obj.get_type();
                else
                    throw has_no_type_exception(m_env, e);
            }
            break;
        }
        case expr_kind::Var: {
            auto p = lookup_ext(ctx, var_idx(e));
            context_entry const & ce = p.first;
            if (ce.get_domain()) {
                context const & ce_ctx   = p.second;
                return lift_free_vars(*(ce.get_domain()), ctx.size() - ce_ctx.size());
            }
            // Remark: the case where ce.get_domain() is not
            // available is not considered cheap.
            break;
        }
        case expr_kind::Eq:
            return mk_bool_type();
        case expr_kind::Value:
            return to_value(e).get_type();
        case expr_kind::Type:
            return mk_type(ty_level(e) + 1);
        case expr_kind::App: case expr_kind::Lambda:
        case expr_kind::Pi:  case expr_kind::Let:
            break; // expensive cases
        }

        check_system("type inference");
        bool shared = false;
        if (is_shared(e)) {
            shared = true;
            auto it = m_cache.find(e);
            if (it != m_cache.end())
                return it->second;
        }

        expr r;
        switch (e.kind()) {
        case expr_kind::Constant: case expr_kind::Eq:
        case expr_kind::Value:    case expr_kind::Type:
        case expr_kind::MetaVar:
            lean_unreachable(); // LCOV_EXCL_LINE
        case expr_kind::Var: {
            auto p = lookup_ext(ctx, var_idx(e));
            context_entry const & ce = p.first;
            context const & ce_ctx   = p.second;
            lean_assert(!ce.get_domain());
            r = lift_free_vars(infer_type(*(ce.get_body()), ce_ctx), ctx.size() - ce_ctx.size());
            break;
        }
        case expr_kind::App: {
            expr const & f = arg(e, 0);
            expr f_t = infer_type(f, ctx);
            r = get_range(f_t, e, ctx);
            break;
        }
        case expr_kind::Lambda: {
            cache::mk_scope sc(m_cache);
            r = mk_pi(abst_name(e), abst_domain(e), infer_type(abst_body(e), extend(ctx, abst_name(e), abst_domain(e))));
            break;
        }
        case expr_kind::Pi: {
            expr t1  = check_type(infer_type(abst_domain(e), ctx), abst_domain(e), ctx);
            expr t2;
            context new_ctx = extend(ctx, abst_name(e), abst_domain(e));
            {
                cache::mk_scope sc(m_cache);
                t2 = check_type(infer_type(abst_body(e), new_ctx), abst_body(e), new_ctx);
            }
            if (is_type(t1) && is_type(t2)) {
                r = mk_type(max(ty_level(t1), ty_level(t2)));
            } else {
                lean_assert(m_uc);
                justification jst = mk_max_type_justification(ctx, e);
                r = m_menv->mk_metavar(ctx);
                m_uc->push_back(mk_max_constraint(new_ctx, lift_free_vars(t1, 0, 1), t2, r, jst));
            }
            break;
        }
        case expr_kind::Let: {
            cache::mk_scope sc(m_cache);
            r = infer_type(let_body(e), extend(ctx, let_name(e), let_type(e), let_value(e)));
            break;
        }}

        if (shared) {
            m_cache.insert(e, r);
        }
        return r;
    }

    void set_ctx(context const & ctx) {
        if (!is_eqp(m_ctx, ctx)) {
            clear();
            m_ctx = ctx;
        }
    }

public:
    imp(ro_environment const & env):
        m_env(env),
        m_normalizer(env) {
        m_uc = nullptr;
    }

    expr operator()(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc) {
        set_ctx(ctx);
        if (m_menv.update(menv))
            clear_cache();
        flet<unification_constraints*> set(m_uc, uc);
        return infer_type(e, ctx);
    }

    void clear_cache() {
        m_cache.clear();
        m_normalizer.clear();
    }

    void clear() {
        clear_cache();
        m_menv.clear();
        m_ctx = context();
    }

    bool is_proposition(expr const & e, context const & ctx, optional<metavar_env> const & menv) {
        // Catch easy cases
        switch (e.kind()) {
        case expr_kind::Lambda: case expr_kind::Pi: case expr_kind::Type: return false;
        case expr_kind::Eq: return true;
        default: break;
        }
        expr t = operator()(e, ctx, menv, nullptr);
        if (is_bool(t))
            return true;
        else
            return is_bool(normalize(t, ctx));
    }
};
type_inferer::type_inferer(ro_environment const & env):m_ptr(new imp(env)) {}
type_inferer::~type_inferer() {}
expr type_inferer::operator()(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc) {
    return m_ptr->operator()(e, ctx, menv, uc);
}
expr type_inferer::operator()(expr const & e, context const & ctx, metavar_env const & menv, buffer<unification_constraint> & uc) {
    return m_ptr->operator()(e, ctx, some_menv(menv), &uc);
}
expr type_inferer::operator()(expr const & e, context const & ctx) {
    return operator()(e, ctx, none_menv(), nullptr);
}
bool type_inferer::is_proposition(expr const & e, context const & ctx, optional<metavar_env> const & menv) {
    return m_ptr->is_proposition(e, ctx, menv);
}
bool type_inferer::is_proposition(expr const & e, context const & ctx) {
    return is_proposition(e, ctx, none_menv());
}
bool type_inferer::is_proposition(expr const & e, context const & ctx, metavar_env const & menv) {
    return is_proposition(e, ctx, some_menv(menv));
}
void type_inferer::clear() { m_ptr->clear(); }

constexpr char const * type_inferer_mt = "type_inferer";
type_inferer & to_type_inferer(lua_State * L, int i) { return *static_cast<type_inferer*>(luaL_checkudata(L, i, type_inferer_mt)); }
DECL_PRED(type_inferer)
DECL_GC(type_inferer)

static int type_inferer_call(lua_State * L) {
    int nargs = lua_gettop(L);
    type_inferer & inferer = to_type_inferer(L, 1);
    if (nargs == 2)
        return push_expr(L, inferer(to_expr(L, 2)));
    else
        return push_expr(L, inferer(to_expr(L, 2), to_context(L, 3)));
}

static int type_inferer_clear(lua_State * L) {
    to_type_inferer(L, 1).clear();
    return 0;
}

static int mk_type_inferer(lua_State * L) {
    void * mem = lua_newuserdata(L, sizeof(type_inferer));
    new (mem) type_inferer(to_environment(L, 1));
    luaL_getmetatable(L, type_inferer_mt);
    lua_setmetatable(L, -2);
    return 1;
}

static const struct luaL_Reg type_inferer_m[] = {
    {"__gc",            type_inferer_gc}, // never throws
    {"__call",          safe_function<type_inferer_call>},
    {"clear",           safe_function<type_inferer_clear>},
    {0, 0}
};

void open_type_inferer(lua_State * L) {
    luaL_newmetatable(L, type_inferer_mt);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    setfuncs(L, type_inferer_m, 0);

    SET_GLOBAL_FUN(mk_type_inferer,          "type_inferer");
    SET_GLOBAL_FUN(type_inferer_pred,        "is_type_inferer");
}
}
<|endoftext|>


<commit_before>#pragma once

#include "machines.hh"
#include "parsed-derivations.hh"
#include "lock.hh"
#include "local-store.hh"

#include <memory>
#include <algorithm>
#include <iostream>
#include <map>
#include <sstream>
#include <thread>
#include <future>
#include <chrono>
#include <regex>
#include <queue>

namespace nix {

using std::map;

/* Forward definition. */
class Worker;
struct HookInstance;

/* A pointer to a goal. */
struct Goal;
class DerivationGoal;
typedef std::shared_ptr<Goal> GoalPtr;
typedef std::weak_ptr<Goal> WeakGoalPtr;

struct CompareGoalPtrs {
    bool operator() (const GoalPtr & a, const GoalPtr & b) const;
};

/* Set of goals. */
typedef set<GoalPtr, CompareGoalPtrs> Goals;
typedef list<WeakGoalPtr> WeakGoals;

/* A map of paths to goals (and the other way around). */
typedef std::map<StorePath, WeakGoalPtr> WeakGoalMap;

struct Goal : public std::enable_shared_from_this<Goal>
{
    typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;

    /* Backlink to the worker. */
    Worker & worker;

    /* Goals that this goal is waiting for. */
    Goals waitees;

    /* Goals waiting for this one to finish.  Must use weak pointers
       here to prevent cycles. */
    WeakGoals waiters;

    /* Number of goals we are/were waiting for that have failed. */
    unsigned int nrFailed;

    /* Number of substitution goals we are/were waiting for that
       failed because there are no substituters. */
    unsigned int nrNoSubstituters;

    /* Number of substitution goals we are/were waiting for that
       failed because othey had unsubstitutable references. */
    unsigned int nrIncompleteClosure;

    /* Name of this goal for debugging purposes. */
    string name;

    /* Whether the goal is finished. */
    ExitCode exitCode;

    /* Exception containing an error message, if any. */
    std::optional<Error> ex;

    Goal(Worker & worker) : worker(worker)
    {
        nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
        exitCode = ecBusy;
    }

    virtual ~Goal()
    {
        trace("goal destroyed");
    }

    virtual void work() = 0;

    void addWaitee(GoalPtr waitee);

    virtual void waiteeDone(GoalPtr waitee, ExitCode result);

    virtual void handleChildOutput(int fd, const string & data)
    {
        abort();
    }

    virtual void handleEOF(int fd)
    {
        abort();
    }

    void trace(const FormatOrString & fs);

    string getName()
    {
        return name;
    }

    /* Callback in case of a timeout.  It should wake up its waiters,
       get rid of any running child processes that are being monitored
       by the worker (important!), etc. */
    virtual void timedOut(Error && ex) = 0;

    virtual string key() = 0;

    void amDone(ExitCode result, std::optional<Error> ex = {});
};

typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;

/* A mapping used to remember for each child process to what goal it
   belongs, and file descriptors for receiving log data and output
   path creation commands. */
struct Child
{
    WeakGoalPtr goal;
    Goal * goal2; // ugly hackery
    set<int> fds;
    bool respectTimeouts;
    bool inBuildSlot;
    steady_time_point lastOutput; /* time we last got output on stdout/stderr */
    steady_time_point timeStarted;
};

/* The worker class. */
class Worker
{
private:

    /* Note: the worker should only have strong pointers to the
       top-level goals. */

    /* The top-level goals of the worker. */
    Goals topGoals;

    /* Goals that are ready to do some work. */
    WeakGoals awake;

    /* Goals waiting for a build slot. */
    WeakGoals wantingToBuild;

    /* Child processes currently running. */
    std::list<Child> children;

    /* Number of build slots occupied.  This includes local builds and
       substitutions but not remote builds via the build hook. */
    unsigned int nrLocalBuilds;

    /* Maps used to prevent multiple instantiations of a goal for the
       same derivation / path. */
    WeakGoalMap derivationGoals;
    WeakGoalMap substitutionGoals;

    /* Goals waiting for busy paths to be unlocked. */
    WeakGoals waitingForAnyGoal;

    /* Goals sleeping for a few seconds (polling a lock). */
    WeakGoals waitingForAWhile;

    /* Last time the goals in `waitingForAWhile' where woken up. */
    steady_time_point lastWokenUp;

    /* Cache for pathContentsGood(). */
    std::map<StorePath, bool> pathContentsGoodCache;

public:

    const Activity act;
    const Activity actDerivations;
    const Activity actSubstitutions;

    /* Set if at least one derivation had a BuildError (i.e. permanent
       failure). */
    bool permanentFailure;

    /* Set if at least one derivation had a timeout. */
    bool timedOut;

    /* Set if at least one derivation fails with a hash mismatch. */
    bool hashMismatch;

    /* Set if at least one derivation is not deterministic in check mode. */
    bool checkMismatch;

    LocalStore & store;

    std::unique_ptr<HookInstance> hook;

    uint64_t expectedBuilds = 0;
    uint64_t doneBuilds = 0;
    uint64_t failedBuilds = 0;
    uint64_t runningBuilds = 0;

    uint64_t expectedSubstitutions = 0;
    uint64_t doneSubstitutions = 0;
    uint64_t failedSubstitutions = 0;
    uint64_t runningSubstitutions = 0;
    uint64_t expectedDownloadSize = 0;
    uint64_t doneDownloadSize = 0;
    uint64_t expectedNarSize = 0;
    uint64_t doneNarSize = 0;

    /* Whether to ask the build hook if it can build a derivation. If
       it answers with "decline-permanently", we don't try again. */
    bool tryBuildHook = true;

    Worker(LocalStore & store);
    ~Worker();

    /* Make a goal (with caching). */

    /* derivation goal */
private:
    std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
        const StorePath & drvPath, const StringSet & wantedOutputs,
        std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
public:
    std::shared_ptr<DerivationGoal> makeDerivationGoal(
        const StorePath & drvPath,
        const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
    std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
        const StorePath & drvPath, const BasicDerivation & drv,
        const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);

    /* substitution goal */
    GoalPtr makeSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);

    /* Remove a dead goal. */
    void removeGoal(GoalPtr goal);

    /* Wake up a goal (i.e., there is something for it to do). */
    void wakeUp(GoalPtr goal);

    /* Return the number of local build and substitution processes
       currently running (but not remote builds via the build
       hook). */
    unsigned int getNrLocalBuilds();

    /* Registers a running child process.  `inBuildSlot' means that
       the process counts towards the jobs limit. */
    void childStarted(GoalPtr goal, const set<int> & fds,
        bool inBuildSlot, bool respectTimeouts);

    /* Unregisters a running child process.  `wakeSleepers' should be
       false if there is no sense in waking up goals that are sleeping
       because they can't run yet (e.g., there is no free build slot,
       or the hook would still say `postpone'). */
    void childTerminated(Goal * goal, bool wakeSleepers = true);

    /* Put `goal' to sleep until a build slot becomes available (which
       might be right away). */
    void waitForBuildSlot(GoalPtr goal);

    /* Wait for any goal to finish.  Pretty indiscriminate way to
       wait for some resource that some other goal is holding. */
    void waitForAnyGoal(GoalPtr goal);

    /* Wait for a few seconds and then retry this goal.  Used when
       waiting for a lock held by another process.  This kind of
       polling is inefficient, but POSIX doesn't really provide a way
       to wait for multiple locks in the main select() loop. */
    void waitForAWhile(GoalPtr goal);

    /* Loop until the specified top-level goals have finished. */
    void run(const Goals & topGoals);

    /* Wait for input to become available. */
    void waitForInput();

    unsigned int exitStatus();

    /* Check whether the given valid path exists and has the right
       contents. */
    bool pathContentsGood(const StorePath & path);

    void markContentsGood(const StorePath & path);

    void updateProgress()
    {
        actDerivations.progress(doneBuilds, expectedBuilds + doneBuilds, runningBuilds, failedBuilds);
        actSubstitutions.progress(doneSubstitutions, expectedSubstitutions + doneSubstitutions, runningSubstitutions, failedSubstitutions);
        act.setExpected(actFileTransfer, expectedDownloadSize + doneDownloadSize);
        act.setExpected(actCopyPath, expectedNarSize + doneNarSize);
    }
};

typedef enum {rpAccept, rpDecline, rpPostpone} HookReply;

class SubstitutionGoal;

/* Unless we are repairing, we don't both to test validity and just assume it,
   so the choices are `Absent` or `Valid`. */
enum struct PathStatus {
    Corrupt,
    Absent,
    Valid,
};

struct InitialOutputStatus {
    StorePath path;
    PathStatus status;
    /* Valid in the store, and additionally non-corrupt if we are repairing */
    bool isValid() const {
        return status == PathStatus::Valid;
    }
    /* Merely present, allowed to be corrupt */
    bool isPresent() const {
        return status == PathStatus::Corrupt
            || status == PathStatus::Valid;
    }
};

struct InitialOutput {
    bool wanted;
    std::optional<InitialOutputStatus> known;
};

class DerivationGoal : public Goal
{
private:
    /* Whether to use an on-disk .drv file. */
    bool useDerivation;

    /* The path of the derivation. */
    StorePath drvPath;

    /* The specific outputs that we need to build.  Empty means all of
       them. */
    StringSet wantedOutputs;

    /* Whether additional wanted outputs have been added. */
    bool needRestart = false;

    /* Whether to retry substituting the outputs after building the
       inputs. */
    bool retrySubstitution;

    /* The derivation stored at drvPath. */
    std::unique_ptr<BasicDerivation> drv;

    std::unique_ptr<ParsedDerivation> parsedDrv;

    /* The remainder is state held during the build. */

    /* Locks on (fixed) output paths. */
    PathLocks outputLocks;

    /* All input paths (that is, the union of FS closures of the
       immediate input paths). */
    StorePathSet inputPaths;

    std::map<std::string, InitialOutput> initialOutputs;

    /* User selected for running the builder. */
    std::unique_ptr<UserLock> buildUser;

    /* The process ID of the builder. */
    Pid pid;

    /* The temporary directory. */
    Path tmpDir;

    /* The path of the temporary directory in the sandbox. */
    Path tmpDirInSandbox;

    /* File descriptor for the log file. */
    AutoCloseFD fdLogFile;
    std::shared_ptr<BufferedSink> logFileSink, logSink;

    /* Number of bytes received from the builder's stdout/stderr. */
    unsigned long logSize;

    /* The most recent log lines. */
    std::list<std::string> logTail;

    std::string currentLogLine;
    size_t currentLogLinePos = 0; // to handle carriage return

    std::string currentHookLine;

    /* Pipe for the builder's standard output/error. */
    Pipe builderOut;

    /* Pipe for synchronising updates to the builder namespaces. */
    Pipe userNamespaceSync;

    /* The mount namespace of the builder, used to add additional
       paths to the sandbox as a result of recursive Nix calls. */
    AutoCloseFD sandboxMountNamespace;

    /* On Linux, whether we're doing the build in its own user
       namespace. */
    bool usingUserNamespace = true;

    /* The build hook. */
    std::unique_ptr<HookInstance> hook;

    /* Whether we're currently doing a chroot build. */
    bool useChroot = false;

    Path chrootRootDir;

    /* RAII object to delete the chroot directory. */
    std::shared_ptr<AutoDelete> autoDelChroot;

    /* The sort of derivation we are building. */
    DerivationType derivationType;

    /* Whether to run the build in a private network namespace. */
    bool privateNetwork = false;

    typedef void (DerivationGoal::*GoalState)();
    GoalState state;

    /* Stuff we need to pass to initChild(). */
    struct ChrootPath {
        Path source;
        bool optional;
        ChrootPath(Path source = "", bool optional = false)
            : source(source), optional(optional)
        { }
    };
    typedef map<Path, ChrootPath> DirsInChroot; // maps target path to source path
    DirsInChroot dirsInChroot;

    typedef map<string, string> Environment;
    Environment env;

#if __APPLE__
    typedef string SandboxProfile;
    SandboxProfile additionalSandboxProfile;
#endif

    /* Hash rewriting. */
    StringMap inputRewrites, outputRewrites;
    typedef map<StorePath, StorePath> RedirectedOutputs;
    RedirectedOutputs redirectedOutputs;

    /* The outputs paths used during the build.

       - Input-addressed derivations or fixed content-addressed outputs are
         sometimes built when some of their outputs already exist, and can not
         be hidden via sandboxing. We use temporary locations instead and
         rewrite after the build. Otherwise the regular predetermined paths are
         put here.

       - Floating content-addressed derivations do not know their final build
         output paths until the outputs are hashed, so random locations are
         used, and then renamed. The randomness helps guard against hidden
         self-references.
     */
    OutputPathMap scratchOutputs;

    /* The final output paths of the build.

       - For input-addressed derivations, always the precomputed paths

       - For content-addressed derivations, calcuated from whatever the hash
         ends up being. (Note that fixed outputs derivations that produce the
         "wrong" output still install that data under its true content-address.)
     */
    OutputPathMap finalOutputs;

    BuildMode buildMode;

    /* If we're repairing without a chroot, there may be outputs that
       are valid but corrupt.  So we redirect these outputs to
       temporary paths. */
    StorePathSet redirectedBadOutputs;

    BuildResult result;

    /* The current round, if we're building multiple times. */
    size_t curRound = 1;

    size_t nrRounds;

    /* Path registration info from the previous round, if we're
       building multiple times. Since this contains the hash, it
       allows us to compare whether two rounds produced the same
       result. */
    std::map<Path, ValidPathInfo> prevInfos;

    uid_t sandboxUid() { return usingUserNamespace ? 1000 : buildUser->getUID(); }
    gid_t sandboxGid() { return usingUserNamespace ?  100 : buildUser->getGID(); }

    const static Path homeDir;

    std::unique_ptr<MaintainCount<uint64_t>> mcExpectedBuilds, mcRunningBuilds;

    std::unique_ptr<Activity> act;

    /* Activity that denotes waiting for a lock. */
    std::unique_ptr<Activity> actLock;

    std::map<ActivityId, Activity> builderActivities;

    /* The remote machine on which we're building. */
    std::string machineName;

    /* The recursive Nix daemon socket. */
    AutoCloseFD daemonSocket;

    /* The daemon main thread. */
    std::thread daemonThread;

    /* The daemon worker threads. */
    std::vector<std::thread> daemonWorkerThreads;

    /* Paths that were added via recursive Nix calls. */
    StorePathSet addedPaths;

    /* Recursive Nix calls are only allowed to build or realize paths
       in the original input closure or added via a recursive Nix call
       (so e.g. you can't do 'nix-store -r /nix/store/<bla>' where
       /nix/store/<bla> is some arbitrary path in a binary cache). */
    bool isAllowed(const StorePath & path)
    {
        return inputPaths.count(path) || addedPaths.count(path);
    }

    friend struct RestrictedStore;

public:
    DerivationGoal(const StorePath & drvPath,
        const StringSet & wantedOutputs, Worker & worker,
        BuildMode buildMode = bmNormal);
    DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
        const StringSet & wantedOutputs, Worker & worker,
        BuildMode buildMode = bmNormal);
    ~DerivationGoal();

    /* Whether we need to perform hash rewriting if there are valid output paths. */
    bool needsHashRewrite();

    void timedOut(Error && ex) override;

    string key() override
    {
        /* Ensure that derivations get built in order of their name,
           i.e. a derivation named "aardvark" always comes before
           "baboon". And substitution goals always happen before
           derivation goals (due to "b$"). */
        return "b$" + std::string(drvPath.name()) + "$" + worker.store.printStorePath(drvPath);
    }

    void work() override;

    StorePath getDrvPath()
    {
        return drvPath;
    }

    /* Add wanted outputs to an already existing derivation goal. */
    void addWantedOutputs(const StringSet & outputs);

    BuildResult getResult() { return result; }

private:
    /* The states. */
    void getDerivation();
    void loadDerivation();
    void haveDerivation();
    void outputsSubstitutionTried();
    void gaveUpOnSubstitution();
    void closureRepaired();
    void inputsRealised();
    void tryToBuild();
    void tryLocalBuild();
    void buildDone();

    void resolvedFinished();

    /* Is the build hook willing to perform the build? */
    HookReply tryBuildHook();

    /* Start building a derivation. */
    void startBuilder();

    /* Fill in the environment for the builder. */
    void initEnv();

    /* Setup tmp dir location. */
    void initTmpDir();

    /* Write a JSON file containing the derivation attributes. */
    void writeStructuredAttrs();

    void startDaemon();

    void stopDaemon();

    /* Add 'path' to the set of paths that may be referenced by the
       outputs, and make it appear in the sandbox. */
    void addDependency(const StorePath & path);

    /* Make a file owned by the builder. */
    void chownToBuilder(const Path & path);

    /* Run the builder's process. */
    void runChild();

    friend int childEntry(void *);

    /* Check that the derivation outputs all exist and register them
       as valid. */
    void registerOutputs();

    /* Check that an output meets the requirements specified by the
       'outputChecks' attribute (or the legacy
       '{allowed,disallowed}{References,Requisites}' attributes). */
    void checkOutputs(const std::map<std::string, ValidPathInfo> & outputs);

    /* Open a log file and a pipe to it. */
    Path openLogFile();

    /* Close the log file. */
    void closeLogFile();

    /* Delete the temporary directory, if we have one. */
    void deleteTmpDir(bool force);

    /* Callback used by the worker to write to the log. */
    void handleChildOutput(int fd, const string & data) override;
    void handleEOF(int fd) override;
    void flushLine();

    /* Wrappers around the corresponding Store methods that first consult the
       derivation.  This is currently needed because when there is no drv file
       there also is no DB entry. */
    std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap();
    OutputPathMap queryDerivationOutputMap();

    /* Return the set of (in)valid paths. */
    void checkPathValidity();

    /* Forcibly kill the child process, if any. */
    void killChild();

    /* Create alternative path calculated from but distinct from the
       input, so we can avoid overwriting outputs (or other store paths)
       that already exist. */
    StorePath makeFallbackPath(const StorePath & path);
    /* Make a path to another based on the output name along with the
       derivation hash. */
    /* FIXME add option to randomize, so we can audit whether our
       rewrites caught everything */
    StorePath makeFallbackPath(std::string_view outputName);

    void repairClosure();

    void started();

    void done(
        BuildResult::Status status,
        std::optional<Error> ex = {});

    StorePathSet exportReferences(const StorePathSet & storePaths);
};

class SubstitutionGoal : public Goal
{
    friend class Worker;

private:
    /* The store path that should be realised through a substitute. */
    StorePath storePath;

    /* The path the substituter refers to the path as. This will be
     * different when the stores have different names. */
    std::optional<StorePath> subPath;

    /* The remaining substituters. */
    std::list<ref<Store>> subs;

    /* The current substituter. */
    std::shared_ptr<Store> sub;

    /* Whether a substituter failed. */
    bool substituterFailed = false;

    /* Path info returned by the substituter's query info operation. */
    std::shared_ptr<const ValidPathInfo> info;

    /* Pipe for the substituter's standard output. */
    Pipe outPipe;

    /* The substituter thread. */
    std::thread thr;

    std::promise<void> promise;

    /* Whether to try to repair a valid path. */
    RepairFlag repair;

    /* Location where we're downloading the substitute.  Differs from
       storePath when doing a repair. */
    Path destPath;

    std::unique_ptr<MaintainCount<uint64_t>> maintainExpectedSubstitutions,
        maintainRunningSubstitutions, maintainExpectedNar, maintainExpectedDownload;

    typedef void (SubstitutionGoal::*GoalState)();
    GoalState state;

    /* Content address for recomputing store path */
    std::optional<ContentAddress> ca;

public:
    SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
    ~SubstitutionGoal();

    void timedOut(Error && ex) override { abort(); };

    string key() override
    {
        /* "a$" ensures substitution goals happen before derivation
           goals. */
        return "a$" + std::string(storePath.name()) + "$" + worker.store.printStorePath(storePath);
    }

    void work() override;

    /* The states. */
    void init();
    void tryNext();
    void gotInfo();
    void referencesValid();
    void tryToRun();
    void finished();

    /* Callback used by the worker to write to the log. */
    void handleChildOutput(int fd, const string & data) override;
    void handleEOF(int fd) override;

    StorePath getStorePath() { return storePath; }
};

struct HookInstance
{
    /* Pipes for talking to the build hook. */
    Pipe toHook;

    /* Pipe for the hook's standard output/error. */
    Pipe fromHook;

    /* Pipe for the builder's standard output/error. */
    Pipe builderOut;

    /* The process ID of the hook. */
    Pid pid;

    FdSink sink;

    std::map<ActivityId, Activity> activities;

    HookInstance();

    ~HookInstance();
};

void addToWeakGoals(WeakGoals & goals, GoalPtr p);

}
<commit_msg>Trim worker.hh<commit_after>#pragma once

#include "types.hh"
#include "lock.hh"
#include "local-store.hh"
#include "goal.hh"

namespace nix {

/* Forward definition. */
class DerivationGoal;

typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;

/* A mapping used to remember for each child process to what goal it
   belongs, and file descriptors for receiving log data and output
   path creation commands. */
struct Child
{
    WeakGoalPtr goal;
    Goal * goal2; // ugly hackery
    set<int> fds;
    bool respectTimeouts;
    bool inBuildSlot;
    steady_time_point lastOutput; /* time we last got output on stdout/stderr */
    steady_time_point timeStarted;
};

/* Forward definition. */
struct HookInstance;

/* The worker class. */
class Worker
{
private:

    /* Note: the worker should only have strong pointers to the
       top-level goals. */

    /* The top-level goals of the worker. */
    Goals topGoals;

    /* Goals that are ready to do some work. */
    WeakGoals awake;

    /* Goals waiting for a build slot. */
    WeakGoals wantingToBuild;

    /* Child processes currently running. */
    std::list<Child> children;

    /* Number of build slots occupied.  This includes local builds and
       substitutions but not remote builds via the build hook. */
    unsigned int nrLocalBuilds;

    /* Maps used to prevent multiple instantiations of a goal for the
       same derivation / path. */
    WeakGoalMap derivationGoals;
    WeakGoalMap substitutionGoals;

    /* Goals waiting for busy paths to be unlocked. */
    WeakGoals waitingForAnyGoal;

    /* Goals sleeping for a few seconds (polling a lock). */
    WeakGoals waitingForAWhile;

    /* Last time the goals in `waitingForAWhile' where woken up. */
    steady_time_point lastWokenUp;

    /* Cache for pathContentsGood(). */
    std::map<StorePath, bool> pathContentsGoodCache;

public:

    const Activity act;
    const Activity actDerivations;
    const Activity actSubstitutions;

    /* Set if at least one derivation had a BuildError (i.e. permanent
       failure). */
    bool permanentFailure;

    /* Set if at least one derivation had a timeout. */
    bool timedOut;

    /* Set if at least one derivation fails with a hash mismatch. */
    bool hashMismatch;

    /* Set if at least one derivation is not deterministic in check mode. */
    bool checkMismatch;

    LocalStore & store;

    std::unique_ptr<HookInstance> hook;

    uint64_t expectedBuilds = 0;
    uint64_t doneBuilds = 0;
    uint64_t failedBuilds = 0;
    uint64_t runningBuilds = 0;

    uint64_t expectedSubstitutions = 0;
    uint64_t doneSubstitutions = 0;
    uint64_t failedSubstitutions = 0;
    uint64_t runningSubstitutions = 0;
    uint64_t expectedDownloadSize = 0;
    uint64_t doneDownloadSize = 0;
    uint64_t expectedNarSize = 0;
    uint64_t doneNarSize = 0;

    /* Whether to ask the build hook if it can build a derivation. If
       it answers with "decline-permanently", we don't try again. */
    bool tryBuildHook = true;

    Worker(LocalStore & store);
    ~Worker();

    /* Make a goal (with caching). */

    /* derivation goal */
private:
    std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
        const StorePath & drvPath, const StringSet & wantedOutputs,
        std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
public:
    std::shared_ptr<DerivationGoal> makeDerivationGoal(
        const StorePath & drvPath,
        const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
    std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
        const StorePath & drvPath, const BasicDerivation & drv,
        const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);

    /* substitution goal */
    GoalPtr makeSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);

    /* Remove a dead goal. */
    void removeGoal(GoalPtr goal);

    /* Wake up a goal (i.e., there is something for it to do). */
    void wakeUp(GoalPtr goal);

    /* Return the number of local build and substitution processes
       currently running (but not remote builds via the build
       hook). */
    unsigned int getNrLocalBuilds();

    /* Registers a running child process.  `inBuildSlot' means that
       the process counts towards the jobs limit. */
    void childStarted(GoalPtr goal, const set<int> & fds,
        bool inBuildSlot, bool respectTimeouts);

    /* Unregisters a running child process.  `wakeSleepers' should be
       false if there is no sense in waking up goals that are sleeping
       because they can't run yet (e.g., there is no free build slot,
       or the hook would still say `postpone'). */
    void childTerminated(Goal * goal, bool wakeSleepers = true);

    /* Put `goal' to sleep until a build slot becomes available (which
       might be right away). */
    void waitForBuildSlot(GoalPtr goal);

    /* Wait for any goal to finish.  Pretty indiscriminate way to
       wait for some resource that some other goal is holding. */
    void waitForAnyGoal(GoalPtr goal);

    /* Wait for a few seconds and then retry this goal.  Used when
       waiting for a lock held by another process.  This kind of
       polling is inefficient, but POSIX doesn't really provide a way
       to wait for multiple locks in the main select() loop. */
    void waitForAWhile(GoalPtr goal);

    /* Loop until the specified top-level goals have finished. */
    void run(const Goals & topGoals);

    /* Wait for input to become available. */
    void waitForInput();

    unsigned int exitStatus();

    /* Check whether the given valid path exists and has the right
       contents. */
    bool pathContentsGood(const StorePath & path);

    void markContentsGood(const StorePath & path);

    void updateProgress()
    {
        actDerivations.progress(doneBuilds, expectedBuilds + doneBuilds, runningBuilds, failedBuilds);
        actSubstitutions.progress(doneSubstitutions, expectedSubstitutions + doneSubstitutions, runningSubstitutions, failedSubstitutions);
        act.setExpected(actFileTransfer, expectedDownloadSize + doneDownloadSize);
        act.setExpected(actCopyPath, expectedNarSize + doneNarSize);
    }
};

}
<|endoftext|>


<commit_before>#include <map>

#include <llvm/IR/Value.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/Support/raw_ostream.h>

#include "AnalysisGeneric.h"
#include "LLVMDependenceGraph.h"
#include "PointsTo.h"

using namespace llvm;

namespace dg {
namespace analysis {

// we assume that if the program uses inttoptr, it access this
// memory only this way - so every access to this memory is done
// via some inttoptr. Here we store the inttoptr objects
static std::map<uint64_t, LLVMNode *> intToPtrMap;

// pointer points to unknown memory location
MemoryObj UnknownMemoryObject(nullptr);
// unknown pointer value
Pointer UnknownMemoryLocation(&UnknownMemoryObject, 0);

bool Pointer::isUnknown() const
{
    return this == &UnknownMemoryLocation;
}

bool Pointer::pointsToUnknown() const
{
    assert(obj && "Pointer has not any memory object set");
    return obj->isUnknown();
}

bool Pointer::isKnown() const
{
    return !isUnknown() && !pointsToUnknown();
}

bool MemoryObj::isUnknown() const
{
    return this == &UnknownMemoryObject;
}

static LLVMNode *createNodeWithMemAlloc(const Value *val)
{
    LLVMNode *n = new LLVMNode(val);
    MemoryObj *&mo = n->getMemoryObj();
    mo = new MemoryObj(n);
    n->addPointsTo(Pointer(mo));

    return n;
}

static LLVMNode *getOrCreateNode(LLVMDependenceGraph *dg, const Value *val)
{
    LLVMNode *n = dg->getNode(val);
    if (n)
        return n;

    if (llvm::isa<llvm::Function>(val)) {
        n = createNodeWithMemAlloc(val);
    } else
        errs() << "ERR: unhandled not to create " << *val << "\n";

    return n;
}

static Pointer handleConstantBitCast(LLVMDependenceGraph *dg, const BitCastInst *BC)
{
    if (!BC->isLosslessCast()) {
        errs() << "WARN: Not a loss less cast unhandled ConstExpr"
               << *BC << "\n";
        return UnknownMemoryLocation;
    }

    const Value *llvmOp = BC->stripPointerCasts();
    LLVMNode *op = getOrCreateNode(dg, llvmOp);
    if (!op) {
        errs() << "ERR: unsupported BitCast constant operand" << *BC << "\n";
    } else {
        PointsToSetT& ptset = op->getPointsTo();
        if (ptset.size() != 1) {
            errs() << "ERR: constant BitCast with not only one pointer "
                   << *BC << "\n";
        } else
            return *ptset.begin();
    }

    return UnknownMemoryLocation;
}

static inline unsigned getPointerBitwidth(const DataLayout *DL, const Value *ptr)
{
    const Type *Ty = ptr->getType();
    return DL->getPointerSizeInBits(Ty->getPointerAddressSpace());
}

static Pointer handleConstantGep(LLVMDependenceGraph *dg,
                                 const GetElementPtrInst *GEP,
                                 const llvm::DataLayout *DL)
{
    const Value *op = GEP->getPointerOperand();
    LLVMNode *opNode = dg->getNode(op);

    // FIXME this is sound, but may be unprecise
    //  - we should use getOperand for getting opNode,
    //  becaues we can have ConstantExpr inserted in ConstantExpr
    //  (getelementptr (inttoptr ..) ...), so we can get null here
    //  as opNode
    if (!opNode) {
        errs() << "No node for Constant GEP operand: " << *GEP << "\n";
        return UnknownMemoryLocation;
    }

    PointsToSetT& S = opNode->getPointsTo();
    // since this is constant expr, there's no way how we could
    // get extra points-to binding in runtime
    assert(S.size() == 1);
    MemoryObj *mo = (*S.begin()).obj;
    if (!mo) {
        errs() << "ERR: no memory object in " << *opNode->getKey() << "\n";
        return UnknownMemoryLocation;
    }

    Pointer pointer(mo, UNKNOWN_OFFSET);
    unsigned bitwidth = getPointerBitwidth(DL, op);
    APInt offset(bitwidth, 0);

    if (GEP->accumulateConstantOffset(*DL, offset)) {
        if (offset.isIntN(bitwidth))
            pointer.offset = offset.getZExtValue();
        else
            errs() << "WARN: Offset greater than "
                   << bitwidth << "-bit" << *GEP << "\n";
    }
    // else offset is set to UNKNOWN (in constructor)

    return pointer;
}

Pointer getConstantExprPointer(const ConstantExpr *CE,
                               LLVMDependenceGraph *dg,
                               const llvm::DataLayout *DL)
{
    Pointer pointer = UnknownMemoryLocation;

    const Instruction *Inst = const_cast<ConstantExpr*>(CE)->getAsInstruction();
    if (const GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Inst)) {
        pointer = handleConstantGep(dg, GEP, DL);
    } else if (const BitCastInst *BC = dyn_cast<BitCastInst>(Inst)) {
        pointer = handleConstantBitCast(dg, BC);
    } else {
            errs() << "ERR: Unsupported ConstantExpr " << *CE << "\n";
            errs() << "      ^^ returning unknown pointer\n";
    }

    delete Inst;
    return pointer;
}

static LLVMNode *getConstantIntToPtrNode(const ConstantExpr *CE,
                                         const llvm::DataLayout *DL)
{
    using namespace llvm;

    const Value *val = CE->getOperand(0);
    if (!isa<ConstantInt>(val)) {
        errs() << "Unhandled constant inttoptr " << *CE << "\n";
        abort();
    }

    const ConstantInt *C = cast<ConstantInt>(val);
    uint64_t value = C->getLimitedValue();

    LLVMNode *&node = intToPtrMap[value];
    if (!node) {
        node = new LLVMNode(C);
        const Value *dest = CE->getOperand(1);
        Type *Ty = dest->getType()->getContainedType(0);
        uint64_t size = 0;
        if (Ty->isSized())
            size = DL->getTypeAllocSize(Ty);

        MemoryObj *&mo = node->getMemoryObj();
        mo = new MemoryObj(node, size);
        node->addPointsTo(mo);
    }

    return node;
}

static LLVMNode *getConstantExprNode(const llvm::ConstantExpr *CE,
                                     LLVMDependenceGraph *dg,
                                     const llvm::DataLayout *DL)
{
    // we have these nodes stored
    if (isa<IntToPtrInst>(CE))
        return getConstantIntToPtrNode(CE, DL);

    // FIXME add these nodes somewhere,
    // so that we can delete them later
    LLVMNode *node = new LLVMNode(CE);

    // set points-to sets
    Pointer ptr = getConstantExprPointer(CE, dg, DL);
    node->addPointsTo(ptr);

    return node;
}

/*
 * we have DependenceGraph::getNode() which retrives existing node.
 * The operand nodes may not exists, though.
 * This function gets the existing node, or creates new one and sets
 * it as an operand.
 */
LLVMNode *getOperand(LLVMNode *node, const llvm::Value *val,
                     unsigned int idx, const llvm::DataLayout *DL)
{
    // ok, before calling this we call llvm::Value::getOperand() to get val
    // and in node->getOperand() we call it too. It is small overhead, but just
    // to know where to optimize when going to extrems

    LLVMNode *op = node->getOperand(idx);
    if (op)
        return op;

    using namespace llvm;
    LLVMDependenceGraph *dg = node->getDG();

    if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(val)) {
        op = getConstantExprNode(CE, dg, DL);
    } else if (isa<Function>(val)) {
        // if the function was created via function pointer during
        // points-to analysis, the operand may not be not be set.
        // What is worse, the function may not be created either,
        // so the node just may not exists at all, so we need to
        // create it
        op = getOrCreateNode(dg, val);
    } else if (isa<ConstantPointerNull>(val)) {
        // what to do with nullptr?
        op = createNodeWithMemAlloc(val);
    } else {
        errs() << "ERR: Unsupported operand: " << *val << "\n";
        abort();
    }

    assert(op && "Did not set op");

    // set new operand
    node->setOperand(op, idx);

    return op;
}

} // namespace analysis
} // namespace dg

<commit_msg>analysis generic: split getOperand into two functions<commit_after>#include <map>

#include <llvm/IR/Value.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/Support/raw_ostream.h>

#include "AnalysisGeneric.h"
#include "LLVMDependenceGraph.h"
#include "PointsTo.h"

using namespace llvm;

namespace dg {
namespace analysis {

// we assume that if the program uses inttoptr, it access this
// memory only this way - so every access to this memory is done
// via some inttoptr. Here we store the inttoptr objects
static std::map<uint64_t, LLVMNode *> intToPtrMap;

// pointer points to unknown memory location
MemoryObj UnknownMemoryObject(nullptr);
// unknown pointer value
Pointer UnknownMemoryLocation(&UnknownMemoryObject, 0);

bool Pointer::isUnknown() const
{
    return this == &UnknownMemoryLocation;
}

bool Pointer::pointsToUnknown() const
{
    assert(obj && "Pointer has not any memory object set");
    return obj->isUnknown();
}

bool Pointer::isKnown() const
{
    return !isUnknown() && !pointsToUnknown();
}

bool MemoryObj::isUnknown() const
{
    return this == &UnknownMemoryObject;
}

static LLVMNode *createNodeWithMemAlloc(const Value *val)
{
    LLVMNode *n = new LLVMNode(val);
    MemoryObj *&mo = n->getMemoryObj();
    mo = new MemoryObj(n);
    n->addPointsTo(Pointer(mo));

    return n;
}

static LLVMNode *getOrCreateNode(LLVMDependenceGraph *dg, const Value *val)
{
    LLVMNode *n = dg->getNode(val);
    if (n)
        return n;

    if (llvm::isa<llvm::Function>(val)) {
        n = createNodeWithMemAlloc(val);
    } else
        errs() << "ERR: unhandled not to create " << *val << "\n";

    return n;
}

static Pointer handleConstantBitCast(LLVMDependenceGraph *dg, const BitCastInst *BC)
{
    if (!BC->isLosslessCast()) {
        errs() << "WARN: Not a loss less cast unhandled ConstExpr"
               << *BC << "\n";
        return UnknownMemoryLocation;
    }

    const Value *llvmOp = BC->stripPointerCasts();
    LLVMNode *op = getOrCreateNode(dg, llvmOp);
    if (!op) {
        errs() << "ERR: unsupported BitCast constant operand" << *BC << "\n";
    } else {
        PointsToSetT& ptset = op->getPointsTo();
        if (ptset.size() != 1) {
            errs() << "ERR: constant BitCast with not only one pointer "
                   << *BC << "\n";
        } else
            return *ptset.begin();
    }

    return UnknownMemoryLocation;
}

static inline unsigned getPointerBitwidth(const DataLayout *DL, const Value *ptr)
{
    const Type *Ty = ptr->getType();
    return DL->getPointerSizeInBits(Ty->getPointerAddressSpace());
}

static Pointer handleConstantGep(LLVMDependenceGraph *dg,
                                 const GetElementPtrInst *GEP,
                                 const llvm::DataLayout *DL)
{
    const Value *op = GEP->getPointerOperand();
    LLVMNode *opNode = dg->getNode(op);

    // FIXME this is sound, but may be unprecise
    //  - we should use getOperand for getting opNode,
    //  becaues we can have ConstantExpr inserted in ConstantExpr
    //  (getelementptr (inttoptr ..) ...), so we can get null here
    //  as opNode
    if (!opNode) {
        errs() << "No node for Constant GEP operand: " << *GEP << "\n";
        return UnknownMemoryLocation;
    }

    PointsToSetT& S = opNode->getPointsTo();
    // since this is constant expr, there's no way how we could
    // get extra points-to binding in runtime
    assert(S.size() == 1);
    MemoryObj *mo = (*S.begin()).obj;
    if (!mo) {
        errs() << "ERR: no memory object in " << *opNode->getKey() << "\n";
        return UnknownMemoryLocation;
    }

    Pointer pointer(mo, UNKNOWN_OFFSET);
    unsigned bitwidth = getPointerBitwidth(DL, op);
    APInt offset(bitwidth, 0);

    if (GEP->accumulateConstantOffset(*DL, offset)) {
        if (offset.isIntN(bitwidth))
            pointer.offset = offset.getZExtValue();
        else
            errs() << "WARN: Offset greater than "
                   << bitwidth << "-bit" << *GEP << "\n";
    }
    // else offset is set to UNKNOWN (in constructor)

    return pointer;
}

Pointer getConstantExprPointer(const ConstantExpr *CE,
                               LLVMDependenceGraph *dg,
                               const llvm::DataLayout *DL)
{
    Pointer pointer = UnknownMemoryLocation;

    const Instruction *Inst = const_cast<ConstantExpr*>(CE)->getAsInstruction();
    if (const GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Inst)) {
        pointer = handleConstantGep(dg, GEP, DL);
    } else if (const BitCastInst *BC = dyn_cast<BitCastInst>(Inst)) {
        pointer = handleConstantBitCast(dg, BC);
    } else {
            errs() << "ERR: Unsupported ConstantExpr " << *CE << "\n";
            errs() << "      ^^ returning unknown pointer\n";
    }

    delete Inst;
    return pointer;
}

static LLVMNode *getConstantIntToPtrNode(const ConstantExpr *CE,
                                         const llvm::DataLayout *DL)
{
    using namespace llvm;

    const Value *val = CE->getOperand(0);
    if (!isa<ConstantInt>(val)) {
        errs() << "Unhandled constant inttoptr " << *CE << "\n";
        abort();
    }

    const ConstantInt *C = cast<ConstantInt>(val);
    uint64_t value = C->getLimitedValue();

    LLVMNode *&node = intToPtrMap[value];
    if (!node) {
        node = new LLVMNode(C);
        const Value *dest = CE->getOperand(1);
        Type *Ty = dest->getType()->getContainedType(0);
        uint64_t size = 0;
        if (Ty->isSized())
            size = DL->getTypeAllocSize(Ty);

        MemoryObj *&mo = node->getMemoryObj();
        mo = new MemoryObj(node, size);
        node->addPointsTo(mo);
    }

    return node;
}

static LLVMNode *getConstantExprNode(const llvm::ConstantExpr *CE,
                                     LLVMDependenceGraph *dg,
                                     const llvm::DataLayout *DL)
{
    // we have these nodes stored
    if (isa<IntToPtrInst>(CE))
        return getConstantIntToPtrNode(CE, DL);

    // FIXME add these nodes somewhere,
    // so that we can delete them later
    LLVMNode *node = new LLVMNode(CE);

    // set points-to sets
    Pointer ptr = getConstantExprPointer(CE, dg, DL);
    node->addPointsTo(ptr);

    return node;
}

static LLVMNode *getUnknownNode(LLVMDependenceGraph *dg, const llvm::Value *val,
                                const llvm::DataLayout *DL)
{
    LLVMNode *node = nullptr;

    using namespace llvm;
    if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(val)) {
        node = getConstantExprNode(CE, dg, DL);
    } else if (isa<Function>(val)) {
        // if the function was created via function pointer during
        // points-to analysis, the operand may not be not be set.
        // What is worse, the function may not be created either,
        // so the node just may not exists at all, so we need to
        // create it
        node = getOrCreateNode(dg, val);
    } else if (isa<ConstantPointerNull>(val)) {
        // what to do with nullptr?
        node = createNodeWithMemAlloc(val);
    } else {
        errs() << "ERR: Unsupported operand: " << *val << "\n";
        abort();
    }

    assert(node && "Did not get a node");
    return node;
}

/*
 * we have DependenceGraph::getNode() which retrives existing node.
 * The operand nodes may not exists, though.
 * This function gets the existing node, or creates new one and sets
 * it as an operand.
 */
LLVMNode *getOperand(LLVMNode *node, const llvm::Value *val,
                     unsigned int idx, const llvm::DataLayout *DL)
{
    // ok, before calling this we call llvm::Value::getOperand() to get val
    // and in node->getOperand() we call it too. It is small overhead, but just
    // to know where to optimize when going to extrems

    LLVMNode *op = node->getOperand(idx);
    if (op)
        return op;

    LLVMDependenceGraph *dg = node->getDG();

    // set new operand
    op = getUnknownNode(dg, val, DL);
    assert(op && "Did not get op");

    node->setOperand(op, idx);
    return op;
}

} // namespace analysis
} // namespace dg

<|endoftext|>